From 4d7988b6ac0a6ef6d1fcbe7958a47232809e1d1b Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Wed, 8 Apr 2026 10:45:20 +0000 Subject: [PATCH] fix: replace BaseHTTPMiddleware with pure ASGI to fix SSE streaming BaseHTTPMiddleware buffers response bodies, which prevents SSE events from streaming to the client in real time. This caused MCP initialization to never complete, resulting in -32602 errors on all tool calls. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/claude_memory/api/app.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/claude_memory/api/app.py b/src/claude_memory/api/app.py index cd8140a..c912dd2 100644 --- a/src/claude_memory/api/app.py +++ b/src/claude_memory/api/app.py @@ -10,13 +10,13 @@ from contextvars import ContextVar from datetime import datetime, timezone from typing import Any, AsyncGenerator, Optional -from fastapi import Depends, FastAPI, HTTPException, Request +from fastapi import Depends, FastAPI, HTTPException from fastapi.responses import Response from fastapi.staticfiles import StaticFiles from mcp.server.fastmcp import FastMCP from mcp.server.sse import SseServerTransport -from starlette.middleware.base import BaseHTTPMiddleware from starlette.routing import Mount, Route +from starlette.types import ASGIApp, Receive, Scope, Send from claude_memory.api.auth import AuthUser, get_current_user, _key_to_user from claude_memory.api.database import close_pool, get_pool, init_pool @@ -1253,16 +1253,22 @@ async def memory_update(id: int, content: str | None = None, tags: str | None = return json.dumps({"updated": id}) -# Auth middleware for /mcp/* routes -class MCPAuthMiddleware(BaseHTTPMiddleware): - async def dispatch(self, request: Request, call_next: Any) -> Response: - if request.url.path.startswith("/mcp"): - auth = request.headers.get("authorization", "") +# Auth middleware for /mcp/* routes — pure ASGI to avoid BaseHTTPMiddleware +# buffering which breaks SSE streaming (responses never reach the client). +class MCPAuthMiddleware: + def __init__(self, app: ASGIApp) -> None: + self.app = app + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + if scope["type"] == "http" and scope["path"].startswith("/mcp"): + headers = dict(scope.get("headers", [])) + auth = headers.get(b"authorization", b"").decode() token = auth.removeprefix("Bearer ").strip() if not _resolve_user_from_token(token): - return Response(content="Unauthorized", status_code=401) - response: Response = await call_next(request) - return response + response = Response(content="Unauthorized", status_code=401) + await response(scope, receive, send) + return + await self.app(scope, receive, send) app.add_middleware(MCPAuthMiddleware)