Skip to content

Commit bc6a658

Browse files
committed
fix(client): send MCP standard POST headers
1 parent 5fc42e9 commit bc6a658

3 files changed

Lines changed: 72 additions & 0 deletions

File tree

.changeset/fresh-headers-flow.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@modelcontextprotocol/client": patch
3+
---
4+
5+
Send MCP standard headers on Streamable HTTP POST requests.

packages/client/src/client/streamableHttp.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,25 @@ export type StreamableHTTPClientTransportOptions = {
163163
protocolVersion?: string;
164164
};
165165

166+
function setMcpStandardPostHeaders(headers: Headers, message: JSONRPCMessage | JSONRPCMessage[]): void {
167+
if (Array.isArray(message) || !('method' in message) || typeof message.method !== 'string') {
168+
return;
169+
}
170+
171+
headers.set('Mcp-Method', message.method);
172+
173+
const params = 'params' in message ? message.params : undefined;
174+
if (!params || typeof params !== 'object' || Array.isArray(params)) {
175+
return;
176+
}
177+
178+
const requestParams = params as { name?: unknown; uri?: unknown };
179+
const mcpName = typeof requestParams.name === 'string' ? requestParams.name : requestParams.uri;
180+
if (typeof mcpName === 'string') {
181+
headers.set('Mcp-Name', mcpName);
182+
}
183+
}
184+
166185
/**
167186
* Client transport for Streamable HTTP: this implements the MCP Streamable HTTP transport specification.
168187
* It will connect to a server using HTTP `POST` for sending messages and HTTP `GET` with Server-Sent Events
@@ -545,6 +564,7 @@ export class StreamableHTTPClientTransport implements Transport {
545564
const userAccept = headers.get('accept');
546565
const types = [...(userAccept?.split(',').map(s => s.trim().toLowerCase()) ?? []), 'application/json', 'text/event-stream'];
547566
headers.set('accept', [...new Set(types)].join(', '));
567+
setMcpStandardPostHeaders(headers, message);
548568

549569
const init = {
550570
...this._requestInit,

packages/client/test/client/streamableHttp.test.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,53 @@ describe('StreamableHTTPClientTransport', () => {
6262
);
6363
});
6464

65+
it('should include MCP standard headers on POST requests', async () => {
66+
const message: JSONRPCMessage = {
67+
jsonrpc: '2.0',
68+
method: 'tools/call',
69+
params: {
70+
name: 'read_file',
71+
arguments: { path: 'README.md' }
72+
},
73+
id: 'test-id'
74+
};
75+
76+
(globalThis.fetch as Mock).mockResolvedValueOnce({
77+
ok: true,
78+
status: 202,
79+
headers: new Headers()
80+
});
81+
82+
await transport.send(message);
83+
84+
const headers = (globalThis.fetch as Mock).mock.calls.at(-1)![1].headers as Headers;
85+
expect(headers.get('mcp-method')).toBe('tools/call');
86+
expect(headers.get('mcp-name')).toBe('read_file');
87+
});
88+
89+
it('should use params.uri for the MCP name header', async () => {
90+
const message: JSONRPCMessage = {
91+
jsonrpc: '2.0',
92+
method: 'resources/read',
93+
params: {
94+
uri: 'file:///README.md'
95+
},
96+
id: 'test-id'
97+
};
98+
99+
(globalThis.fetch as Mock).mockResolvedValueOnce({
100+
ok: true,
101+
status: 202,
102+
headers: new Headers()
103+
});
104+
105+
await transport.send(message);
106+
107+
const headers = (globalThis.fetch as Mock).mock.calls.at(-1)![1].headers as Headers;
108+
expect(headers.get('mcp-method')).toBe('resources/read');
109+
expect(headers.get('mcp-name')).toBe('file:///README.md');
110+
});
111+
65112
it('should send batch messages', async () => {
66113
const messages: JSONRPCMessage[] = [
67114
{ jsonrpc: '2.0', method: 'test1', params: {}, id: 'id1' },

0 commit comments

Comments
 (0)