SSEClientTransport ignores requestInit headers (Authorization) in MCP typescript-sdk
When using SSEClientTransport from the MCP TypeScript SDK, headers passed via requestInit.headers (for example an Authorization bearer token) are not sent to the server. The SSE connection omits them. Is this a bug in the SDK, and is there a fix or workaround?
Originally asked on GitHub: https://github.com/modelcontextprotocol/typescript-sdk/issues/436
5 answers
Status: fixed. The
_commonHeaders()merge bug in the SSE client was fixed in PR #856 (commit 5ceabfb3), present on bothmainandv1.x. The old code did{ ...headers, ...this._requestInit?.headers }, which silently dropsHeadersinstances because they have no enumerable own string keys; the fix usesnormalizeHeaders(this._requestInit?.headers), which handles plain objects,Headersinstances, and[key, value][]arrays. The SSE GET stream is covered too, since_startOrAuth()wraps EventSource with a fetch that calls_commonHeaders(), so headers reach both the initial SSE connection and the POST message channel — the maintainer says theeventSourceInit.fetchworkaround is no longer needed on current code.Workaround that worked for users before the fix:
jsconst transport = new SSEClientTransport(new URL("http://example.com/sse"), { requestInit: { headers: { Authorization: "Bearer <token>" } }, eventSourceInit: { fetch: (url, init) => { const headers = new Headers(init?.headers || {}); headers.set("Authorization", "Bearer <token>"); return fetch(url, { ...init, headers }); } } });One commenter confirmed this worked for sending an Authorization bearer header to their MCP server, and noted that for Node.js you need
globalThis.EventSource = EventSource(from theeventsourcepackage) if not already set. A later commenter recommended building the headers withnew Headers(init.headers)rather than object spread, since spread strips the SDK's Content-Type on POST requests and breaks the message channel; they also passed the same custom fetch to the transport'sfetchoption to cover both channels. Another commenter pointed out that someeventsourceimplementations ignore custom headers on the GET /sse request entirely, which is why a fetch wrapper was needed at all.Did this work for you?What a GitHub user tried on GitHub (Didn't work)
Created SSEClientTransport with requestInit.headers containing extra headers (Authorization, custom headers) and observed they were never sent to the server. Proposed merging with new Headers({ ...headers, ...this._requestInit?.headers }) mirroring the streamable HTTP transport.
Source: https://github.com/modelcontextprotocol/typescript-sdk/issues/436 (opens external site)
Did this work for you?What @evalstate tried on GitHub (Worked)
Member-suggested workaround: pass eventSourceInit.fetch that wraps the initial SSE fetch, copies init.headers into a new Headers object, sets Authorization, and calls fetch with the merged headers. Confirmed working by another user for sending a bearer token to their MCP server.
Did this work for you?What @Anirudhx7 tried on GitHub (Worked)
Suggested passing a custom fetch wrapper (adds Authorization only if missing) to both eventSourceInit and the transport's fetch option; warned that object spread strips the SDK's Content-Type on POST. Presented a full working example, though a later commenter said this was accurate only for pre-fix code.
Did this work for you?What a GitHub user tried on GitHub (Worked)
Verified after the fix with a repro script that plain-object headers, Headers instances, and authProvider token plus extra headers all appear in the merged output of _commonHeaders().
Did this work for you?
Related questions
- MCP TypeScript SDK StreamableHttp without sticky sessions in multi-replica deployments?
- MCP stdio server: SyntaxError Unexpected token in deserializeMessage when using console.log
- MCP typescript-sdk: tool inputSchema/outputSchema reject JSON Schema type 'array'?
- Use Zod union/intersection as MCP tool paramsSchema in typescript-sdk?
- MCP Inspector: "Invalid transport type: undefined" when connecting to an SSE server