Core Architectural Concepts¶
litestar-mcp is architected as a native Litestar plugin rather than a sidecar server. Every MCP primitive compiles from your Litestar route handlers and controllers, and every execution request flows through Litestar's internal ASGI pipeline.
1. Internal ASGI Dispatch Pipeline¶
When an MCP client invokes tools/call or prompts/get on /mcp (or over litestar mcp stdio), MCPHandlerService constructs a synthetic ASGI scope and dispatches it directly through the host Litestar application:
This design provides four structural guarantees:
- Single Security Boundary: Authentication middleware,
MCPConfig.guards, controller guards, and handler guards execute on MCP invocations identically to HTTP requests. - Zero Loopback Overhead: Dispatch happens in-memory via the ASGI callable without opening a loopback TCP socket or serializing headers over the network.
- Full Lifecycle Fidelity:
before_request,after_request,after_response, exception handlers, and OpenTelemetry spans run around every tool and prompt call. - Header & Identity Forwarding: Authorization headers,
request.user,request.auth,litestar-securitySecurityContext, and trace contexts propagate automatically from/mcpinto the target handler scope.
2. Plugin Registration & Ordering¶
Register LitestarMCP in your application's plugins list:
"""Snippet: framework-level Litestar registration. Referenced from docs/usage/framework.rst."""
from litestar import Litestar
from litestar_mcp import LitestarMCP
def build() -> "Litestar":
# start-example
app = Litestar(plugins=[LitestarMCP()])
# end-example
return app
Register LitestarMCP after route-mutating plugins
LitestarMCP discovers marked routes during on_app_init. If your application uses plugins that wrap or mutate route handlers (such as Dishka's setup_dishka, serialization plugins, or security plugins), register LitestarMCP after them (or call setup_dishka(container, app) on the constructed app) so their metadata is visible when the MCP Registry compiles schemas.
3. Universal Dependency Injection¶
litestar-mcp supports five dependency injection mechanisms across route handlers, PromptController, SkillController, and @mcp.tool() functions. Internal DI parameters are automatically stripped from the public MCP JSON Schema so LLM clients only see user-supplied arguments:
| DI Pattern | Where Used | Schema Visibility | Runtime Resolution |
|---|---|---|---|
Litestar Provide(...) |
dependencies={"svc": Provide(fn)} on app, router, controller, or route |
Stripped if return type is an internal service; included if provider takes user query/body arguments | Resolved by Litestar's native dependency graph per request |
Dishka FromDishka[T] |
Route handlers, PromptController, SkillController, background tasks |
Always stripped from public MCP tool/prompt JSON Schema | Resolved from the active Dishka Scope.REQUEST container (including rehydrated worker scopes in QueueTaskExecutor and DurableWorkflowEngine) |
Inject() Marker |
svc: Annotated[MyService, Inject()] or svc: MyService = Inject() on PromptController / SkillController |
Always stripped from public MCP schema | Resolved from Litestar dependencies or the Dishka request container |
Depends(fn) Marker |
svc: Annotated[MyService, Depends(build_svc)] on PromptController / SkillController |
Always stripped from public MCP schema | Calls fn (sync or async, accepting Request if annotated) per invocation |
ToolContext |
ctx: ToolContext parameter on any MCP tool handler |
Always stripped from public MCP tool JSON Schema | Injected by MCPHandlerService and MCPTaskExecutor; provides ctx.report_progress(...), ctx.request_input(...), ctx.task_id, and ctx.request |
4. OpenAPI Integration¶
- Marked Routes in OpenAPI: Routes marked with
mcp_tool,mcp_resource, ormcp_promptremain normal Litestar handlers and appear in your application's OpenAPI documentation unless you setinclude_in_schema=Falseon the route decorator. - The
/mcpTransport Route: The/mcpJSON-RPC endpoint itself is excluded from OpenAPI by default (MCPConfig.include_in_schema=False). SetMCPConfig(include_in_schema=True)if you want/mcplisted in your OpenAPI specification. - Built-in
litestar://openapiResource:LitestarMCPautomatically registers thelitestar://openapiresource URI, allowing MCP clients to read your application's live OpenAPI schema viaresources/read.
5. Tag and Operation Filtering¶
Use MCPConfig filtering options to expose a subset of marked routes on /mcp without modifying individual route decorators:
MCPConfig Field |
Type | Behavior |
|---|---|---|
include_tags |
set[str] | None |
Only expose marked handlers that carry at least one matching Litestar tags=[...] entry |
exclude_tags |
set[str] | None |
Hide any marked handler carrying a matching tag (takes precedence over include_tags) |
include_operations |
set[str] | None |
Only expose marked handlers whose MCP tool/resource name or operation_id is in this set |
exclude_operations |
set[str] | None |
Hide any marked handler whose MCP name or operation_id is in this set (takes precedence over include_operations) |
Filters apply consistently across discovery (tools/list, resources/list, resources/templates/list) and direct invocation (tools/call, resources/read).
Filtering is not access control
Tag and operation filters shape the public MCP catalog at startup. For per-user or per-tenant authorization, use Litestar authentication middleware, MCPConfig(guards=[...]), or first-party litestar-security route options (MCPConfig(route_opt={"auth": required(...)})).