fix: proxy-headers HTTPS redirect, httpx manquant, mcp<2.0.0, migration SSE->StreamableHTTP (30/07/2026)

This commit is contained in:
2026-07-30 21:41:19 +00:00
parent a29623d782
commit 41ea52ca2d
4 changed files with 34 additions and 27 deletions
+23 -21
View File
@@ -4,7 +4,10 @@ import contextvars
from fastapi import APIRouter, Depends, Request, HTTPException
from fastapi.responses import Response, JSONResponse
from mcp.server import Server
from mcp.server.sse import SseServerTransport
try:
from mcp.server.streamable_http_manager import StreamableHTTPSessionManager
except ImportError:
from mcp.server.streamable_http import StreamableHTTPSessionManager
from mcp.types import Tool, TextContent
from app.auth import get_current_agent
from app.scopes import has_scope_access, ALL_SCOPES
@@ -223,16 +226,20 @@ async def handle_call_tool(name: str, arguments: dict) -> list[TextContent]:
return [TextContent(type="text", text=json.dumps({"error": "Unknown tool"}))]
# FastMCP / SSE Integration
# connect_sse() ET handle_post_message() envoient chacun leur reponse ASGI
# completement par eux-memes (via le send() qu'on leur passe). Les faire
# passer par des routes FastAPI classiques (Depends + return Response) fait
# que FastAPI tente un second envoi une fois le SDK termine -> RuntimeError
# uvicorn ("Unexpected ASGI message 'http.response.start' sent, after response
# already completed"). Un seul montage ASGI brut gere GET (ouverture SSE) et
# POST (messages) sans jamais repasser par le wrapping FastAPI ; l'auth
# X-API-Key est donc verifiee ici a la main plutot que via Depends.
sse_transport = SseServerTransport("/messages")
# Streamable HTTP Integration (mcp SDK v1.x, transport recommande depuis la spec 2025-03-26).
# Remplace l ancien transport SSE (SseServerTransport) qui exigeait un session_id
# etabli via un GET prealable -- les clients MCP modernes (Gemini/Antigravity inclus)
# postent directement le JSON-RPC initialize sans cette poignee de main, d ou les 400
# Bad Request observes avec l ancienne implementation.
# L auth X-API-Key reste verifiee a la main avant de deleguer a session_manager.handle_request,
# meme principe qu avant : current_agent_var est positionne pour la duree de la requete,
# lu ensuite par les handlers call_tool/list_tools via caller_agent.
session_manager = StreamableHTTPSessionManager(
app=mcp_server,
event_store=None,
json_response=True,
stateless=True,
)
async def _mcp_asgi_app(scope, receive, send):
if scope["type"] != "http":
@@ -247,15 +254,10 @@ async def _mcp_asgi_app(scope, receive, send):
return
await update_key_last_used(api_key)
if scope["method"] == "POST":
await sse_transport.handle_post_message(scope, receive, send)
return
async with sse_transport.connect_sse(scope, receive, send) as (read_stream, write_stream):
token = current_agent_var.set(agent)
try:
await mcp_server.run(read_stream, write_stream, mcp_server.create_initialization_options())
finally:
current_agent_var.reset(token)
token = current_agent_var.set(agent)
try:
await session_manager.handle_request(scope, receive, send)
finally:
current_agent_var.reset(token)
router.mount("/mcp", _mcp_asgi_app)