feat(mcp): câblage FastAPI + nginx proxy + docker-compose MCP_API_KEY
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+4
-1
@@ -7,17 +7,18 @@ from app.api.admin import router as admin_router
|
||||
from app.api.events import router as events_router
|
||||
from app.api.health import router as health_router
|
||||
from app.api.media import router as media_router
|
||||
from app.api.mcp_server import mcp
|
||||
from app.api.notes import router as notes_router
|
||||
from app.api.todos import router as todos_router
|
||||
from app.api.shopping import router as shopping_router
|
||||
from app.core.config import settings
|
||||
from app.core.mcp_auth import MCPAuthMiddleware
|
||||
from app.core.redis import init_redis, close_redis
|
||||
from app.data.seed import run_seed
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
# Crée les dossiers data/ au démarrage
|
||||
for subdir in ("uploads", "notes", "backup"):
|
||||
Path(settings.data_dir, subdir).mkdir(parents=True, exist_ok=True)
|
||||
await run_seed()
|
||||
@@ -35,6 +36,7 @@ app.add_middleware(
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
app.add_middleware(MCPAuthMiddleware)
|
||||
|
||||
app.include_router(health_router, prefix="/api")
|
||||
app.include_router(events_router, prefix="/api/events")
|
||||
@@ -44,4 +46,5 @@ app.include_router(notes_router, prefix="/api/notes")
|
||||
app.include_router(todos_router, prefix="/api/todos")
|
||||
app.include_router(shopping_router, prefix="/api/shopping")
|
||||
|
||||
app.mount("/mcp", mcp.streamable_http_app())
|
||||
app.mount("/media", StaticFiles(directory=str(settings.upload_path)), name="media")
|
||||
|
||||
@@ -3,6 +3,7 @@ import uuid
|
||||
import pytest
|
||||
from sqlalchemy import delete
|
||||
import app.api.mcp_server as mcp_server_module
|
||||
from app.core.config import settings
|
||||
from app.api.mcp_server import (
|
||||
get_todos, create_todo, update_todo, postpone_todo, delete_todo,
|
||||
)
|
||||
@@ -211,3 +212,27 @@ async def test_add_item_liste_invalide():
|
||||
result = await add_shopping_item(list_id="pas-un-uuid", name="article")
|
||||
data = json.loads(result)
|
||||
assert "error" in data
|
||||
|
||||
|
||||
# ── AUTH ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
async def test_mcp_auth_rejet_sans_token(client):
|
||||
"""Le middleware renvoie 401 si aucun header Authorization."""
|
||||
resp = await client.get("/mcp")
|
||||
assert resp.status_code == 401
|
||||
|
||||
|
||||
async def test_mcp_auth_rejet_mauvais_token(client):
|
||||
"""Le middleware renvoie 401 si le token est incorrect."""
|
||||
resp = await client.get("/mcp", headers={"Authorization": "Bearer mauvais-token"})
|
||||
assert resp.status_code == 401
|
||||
|
||||
|
||||
async def test_mcp_auth_accepte_bon_token(client, monkeypatch):
|
||||
"""Le middleware laisse passer avec le token correct."""
|
||||
monkeypatch.setattr(settings, "mcp_api_key", "test-mcp-key-xyz")
|
||||
resp = await client.get(
|
||||
"/mcp",
|
||||
headers={"Authorization": "Bearer test-mcp-key-xyz"},
|
||||
)
|
||||
assert resp.status_code != 401
|
||||
|
||||
@@ -32,6 +32,7 @@ services:
|
||||
DATA_DIR: /data
|
||||
REDIS_URL: redis://redis:6379
|
||||
CORS_ORIGINS: http://localhost:3001,http://localhost:3000
|
||||
MCP_API_KEY: "4zfCmiC3Z_28F3xPOxBSDi0DQx6aRzAQrpplyywo_VI"
|
||||
volumes:
|
||||
- ./backend/app:/app/app
|
||||
- ./data:/data
|
||||
|
||||
@@ -25,6 +25,17 @@ server {
|
||||
proxy_read_timeout 86400s;
|
||||
}
|
||||
|
||||
location /mcp {
|
||||
proxy_pass http://backend:8000/mcp;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
proxy_read_timeout 86400s;
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
proxy_pass http://backend:8000/api/;
|
||||
proxy_set_header Host $host;
|
||||
|
||||
Reference in New Issue
Block a user