Authentication#
Authentication for the MCP endpoint is a Litestar middleware concern.
Whatever authentication middleware the application installs (Litestar's
JWT backends, a Google IAP validator, an API-key check) runs before any
route handler, so MCP tool handlers inherit request.user and
request.auth exactly as HTTP handlers do. litestar-mcp ships no token
validator, JWKS cache, or discovery document of its own.
Authentication only establishes caller identity. See Security for object-level authorization patterns, transport identity boundaries, and safe file/path argument guidance.
Bring Your Own Middleware#
Subclass
AbstractAuthenticationMiddleware
(or register one of Litestar's built-in JWT backends) and install it on the
app. The middleware populates request.user / request.auth before the
MCP JSON-RPC handler dispatches a tool, so the plugin needs no
authentication configuration of its own:
docs/examples/snippets/auth_middleware.py#class ApiKeyAuthenticationMiddleware(AbstractAuthenticationMiddleware):
"""Populate ``request.user`` / ``request.auth`` from an ``X-API-Key`` header."""
async def authenticate_request(self, connection: "ASGIConnection[Any, Any, Any, Any]") -> "AuthenticationResult":
claims = API_KEYS.get(connection.headers.get("X-API-Key", ""))
if claims is None:
msg = "Invalid API key"
raise NotAuthorizedException(msg)
return AuthenticationResult(user=claims["sub"], auth=claims)
@get("/whoami", mcp_tool="whoami", sync_to_thread=False)
def whoami(request: "Any") -> "dict[str, Any]":
return {"user": request.user, "roles": request.auth["roles"]}
app = Litestar(
route_handlers=[whoami],
middleware=[DefineMiddleware(ApiKeyAuthenticationMiddleware)],
plugins=[LitestarMCP()],
)
Litestar's OAuth2PasswordBearerAuth works the
same way: pass the backend's on_app_init hook to
Litestar and the MCP routes are guarded like every
other route. docs/examples/notes/sqlspec/jwt_auth.py is a complete JWT
example.
litestar-security#
Applications that standardise on litestar-security keep the same shape:
install the app's SecurityPlugin, then name the mechanism that guards
MCP by passing an opt-based policy through
MCPConfig(route_opt={"auth": required("api-key")}) (required is
exported from litestar_security). litestar-mcp registers its routes and
forwards the opt mapping; it builds no verifier. Guards and dependencies on
tool handlers read connection.user (a Principal) and
connection.auth (a SecurityContext) as they would on any other
route.
MCP clients discover the authorization server through the
WWW-Authenticate: Bearer resource_metadata=... challenge. litestar-security
emits that header only when the mechanism declares
security_scheme=SecurityScheme(type="http", scheme="bearer") and only
for evaluator failures, not for guard failures. Publish the RFC 9728
protected-resource document with SecurityConfig.protected_resource
(a ProtectedResourceConfig). JWKS caching, service tokens, and Google IAP
support are configured on litestar-security, not on the MCP plugin.
Identity Proxies#
Identity proxies such as Google IAP verify the caller and forward a signed
assertion in a custom header (x-goog-iap-jwt-assertion for IAP). Validate
that header in your own middleware, as
docs/examples/notes/sqlspec/google_iap.py does with a plain
AbstractAuthenticationMiddleware subclass, or configure
litestar-security's IAP support.
Mapping Claims to Users#
Read
request.userdirectly in the handler signature.Write a
Provide(...)dependency that extracts the identity fromrequest.userand returns a domain type.Enforce scopes or roles via guards that inspect
request.auth.
Stdio Transports#
In-process stdio (litestar mcp stdio and MCP.run(transport="stdio"))
dispatches every frame through the same ASGI app, so the same middleware
runs and tool handlers see the same request.user / request.auth.
Standalone apps that run without an authentication middleware seed the
caller identity with MCPStdioContext instead; see Standalone Application.