From 7a91aced68aa2ac6815a3ba2c909a29f2740b40f Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Tue, 7 Jul 2026 16:52:30 +0200 Subject: [PATCH] feat(mcp): assemble streamable-http transport app --- .../surfsense_mcp/core/transport/__init__.py | 5 ++++ .../src/surfsense_mcp/core/transport/http.py | 27 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 surfsense_mcp/src/surfsense_mcp/core/transport/__init__.py create mode 100644 surfsense_mcp/src/surfsense_mcp/core/transport/http.py diff --git a/surfsense_mcp/src/surfsense_mcp/core/transport/__init__.py b/surfsense_mcp/src/surfsense_mcp/core/transport/__init__.py new file mode 100644 index 000000000..53aaa6b8c --- /dev/null +++ b/surfsense_mcp/src/surfsense_mcp/core/transport/__init__.py @@ -0,0 +1,5 @@ +"""Transport wiring for the MCP server (streamable-http app assembly).""" + +from .http import build_http_app + +__all__ = ["build_http_app"] diff --git a/surfsense_mcp/src/surfsense_mcp/core/transport/http.py b/surfsense_mcp/src/surfsense_mcp/core/transport/http.py new file mode 100644 index 000000000..2f2cf8f53 --- /dev/null +++ b/surfsense_mcp/src/surfsense_mcp/core/transport/http.py @@ -0,0 +1,27 @@ +"""Assemble the streamable-http ASGI app for the remote transport. + +Wraps the SDK's MCP endpoint with the API-key identity middleware and CORS. +CORS sits outermost so browser preflight (which carries no key) is answered +before the identity middleware, and clients can read the ``Mcp-Session-Id`` +header the streamable-http protocol relies on. +""" + +from __future__ import annotations + +from mcp.server.fastmcp import FastMCP +from starlette.middleware.cors import CORSMiddleware +from starlette.types import ASGIApp + +from ..auth.middleware import ApiKeyIdentityMiddleware + + +def build_http_app(mcp: FastMCP) -> ASGIApp: + """Return the MCP streamable-http app wrapped with identity + CORS.""" + app: ASGIApp = ApiKeyIdentityMiddleware(mcp.streamable_http_app()) + return CORSMiddleware( + app, + allow_origins=["*"], + allow_methods=["GET", "POST", "DELETE", "OPTIONS"], + allow_headers=["*"], + expose_headers=["Mcp-Session-Id"], + )