@modelcontextprotocol/inspector 0.3.0 → 0.4.1

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.
@@ -835,7 +835,7 @@
835
835
  --tw-contain-paint: ;
836
836
  --tw-contain-style: ;
837
837
  }/*
838
- ! tailwindcss v3.4.16 | MIT License | https://tailwindcss.com
838
+ ! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com
839
839
  *//*
840
840
  1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
841
841
  2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
@@ -1435,8 +1435,8 @@ video {
1435
1435
  .w-9 {
1436
1436
  width: 2.25rem;
1437
1437
  }
1438
- .w-\[120px\] {
1439
- width: 120px;
1438
+ .w-\[100px\] {
1439
+ width: 100px;
1440
1440
  }
1441
1441
  .w-full {
1442
1442
  width: 100%;
@@ -1456,6 +1456,9 @@ video {
1456
1456
  .flex-shrink-0 {
1457
1457
  flex-shrink: 0;
1458
1458
  }
1459
+ .shrink-0 {
1460
+ flex-shrink: 0;
1461
+ }
1459
1462
  .transform {
1460
1463
  transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
1461
1464
  }
@@ -1488,9 +1491,6 @@ video {
1488
1491
  .grid-cols-3 {
1489
1492
  grid-template-columns: repeat(3, minmax(0, 1fr));
1490
1493
  }
1491
- .grid-cols-\[1fr\2c auto\] {
1492
- grid-template-columns: 1fr auto;
1493
- }
1494
1494
  .flex-col {
1495
1495
  flex-direction: column;
1496
1496
  }
@@ -1514,11 +1514,6 @@ video {
1514
1514
  margin-right: calc(0.5rem * var(--tw-space-x-reverse));
1515
1515
  margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse)));
1516
1516
  }
1517
- .space-y-1 > :not([hidden]) ~ :not([hidden]) {
1518
- --tw-space-y-reverse: 0;
1519
- margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse)));
1520
- margin-bottom: calc(0.25rem * var(--tw-space-y-reverse));
1521
- }
1522
1517
  .space-y-2 > :not([hidden]) ~ :not([hidden]) {
1523
1518
  --tw-space-y-reverse: 0;
1524
1519
  margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse)));
@@ -1707,6 +1702,9 @@ video {
1707
1702
  padding-top: 1.5rem;
1708
1703
  padding-bottom: 1.5rem;
1709
1704
  }
1705
+ .pb-4 {
1706
+ padding-bottom: 1rem;
1707
+ }
1710
1708
  .pl-2 {
1711
1709
  padding-left: 0.5rem;
1712
1710
  }
@@ -1793,6 +1791,10 @@ video {
1793
1791
  --tw-text-opacity: 1;
1794
1792
  color: rgb(55 65 81 / var(--tw-text-opacity, 1));
1795
1793
  }
1794
+ .text-gray-800 {
1795
+ --tw-text-opacity: 1;
1796
+ color: rgb(31 41 55 / var(--tw-text-opacity, 1));
1797
+ }
1796
1798
  .text-gray-900 {
1797
1799
  --tw-text-opacity: 1;
1798
1800
  color: rgb(17 24 39 / var(--tw-text-opacity, 1));
@@ -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-BKN3G2r3.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-DxcZhiGz.css">
8
+ <script type="module" crossorigin src="/assets/index-DVugRvpa.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-eOhPEcGC.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modelcontextprotocol/inspector",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "Model Context Protocol inspector",
5
5
  "license": "MIT",
6
6
  "author": "Anthropic, PBC (https://anthropic.com)",
@@ -33,14 +33,16 @@
33
33
  "publish-all": "npm publish --workspaces --access public && npm publish --access public"
34
34
  },
35
35
  "dependencies": {
36
- "@modelcontextprotocol/inspector-client": "0.3.0",
37
- "@modelcontextprotocol/inspector-server": "0.3.0",
36
+ "@modelcontextprotocol/inspector-client": "0.4.1",
37
+ "@modelcontextprotocol/inspector-server": "0.4.1",
38
38
  "concurrently": "^9.0.1",
39
+ "shell-quote": "^1.8.2",
39
40
  "spawn-rx": "^5.1.0",
40
41
  "ts-node": "^10.9.2"
41
42
  },
42
43
  "devDependencies": {
43
44
  "@types/node": "^22.7.5",
45
+ "@types/shell-quote": "^1.7.5",
44
46
  "prettier": "3.3.3"
45
47
  }
46
48
  }
@@ -1,16 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  import cors from "cors";
3
- import EventSource from "eventsource";
4
3
  import { parseArgs } from "node:util";
5
- import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
4
+ import { parse as shellParseArgs } from "shell-quote";
5
+ import { SSEClientTransport, SseError, } from "@modelcontextprotocol/sdk/client/sse.js";
6
6
  import { StdioClientTransport, getDefaultEnvironment, } from "@modelcontextprotocol/sdk/client/stdio.js";
7
7
  import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
8
8
  import express from "express";
9
- import mcpProxy from "./mcpProxy.js";
10
9
  import { findActualExecutable } from "spawn-rx";
11
- // Polyfill EventSource for an SSE client in Node.js
12
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
13
- global.EventSource = EventSource;
10
+ import mcpProxy from "./mcpProxy.js";
11
+ const SSE_HEADERS_PASSTHROUGH = ["authorization"];
12
+ const defaultEnvironment = {
13
+ ...getDefaultEnvironment(),
14
+ ...(process.env.MCP_ENV_VARS ? JSON.parse(process.env.MCP_ENV_VARS) : {}),
15
+ };
14
16
  const { values } = parseArgs({
15
17
  args: process.argv.slice(2),
16
18
  options: {
@@ -21,15 +23,17 @@ const { values } = parseArgs({
21
23
  const app = express();
22
24
  app.use(cors());
23
25
  let webAppTransports = [];
24
- const createTransport = async (query) => {
26
+ const createTransport = async (req) => {
27
+ const query = req.query;
25
28
  console.log("Query parameters:", query);
26
29
  const transportType = query.transportType;
27
30
  if (transportType === "stdio") {
28
31
  const command = query.command;
29
- const origArgs = query.args.split(/\s+/);
30
- const env = query.env ? JSON.parse(query.env) : undefined;
32
+ const origArgs = shellParseArgs(query.args);
33
+ const queryEnv = query.env ? JSON.parse(query.env) : {};
34
+ const env = { ...process.env, ...defaultEnvironment, ...queryEnv };
31
35
  const { cmd, args } = findActualExecutable(command, origArgs);
32
- console.log(`Stdio transport: command=${cmd}, args=${args}, env=${JSON.stringify(env)}`);
36
+ console.log(`Stdio transport: command=${cmd}, args=${args}`);
33
37
  const transport = new StdioClientTransport({
34
38
  command: cmd,
35
39
  args,
@@ -42,8 +46,23 @@ const createTransport = async (query) => {
42
46
  }
43
47
  else if (transportType === "sse") {
44
48
  const url = query.url;
45
- console.log(`SSE transport: url=${url}`);
46
- const transport = new SSEClientTransport(new URL(url));
49
+ const headers = {};
50
+ for (const key of SSE_HEADERS_PASSTHROUGH) {
51
+ if (req.headers[key] === undefined) {
52
+ continue;
53
+ }
54
+ const value = req.headers[key];
55
+ headers[key] = Array.isArray(value) ? value[value.length - 1] : value;
56
+ }
57
+ console.log(`SSE transport: url=${url}, headers=${Object.keys(headers)}`);
58
+ const transport = new SSEClientTransport(new URL(url), {
59
+ eventSourceInit: {
60
+ fetch: (url, init) => fetch(url, { ...init, headers }),
61
+ },
62
+ requestInit: {
63
+ headers,
64
+ },
65
+ });
47
66
  await transport.start();
48
67
  console.log("Connected to SSE transport");
49
68
  return transport;
@@ -56,7 +75,18 @@ const createTransport = async (query) => {
56
75
  app.get("/sse", async (req, res) => {
57
76
  try {
58
77
  console.log("New SSE connection");
59
- const backingServerTransport = await createTransport(req.query);
78
+ let backingServerTransport;
79
+ try {
80
+ backingServerTransport = await createTransport(req);
81
+ }
82
+ catch (error) {
83
+ if (error instanceof SseError && error.code === 401) {
84
+ console.error("Received 401 Unauthorized from MCP server:", error.message);
85
+ res.status(401).json(error);
86
+ return;
87
+ }
88
+ throw error;
89
+ }
60
90
  console.log("Connected MCP client to backing server transport");
61
91
  const webAppTransport = new SSEServerTransport("/message", res);
62
92
  console.log("Created web app transport");
@@ -77,9 +107,6 @@ app.get("/sse", async (req, res) => {
77
107
  mcpProxy({
78
108
  transportToClient: webAppTransport,
79
109
  transportToServer: backingServerTransport,
80
- onerror: (error) => {
81
- console.error(error);
82
- },
83
110
  });
84
111
  console.log("Set up MCP proxy");
85
112
  }
@@ -106,7 +133,6 @@ app.post("/message", async (req, res) => {
106
133
  });
107
134
  app.get("/config", (req, res) => {
108
135
  try {
109
- const defaultEnvironment = getDefaultEnvironment();
110
136
  res.json({
111
137
  defaultEnvironment,
112
138
  defaultCommand: values.env,
@@ -119,4 +145,15 @@ app.get("/config", (req, res) => {
119
145
  }
120
146
  });
121
147
  const PORT = process.env.PORT || 3000;
122
- app.listen(PORT, () => { });
148
+ try {
149
+ const server = app.listen(PORT);
150
+ server.on("listening", () => {
151
+ const addr = server.address();
152
+ const port = typeof addr === "string" ? addr : addr?.port;
153
+ console.log(`Proxy server listening on port ${port}`);
154
+ });
155
+ }
156
+ catch (error) {
157
+ console.error("Failed to start server:", error);
158
+ process.exit(1);
159
+ }
@@ -1,26 +1,32 @@
1
- export default function mcpProxy({ transportToClient, transportToServer, onerror, }) {
1
+ function onClientError(error) {
2
+ console.error("Error from inspector client:", error);
3
+ }
4
+ function onServerError(error) {
5
+ console.error("Error from MCP server:", error);
6
+ }
7
+ export default function mcpProxy({ transportToClient, transportToServer, }) {
2
8
  let transportToClientClosed = false;
3
9
  let transportToServerClosed = false;
4
10
  transportToClient.onmessage = (message) => {
5
- transportToServer.send(message).catch(onerror);
11
+ transportToServer.send(message).catch(onServerError);
6
12
  };
7
13
  transportToServer.onmessage = (message) => {
8
- transportToClient.send(message).catch(onerror);
14
+ transportToClient.send(message).catch(onClientError);
9
15
  };
10
16
  transportToClient.onclose = () => {
11
17
  if (transportToServerClosed) {
12
18
  return;
13
19
  }
14
20
  transportToClientClosed = true;
15
- transportToServer.close().catch(onerror);
21
+ transportToServer.close().catch(onServerError);
16
22
  };
17
23
  transportToServer.onclose = () => {
18
24
  if (transportToClientClosed) {
19
25
  return;
20
26
  }
21
27
  transportToServerClosed = true;
22
- transportToClient.close().catch(onerror);
28
+ transportToClient.close().catch(onClientError);
23
29
  };
24
- transportToClient.onerror = onerror;
25
- transportToServer.onerror = onerror;
30
+ transportToClient.onerror = onClientError;
31
+ transportToServer.onerror = onServerError;
26
32
  }