@modelcontextprotocol/inspector 0.16.5 → 0.16.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,8 +5,8 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/mcp.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>MCP Inspector</title>
8
- <script type="module" crossorigin src="/assets/index-BIvDs4Sc.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-BWZfLo5E.css">
8
+ <script type="module" crossorigin src="/assets/index-Dg7mXHRE.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-DZ1A0VH0.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root" class="w-full"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modelcontextprotocol/inspector",
3
- "version": "0.16.5",
3
+ "version": "0.16.7",
4
4
  "description": "Model Context Protocol inspector",
5
5
  "license": "MIT",
6
6
  "author": "Anthropic, PBC (https://anthropic.com)",
@@ -41,16 +41,16 @@
41
41
  "prettier-fix": "prettier --write .",
42
42
  "prettier-check": "prettier --check .",
43
43
  "lint": "prettier --check . && cd client && npm run lint",
44
- "prepare": "npm run build",
44
+ "prepare": "husky && npm run build",
45
45
  "publish-all": "npm publish --workspaces --access public && npm publish --access public",
46
46
  "update-version": "node scripts/update-version.js",
47
47
  "check-version": "node scripts/check-version-consistency.js"
48
48
  },
49
49
  "dependencies": {
50
- "@modelcontextprotocol/inspector-cli": "^0.16.5",
51
- "@modelcontextprotocol/inspector-client": "^0.16.5",
52
- "@modelcontextprotocol/inspector-server": "^0.16.5",
53
- "@modelcontextprotocol/sdk": "^1.17.3",
50
+ "@modelcontextprotocol/inspector-cli": "^0.16.7",
51
+ "@modelcontextprotocol/inspector-client": "^0.16.7",
52
+ "@modelcontextprotocol/inspector-server": "^0.16.7",
53
+ "@modelcontextprotocol/sdk": "^1.18.0",
54
54
  "concurrently": "^9.2.0",
55
55
  "open": "^10.2.0",
56
56
  "shell-quote": "^1.8.3",
@@ -63,12 +63,19 @@
63
63
  "@types/jest": "^29.5.14",
64
64
  "@types/node": "^22.17.0",
65
65
  "@types/shell-quote": "^1.7.5",
66
+ "husky": "^9.1.7",
66
67
  "jest-fixed-jsdom": "^0.0.9",
68
+ "lint-staged": "^16.1.5",
67
69
  "prettier": "^3.6.2",
68
70
  "rimraf": "^6.0.1",
69
71
  "typescript": "^5.4.2"
70
72
  },
71
73
  "engines": {
72
74
  "node": ">=22.7.5"
75
+ },
76
+ "lint-staged": {
77
+ "**/*.{js,ts,jsx,tsx,json,md}": [
78
+ "prettier --write"
79
+ ]
73
80
  }
74
81
  }
@@ -12,12 +12,6 @@ import { findActualExecutable } from "spawn-rx";
12
12
  import mcpProxy from "./mcpProxy.js";
13
13
  import { randomUUID, randomBytes, timingSafeEqual } from "node:crypto";
14
14
  const DEFAULT_MCP_PROXY_LISTEN_PORT = "6277";
15
- const SSE_HEADERS_PASSTHROUGH = ["authorization"];
16
- const STREAMABLE_HTTP_HEADERS_PASSTHROUGH = [
17
- "authorization",
18
- "mcp-session-id",
19
- "last-event-id",
20
- ];
21
15
  const defaultEnvironment = {
22
16
  ...getDefaultEnvironment(),
23
17
  ...(process.env.MCP_ENV_VARS ? JSON.parse(process.env.MCP_ENV_VARS) : {}),
@@ -33,34 +27,71 @@ const { values } = parseArgs({
33
27
  },
34
28
  });
35
29
  // Function to get HTTP headers.
