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()])

Note

Both forms end up at the same registry entry. Pick one per project for consistency; mixing is supported but harder to audit.