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, mcp_resource, or mcp_prompt as part of the MCP surface.
Standalone prompt callables that are not routed under HTTP are registered
separately via LitestarMCP(prompts=[...]) — see
Prompt Marker below.
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()])
Prompt Marker¶
Prompts are templated instructions for a model. The plugin recognises two registration paths.
Handler-based prompts. Tag a Litestar route handler with
mcp_prompt="<prompt_name>". The handler stays a normal HTTP endpoint
and is also reachable through prompts/get. Override the description,
title, argument list, or icons through dedicated opt keys:
Opt key |
Effect |
|---|---|
|
Required. The prompt name used in |
|
LLM-facing description (falls back to the handler's docstring). |
|
Optional human-readable title for UI clients. |
|
Explicit argument list (a |
|
Optional list of MCP icon objects ( |
Standalone prompt functions. Decorate a plain callable with
mcp_prompt() and pass it to
LitestarMCP(prompts=[...]). These never appear in the HTTP route
table — they are MCP-only.
docs/examples/snippets/marking_prompts.py¶@mcp_prompt(name="summarize", description="Summarise a document in a chosen style.")
async def summarize(text: str, style: str = "concise") -> str:
"""Build a summarisation prompt.
Args:
text: The document to summarise.
style: Summary style, e.g. ``concise`` or ``detailed``.
"""
return f"Summarise the following in a {style} style:\n\n{text}"
@get(
"/prompts/code-review",
mcp_prompt="code_review",
mcp_prompt_description="Ask the model to review a code diff.",
)
async def code_review(code: str) -> dict[str, object]:
"""Handler-based prompt: routed under HTTP *and* exposed via ``prompts/get``."""
return {"messages": [{"role": "user", "content": {"type": "text", "text": f"Review: {code}"}}]}
app = Litestar(
route_handlers=[code_review],
plugins=[LitestarMCP(prompts=[summarize])],
)
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(), mcp_resource(),
and mcp_prompt(). The tool and resource decorators
carry the same metadata as the opt kwargs and are interchangeable at
discovery time on a route handler.
mcp_prompt is different: the decorator and the mcp_prompt_* opt
keys target different registration paths. Use the opt keys to expose a
Litestar route handler as a prompt. Use the decorator to mark a plain
callable that you hand to LitestarMCP(prompts=[...]). The decorator
does not act as the route-handler marker.
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.