Fix MCP server error -32602: Authorization header lost in background tasks#172
Fix MCP server error -32602: Authorization header lost in background tasks#172Copilot wants to merge 3 commits into
Conversation
…602) - Add MCPAuthHeaderMiddleware (pure-ASGI) that captures the Authorization header in a ContextVar before FastMCP spawns background tasks - Update UserAuthMiddleware to read from ContextVar first, with fallback to Starlette request context - Add nc is not None guard in ToolListMiddleware to avoid crashes on messages like initialize before auth completes - Register MCPAuthHeaderMiddleware on APP in main.py Agent-Logs-Url: https://github.com/nextcloud/context_agent/sessions/667c0979-fc0f-4775-bad9-aeef8c975517 Co-authored-by: R0Wi <19730957+R0Wi@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR addresses MCP JSON-RPC failures (-32602) caused by FastMCP processing stateless streamable_http messages in background asyncio tasks where Starlette’s request context is no longer available, which previously broke UserAuthMiddleware when reading the Authorization header.
Changes:
- Introduces an ASGI middleware (
MCPAuthHeaderMiddleware) plus aContextVarto snapshot and propagate theAuthorizationheader into FastMCP background tasks. - Updates
UserAuthMiddlewareto prefer theContextVarvalue and fall back to the live request context when available. - Prevents
ToolListMiddlewarefrom crashing on unauthenticated / pre-auth message flows by guarding tool refresh onnextcloudstate being present.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
ex_app/lib/mcp_server.py |
Adds ContextVar-backed auth header propagation middleware; updates auth and tool-list middleware to avoid request-context and pre-auth crashes. |
ex_app/lib/main.py |
Registers MCPAuthHeaderMiddleware on the outer FastAPI app so the header snapshot is available before FastMCP background tasks run. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| from ex_app.lib.tools import get_tools | ||
| import requests | ||
|
|
||
| logger = logging.getLogger(__name__) |
| except Exception: | ||
| pass |
| if not authorization_header: | ||
| raise Exception("Authorization header is missing/invalid") | ||
|
|
||
| nc = AsyncNextcloudApp() | ||
| user = get_user(authorization_header, nc) |
|
@R0Wi are you planning to get this PR into a more refined state at some point? It would be nice, but right now it is a bit too raw for us to review this. Please have a look at our newly released AI policy for all contributions within Nextcloud: https://github.com/nextcloud/.github/blob/master/AI_POLICY.md |
Signed-off-by: Marcel Klehr <mklehr@gmx.net>
|
Superseded by #174 |
FastMCP's
streamable_httpstateless transport processes JSON-RPC messages (e.g.initialize) in background asyncio tasks where Starlette's request context is unavailable.UserAuthMiddlewarecrashed trying to readAuthorizationfrom the torn-down request context, surfacing as MCP error -32602.Changes
ex_app/lib/mcp_server.py_mcp_auth_header: ContextVar[str | None]— asyncio copies the context snapshot when spawning tasks, so a value set here before task creation survives inside the task after the request context is goneMCPAuthHeaderMiddleware(pure-ASGI) — capturesAuthorizationfrom every HTTP request into_mcp_auth_headerbefore FastMCP takes ownershipUserAuthMiddleware.on_message()— reads from_mcp_auth_headerfirst; falls back to live Starlette request context for forward-compatibilityToolListMiddleware.on_message()withnc is not None— prevents a secondary crash on pre-auth messages likeinitializewherenchasn't been set yetex_app/lib/main.pyMCPAuthHeaderMiddlewareand register it onAPPimmediately afterAppAPIAuthMiddlewareThe ContextVar propagation relies on the fact that
asyncio.create_task()takes a copy of the current context at task-creation time, so_mcp_auth_headeris readable inside any background task spawned during request handling — even after Starlette tears down its ownrequestcontext object.Original prompt
Fix: MCP server fails with "Context is not available outside of a request" (-32602)
Problem
When connecting to the MCP server via MCP Inspector or other MCP clients, requests fail with
MCP error -32602: Invalid request parameters. The docker logs show:There are two root causes:
Wrong Authorization header format in docs: The docs say to use
Authorization: Bearer <app-password>, but the Nextcloud App API proxy interceptsBearertokens and returns an HTML login page. The correct format isAuthorization: Basic base64(username:app-password).FastMCP background task context loss: The
streamable_httptransport in stateless mode processes MCP JSON-RPC messages (likeinitialize) in a background task, outside the original FastAPI request lifecycle.UserAuthMiddlewaretries to read theAuthorizationheader fromcontext.fastmcp_context.request_context.request.headers, but Starlette's request context is not propagated into background tasks, causing the crash.Fix
ex_app/lib/mcp_server.py— replace the entire file with: