Skip to content

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:

Stateless Streamable HTTP and Internal ASGI Dispatch Pipeline JSON-RPC requests arrive at MCPController (/mcp), negotiate 2026-07-28 or 2025-06-18 via MCPHandlerService, and execute tools and prompts through Litestar internal ASGI dispatch with middleware, guards, DI, and OpenTelemetry spans. MCP Client POST / DELETE /mcp 2026-07-28: server/discover 2025-06-18: initialize & ping SEP-2575 Capability Headers JSON or text/event-stream MCPController MCPHandlerService Router Guards & route_opt Origin & Protocol Validation 202 Accepted (notifications) 204 No Content (DELETE /mcp) Internal ASGI Dispatch execute_tool / prompt Forwards Auth & Trace Headers Preserves request.user & auth Binds ToolContext in scope Zero TCP Loopback Hop Litestar Handler @get / @post / @tool Middleware & Route Guards Provide & FromDishka[T] DTOs & msgspec Validation before/after_request Hooks Cross-Cutting Observability & Registry Layer Registry (Tools • Resources • Prompts • Skills) | TelemetryConfig OpenTelemetry Spans (mcp.request • mcp.tool.call)
In-Process Stdio and HTTP Bridge Pipeline Desktop MCP clients communicate over newline-delimited JSON on stdin/stdout using either in-process Litestar.lifespan() execution with MCPStdioContext or the stateless HTTP relay bridge. Desktop Host stdin / stdout NDJSON Claude Desktop / Cursor / IDE litestar mcp stdio litestar mcp bridge mcp.run(transport="stdio") Stdio Lifecycle Adapter run_stdio_async() Enters Litestar.lifespan() Injects MCPStdioContext user • auth • session • state Bounded shutdown_timeout=5.0s Synthetic ASGI Scope MCPHandlerService In-Memory ASGI Dispatch Or Remote HTTP Relay (--url) Owner ID: user:<subject> SSE Progress Notification Loop Application DI Full Litestar Pipeline DB Pools & Stores Active Dishka Scope.REQUEST Route Guards Enforced Identical Tool Semantics Two CLI Modes: Local In-Process vs Remote HTTP Relay litestar --app app:app mcp stdio (in-process ASGI) | litestar mcp bridge --url https://api.example.com/mcp (remote HTTP)

This design provides four structural guarantees:

  1. Single Security Boundary: Authentication middleware, MCPConfig.guards, controller guards, and handler guards execute on MCP invocations identically to HTTP requests.
  2. Zero Loopback Overhead: Dispatch happens in-memory via the ASGI callable without opening a loopback TCP socket or serializing headers over the network.
  3. Full Lifecycle Fidelity: before_request, after_request, after_response, exception handlers, and OpenTelemetry spans run around every tool and prompt call.
  4. Header & Identity Forwarding: Authorization headers, request.user, request.auth, litestar-security SecurityContext, and trace contexts propagate automatically from /mcp into the target handler scope.

2. Plugin Registration & Ordering

Register LitestarMCP in your application's plugins list:

docs/examples/snippets/framework_litestar.py
"""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, or mcp_prompt remain normal Litestar handlers and appear in your application's OpenAPI documentation unless you set include_in_schema=False on the route decorator.
  • The /mcp Transport Route: The /mcp JSON-RPC endpoint itself is excluded from OpenAPI by default (MCPConfig.include_in_schema=False). Set MCPConfig(include_in_schema=True) if you want /mcp listed in your OpenAPI specification.
  • Built-in litestar://openapi Resource: LitestarMCP automatically registers the litestar://openapi resource URI, allowing MCP clients to read your application's live OpenAPI schema via resources/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(...)})).