fix: proxy-headers HTTPS redirect, httpx manquant, mcp<2.0.0, migration SSE->StreamableHTTP (30/07/2026)
This commit is contained in:
+1
-1
@@ -20,4 +20,4 @@ EXPOSE 8000
|
|||||||
RUN mkdir -p /app/data
|
RUN mkdir -p /app/data
|
||||||
|
|
||||||
# Run seed data on startup, then start uvicorn
|
# Run seed data on startup, then start uvicorn
|
||||||
CMD ["sh", "-c", "python -m app.seed && uvicorn app.main:app --host 0.0.0.0 --port 8000"]
|
CMD ["sh", "-c", "python -m app.seed && uvicorn app.main:app --host 0.0.0.0 --port 8000 --proxy-headers --forwarded-allow-ips=*"]
|
||||||
|
|||||||
+8
-4
@@ -1,11 +1,18 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
import contextlib
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from app.routes import api, admin, mcp
|
from app.routes import api, admin, mcp
|
||||||
from app.models import init_db
|
from app.models import init_db
|
||||||
|
|
||||||
app = FastAPI(title="context-hub", version="1.0.0")
|
@contextlib.asynccontextmanager
|
||||||
|
async def lifespan(app: FastAPI):
|
||||||
|
await init_db()
|
||||||
|
async with mcp.session_manager.run():
|
||||||
|
yield
|
||||||
|
|
||||||
|
app = FastAPI(title="context-hub", version="1.0.0", lifespan=lifespan)
|
||||||
|
|
||||||
app.add_middleware(
|
app.add_middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
@@ -21,9 +28,6 @@ app.include_router(api.router)
|
|||||||
app.include_router(admin.router)
|
app.include_router(admin.router)
|
||||||
app.include_router(mcp.router)
|
app.include_router(mcp.router)
|
||||||
|
|
||||||
@app.on_event("startup")
|
|
||||||
async def on_startup():
|
|
||||||
await init_db()
|
|
||||||
|
|
||||||
@app.get("/health")
|
@app.get("/health")
|
||||||
async def health_check():
|
async def health_check():
|
||||||
|
|||||||
+23
-21
@@ -4,7 +4,10 @@ import contextvars
|
|||||||
from fastapi import APIRouter, Depends, Request, HTTPException
|
from fastapi import APIRouter, Depends, Request, HTTPException
|
||||||
from fastapi.responses import Response, JSONResponse
|
from fastapi.responses import Response, JSONResponse
|
||||||
from mcp.server import Server
|
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 mcp.types import Tool, TextContent
|
||||||
from app.auth import get_current_agent
|
from app.auth import get_current_agent
|
||||||
from app.scopes import has_scope_access, ALL_SCOPES
|
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"}))]
|
return [TextContent(type="text", text=json.dumps({"error": "Unknown tool"}))]
|
||||||
|
|
||||||
# FastMCP / SSE Integration
|
# Streamable HTTP Integration (mcp SDK v1.x, transport recommande depuis la spec 2025-03-26).
|
||||||
# connect_sse() ET handle_post_message() envoient chacun leur reponse ASGI
|
# Remplace l ancien transport SSE (SseServerTransport) qui exigeait un session_id
|
||||||
# completement par eux-memes (via le send() qu'on leur passe). Les faire
|
# etabli via un GET prealable -- les clients MCP modernes (Gemini/Antigravity inclus)
|
||||||
# passer par des routes FastAPI classiques (Depends + return Response) fait
|
# postent directement le JSON-RPC initialize sans cette poignee de main, d ou les 400
|
||||||
# que FastAPI tente un second envoi une fois le SDK termine -> RuntimeError
|
# Bad Request observes avec l ancienne implementation.
|
||||||
# uvicorn ("Unexpected ASGI message 'http.response.start' sent, after response
|
# L auth X-API-Key reste verifiee a la main avant de deleguer a session_manager.handle_request,
|
||||||
# already completed"). Un seul montage ASGI brut gere GET (ouverture SSE) et
|
# meme principe qu avant : current_agent_var est positionne pour la duree de la requete,
|
||||||
# POST (messages) sans jamais repasser par le wrapping FastAPI ; l'auth
|
# lu ensuite par les handlers call_tool/list_tools via caller_agent.
|
||||||
# X-API-Key est donc verifiee ici a la main plutot que via Depends.
|
session_manager = StreamableHTTPSessionManager(
|
||||||
sse_transport = SseServerTransport("/messages")
|
app=mcp_server,
|
||||||
|
event_store=None,
|
||||||
|
json_response=True,
|
||||||
|
stateless=True,
|
||||||
|
)
|
||||||
|
|
||||||
async def _mcp_asgi_app(scope, receive, send):
|
async def _mcp_asgi_app(scope, receive, send):
|
||||||
if scope["type"] != "http":
|
if scope["type"] != "http":
|
||||||
@@ -247,15 +254,10 @@ async def _mcp_asgi_app(scope, receive, send):
|
|||||||
return
|
return
|
||||||
await update_key_last_used(api_key)
|
await update_key_last_used(api_key)
|
||||||
|
|
||||||
if scope["method"] == "POST":
|
token = current_agent_var.set(agent)
|
||||||
await sse_transport.handle_post_message(scope, receive, send)
|
try:
|
||||||
return
|
await session_manager.handle_request(scope, receive, send)
|
||||||
|
finally:
|
||||||
async with sse_transport.connect_sse(scope, receive, send) as (read_stream, write_stream):
|
current_agent_var.reset(token)
|
||||||
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)
|
|
||||||
|
|
||||||
router.mount("/mcp", _mcp_asgi_app)
|
router.mount("/mcp", _mcp_asgi_app)
|
||||||
|
|||||||
+2
-1
@@ -4,6 +4,7 @@ aiosqlite>=0.19.0
|
|||||||
PyJWT>=2.8.0
|
PyJWT>=2.8.0
|
||||||
jinja2>=3.1.3
|
jinja2>=3.1.3
|
||||||
python-multipart>=0.0.9
|
python-multipart>=0.0.9
|
||||||
mcp>=1.2.0
|
mcp>=1.2.0,<2.0.0
|
||||||
python-dotenv>=1.0.1
|
python-dotenv>=1.0.1
|
||||||
sse-starlette>=1.6.5
|
sse-starlette>=1.6.5
|
||||||
|
httpx>=0.27.0
|
||||||
|
|||||||
Reference in New Issue
Block a user