You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Is your feature request related to a problem? Please describe.
I have an MCP server that sits in front of my API to allow LLMs to interact with it. My API requires an authorization header.
I have hacked a way to do this in my fork, but essentially the MCP client is able to pass through headers. Only need to use this for the /sse request. Currently, the handlers extract the arguments they need in the decorator. We could instead add a field to the base Request class called raw_request or headers if we just need that and then ensure this is added to the request object before passing it to the handler.
Describe the solution you'd like
# src/mcp/types.pyclassRequest(BaseModel, Generic[RequestParamsT, MethodT]):
"""Base class for JSON-RPC requests."""method: MethodTparams: RequestParamsTheaders: dict[str, Any] |None=None# <<<<<<<<model_config=ConfigDict(extra="allow")
---------------------# src/mcp/server/fastmcp/server.pydefcall_tool(self):
defdecorator(
func: Callable[
...,
Awaitable[
Sequence[
types.TextContent|types.ImageContent|types.EmbeddedResource
]
],
],
):
logger.debug("Registering handler for CallToolRequest")
asyncdefhandler(req: types.CallToolRequest):
try:
results=awaitfunc(req) # <<<<<<<<<returntypes.ServerResult(
types.CallToolResult(content=list(results), isError=False)
)
exceptExceptionase:
returntypes.ServerResult(
types.CallToolResult(
content=[types.TextContent(type="text", text=str(e))],
isError=True,
)
)
self.request_handlers[types.CallToolRequest] =handlerreturnfuncreturndecorator-----------------------------# src/mcp/server/fastmcp/server.pyasyncdefrun_sse_async(self, middleware: list[type] = []) ->None:
"""Run the server using SSE transport."""fromstarlette.applicationsimportStarlettefromstarlette.routingimportMount, Routesse=SseServerTransport("/messages/")
asyncdefhandle_sse(request):
asyncwithsse.connect_sse(
request.scope, request.receive, request._send
) asstreams:
awaitself._mcp_server.run(
streams[0],
streams[1],
self._mcp_server.create_initialization_options(),
raw_request=request, # <<<<<<<<<<<<<<
)
starlette_app=Starlette(
debug=self.settings.debug,
routes=[
Route("/sse", endpoint=handle_sse),
Mount("/messages/", app=sse.handle_post_message),
],
)
config=uvicorn.Config(
starlette_app,
host=self.settings.host,
port=self.settings.port,
log_level=self.settings.log_level.lower(),
)
server=uvicorn.Server(config)
awaitserver.serve()
---------------------------# src/mcp/server/lowlevel/server.pyasyncdefrun(
self,
read_stream: MemoryObjectReceiveStream[types.JSONRPCMessage|Exception],
write_stream: MemoryObjectSendStream[types.JSONRPCMessage],
initialization_options: InitializationOptions,
raw_request: Any|None=None, # <<<<<<<<<<<<<<<<<<<<<<<<<# When False, exceptions are returned as messages to the client.# When True, exceptions are raised, which will cause the server to shut down# but also make tracing exceptions much easier during testing and when using# in-process servers.raise_exceptions: bool=False,
):
withwarnings.catch_warnings(record=True) asw:
asyncwithServerSession(
read_stream, write_stream, initialization_options
) assession:
asyncformessageinsession.incoming_messages:
logger.debug(f"Received message: {message}")
matchmessage:
case (
RequestResponder(
request=types.ClientRequest(root=req)
) asresponder
):
withresponder:
ifraw_requestisnotNone:
req.headers=raw_request.headers# <<<<<<<<<<<<<<<<awaitself._handle_request(
message, req, session, raise_exceptions
)
casetypes.ClientNotification(root=notify):
awaitself._handle_notification(notify)
forwarninginw:
logger.info(
f"Warning: {warning.category.__name__}: {warning.message}"
)
and then use this like:
# already supported on clienttransport=awaitexit_stack.enter_async_context(
sse_client(url, headers={"authorization": "..."})
)
# on servermcp_server=FastMCP("example", transport="sse")
asyncdefhandle_call_tool(
self: FastMCP, req: types.CallToolRequest# <<<<<<<<<<<<<<<<<<
) ->Sequence[types.TextContent|types.ImageContent|types.EmbeddedResource]:
headers= {}
if"authorization"inreq.headers:
headers= {"Authorization": req.headers["authorization"]}
# ...http client call to api or if MCP is served from the app itself, check the key
I know auth is a part of the 2025 H1 roadmap so this may be usurped already in terms of how things will be supported. This goes beyond auth headers though since it could be useful to have access to the raw request in total instead within the tool execution context.
The text was updated successfully, but these errors were encountered:
These servers can be extended with custom routes while retaining full MCP SSE capabilities. Since these example servers are fully developed platforms, it is possible to add the auth in FastAPI or Starlette. In /sse route and handle_sse function, add the auth part there: https://github.com/panz2018/fastapi_mcp_sse/blob/main/src/app.py#L35
Is your feature request related to a problem? Please describe.
I have an MCP server that sits in front of my API to allow LLMs to interact with it. My API requires an authorization header.
I have hacked a way to do this in my fork, but essentially the MCP client is able to pass through headers. Only need to use this for the
/sse
request. Currently, the handlers extract the arguments they need in the decorator. We could instead add a field to the base Request class calledraw_request
orheaders
if we just need that and then ensure this is added to the request object before passing it to the handler.Describe the solution you'd like
and then use this like:
I know auth is a part of the 2025 H1 roadmap so this may be usurped already in terms of how things will be supported. This goes beyond auth headers though since it could be useful to have access to the raw request in total instead within the tool execution context.
The text was updated successfully, but these errors were encountered: