Skip to content

Commit 4d0ce3c

Browse files
committed
feat(core): implement SEP-2243 routing headers for HTTP transport
1 parent 987bc41 commit 4d0ce3c

1 file changed

Lines changed: 78 additions & 0 deletions

File tree

  • packages/toolbox-core/src/toolbox_core/mcp/v20260618

packages/toolbox-core/src/toolbox_core/mcp/v20260618/mcp.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ export class McpHttpTransportV20260618 extends McpHttpTransportBase {
4848
const isNotification = !('getResultModel' in request);
4949
const method = request.method;
5050

51+
if (isNotification) {
52+
payload = {
53+
jsonrpc: '2.0',
54+
method,
55+
params: params as Record<string, unknown>,
56+
};
57+
} else {
5158
payload = {
5259
jsonrpc: '2.0',
5360
id: uuidv4(),
@@ -59,26 +66,62 @@ export class McpHttpTransportV20260618 extends McpHttpTransportBase {
5966
// Inject Protocol Version into headers (v2025-06-18 specific)
6067
const reqHeaders = {...(headers || {})};
6168
reqHeaders['MCP-Protocol-Version'] = this._protocolVersion;
69+
reqHeaders['Mcp-Method'] = method;
70+
if (
71+
method === 'tools/call' &&
72+
typeof params === 'object' &&
73+
params !== null &&
74+
'name' in params
75+
) {
76+
reqHeaders['Mcp-Name'] = String((params as Record<string, unknown>).name);
6277
}
6378

6479
try {
6580
const response = await this._session.post(url, payload, {
6681
headers: reqHeaders,
6782
});
6883

84+
if (
85+
response.status !== 200 &&
86+
response.status !== 204 &&
87+
response.status !== 202
88+
) {
89+
const errorText = JSON.stringify(response.data);
90+
throw new Error(
6991
`API request failed with status ${response.status} (${response.statusText}). Server response: ${errorText}`,
7092
);
7193
}
7294

95+
if (response.status === 204 || response.status === 202) {
96+
return null;
97+
}
98+
99+
const jsonResp = response.data;
100+
101+
if (jsonResp.error) {
73102
const errResult = types.JSONRPCErrorSchema.safeParse(jsonResp);
74103
let message = `MCP request failed: ${JSON.stringify(jsonResp.error)}`;
75104
let code = 'MCP_ERROR';
76105

106+
if (errResult.success) {
107+
const err = errResult.data.error;
108+
message = `MCP request failed with code ${err.code}: ${err.message}`;
109+
code = String(err.code);
110+
111+
if (
112+
err.code === -32004 &&
77113
err.data &&
78114
typeof err.data === 'object' &&
79115
'supported' in err.data
80116
) {
81117
const supported = (err.data as Record<string, unknown>).supported;
118+
if (Array.isArray(supported) && supported.length > 0) {
119+
throw new ProtocolNegotiationError(supported[0]);
120+
}
121+
}
122+
}
123+
124+
throw new AxiosError(
82125
message,
83126
code,
84127
response.config,
@@ -88,6 +131,13 @@ export class McpHttpTransportV20260618 extends McpHttpTransportBase {
88131
}
89132

90133
// Parse Result
134+
if (!isNotification && 'getResultModel' in request) {
135+
const rpcRespResult = types.JSONRPCResponseSchema.safeParse(jsonResp);
136+
if (rpcRespResult.success) {
137+
const resultModel = request.getResultModel();
138+
return resultModel.parse(rpcRespResult.data.result);
139+
}
140+
throw new Error('Failed to parse JSON-RPC response structure');
91141
}
92142

93143
return null;
@@ -116,6 +166,13 @@ export class McpHttpTransportV20260618 extends McpHttpTransportBase {
116166
headers,
117167
);
118168

169+
if (!result) {
170+
const error = new Error('Failed to list tools: No response from server.');
171+
logApiError(`Error listing tools from ${url}`, error);
172+
throw error;
173+
}
174+
175+
if (this._serverVersion === null) {
119176
const error = new Error('Server version not available.');
120177
logApiError('Error listing tools', error);
121178
throw error;
@@ -145,6 +202,13 @@ export class McpHttpTransportV20260618 extends McpHttpTransportBase {
145202
headers?: Record<string, string>,
146203
): Promise<ZodManifest> {
147204
const manifest = await this.toolsList(undefined, headers);
205+
if (!manifest.tools[toolName]) {
206+
const error = new Error(`Tool '${toolName}' not found.`);
207+
logApiError(`Error getting tool ${toolName}`, error);
208+
throw error;
209+
}
210+
211+
return {
148212
serverVersion: manifest.serverVersion,
149213
tools: {
150214
[toolName]: manifest.tools[toolName],
@@ -159,6 +223,13 @@ export class McpHttpTransportV20260618 extends McpHttpTransportBase {
159223
): Promise<string> {
160224
await this.ensureInitialized(headers);
161225

226+
if (Object.keys(headers).length > 0) {
227+
warnIfHttpAndHeaders(this._mcpBaseUrl, headers);
228+
}
229+
230+
const params: types.CallToolRequestParams & {
231+
_meta?: Record<string, unknown>;
232+
} = {
162233
name: toolName,
163234
arguments: arguments_,
164235
_meta: this.#getMeta(),
@@ -171,6 +242,13 @@ export class McpHttpTransportV20260618 extends McpHttpTransportBase {
171242
headers,
172243
);
173244

245+
if (!result) {
246+
const error = new Error(
247+
`Failed to invoke tool '${toolName}': No response from server.`,
248+
);
249+
logApiError(`Error invoking tool ${toolName}`, error);
250+
throw error;
251+
}
174252

175253
return this.processToolResultContent(result.content);
176254
}

0 commit comments

Comments
 (0)