-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Open
Labels
authRelated to authentication (Bearer, JWT, OAuth, WorkOS) for client or server.Related to authentication (Bearer, JWT, OAuth, WorkOS) for client or server.bugSomething isn't working. Reports of errors, unexpected behavior, or broken functionality.Something isn't working. Reports of errors, unexpected behavior, or broken functionality.httpRelated to HTTP transport, networking, or web server functionality.Related to HTTP transport, networking, or web server functionality.
Description
Description
Observing authentication issues with a fastapi/fastmcp combined app when the authentication is nested a bit deeper in the app.
- The MRE below has two FastAPI routes. One is protected with BearerAuth while the other is open.
- The MCP http app is created from the Fastapi app and all 3 routes (
/protected,/open,/mcp) are mounted in the combined app. - I have included middleware (auth_logger) for tracking the
Authorization: bearer testheader from inbound API > MCP > Route.
The two problems I observe are as follows:
- With
auth=DebugTokenVerifier()set, the intialize always fails. Logging shows the Auth header gets to the combined API App, but FastMCP rejects with 401 before the MCP logging middlware logs the header.INFO:auth_logger:Authorization Header (API): test [01/08/26 21:58:47] INFO Auth error returned: invalid_token (status=401) middleware.py:92
- With the previous item disabled: intialize and list tools both work but when attempting to use the protected tool; the call fails although the Auth header is passed from API > MCP > Route (as confirmed by the open test)
# test_endpoint_auth_protected_get MCP Tool Call INFO:auth_logger:Authorization Header (API): test DEBUG:mcp.server.streamable_http_manager:Session already exists, handling request directly INFO:mcp.server.lowlevel.server:Processing request of type CallToolRequest DEBUG:mcp.server.lowlevel.server:Dispatching request of type CallToolRequest INFO:auth_logger:Authorization Header (MCP): test INFO:httpx:HTTP Request: GET http://fastapi/protected "HTTP/1.1 401 Unauthorized" [01/08/26 22:00:00] Error calling tool 'test_endpoint_auth_protected_get' # test_endpoint_open_open_get MCP Tool Call INFO:auth_logger:Authorization Header (API): test DEBUG:mcp.server.streamable_http_manager:Session already exists, handling request directly INFO:mcp.server.lowlevel.server:Processing request of type CallToolRequest DEBUG:mcp.server.lowlevel.server:Dispatching request of type CallToolRequest INFO:auth_logger:Authorization Header (MCP): test INFO:auth_logger:Authorization Header (ROUTE): test INFO:httpx:HTTP Request: GET http://fastapi/open "HTTP/1.1 200 OK"
Sample of CURL to protected route to verify it is working as expected with and without the auth header.
curl -X GET http://127.0.0.1:8000/protected -H "Authorization: Bearer test"
{"message":"Auth is working!"}
curl -X GET http://127.0.0.1:8000/protected
{"detail":"Not authenticated"}CURL Commands (I have been testing with MCP Inspector for the second tool call error) but these help for quick illustrations like the previous example.
curl -X GET http://127.0.0.1:8000/protected -H "Authorization: Bearer test"
curl -X GET http://127.0.0.1:8000/open -H "Authorization: Bearer test"
curl -s -D - -X POST \
-H "Authorization: Bearer test" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"method": "initialize",
"params": {
"protocolVersion": "2025-03-26",
"capabilities": {},
"clientInfo": {
"name": "curl-test",
"version": "1.0.0"
}
},
"id": 1
}' \
"http://127.0.0.1:8000/mcp"Example Code
from fastmcp import FastMCP
from fastmcp.server.middleware import Middleware, MiddlewareContext
from fastmcp.server.dependencies import get_http_headers
from fastmcp.server.auth.providers.debug import DebugTokenVerifier
from fastapi import Depends, FastAPI
from fastapi.security import HTTPBearer
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("auth_logger")
# API application
api_app = FastAPI(title="API")
@api_app.get("/protected", dependencies=[Depends(HTTPBearer())])
async def test_endpoint_auth(request: Request):
logger.info(
f"Authorization Header (ROUTE): {request.headers.get('authorization', 'No Authorization Header')}"
)
return {"message": "Auth is working!"}
@api_app.get("/open")
async def test_endpoint_open(request: Request):
logger.info(
f"Authorization Header (ROUTE): {request.headers.get('authorization', 'No Authorization Header')}"
)
return {"message": "API is working!"}
# MCP integration
mcp = FastMCP.from_fastapi(
app=api_app,
name="MCP",
httpx_client_kwargs={
"headers": {
"Authorization": f"Bearer asdf", # Added while testing, does not do anything that I can see
}
},
auth=DebugTokenVerifier(), # Disable this line to test issue number 2
)
class McpLoggerMiddleware(Middleware):
async def on_message(self, context: MiddlewareContext, call_next):
headers = get_http_headers()
auth_header = headers.get("authorization", "")
logger.info(f"Authorization Header (MCP): {auth_header}")
return await call_next(context)
mcp.add_middleware(McpLoggerMiddleware())
mcp_app = mcp.http_app(path="/mcp")
# Combined application
app = FastAPI(
title="API with MCP",
routes=[
*mcp_app.routes, # MCP routes
*api_app.routes, # Original API routes
],
lifespan=mcp_app.lifespan,
)
class ApiLoggerMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
auth_header = request.headers.get("authorization", "No Authorization Header")
logger.info(f"Authorization Header (API): {auth_header}")
response = await call_next(request)
return response
app.add_middleware(ApiLoggerMiddleware)Version Information
FastMCP version: 2.14.2
MCP version: 1.25.0
Python version: 3.12.3
Platform: Linux-6.6.87.2-microsoft-standard-WSL2-x86_64-with-glibc2.39
FastMCP root path: /home/kydavis/scratchpad/fastmcp-issue/.venv/lib/python3.12/site-packages
Metadata
Metadata
Assignees
Labels
authRelated to authentication (Bearer, JWT, OAuth, WorkOS) for client or server.Related to authentication (Bearer, JWT, OAuth, WorkOS) for client or server.bugSomething isn't working. Reports of errors, unexpected behavior, or broken functionality.Something isn't working. Reports of errors, unexpected behavior, or broken functionality.httpRelated to HTTP transport, networking, or web server functionality.Related to HTTP transport, networking, or web server functionality.