-
Notifications
You must be signed in to change notification settings - Fork 1k
Description
Enhancement Description
Allowing flexible customization of request headers in client sessions, as proposed in #1233, would unlock notable use-case advantages. The client-side mutable request headers, accessible via the server's get_http_headers
method, enable rapid sharing of temporary state information through request headers.
Currently, implementing this functionality requires users to develop additional server-side interfaces or tools to modify the state, followed by corresponding client-side calls—an approach that introduces significant unnecessary overhead.
Another similar but more refined method is the one proposed in #1315 to add metadata to each request.
Use Case
No response
Proposed Implementation
The mutable request headers feature can be implemented through the httpx_client_factory
parameter of StreamableHttpTransport
, as demonstrated in the example I provided in #1233.
# client -- Send Multiple Requests with Different Tokens:
import asyncio
import httpx
from fastmcp import Client
from fastmcp.client.transports import StreamableHttpTransport
async def test_multiple_requests():
httpx_client = httpx.AsyncClient()
def httpx_client_factory(
headers: dict[str, str],
timeout: httpx.Timeout | None = None,
auth: httpx.Auth | None = None,
):
httpx_client.headers = headers
if timeout:
httpx_client.timeout = timeout
if auth:
httpx_client.auth = auth
return httpx_client
async with Client(
transport=StreamableHttpTransport(
"http://127.0.0.1:3000/mcp/",
httpx_client_factory=httpx_client_factory,
)
) as client:
httpx_client.headers["x-forwarded-access-token"] = "TOKEN_1"
# Request 1
result1 = await client.call_tool("test_token", {})
print(f"Request 1 result: {result1.content[0].text}")
# the subsequent request within the same session, but the token should be updated by the reverse proxy
httpx_client.headers["x-forwarded-access-token"] = "TOKEN_2"
result2 = await client.call_tool("test_token", {})
print(f"Request 2 result: {result2.content[0].text}")
asyncio.run(test_multiple_requests())
It's worth discussing how we should implement this feature in practice.
While the first implementation option would create a stateful SHTTP transport that preserves the HTTP client and exposes headers
, this design would fundamentally compromise the transport's reusability across multiple clients.
Alternatively, we could maintain the HTTP client in the client session and expose mutable headers through either the Client object or ClientSession (SHTTP transport only). However, implementing this would necessitate moderate refactoring across SHTTPTransport, ClientSession, and potentially the Client class - I'm uncertain about the appropriate scope of changes that would gain consensus.