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_toolboxreturns each tool's name, description, input schema and the proxy to run it with:call_read_onlyfor read only tools, which clients can run without asking for confirmation, andcall_toolfor the rest. Proxied calls behave exactly like direct ones, and clients can also call any tool directly. - Root items: anything marked
root: trueis 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::RoomsandMyApp::Api::Rooms::Bookingsbecomeroomsandrooms_bookings. - Tool and prompt names:
<toolbox>_<method>, such asrooms_index. - Multiple routes: a method with several route annotations is one tool. It uses
the method's first
GETroute, 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.