Skip to content

MCP server#

Spider-Gazelle can expose your API to AI agents as a Model Context Protocol (MCP) server. Claude, VS Code, Cursor and other MCP clients can then discover and call your routes as tools, and offer your prompts to users. There are no tool definitions to write: they're generated from the same annotations, types and doc comments that drive routing and OpenAPI.

claude mcp add --transport http my-app http://localhost:3000/mcp

Why it's built in#

Your API is already the tool definition

Hand-written tool definitions duplicate your API, and they drift: a renamed parameter or a changed type quietly breaks the agent. In Spider-Gazelle the tool is the route:

  • the description is the method's doc comment;
  • the input schema is generated from the argument types, @[AC::Param::Info] and the request body type;
  • a call runs the real route in-process, with the same parameter parsing, filters (authentication!), error handlers and responders as an HTTP request.

Improve a doc comment and your OpenAPI docs and agent tools both get better. Fix a validation bug and it's fixed for agents too.

How agents see your API#

Large APIs have hundreds of routes, and listing them all would fill the model's context before it does any work. Spider-Gazelle uses progressive disclosure instead:

  • Toolboxes: each controller is a toolbox, described by the controller's doc comment.
  • Listed tools: the tool list has just four tools, plus any root items:
Tool Purpose
list_toolboxes lists the toolboxes, their descriptions, and tool and prompt counts
open_toolbox(name) returns the definitions of a toolbox's tools, and the names of its prompts
call_read_only(name, arguments) runs a read only tool from a toolbox
call_tool(name, arguments) runs any tool from a toolbox. Tools marked visibility: :card are refused by both proxies
  • Loading on demand: the model opens only the toolboxes it needs. open_toolbox returns each tool's name, description, input schema and the proxy to run it with: call_read_only for read only tools, which clients can run without asking for confirmation, and call_tool for the rest. Proxied calls behave exactly like direct ones, and clients can also call any tool directly.
  • Root items: anything marked root: true is always listed. Use it for the few tools and prompts most conversations need.
  • Prompts: every prompt is listed. Users pick prompts, so they don't use the model's context.
  • Stateless: opening a toolbox doesn't change anything on the server, so the tool list is the same for every request. There are no MCP sessions, and any instance of your app can serve any request.

To list every tool instead of using toolboxes, set tool_proxy = false.

# Manage meeting rooms and their bookings
class Rooms < AC::Base
  base "/rooms"

  # Lists the rooms in a building
  @[AC::Route::GET("/")]
  def index(building_id : String) : Array(Room)
    Room.where(building_id: building_id).to_a
  end
end

Here the toolbox is rooms ("Manage meeting rooms and their bookings"), and opening it returns the rooms_index tool ("Lists the rooms in a building").

Naming#

  • Toolbox names: the snake case controller name. The module namespace shared by every controller is left out, so MyApp::Api::Rooms and MyApp::Api::Rooms::Bookings become rooms and rooms_bookings.
  • Tool and prompt names: <toolbox>_<method>, such as rooms_index.
  • Multiple routes: a method with several route annotations is one tool. It uses the method's first GET route, otherwise its first route in verb order (POST, PUT, PATCH, DELETE).

What's exposed#

Exposed?
Annotated routes (@[AC::Route::GET] etc.) yes, as tools
Methods marked @[AC::MCP(prompt: true)] yes, as prompts
GET routes marked @[AC::MCP(resource: "app://...")] yes, as resource templates as well as tools
Routes marked @[AC::MCP(hide: true)] no
WebSocket and OPTIONS routes no
Macro DSL routes (get "/" do) no

Calls run as the user#

Tool calls forward the client's Authorization, Cookie and X-API-Key headers to the route, so your existing authentication applies to every call. For OAuth sign-in from MCP clients, see Authentication.

In this section#

  • Setup: mounting the server, generating mcp.yml, connecting clients and testing.
  • Annotation options: prompts, root, hide, behaviour, visibility, titles and icons.
  • Controller endpoints: serving a controller as its own MCP server, such as one per room or account.
  • UI cards: interactive HTML cards for tool results (MCP Apps).
  • Resources and subscriptions: resource templates, notifying clients of changes, and long running routes that stream progress.
  • Authentication: API keys, and OAuth sign-in with multi_auth and authly.
  • Configuration: every option, and transport details.