Implement dynamic header injection for proxy transports. #787
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Description
Adds a mechanism to dynamically inject and update HTTP headers for ongoing proxy sessions, resolving an issue where headers introduced after the initial handshake (e.g, Mcp-Protocol-Version) were not being forwarded to the server on subsequent exchanges.
In
src/index.ts:sessionHeaderHolders, aMapto store the most recent set of headers for each active session ID.createCustomFetch, a new helper function that creates afetchwrapper. This wrapper merges the session headers fromsessionHeaderHolderswith the request-specific headers from the SDK (likeContent-Type)createTransport, usecreateCustomFetchfor both theStreamableHTTPClientTransportandSSEClientTransport. This injects the dynamic header logic into the transport layer./mcp,/sse,/messageroute handlers, added logic to update thesessionHeaderHoldersmap with the latest headers from every incoming client request, using useupdateHeadersInPlaceto replace current headers with new headers, retaining the Accept header.onsessionclosedcallback and the/mcpDELETE handler to remove session data fromsessionHeaderHolders.getHttpHeaders, more robust handling ofstring[]andundefinedvalues fromreq.headers, satisfying the"strict": trueTypeScript configuration.updateHeadersInPlacehelper function to solve a stale header reference issue inSSEClientTransport. It mutates the header object in-place, ensuring the transport sees all updates, while carefully preserving the originalAcceptheader.Motivation and Context
The core problem was that the
transportToServerinstance was created once with a static set of headers. This meant that dynamically added headers likemcp-protocol-version(which is negotiated during initialization) andlast-event-idwere being dropped in subsequent requests.The solution implemented here is to wrap the
fetchfunction used by the HTTP-based transports (StreamableHttpandSSE). This custom wrapper merges the latest headers from the client with the headers of each outgoing request from the proxy, ensuring all headers are preserved for the lifetime of the session.Fixes #679, #758, #723
How Has This Been Tested?
StreamableHttp With OAuth Client Side
with-oauth.mov
Sending
mcp-protocol-versionduring server metadata discoveryStreamableHttp Without OAuth Client Side
without-oauth.mov
StreamableHttp Server Side
mcp-session-idandmcp-protocol-versionare negotiated on the first exchange and present thereafterSSE Without OAuth Client Side
sse-without-oauth.mov
SSE Without OAuth Server Side
mcp-session-idis not a part of SSE connectionsmcp-protocol-versionis negotiated on the second exchange and present thereafterBreaking Changes
Nope.
Types of changes
Checklist
Additional context