@mcpjam/inspector 0.2.3 → 0.3.0

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.
@@ -36,9 +36,9 @@ app.use((req, res, next) => {
36
36
  next();
37
37
  });
38
38
  const webAppTransports = new Map(); // Transports by sessionId
39
+ const backingServerTransports = new Map();
39
40
  const createTransport = async (req) => {
40
41
  const query = req.query;
41
- console.log("Query parameters:", query);
42
42
  const transportType = query.transportType;
43
43
  if (transportType === "stdio") {
44
44
  const command = query.command;
@@ -46,7 +46,7 @@ const createTransport = async (req) => {
46
46
  const queryEnv = query.env ? JSON.parse(query.env) : {};
47
47
  const env = { ...process.env, ...defaultEnvironment, ...queryEnv };
48
48
  const { cmd, args } = findActualExecutable(command, origArgs);
49
- console.log(`Stdio transport: command=${cmd}, args=${args}`);
49
+ console.log(`🚀 Stdio transport: command=${cmd}, args=${args}`);
50
50
  const transport = new StdioClientTransport({
51
51
  command: cmd,
52
52
  args,
@@ -54,7 +54,6 @@ const createTransport = async (req) => {
54
54
  stderr: "pipe",
55
55
  });
56
56
  await transport.start();
57
- console.log("Spawned stdio transport");
58
57
  return transport;
59
58
  }
60
59
  else if (transportType === "sse") {
@@ -69,7 +68,6 @@ const createTransport = async (req) => {
69
68
  const value = req.headers[key];
70
69
  headers[key] = Array.isArray(value) ? value[value.length - 1] : value;
71
70
  }
72
- console.log(`SSE transport: url=${url}, headers=${Object.keys(headers)}`);
73
71
  const transport = new SSEClientTransport(new URL(url), {
74
72
  eventSourceInit: {
75
73
  fetch: (url, init) => fetch(url, { ...init, headers }),
@@ -79,7 +77,6 @@ const createTransport = async (req) => {
79
77
  },
80
78
  });
81
79
  await transport.start();
82
- console.log("Connected to SSE transport");
83
80
  return transport;
84
81
  }
85
82
  else if (transportType === "streamable-http") {
@@ -99,18 +96,16 @@ const createTransport = async (req) => {
99
96
  },
100
97
  });
101
98
  await transport.start();
102
- console.log("Connected to Streamable HTTP transport");
103
99
  return transport;
104
100
  }
105
101
  else {
106
- console.error(`Invalid transport type: ${transportType}`);
102
+ console.error(`❌ Invalid transport type: ${transportType}`);
107
103
  throw new Error("Invalid transport type specified");
108
104
  }
109
105
  };
110
- let backingServerTransport;
111
106
  app.get("/mcp", async (req, res) => {
112
107
  const sessionId = req.headers["mcp-session-id"];
113
- console.log(`Received GET message for sessionId ${sessionId}`);
108
+ console.log(`📥 Received GET message for sessionId ${sessionId}`);
114
109
  try {
115
110
  const transport = webAppTransports.get(sessionId);
116
111
  if (!transport) {
@@ -122,45 +117,51 @@ app.get("/mcp", async (req, res) => {
122
117
  }
123
118
  }
124
119
  catch (error) {
125
- console.error("Error in /mcp route:", error);
120
+ console.error("Error in /mcp route:", error);
126
121
  res.status(500).json(error);
127
122
  }
128
123
  });
129
124
  app.post("/mcp", async (req, res) => {
130
125
  const sessionId = req.headers["mcp-session-id"];
131
- console.log(`Received POST message for sessionId ${sessionId}`);
126
+ console.log(`📥 Received POST message for sessionId ${sessionId}`);
132
127
  if (!sessionId) {
133
128
  try {
134
- console.log("New streamable-http connection");
129
+ console.log("🔄 New streamable-http connection");
130
+ let backingServerTransport;
135
131
  try {
136
- await backingServerTransport?.close();
137
132
  backingServerTransport = await createTransport(req);
138
133
  }
139
134
  catch (error) {
140
135
  if (error instanceof SseError && error.code === 401) {
141
- console.error("Received 401 Unauthorized from MCP server:", error.message);
136
+ console.error("🔒 Received 401 Unauthorized from MCP server:", error.message);
142
137
  res.status(401).json(error);
143
138
  return;
144
139
  }
145
140
  throw error;
146
141
  }
147
- console.log("Connected MCP client to backing server transport");
148
142
  const webAppTransport = new StreamableHTTPServerTransport({
149
143
  sessionIdGenerator: randomUUID,
150
- onsessioninitialized: (sessionId) => {
151
- webAppTransports.set(sessionId, webAppTransport);
152
- console.log("Created streamable web app transport " + sessionId);
144
+ onsessioninitialized: (newSessionId) => {
145
+ console.log("✨ Created streamable web app transport " + newSessionId);
146
+ webAppTransports.set(newSessionId, webAppTransport);
147
+ backingServerTransports.set(newSessionId, backingServerTransport);
148
+ console.log(`✨ Connected MCP client to backing server transport for session ${newSessionId}`);
149
+ mcpProxy({
150
+ transportToClient: webAppTransport,
151
+ transportToServer: backingServerTransport,
152
+ });
153
+ webAppTransport.onclose = () => {
154
+ console.log(`🧹 Cleaning up transports for session ${newSessionId}`);
155
+ webAppTransports.delete(newSessionId);
156
+ backingServerTransports.delete(newSessionId);
157
+ };
153
158
  },
154
159
  });
155
160
  await webAppTransport.start();
156
- mcpProxy({
157
- transportToClient: webAppTransport,
158
- transportToServer: backingServerTransport,
159
- });
160
161
  await webAppTransport.handleRequest(req, res, req.body);
161
162
  }
162
163
  catch (error) {
163
- console.error("Error in /mcp POST route:", error);
164
+ console.error("Error in /mcp POST route:", error);
164
165
  res.status(500).json(error);
165
166
  }
166
167
  }
@@ -175,86 +176,96 @@ app.post("/mcp", async (req, res) => {
175
176
  }
176
177
  }
177
178
  catch (error) {
178
- console.error("Error in /mcp route:", error);
179
+ console.error("Error in /mcp route:", error);
179
180
  res.status(500).json(error);
180
181
  }
181
182
  }
182
183
  });
183
184
  app.get("/stdio", async (req, res) => {
184
185
  try {
185
- console.log("New connection");
186
+ console.log("🔄 New stdio/sse connection");
187
+ const webAppTransport = new SSEServerTransport("/message", res);
188
+ const sessionId = webAppTransport.sessionId;
189
+ webAppTransports.set(sessionId, webAppTransport);
186
190
  try {
187
- await backingServerTransport?.close();
188
- backingServerTransport = await createTransport(req);
191
+ const backingServerTransport = await createTransport(req);
192
+ backingServerTransports.set(sessionId, backingServerTransport);
193
+ webAppTransport.onclose = () => {
194
+ console.log(`🧹 Cleaning up transports for session ${sessionId}`);
195
+ webAppTransports.delete(sessionId);
196
+ backingServerTransports.delete(sessionId);
197
+ };
198
+ await webAppTransport.start();
199
+ if (backingServerTransport instanceof StdioClientTransport) {
200
+ backingServerTransport.stderr.on("data", (chunk) => {
201
+ webAppTransport.send({
202
+ jsonrpc: "2.0",
203
+ method: "stderr",
204
+ params: {
205
+ data: chunk.toString(),
206
+ },
207
+ });
208
+ });
209
+ }
210
+ mcpProxy({
211
+ transportToClient: webAppTransport,
212
+ transportToServer: backingServerTransport,
213
+ });
214
+ console.log(`✨ Connected MCP client to backing server transport for session ${sessionId}`);
189
215
  }
190
216
  catch (error) {
191
217
  if (error instanceof SseError && error.code === 401) {
192
- console.error("Received 401 Unauthorized from MCP server:", error.message);
218
+ console.error("🔒 Received 401 Unauthorized from MCP server:", error.message);
193
219
  res.status(401).json(error);
194
220
  return;
195
221
  }
196
222
  throw error;
197
223
  }
198
- console.log("Connected MCP client to backing server transport");
199
- const webAppTransport = new SSEServerTransport("/message", res);
200
- webAppTransports.set(webAppTransport.sessionId, webAppTransport);
201
- console.log("Created web app transport");
202
- await webAppTransport.start();
203
- backingServerTransport.stderr.on("data", (chunk) => {
204
- webAppTransport.send({
205
- jsonrpc: "2.0",
206
- method: "notifications/stderr",
207
- params: {
208
- content: chunk.toString(),
209
- },
210
- });
211
- });
212
- mcpProxy({
213
- transportToClient: webAppTransport,
214
- transportToServer: backingServerTransport,
215
- });
216
- console.log("Set up MCP proxy");
217
224
  }
218
225
  catch (error) {
219
- console.error("Error in /stdio route:", error);
220
- res.status(500).json(error);
226
+ console.error("Error in /stdio route:", error);
227
+ // Can't send a 500 response if headers already sent (which they are for SSE)
221
228
  }
222
229
  });
223
230
  app.get("/sse", async (req, res) => {
224
231
  try {
225
- console.log("New SSE connection. NOTE: The sse transport is deprecated and has been replaced by streamable-http");
232
+ console.log("🔄 New sse connection");
233
+ const webAppTransport = new SSEServerTransport("/message", res);
234
+ const sessionId = webAppTransport.sessionId;
235
+ webAppTransports.set(sessionId, webAppTransport);
226
236
  try {
227
- await backingServerTransport?.close();
228
- backingServerTransport = await createTransport(req);
237
+ const backingServerTransport = await createTransport(req);
238
+ backingServerTransports.set(sessionId, backingServerTransport);
239
+ webAppTransport.onclose = () => {
240
+ console.log(`🧹 Cleaning up transports for session ${sessionId}`);
241
+ webAppTransports.delete(sessionId);
242
+ backingServerTransports.delete(sessionId);
243
+ };
244
+ await webAppTransport.start();
245
+ mcpProxy({
246
+ transportToClient: webAppTransport,
247
+ transportToServer: backingServerTransport,
248
+ });
249
+ console.log(`✨ Connected MCP client to backing server transport for session ${sessionId}`);
229
250
  }
230
251
  catch (error) {
231
252
  if (error instanceof SseError && error.code === 401) {
232
- console.error("Received 401 Unauthorized from MCP server:", error.message);
253
+ console.error("🔒 Received 401 Unauthorized from MCP server:", error.message);
233
254
  res.status(401).json(error);
234
255
  return;
235
256
  }
236
257
  throw error;
237
258
  }
238
- console.log("Connected MCP client to backing server transport");
239
- const webAppTransport = new SSEServerTransport("/message", res);
240
- webAppTransports.set(webAppTransport.sessionId, webAppTransport);
241
- console.log("Created web app transport");
242
- await webAppTransport.start();
243
- mcpProxy({
244
- transportToClient: webAppTransport,
245
- transportToServer: backingServerTransport,
246
- });
247
- console.log("Set up MCP proxy");
248
259
  }
249
260
  catch (error) {
250
- console.error("Error in /sse route:", error);
251
- res.status(500).json(error);
261
+ console.error("Error in /sse route:", error);
262
+ // Can't send a 500 response if headers already sent (which they are for SSE)
252
263
  }
253
264
  });
254
265
  app.post("/message", async (req, res) => {
255
266
  try {
256
267
  const sessionId = req.query.sessionId;
257
- console.log(`Received message for sessionId ${sessionId}`);
268
+ console.log(`📥 Received message for sessionId ${sessionId}`);
258
269
  const transport = webAppTransports.get(sessionId);
259
270
  if (!transport) {
260
271
  res.status(404).end("Session not found");
@@ -263,7 +274,7 @@ app.post("/message", async (req, res) => {
263
274
  await transport.handlePostMessage(req, res);
264
275
  }
265
276
  catch (error) {
266
- console.error("Error in /message route:", error);
277
+ console.error("Error in /message route:", error);
267
278
  res.status(500).json(error);
268
279
  }
269
280
  });
@@ -281,7 +292,7 @@ app.get("/config", (req, res) => {
281
292
  });
282
293
  }
283
294
  catch (error) {
284
- console.error("Error in /config route:", error);
295
+ console.error("Error in /config route:", error);
285
296
  res.status(500).json(error);
286
297
  }
287
298
  });
@@ -295,10 +306,12 @@ const findAvailablePort = async (startPort) => {
295
306
  resolve(port);
296
307
  });
297
308
  });
298
- server.on('error', (err) => {
299
- if (err.code === 'EADDRINUSE') {
309
+ server.on("error", (err) => {
310
+ if (err.code === "EADDRINUSE") {
300
311
  // Port is in use, try the next one
301
- findAvailablePort(startPort + 1).then(resolve).catch(reject);
312
+ findAvailablePort(startPort + 1)
313
+ .then(resolve)
314
+ .catch(reject);
302
315
  }
303
316
  else {
304
317
  reject(err);
@@ -316,7 +329,7 @@ const startServer = async () => {
316
329
  if (availablePort !== Number(PORT)) {
317
330
  console.log(`⚠️ Port ${PORT} was in use, using available port ${availablePort} instead`);
318
331
  }
319
- console.log(`⚙️ Proxy server listening on port ${availablePort}`);
332
+ console.log(`\x1b[32m%s\x1b[0m`, `⚙️ Proxy server listening on port ${availablePort}`);
320
333
  });
321
334
  server.on("error", (err) => {
322
335
  console.error(`❌ Server error: ${err.message}`);