MCP
Status: Draft; implementation checked against the development source on 2026-09-08.
What it does
Exposes your @route methods as tools an AI agent can call over the Model
Context Protocol (MCP), served as JSON-RPC over Streamable HTTP. Because
genro-routes is protocol-neutral, the same route tree can answer REST and MCP at
once.
When to use it
When you want an AI agent to invoke your application’s operations as tools — either as a pure MCP endpoint or side-by-side with a REST/OpenAPI face.
Setup
Two base classes cover the two shapes:
McpApplication— the whole application is a single JSON-RPC MCP endpoint.from genro_asgi import McpApplication app = McpApplication(routing_class=..., code="mcp")
McpOpenApiApplication— a dual-faced application: a REST/OpenAPI face and an MCP face served on/mcp. The MCP segment is configurable viamcp_name_segment="mcp".from genro_asgi import McpOpenApiApplication # subclass of OpenApiApplication: REST + OpenAPI + MCP on /mcp
Both are importable from genro_asgi.
Marking a route as a tool
Expose a method as an MCP tool with channel_channels:
from genro_asgi import AsgiServer, McpOpenApiApplication
from genro_routes import route
class Calc(McpOpenApiApplication):
mount = ""
openapi_info = {"title": "Calc", "version": "1.0.0"}
@route(channel_channels="mcp")
def add(self, a: int = 0, b: int = 0) -> dict:
return {"result": a + b}
@route(channel_channels="mcp,rest")
def mul(self, a: int = 0, b: int = 0) -> dict:
return {"result": a * b}
if __name__ == "__main__":
AsgiServer(applications=[Calc()]).serve(host="127.0.0.1", port=8000)
Save this as calc.py, run python calc.py, and stop it with Ctrl-C after
the checks below.
channel_channels="mcp"includes the method in the MCP tool surface.channel_channels="mcp,rest"declares both channel names for consumers that filter by channel.
HTTP dispatch does not apply this channel filter. Either declaration leaves the route callable directly over HTTP; channel metadata is not an HTTP access restriction. Use authorization rules to restrict callers.
AsgiServer automatically arms the pydantic and openapi plugins used by
this example. Explicit entries are needed only to configure their options.
The /mcp endpoint
The MCP face speaks JSON-RPC on /mcp, protocol version 2025-11-25:
initializetools/listtools/callping
Parameter handling for tools/call is the same as for REST: the arguments bind
to the method signature, typed and with defaults.
A GET on /mcp opens an SSE push stream (see streaming). It
returns 405 when tasks are explicitly disabled (tasks=False) or the server
has no task capability. Tasks are enabled by default on AsgiServer.
How to verify it
List the tools with a JSON-RPC call:
$ curl -X POST http://127.0.0.1:8000/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Call one:
curl -X POST http://127.0.0.1:8000/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call",
"params":{"name":"add","arguments":{"a":2,"b":3}}}'
Gotchas
Only routes carrying
channel_channelsthat includes"mcp"appear as tools. A plain@route()is not offered as an MCP tool. Channel metadata does not prevent HTTP access; authorization rules govern access restrictions.GET /mcp(the SSE push stream) is405unless the server has tasks armed — see the tasks guide.The MCP JSON-RPC lives on
/mcp(segment configurable viamcp_name_segment), separate from the OpenAPI_metaprefix.McpApplication,McpOpenApiApplication,McpEngineandMcpErrorare importable fromgenro_asgi.