36
- // Supports only "sse" and "streamable-http" transport types.
37
- const getHttpHeaders = (req, transportType) => {
38
- const headers = {
39
- Accept: transportType === "sse"
40
- ? "text/event-stream"
41
- : "text/event-stream, application/json",
42
- };
43
- const defaultHeaders = transportType === "sse"
44
- ? SSE_HEADERS_PASSTHROUGH
45
- : STREAMABLE_HTTP_HEADERS_PASSTHROUGH;
46
- for (const key of defaultHeaders) {
47
- if (req.headers[key] === undefined) {
48
- continue;
30
+ const getHttpHeaders = (req) => {
31
+ const headers = {};
32
+ // Iterate over all headers in the request
33
+ for (const key in req.headers) {
34
+ const lowerKey = key.toLowerCase();
35
+ // Check if the header is one we want to forward
36
+ if (lowerKey.startsWith("mcp-") ||
37
+ lowerKey === "authorization" ||
38
+ lowerKey === "last-event-id") {
39
+ // Exclude the proxy's own authentication header
40
+ if (lowerKey !== "x-mcp-proxy-auth") {
41
+ const value = req.headers[key];
42
+ if (typeof value === "string") {
43
+ // If the value is a string, use it directly
44
+ headers[key] = value;
45
+ }
46
+ else if (Array.isArray(value)) {
47
+ // If the value is an array, use the last element
48
+ const lastValue = value.at(-1);
49
+ if (lastValue !== undefined) {
50
+ headers[key] = lastValue;
51
+ }
52
+ }
53
+ // If value is undefined, it's skipped, which is correct.
54
+ }
49
55
  }
50
- const value = req.headers[key];
51
- headers[key] = Array.isArray(value) ? value[value.length - 1] : value;
52
56
  }
53
- // If the header "x-custom-auth-header" is present, use its value as the custom header name.
54
- if (req.headers["x-custom-auth-header"] !== undefined) {
55
- const customHeaderName = req.headers["x-custom-auth-header"];
56
- const lowerCaseHeaderName = customHeaderName.toLowerCase();
57
- if (req.headers[lowerCaseHeaderName] !== undefined) {
58
- const value = req.headers[lowerCaseHeaderName];
59
- headers[customHeaderName] = value;
57
+ // Handle the custom auth header separately. We expect `x-custom-auth-header`
58
+ // to be a string containing the name of the actual authentication header.
59
+ const customAuthHeaderName = req.headers["x-custom-auth-header"];
60
+ if (typeof customAuthHeaderName === "string") {
61
+ const lowerCaseHeaderName = customAuthHeaderName.toLowerCase();
62
+ const value = req.headers[lowerCaseHeaderName];
63
+ if (typeof value === "string") {
64
+ headers[customAuthHeaderName] = value;
65
+ }
66
+ else if (Array.isArray(value)) {
67
+ // If the actual auth header was sent multiple times, use the last value.
68
+ const lastValue = value.at(-1);
69
+ if (lastValue !== undefined) {
70
+ headers[customAuthHeaderName] = lastValue;
71
+ }
60
72
  }
61
73
  }
62
74
  return headers;
63
75
  };
76
+ /**
77
+ * Updates a headers object in-place, preserving the original Accept header.
78
+ * This is necessary to ensure that transports holding a reference to the headers
79
+ * object see the updates.
80
+ * @param currentHeaders The headers object to update.
81
+ * @param newHeaders The new headers to apply.
82
+ */
83
+ const updateHeadersInPlace = (currentHeaders, newHeaders) => {
84
+ // Preserve the Accept header, which is set at transport creation and
85
+ // is not present in subsequent client requests.
86
+ const accept = currentHeaders["Accept"];
87
+ // Clear the old headers and apply the new ones.
88
+ Object.keys(currentHeaders).forEach((key) => delete currentHeaders[key]);
89
+ Object.assign(currentHeaders, newHeaders);
90
+ // Restore the Accept header.
91
+ if (accept) {
92
+ currentHeaders["Accept"] = accept;
93
+ }
94
+ };
64
95
  const app = express();
65
96
  app.use(cors());
66
97
  app.use((req, res, next) => {
@@ -69,6 +100,7 @@ app.use((req, res, next) => {
69
100
  });
70
101
  const webAppTransports = new Map(); // Web app transports by web app sessionId
71
102
  const serverTransports = new Map(); // Server Transports by web app sessionId
103
+ const sessionHeaderHolders = new Map(); // For dynamic header updates
72
104
  // Use provided token from environment or generate a new one
73
105
  const sessionToken = process.env.MCP_PROXY_AUTH_TOKEN || randomBytes(32).toString("hex");
74
106
  const authDisabled = !!process.env.DANGEROUSLY_OMIT_AUTH;
@@ -126,12 +158,33 @@ const authMiddleware = (req, res, next) => {
126
158
  }
127
159
  next();
128
160
  };
161
+ /**
162
+ * Creates a `fetch` function that merges dynamic session headers with the
163
+ * headers from the actual request, ensuring that request-specific headers like
164
+ * `Content-Type` are preserved.
165
+ */
166
+ const createCustomFetch = (headerHolder) => {
167
+ return (input, init) => {
168
+ // Determine the headers from the original request/init.
169
+ // The SDK may pass a Request object or a URL and an init object.
170
+ const originalHeaders = input instanceof Request ? input.headers : init?.headers;
171
+ // Start with our dynamic session headers.
172
+ const finalHeaders = new Headers(headerHolder.headers);
173
+ // Merge the SDK's request-specific headers, letting them overwrite.
174
+ // This is crucial for preserving Content-Type on POST requests.
175
+ new Headers(originalHeaders).forEach((value, key) => {
176
+ finalHeaders.set(key, value);
177
+ });
178
+ // This works for both `fetch(url, init)` and `fetch(request)` style calls.
179
+ return fetch(input, { ...init, headers: finalHeaders });
180
+ };
181
+ };
129
182
  const createTransport = async (req) => {
130
183
  const query = req.query;
131
184
  console.log("Query parameters:", JSON.stringify(query));
132
185
  const transportType = query.transportType;
133
186
  if (transportType === "stdio") {
134
- const command = query.command;
187
+ const command = query.command.trim();
135
188
  const origArgs = shellParseArgs(query.args);
136
189
  const queryEnv = query.env ? JSON.parse(query.env) : {};
137
190
  const env = { ...defaultEnvironment, ...process.env, ...queryEnv };
@@ -144,32 +197,35 @@ const createTransport = async (req) => {
144
197
  stderr: "pipe",
145
198
  });
146
199
  await transport.start();
147
- return transport;
200
+ return { transport };
148
201
  }
149
202
  else if (transportType === "sse") {
150
203
  const url = query.url;
151
- const headers = getHttpHeaders(req, transportType);
204
+ const headers = getHttpHeaders(req);
205
+ headers["Accept"] = "text/event-stream";
206
+ const headerHolder = { headers };
152
207
  console.log(`SSE transport: url=${url}, headers=${JSON.stringify(headers)}`);
153
208
  const transport = new SSEClientTransport(new URL(url), {
154
209
  eventSourceInit: {
155
- fetch: (url, init) => fetch(url, { ...init, headers }),
210
+ fetch: createCustomFetch(headerHolder),
156
211
  },
157
212
  requestInit: {
158
- headers,
213
+ headers: headerHolder.headers,
159
214
  },
160
215
  });
161
216
  await transport.start();
162
- return transport;
217
+ return { transport, headerHolder };
163
218
  }
164
219
  else if (transportType === "streamable-http") {
165
- const headers = getHttpHeaders(req, transportType);
220
+ const headers = getHttpHeaders(req);
221
+ headers["Accept"] = "text/event-stream, application/json";
222
+ const headerHolder = { headers };
166
223
  const transport = new StreamableHTTPClientTransport(new URL(query.url), {
167
- requestInit: {
168
- headers,
169
- },
224
+ // Pass a custom fetch to inject the latest headers on each request
225
+ fetch: createCustomFetch(headerHolder),
170
226
  });
171
227
  await transport.start();
172
- return transport;
228
+ return { transport, headerHolder };
173
229
  }
174
230
  else {
175
231
  console.error(`Invalid transport type: ${transportType}`);
@@ -179,6 +235,10 @@ const createTransport = async (req) => {
179
235
  app.get("/mcp", originValidationMiddleware, authMiddleware, async (req, res) => {
180
236
  const sessionId = req.headers["mcp-session-id"];
181
237
  console.log(`Received GET message for sessionId ${sessionId}`);
238
+ const headerHolder = sessionHeaderHolders.get(sessionId);
239
+ if (headerHolder) {
240
+ updateHeadersInPlace(headerHolder.headers, getHttpHeaders(req));
241
+ }
182
242
  try {
183
243
  const transport = webAppTransports.get(sessionId);
184
244
  if (!transport) {
@@ -196,29 +256,45 @@ app.get("/mcp", originValidationMiddleware, authMiddleware, async (req, res) =>
196
256
  });
197
257
  app.post("/mcp", originValidationMiddleware, authMiddleware, async (req, res) => {
198
258
  const sessionId = req.headers["mcp-session-id"];
199
- let serverTransport;
200
- if (!sessionId) {
259
+ if (sessionId) {
260
+ console.log(`Received POST message for sessionId ${sessionId}`);
261
+ const headerHolder = sessionHeaderHolders.get(sessionId);
262
+ if (headerHolder) {
263
+ updateHeadersInPlace(headerHolder.headers, getHttpHeaders(req));
264
+ }
201
265
  try {
202
- console.log("New StreamableHttp connection request");
203
- try {
204
- serverTransport = await createTransport(req);
266
+ const transport = webAppTransports.get(sessionId);
267
+ if (!transport) {
268
+ res.status(404).end("Transport not found for sessionId " + sessionId);
205
269
  }
206
- catch (error) {
207
- if (error instanceof SseError && error.code === 401) {
208
- console.error("Received 401 Unauthorized from MCP server:", error.message);
209
- res.status(401).json(error);
210
- return;
211
- }
212
- throw error;
270
+ else {
271
+ await transport.handleRequest(req, res);
213
272
  }
214
- console.log("Created StreamableHttp server transport");
273
+ }
274
+ catch (error) {
275
+ console.error("Error in /mcp route:", error);
276
+ res.status(500).json(error);
277
+ }
278
+ }
279
+ else {
280
+ console.log("New StreamableHttp connection request");
281
+ try {
282
+ const { transport: serverTransport, headerHolder } = await createTransport(req);
215
283
  const webAppTransport = new StreamableHTTPServerTransport({
216
284
  sessionIdGenerator: randomUUID,
217
285
  onsessioninitialized: (sessionId) => {
218
286
  webAppTransports.set(sessionId, webAppTransport);
219
- serverTransports.set(sessionId, serverTransport);
287
+ serverTransports.set(sessionId, serverTransport); // eslint-disable-line @typescript-eslint/no-non-null-assertion
288
+ if (headerHolder) {
289
+ sessionHeaderHolders.set(sessionId, headerHolder);
290
+ }
220
291
  console.log("Client <-> Proxy sessionId: " + sessionId);
221
292
  },
293
+ onsessionclosed: (sessionId) => {
294
+ webAppTransports.delete(sessionId);
295
+ serverTransports.delete(sessionId);
296
+ sessionHeaderHolders.delete(sessionId);
297
+ },
222
298
  });
223
299
  console.log("Created StreamableHttp client transport");
224
300
  await webAppTransport.start();
@@ -229,23 +305,12 @@ app.post("/mcp", originValidationMiddleware, authMiddleware, async (req, res) =>
229
305
  await webAppTransport.handleRequest(req, res, req.body);
230
306
  }
231
307
  catch (error) {
232
- console.error("Error in /mcp POST route:", error);
233
- res.status(500).json(error);
234
- }
235
- }
236
- else {
237
- console.log(`Received POST message for sessionId ${sessionId}`);
238
- try {
239
- const transport = webAppTransports.get(sessionId);
240
- if (!transport) {
241
- res.status(404).end("Transport not found for sessionId " + sessionId);
242
- }
243
- else {
244
- await transport.handleRequest(req, res);
308
+ if (error instanceof SseError && error.code === 401) {
309
+ console.error("Received 401 Unauthorized from MCP server:", error.message);
310
+ res.status(401).json(error);
311
+ return;
245
312
  }
246
- }
247
- catch (error) {
248
- console.error("Error in /mcp route:", error);
313
+ console.error("Error in /mcp POST route:", error);
249
314
  res.status(500).json(error);
250
315
  }
251
316
  }
@@ -253,10 +318,9 @@ app.post("/mcp", originValidationMiddleware, authMiddleware, async (req, res) =>
253
318
  app.delete("/mcp", originValidationMiddleware, authMiddleware, async (req, res) => {
254
319
  const sessionId = req.headers["mcp-session-id"];
255
320
  console.log(`Received DELETE message for sessionId ${sessionId}`);
256
- let serverTransport;
257
321
  if (sessionId) {
258
322
  try {
259
- serverTransport = serverTransports.get(sessionId);
323
+ const serverTransport = serverTransports.get(sessionId);
260
324
  if (!serverTransport) {
261
325
  res.status(404).end("Transport not found for sessionId " + sessionId);
262
326
  }
@@ -264,6 +328,7 @@ app.delete("/mcp", originValidationMiddleware, authMiddleware, async (req, res)
264
328
  await serverTransport.terminateSession();
265
329
  webAppTransports.delete(sessionId);
266
330
  serverTransports.delete(sessionId);
331
+ sessionHeaderHolders.delete(sessionId);
267
332
  console.log(`Transports removed for sessionId ${sessionId}`);
268
333
  }
269
334
  res.status(200).end();
@@ -277,45 +342,91 @@ app.delete("/mcp", originValidationMiddleware, authMiddleware, async (req, res)
277
342
  app.get("/stdio", originValidationMiddleware, authMiddleware, async (req, res) => {
278
343
  try {
279
344
  console.log("New STDIO connection request");
280
- let serverTransport;
281
- try {
282
- serverTransport = await createTransport(req);
283
- console.log("Created server transport");
284
- }
285
- catch (error) {
286
- if (error instanceof SseError && error.code === 401) {
287
- console.error("Received 401 Unauthorized from MCP server. Authentication failure.");
288
- res.status(401).json(error);
289
- return;
290
- }
291
- throw error;
292
- }
293
- const webAppTransport = new SSEServerTransport("/message", res);
294
- console.log("Created client transport");
345
+ const { transport: serverTransport } = await createTransport(req);
346
+ const proxyFullAddress = req.query.proxyFullAddress || "";
347
+ const prefix = proxyFullAddress || "";
348
+ const endpoint = `${prefix}/message`;
349
+ const webAppTransport = new SSEServerTransport(endpoint, res);
295
350
  webAppTransports.set(webAppTransport.sessionId, webAppTransport);
351
+ console.log("Created client transport");
296
352
  serverTransports.set(webAppTransport.sessionId, serverTransport);
353
+ console.log("Created server transport");
297
354
  await webAppTransport.start();
298
355
  serverTransport.stderr.on("data", (chunk) => {
299
356
  if (chunk.toString().includes("MODULE_NOT_FOUND")) {
357
+ // Server command not found, remove transports
358
+ const message = "Command not found, transports removed";
300
359
  webAppTransport.send({
301
360
  jsonrpc: "2.0",
302
- method: "notifications/stderr",
361
+ method: "notifications/message",
303
362
  params: {
304
- content: "Command not found, transports removed",
363
+ level: "emergency",
364
+ logger: "proxy",
365
+ data: {
366
+ message,
367
+ },
305
368
  },
306
369
  });
307
370
  webAppTransport.close();
308
371
  serverTransport.close();
309
372
  webAppTransports.delete(webAppTransport.sessionId);
310
373
  serverTransports.delete(webAppTransport.sessionId);
311
- console.error("Command not found, transports removed");
374
+ sessionHeaderHolders.delete(webAppTransport.sessionId);
375
+ console.error(message);
312
376
  }
313
377
  else {
378
+ // Inspect message and attempt to assign a RFC 5424 Syslog Protocol level
379
+ let level;
380
+ let message = chunk.toString().trim();
381
+ let ucMsg = chunk.toString().toUpperCase();
382
+ if (ucMsg.includes("DEBUG")) {
383
+ level = "debug";
384
+ }
385
+ else if (ucMsg.includes("INFO")) {
386
+ level = "info";
387
+ }
388
+ else if (ucMsg.includes("NOTICE")) {
389
+ level = "notice";
390
+ }
391
+ else if (ucMsg.includes("WARN")) {
392
+ level = "warning";
393
+ }
394
+ else if (ucMsg.includes("ERROR")) {
395
+ level = "error";
396
+ }
397
+ else if (ucMsg.includes("CRITICAL")) {
398
+ level = "critical";
399
+ }
400
+ else if (ucMsg.includes("ALERT")) {
401
+ level = "alert";
402
+ }
403
+ else if (ucMsg.includes("EMERGENCY")) {
404
+ level = "emergency";
405
+ }
406
+ else if (ucMsg.includes("SIGINT")) {
407
+ message = "SIGINT received. Server shutdown.";
408
+ level = "emergency";
409
+ }
410
+ else if (ucMsg.includes("SIGHUP")) {
411
+ message = "SIGHUP received. Server shutdown.";
412
+ level = "emergency";
413
+ }
414
+ else if (ucMsg.includes("SIGTERM")) {
415
+ message = "SIGTERM received. Server shutdown.";
416
+ level = "emergency";
417
+ }
418
+ else {
419
+ level = "info";
420
+ }
314
421
  webAppTransport.send({
315
422
  jsonrpc: "2.0",
316
- method: "notifications/stderr",
423
+ method: "notifications/message",
317
424
  params: {
318
- content: chunk.toString(),
425
+ level,
426
+ logger: "stdio",
427
+ data: {
428
+ message,
429
+ },
319
430
  },
320
431
  });
321
432
  }
@@ -326,50 +437,51 @@ app.get("/stdio", originValidationMiddleware, authMiddleware, async (req, res) =
326
437
  });
327
438
  }
328
439
  catch (error) {
440
+ if (error instanceof SseError && error.code === 401) {
441
+ console.error("Received 401 Unauthorized from MCP server. Authentication failure.");
442
+ res.status(401).json(error);
443
+ return;
444
+ }
329
445
  console.error("Error in /stdio route:", error);
330
446
  res.status(500).json(error);
331
447
  }
332
448
  });
333
449
  app.get("/sse", originValidationMiddleware, authMiddleware, async (req, res) => {
334
450
  try {
335
- console.log("New SSE connection request. NOTE: The sse transport is deprecated and has been replaced by StreamableHttp");
336
- let serverTransport;
337
- try {
338
- serverTransport = await createTransport(req);
339
- }
340
- catch (error) {
341
- if (error instanceof SseError && error.code === 401) {
342
- console.error("Received 401 Unauthorized from MCP server. Authentication failure.");
343
- res.status(401).json(error);
344
- return;
345
- }
346
- else if (error instanceof SseError && error.code === 404) {
347
- console.error("Received 404 not found from MCP server. Does the MCP server support SSE?");
348
- res.status(404).json(error);
349
- return;
350
- }
351
- else if (JSON.stringify(error).includes("ECONNREFUSED")) {
352
- console.error("Connection refused. Is the MCP server running?");
353
- res.status(500).json(error);
354
- }
355
- else {
356
- throw error;
357
- }
358
- }
359
- if (serverTransport) {
360
- const webAppTransport = new SSEServerTransport("/message", res);
361
- webAppTransports.set(webAppTransport.sessionId, webAppTransport);
362
- console.log("Created client transport");
363
- serverTransports.set(webAppTransport.sessionId, serverTransport);
364
- console.log("Created server transport");
365
- await webAppTransport.start();
366
- mcpProxy({
367
- transportToClient: webAppTransport,
368
- transportToServer: serverTransport,
369
- });
451
+ console.log("New SSE connection request. NOTE: The SSE transport is deprecated and has been replaced by StreamableHttp");
452
+ const { transport: serverTransport, headerHolder } = await createTransport(req);
453
+ const proxyFullAddress = req.query.proxyFullAddress || "";
454
+ const prefix = proxyFullAddress || "";
455
+ const endpoint = `${prefix}/message`;
456
+ const webAppTransport = new SSEServerTransport(endpoint, res);
457
+ webAppTransports.set(webAppTransport.sessionId, webAppTransport);
458
+ console.log("Created client transport");
459
+ serverTransports.set(webAppTransport.sessionId, serverTransport); // eslint-disable-line @typescript-eslint/no-non-null-assertion
460
+ if (headerHolder) {
461
+ sessionHeaderHolders.set(webAppTransport.sessionId, headerHolder);
370
462
  }
463
+ console.log("Created server transport");
464
+ await webAppTransport.start();
465
+ mcpProxy({
466
+ transportToClient: webAppTransport,
467
+ transportToServer: serverTransport,
468
+ });
371
469
  }
372
470
  catch (error) {
471
+ if (error instanceof SseError && error.code === 401) {
472
+ console.error("Received 401 Unauthorized from MCP server. Authentication failure.");
473
+ res.status(401).json(error);
474
+ return;
475
+ }
476
+ else if (error instanceof SseError && error.code === 404) {
477
+ console.error("Received 404 not found from MCP server. Does the MCP server support SSE?");
478
+ res.status(404).json(error);
479
+ return;
480
+ }
481
+ else if (JSON.stringify(error).includes("ECONNREFUSED")) {
482
+ console.error("Connection refused. Is the MCP server running?");
483
+ res.status(500).json(error);
484
+ }
373
485
  console.error("Error in /sse route:", error);
374
486
  res.status(500).json(error);
375
487
  }
@@ -378,6 +490,10 @@ app.post("/message", originValidationMiddleware, authMiddleware, async (req, res
378
490
  try {
379
491
  const sessionId = req.query.sessionId;
380
492
  console.log(`Received POST message for sessionId ${sessionId}`);
493
+ const headerHolder = sessionHeaderHolders.get(sessionId);
494
+ if (headerHolder) {
495
+ updateHeadersInPlace(headerHolder.headers, getHttpHeaders(req));
496
+ }
381
497
  const transport = webAppTransports.get(sessionId);
382
498
  if (!transport) {
383
499
  res.status(404).end("Session not found");