fastmcp icon indicating copy to clipboard operation
fastmcp copied to clipboard

Can’t pass per-request headers like Authorization using from_openapi

Open jlowin opened this issue 6 months ago • 1 comments

Discussed in https://github.com/jlowin/fastmcp/discussions/344

Originally posted by JoeyKoetting May 6, 2025 I’m trying to use FastMCP.from_openapi to build a server from an OpenAPI spec, but I can’t figure out how to forward per-request headers (like Authorization or Cookie) from the MCP client.

The headers parameter on from_openapi only supports static headers, which doesn’t work in a multi-user setup. Ideally, the MCP client would send headers and the server would forward them for each request, but I haven’t found a way to do that with from_openapi.

Here’s the basic setup:

mcp = FastMCP.from_openapi(
    openapi_spec=openapi_spec,
    client=api_client,
    name="OpenAPI Server",
    # headers=headers  # not useful here since they're static
)

I’ve worked around this by creating a manual tool like this:

@mcp.tool()
async def v1_manual_tool(ctx: Context, uuid: str):
    headers = ctx.get_http_request().headers
    api_headers = {
        "Authorization": headers.get("Authorization"),
        "Cookie": headers.get("Cookie"),
        ...
    }
    api_client = httpx.AsyncClient(base_url="http://127.0.0.1:8000/", headers=api_headers)
    response = await api_client.delete(f"/api/v1/someMethod/")
    await api_client.aclose()
    return response.json()

But this feels like a hack, and it defeats the point of using from_openapi. Is there a built-in or recommended way to propagate client headers dynamically?

jlowin avatar May 06 '25 23:05 jlowin

Confirmed. FastMCP.from_fastapi also doesn't accept per-request authorization header The root is in the FastMCPOpenAPI.

Oaklight avatar May 11 '25 19:05 Oaklight

@jlowin big +1 but on my side using FastMCP.from_fastapi

All my REST APIs come with a security scheme like this one:

"securitySchemes": {
       "x-account-id": {
           "type": "apiKey",
           "description": "Account ID from RBAC",
           "in": "header",
          "name": "x-account-id"
       },
      "HTTPBearer": {
          "type": "http",
          "scheme": "bearer"
      }
}

It means that my API endpoints require both a Authorization: Bearer <JWT> and a custom header x-account-id: <value>. So we need the ability to tell the MCP server to forward a list of headers to the internal HTTP client created to call the endpoints (this client: https://github.com/jlowin/fastmcp/blob/v2.4.0/src/fastmcp/server/server.py#L1197).

asaintsever avatar May 23 '25 08:05 asaintsever

I think this might now work manually with https://gofastmcp.com/patterns/http-requests but I think we can do better and auto-forward headers for all proxy openapi servers

jlowin avatar May 23 '25 12:05 jlowin

I am working on a PR that is almost working for my use case. I will submit it soon to have your feedback @jlowin, it may serve as a starting point at least.

asaintsever avatar May 23 '25 12:05 asaintsever

Sounds good - be aware I'm prepping (and have already merged some of) a significant refactor of OpenAPI logic in preparation for 2.5, so I may sneak some related logic in

jlowin avatar May 23 '25 12:05 jlowin

FYI just proposed https://github.com/jlowin/fastmcp/pull/570 to tackle the FastMCP.from_fastapi case. The FastMCP.from_openapi case can directly be solved by injecting a HTTPX client with the same kind of request hook I defined.

asaintsever avatar May 23 '25 14:05 asaintsever

@asaintsever I think #570 solves a very different problem than this issue. 570 passes dynamic server-set headers; this issue is about dynamic client-set headers.

That said I think a much simpler solution to your problem is to permit **httpx_kwargs to be passed from from_fastapi down to the HTTPX client. I'll comment on #570 and provide an alternative.

edit -- sorry looking more closely, 570 will load the client headers but is mixing server and client logic (and http logic) in a way that isn't inline with the library design. I'll propose an alternative

jlowin avatar May 23 '25 15:05 jlowin