Marking Routes¶
Routes are exposed to MCP by attaching metadata to their opt dictionary.
The plugin scans every handler at startup and treats any route tagged with
mcp_tool or mcp_resource as part of the MCP surface.
Tool Marker¶
Tools are executable operations - anything that takes arguments and returns
structured output. Tag a handler with mcp_tool="<tool_name>" and the
plugin publishes it via tools/list and tools/call.
docs/examples/snippets/marking_tools.py¶@get("/users/{user_id:int}", mcp_tool="get_user")
async def get_user(user_id: int) -> dict[str, int]:
"""Return a user by ID - exposed as the ``get_user`` MCP tool."""
return {"id": user_id}
app = Litestar(route_handlers=[get_user], plugins=[LitestarMCP()])
Resource Marker¶
Resources are read-only payloads such as schemas, capability summaries, or
cached projections. Tag a handler with mcp_resource="<resource_name>" to
expose it via resources/list and resources/read.
docs/examples/snippets/marking_resources.py¶@get("/api/schema", mcp_resource="api_schema")
async def get_schema() -> dict[str, Any]:
"""Return the API JSON schema - exposed as the ``api_schema`` MCP resource."""
return {"type": "object", "properties": {}}
app = Litestar(route_handlers=[get_schema], plugins=[LitestarMCP()])
Dependency Injection¶
Marked routes participate in Litestar's dependency injection system exactly
like any other handler. Provide dependencies via dependencies={...} and
declare them in the signature; the plugin resolves them for each tools/call.
docs/examples/snippets/marking_dependencies.py¶@get("/me", mcp_tool="whoami", dependencies={"current_user": Provide(provide_current_user)})
async def whoami(current_user: "dict[str, Any]") -> "dict[str, Any]":
"""Return the resolved current user - exposed as the ``whoami`` MCP tool."""
return current_user
app = Litestar(route_handlers=[whoami], plugins=[LitestarMCP()])
Decorator Variant¶
If you prefer a dedicated decorator over the kwargs form, litestar_mcp
ships mcp_tool() and
mcp_resource(). Both carry the same metadata
as the opt kwargs and are interchangeable at discovery time.
Prefer this when you are already passing route options inline:
@get("/users/{user_id:int}", mcp_tool="get_user")
async def get_user(user_id: int) -> dict[str, int]:
"""Return a user by ID - exposed as the ``get_user`` MCP tool."""
return {"id": user_id}
app = Litestar(route_handlers=[get_user], plugins=[LitestarMCP()])
Use the explicit decorator when composing marked routes across modules or when you want an import-time marker:
docs/examples/snippets/marking_decorator.py¶@mcp_tool(name="get_user")
@get("/users/{user_id:int}")
async def get_user(user_id: int) -> dict[str, int]:
"""Return a user by ID - exposed via the ``mcp_tool`` decorator."""
return {"id": user_id}
app = Litestar(route_handlers=[get_user], plugins=[LitestarMCP()])
Note
Both forms end up at the same registry entry. Pick one per project for consistency; mixing is supported but harder to audit.