From 35c982ef62627e2acd6723cf31c55b494743447f Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 21 Jun 2026 20:38:17 +0000 Subject: [PATCH] feat: optional bearer-token auth for streamable-http transport Pure-ASGI middleware requiring Authorization: Bearer on every HTTP request when MCP_API_KEY is set; open/stdio behaviour preserved when unset. Closes unauthenticated-MCP exposure (HOME-119 P1). Co-Authored-By: Claude Opus 4.8 --- mcp_server.py | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/mcp_server.py b/mcp_server.py index e5f3946..7f5fafd 100644 --- a/mcp_server.py +++ b/mcp_server.py @@ -224,10 +224,49 @@ def delete_paste(paste_id: str) -> dict: # Entry point # --------------------------------------------------------------------------- +MCP_API_KEY = os.environ.get("MCP_API_KEY", "").strip() + + +class _BearerAuthMiddleware: + """Pure-ASGI middleware rejecting HTTP requests without a valid bearer token. + + Enforced only when MCP_API_KEY is set; stdio transport is unaffected. + """ + + def __init__(self, app, token: str) -> None: + self.app = app + self._expected = f"Bearer {token}".encode() + + async def __call__(self, scope, receive, send) -> None: + if scope["type"] != "http": + await self.app(scope, receive, send) + return + headers = dict(scope.get("headers") or []) + if headers.get(b"authorization") != self._expected: + from starlette.responses import JSONResponse + + await JSONResponse( + {"jsonrpc": "2.0", + "error": {"code": -32001, "message": "Unauthorized"}, + "id": None}, + status_code=401, + headers={"WWW-Authenticate": "Bearer"}, + )(scope, receive, send) + return + await self.app(scope, receive, send) + + def main() -> None: if MCP_TRANSPORT not in {"stdio", "sse", "streamable-http"}: raise ValueError("PASTEBIN_MCP_TRANSPORT must be stdio, sse, or streamable-http.") - mcp.run(transport=MCP_TRANSPORT) + if MCP_TRANSPORT == "streamable-http" and MCP_API_KEY: + import uvicorn + + app = mcp.streamable_http_app() + app.add_middleware(_BearerAuthMiddleware, token=MCP_API_KEY) + uvicorn.run(app, host=MCP_HOST, port=MCP_PORT) + else: + mcp.run(transport=MCP_TRANSPORT) if __name__ == "__main__":