Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3110cf9343 | ||
|
|
2c04fa31e8 | ||
|
|
e700bc713a | ||
|
|
bea86af65b | ||
|
|
68a6130b17 | ||
|
|
853a3b4faf | ||
|
|
6f62066d34 | ||
|
|
c770d217e7 | ||
|
|
98470a12f9 | ||
|
|
a00564fafa | ||
|
|
62546dec58 | ||
|
|
886ac5fc7b | ||
|
|
722df4d798 | ||
|
|
407e304585 | ||
|
|
60578314aa | ||
|
|
3c4cb17d09 | ||
|
|
fbac5b78bc | ||
|
|
f876b1ec0d | ||
|
|
aecfa21d47 | ||
|
|
a3d542c0a3 | ||
|
|
2b79b6ffd4 | ||
|
|
1f28b4474c | ||
|
|
d69d67cb64 | ||
|
|
7792070d81 | ||
|
|
34a2843756 | ||
|
|
2a34770959 | ||
|
|
6b674b0827 | ||
|
|
ca8db1f417 | ||
|
|
eb4456d1e3 | ||
|
|
780b92274d | ||
|
|
b825784b8f | ||
|
|
52c7e98055 | ||
|
|
4862aa7c1d | ||
|
|
561ea91504 |
35
README.md
35
README.md
@@ -2,18 +2,47 @@
|
|||||||
|
|
||||||
The MCP inspector is a developer tool for testing and debugging MCP servers.
|
The MCP inspector is a developer tool for testing and debugging MCP servers.
|
||||||
|
|
||||||
It can be run easily from `npx`. For example, in a folder where there's a built JavaScript server at `build/index.js`:
|

|
||||||
|
|
||||||
```
|
## Running the Inspector
|
||||||
|
|
||||||
|
### From an MCP server repository
|
||||||
|
|
||||||
|
To inspect an MCP server implementation, there's no need to clone this repo. Instead, use `npx`. For example, if your server is built at `build/index.js`:
|
||||||
|
|
||||||
|
```bash
|
||||||
npx @modelcontextprotocol/inspector build/index.js
|
npx @modelcontextprotocol/inspector build/index.js
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also pass arguments along to the server:
|
You can also pass arguments along which will get passed as arguments to your MCP server:
|
||||||
|
|
||||||
```
|
```
|
||||||
npx @modelcontextprotocol/inspector build/index.js arg1 arg2 ...
|
npx @modelcontextprotocol/inspector build/index.js arg1 arg2 ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The inspector runs both a client UI (default port 5173) and an MCP proxy server (default port 3000). Open the client UI in your browser to use the inspector. You can customize the ports if needed:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
CLIENT_PORT=8080 SERVER_PORT=9000 npx @modelcontextprotocol/inspector build/index.js
|
||||||
|
```
|
||||||
|
|
||||||
|
### From this repository
|
||||||
|
|
||||||
|
If you're working on the inspector itself:
|
||||||
|
|
||||||
|
Development mode:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Production mode:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is licensed under the MIT License—see the [LICENSE](LICENSE) file for details.
|
This project is licensed under the MIT License—see the [LICENSE](LICENSE) file for details.
|
||||||
|
|||||||
125
bin/cli.js
125
bin/cli.js
@@ -1,65 +1,88 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
import { join, dirname } from "path";
|
import { resolve, dirname } from "path";
|
||||||
|
import { spawnPromise } from "spawn-rx";
|
||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
import concurrently from "concurrently";
|
|
||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
// Get command line arguments
|
function delay(ms) {
|
||||||
const [, , command, ...mcpServerArgs] = process.argv;
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
|
||||||
const inspectorServerPath = join(__dirname, "../server/build/index.js");
|
|
||||||
|
|
||||||
// Path to the client entry point
|
|
||||||
const inspectorClientPath = join(__dirname, "../client/bin/cli.js");
|
|
||||||
|
|
||||||
console.log("Starting MCP inspector...");
|
|
||||||
|
|
||||||
function escapeArg(arg) {
|
|
||||||
if (arg.includes(" ") || arg.includes("'") || arg.includes('"')) {
|
|
||||||
return `\\"${arg.replace(/"/g, '\\\\\\"')}\\"`;
|
|
||||||
}
|
|
||||||
return arg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const serverCommand = [
|
async function main() {
|
||||||
`node`,
|
// Get command line arguments
|
||||||
inspectorServerPath,
|
const [, , command, ...mcpServerArgs] = process.argv;
|
||||||
command ? `--env ${escapeArg(command)}` : "",
|
|
||||||
mcpServerArgs.length
|
|
||||||
? `--args="${mcpServerArgs.map(escapeArg).join(" ")}"`
|
|
||||||
: "",
|
|
||||||
]
|
|
||||||
.filter(Boolean)
|
|
||||||
.join(" ");
|
|
||||||
|
|
||||||
const CLIENT_PORT = process.env.CLIENT_PORT ?? "";
|
const inspectorServerPath = resolve(
|
||||||
const SERVER_PORT = process.env.SERVER_PORT ?? "";
|
__dirname,
|
||||||
|
"..",
|
||||||
|
"server",
|
||||||
|
"build",
|
||||||
|
"index.js",
|
||||||
|
);
|
||||||
|
|
||||||
const { result } = concurrently(
|
// Path to the client entry point
|
||||||
[
|
const inspectorClientPath = resolve(
|
||||||
|
__dirname,
|
||||||
|
"..",
|
||||||
|
"client",
|
||||||
|
"bin",
|
||||||
|
"cli.js",
|
||||||
|
);
|
||||||
|
|
||||||
|
const CLIENT_PORT = process.env.CLIENT_PORT ?? "5173";
|
||||||
|
const SERVER_PORT = process.env.SERVER_PORT ?? "3000";
|
||||||
|
|
||||||
|
console.log("Starting MCP inspector...");
|
||||||
|
|
||||||
|
const abort = new AbortController();
|
||||||
|
|
||||||
|
let cancelled = false;
|
||||||
|
process.on("SIGINT", () => {
|
||||||
|
cancelled = true;
|
||||||
|
abort.abort();
|
||||||
|
});
|
||||||
|
|
||||||
|
const server = spawnPromise(
|
||||||
|
"node",
|
||||||
|
[
|
||||||
|
inspectorServerPath,
|
||||||
|
...(command ? [`--env`, command] : []),
|
||||||
|
...(mcpServerArgs ? ["--args", mcpServerArgs.join(" ")] : []),
|
||||||
|
],
|
||||||
{
|
{
|
||||||
command: `PORT=${SERVER_PORT} ${serverCommand}`,
|
env: { ...process.env, PORT: SERVER_PORT },
|
||||||
name: "server",
|
signal: abort.signal,
|
||||||
|
echoOutput: true,
|
||||||
},
|
},
|
||||||
{
|
);
|
||||||
command: `PORT=${CLIENT_PORT} node ${inspectorClientPath}`,
|
|
||||||
name: "client",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
{
|
|
||||||
prefix: "name",
|
|
||||||
killOthers: ["failure", "success"],
|
|
||||||
restartTries: 3,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log(
|
const client = spawnPromise("node", [inspectorClientPath], {
|
||||||
`\n🔍 MCP Inspector is up and running at http://localhost:${CLIENT_PORT || 5173} 🚀`,
|
env: { ...process.env, PORT: CLIENT_PORT },
|
||||||
);
|
signal: abort.signal,
|
||||||
|
echoOutput: true,
|
||||||
|
});
|
||||||
|
|
||||||
result.catch((err) => {
|
// Make sure our server/client didn't immediately fail
|
||||||
console.error("An error occurred:", err);
|
await Promise.any([server, client, delay(2 * 1000)]);
|
||||||
process.exit(1);
|
const portParam = SERVER_PORT === "3000" ? "" : `?proxyPort=${SERVER_PORT}`;
|
||||||
});
|
console.log(
|
||||||
|
`\n🔍 MCP Inspector is up and running at http://localhost:${CLIENT_PORT}${portParam} 🚀`,
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Promise.any([server, client]);
|
||||||
|
} catch (e) {
|
||||||
|
if (!cancelled || process.env.DEBUG) throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
.then((_) => process.exit(0))
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@modelcontextprotocol/inspector-client",
|
"name": "@modelcontextprotocol/inspector-client",
|
||||||
"version": "0.2.2",
|
"version": "0.2.4",
|
||||||
"description": "Client-side application for the Model Context Protocol inspector",
|
"description": "Client-side application for the Model Context Protocol inspector",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"author": "Anthropic, PBC (https://anthropic.com)",
|
"author": "Anthropic, PBC (https://anthropic.com)",
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@modelcontextprotocol/sdk": "0.7.0",
|
"@modelcontextprotocol/sdk": "^1.0.1",
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
"@radix-ui/react-label": "^2.1.0",
|
"@radix-ui/react-label": "^2.1.0",
|
||||||
"@radix-ui/react-select": "^2.1.2",
|
"@radix-ui/react-select": "^2.1.2",
|
||||||
|
|||||||
@@ -57,6 +57,10 @@ import ToolsTab from "./components/ToolsTab";
|
|||||||
|
|
||||||
const DEFAULT_REQUEST_TIMEOUT_MSEC = 10000;
|
const DEFAULT_REQUEST_TIMEOUT_MSEC = 10000;
|
||||||
|
|
||||||
|
const params = new URLSearchParams(window.location.search);
|
||||||
|
const PROXY_PORT = params.get("proxyPort") ?? "3000";
|
||||||
|
const PROXY_SERVER_URL = `http://localhost:${PROXY_PORT}`;
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const [connectionStatus, setConnectionStatus] = useState<
|
const [connectionStatus, setConnectionStatus] = useState<
|
||||||
"disconnected" | "connected" | "error"
|
"disconnected" | "connected" | "error"
|
||||||
@@ -82,7 +86,8 @@ const App = () => {
|
|||||||
const [args, setArgs] = useState<string>(() => {
|
const [args, setArgs] = useState<string>(() => {
|
||||||
return localStorage.getItem("lastArgs") || "";
|
return localStorage.getItem("lastArgs") || "";
|
||||||
});
|
});
|
||||||
const [url, setUrl] = useState<string>("http://localhost:3001/sse");
|
|
||||||
|
const [sseUrl, setSseUrl] = useState<string>("http://localhost:3001/sse");
|
||||||
const [transportType, setTransportType] = useState<"stdio" | "sse">("stdio");
|
const [transportType, setTransportType] = useState<"stdio" | "sse">("stdio");
|
||||||
const [requestHistory, setRequestHistory] = useState<
|
const [requestHistory, setRequestHistory] = useState<
|
||||||
{ request: string; response?: string }[]
|
{ request: string; response?: string }[]
|
||||||
@@ -191,7 +196,7 @@ const App = () => {
|
|||||||
}, [args]);
|
}, [args]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetch("http://localhost:3000/config")
|
fetch(`${PROXY_SERVER_URL}/config`)
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
setEnv(data.defaultEnvironment);
|
setEnv(data.defaultEnvironment);
|
||||||
@@ -404,7 +409,7 @@ const App = () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const backendUrl = new URL("http://localhost:3000/sse");
|
const backendUrl = new URL(`${PROXY_SERVER_URL}/sse`);
|
||||||
|
|
||||||
backendUrl.searchParams.append("transportType", transportType);
|
backendUrl.searchParams.append("transportType", transportType);
|
||||||
if (transportType === "stdio") {
|
if (transportType === "stdio") {
|
||||||
@@ -412,7 +417,7 @@ const App = () => {
|
|||||||
backendUrl.searchParams.append("args", args);
|
backendUrl.searchParams.append("args", args);
|
||||||
backendUrl.searchParams.append("env", JSON.stringify(env));
|
backendUrl.searchParams.append("env", JSON.stringify(env));
|
||||||
} else {
|
} else {
|
||||||
backendUrl.searchParams.append("url", url);
|
backendUrl.searchParams.append("url", sseUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
const clientTransport = new SSEClientTransport(backendUrl);
|
const clientTransport = new SSEClientTransport(backendUrl);
|
||||||
@@ -469,8 +474,8 @@ const App = () => {
|
|||||||
setCommand={setCommand}
|
setCommand={setCommand}
|
||||||
args={args}
|
args={args}
|
||||||
setArgs={setArgs}
|
setArgs={setArgs}
|
||||||
url={url}
|
sseUrl={sseUrl}
|
||||||
setUrl={setUrl}
|
setSseUrl={setSseUrl}
|
||||||
env={env}
|
env={env}
|
||||||
setEnv={setEnv}
|
setEnv={setEnv}
|
||||||
onConnect={connectMcpServer}
|
onConnect={connectMcpServer}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
import { Play, ChevronDown, ChevronRight } from "lucide-react";
|
import { Play, ChevronDown, ChevronRight } from "lucide-react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
@@ -13,6 +12,7 @@ import {
|
|||||||
import { StdErrNotification } from "@/lib/notificationTypes";
|
import { StdErrNotification } from "@/lib/notificationTypes";
|
||||||
|
|
||||||
import useTheme from "../lib/useTheme";
|
import useTheme from "../lib/useTheme";
|
||||||
|
import { version } from "../../../package.json";
|
||||||
|
|
||||||
interface SidebarProps {
|
interface SidebarProps {
|
||||||
connectionStatus: "disconnected" | "connected" | "error";
|
connectionStatus: "disconnected" | "connected" | "error";
|
||||||
@@ -22,8 +22,8 @@ interface SidebarProps {
|
|||||||
setCommand: (command: string) => void;
|
setCommand: (command: string) => void;
|
||||||
args: string;
|
args: string;
|
||||||
setArgs: (args: string) => void;
|
setArgs: (args: string) => void;
|
||||||
url: string;
|
sseUrl: string;
|
||||||
setUrl: (url: string) => void;
|
setSseUrl: (url: string) => void;
|
||||||
env: Record<string, string>;
|
env: Record<string, string>;
|
||||||
setEnv: (env: Record<string, string>) => void;
|
setEnv: (env: Record<string, string>) => void;
|
||||||
onConnect: () => void;
|
onConnect: () => void;
|
||||||
@@ -38,8 +38,8 @@ const Sidebar = ({
|
|||||||
setCommand,
|
setCommand,
|
||||||
args,
|
args,
|
||||||
setArgs,
|
setArgs,
|
||||||
url,
|
sseUrl,
|
||||||
setUrl,
|
setSseUrl,
|
||||||
env,
|
env,
|
||||||
setEnv,
|
setEnv,
|
||||||
onConnect,
|
onConnect,
|
||||||
@@ -52,7 +52,9 @@ const Sidebar = ({
|
|||||||
<div className="w-80 bg-card border-r border-border flex flex-col h-full">
|
<div className="w-80 bg-card border-r border-border flex flex-col h-full">
|
||||||
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
<div className="flex items-center justify-between p-4 border-b border-gray-200">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<h1 className="ml-2 text-lg font-semibold">MCP Inspector</h1>
|
<h1 className="ml-2 text-lg font-semibold">
|
||||||
|
MCP Inspector v{version}
|
||||||
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -75,6 +77,7 @@ const Sidebar = ({
|
|||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{transportType === "stdio" ? (
|
{transportType === "stdio" ? (
|
||||||
<>
|
<>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
@@ -99,8 +102,8 @@ const Sidebar = ({
|
|||||||
<label className="text-sm font-medium">URL</label>
|
<label className="text-sm font-medium">URL</label>
|
||||||
<Input
|
<Input
|
||||||
placeholder="URL"
|
placeholder="URL"
|
||||||
value={url}
|
value={sseUrl}
|
||||||
onChange={(e) => setUrl(e.target.value)}
|
onChange={(e) => setSseUrl(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import { Label } from "@/components/ui/label";
|
|||||||
import { TabsContent } from "@/components/ui/tabs";
|
import { TabsContent } from "@/components/ui/tabs";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import {
|
import {
|
||||||
CallToolResult,
|
|
||||||
ListToolsResult,
|
ListToolsResult,
|
||||||
Tool,
|
Tool,
|
||||||
|
CallToolResultSchema,
|
||||||
} from "@modelcontextprotocol/sdk/types.js";
|
} from "@modelcontextprotocol/sdk/types.js";
|
||||||
import { AlertCircle, Send } from "lucide-react";
|
import { AlertCircle, Send } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
@@ -40,7 +40,27 @@ const ToolsTab = ({
|
|||||||
if (!toolResult) return null;
|
if (!toolResult) return null;
|
||||||
|
|
||||||
if ("content" in toolResult) {
|
if ("content" in toolResult) {
|
||||||
const structuredResult = toolResult as CallToolResult;
|
const parsedResult = CallToolResultSchema.safeParse(toolResult);
|
||||||
|
if (!parsedResult.success) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h4 className="font-semibold mb-2">Invalid Tool Result:</h4>
|
||||||
|
<pre className="bg-gray-50 dark:bg-gray-800 dark:text-gray-100 p-4 rounded text-sm overflow-auto max-h-64">
|
||||||
|
{JSON.stringify(toolResult, null, 2)}
|
||||||
|
</pre>
|
||||||
|
<h4 className="font-semibold mb-2">Errors:</h4>
|
||||||
|
{parsedResult.error.errors.map((error, idx) => (
|
||||||
|
<pre
|
||||||
|
key={idx}
|
||||||
|
className="bg-gray-50 dark:bg-gray-800 dark:text-gray-100 p-4 rounded text-sm overflow-auto max-h-64"
|
||||||
|
>
|
||||||
|
{JSON.stringify(error, null, 2)}
|
||||||
|
</pre>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const structuredResult = parsedResult.data;
|
||||||
const isError = structuredResult.isError ?? false;
|
const isError = structuredResult.isError ?? false;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -63,7 +83,7 @@ const ToolsTab = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{item.type === "resource" && (
|
{item.type === "resource" && (
|
||||||
<pre className="bg-gray-50 p-4 rounded text-sm overflow-auto max-h-64">
|
<pre className="bg-gray-50 dark:bg-gray-800 dark:text-gray-100 whitespace-pre-wrap break-words p-4 rounded text-sm overflow-auto max-h-64">
|
||||||
{JSON.stringify(item.resource, null, 2)}
|
{JSON.stringify(item.resource, null, 2)}
|
||||||
</pre>
|
</pre>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
import animate from "tailwindcss-animate";
|
||||||
export default {
|
export default {
|
||||||
darkMode: ["class"],
|
darkMode: ["class"],
|
||||||
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
|
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
|
||||||
@@ -53,5 +54,5 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [require("tailwindcss-animate")],
|
plugins: [animate],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,7 +23,8 @@
|
|||||||
"strict": true,
|
"strict": true,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"noFallthroughCasesInSwitch": true
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"resolveJsonModule": true
|
||||||
},
|
},
|
||||||
"include": ["src"]
|
"include": ["src"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,4 +10,12 @@ export default defineConfig({
|
|||||||
"@": path.resolve(__dirname, "./src"),
|
"@": path.resolve(__dirname, "./src"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
build: {
|
||||||
|
minify: false,
|
||||||
|
rollupOptions: {
|
||||||
|
output: {
|
||||||
|
manualChunks: undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
BIN
mcp-inspector.png
Normal file
BIN
mcp-inspector.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 294 KiB |
2425
package-lock.json
generated
2425
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@modelcontextprotocol/inspector",
|
"name": "@modelcontextprotocol/inspector",
|
||||||
"version": "0.2.2",
|
"version": "0.2.4",
|
||||||
"description": "Model Context Protocol inspector",
|
"description": "Model Context Protocol inspector",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"author": "Anthropic, PBC (https://anthropic.com)",
|
"author": "Anthropic, PBC (https://anthropic.com)",
|
||||||
@@ -27,18 +27,20 @@
|
|||||||
"build": "npm run build-server && npm run build-client",
|
"build": "npm run build-server && npm run build-client",
|
||||||
"start-server": "cd server && npm run start",
|
"start-server": "cd server && npm run start",
|
||||||
"start-client": "cd client && npm run preview",
|
"start-client": "cd client && npm run preview",
|
||||||
"start": "./bin/cli.js",
|
"start": "node ./bin/cli.js",
|
||||||
"prepare": "npm run build",
|
"prepare": "npm run build",
|
||||||
"prettier-fix": "prettier --write .",
|
"prettier-fix": "prettier --write .",
|
||||||
"publish-all": "npm publish --workspaces --access public && npm publish --access public"
|
"publish-all": "npm publish --workspaces --access public && npm publish --access public"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@modelcontextprotocol/inspector-client": "0.2.1",
|
"@modelcontextprotocol/inspector-client": "0.2.4",
|
||||||
"@modelcontextprotocol/inspector-server": "0.2.1",
|
"@modelcontextprotocol/inspector-server": "0.2.4",
|
||||||
"concurrently": "^9.0.1"
|
"concurrently": "^9.0.1",
|
||||||
|
"spawn-rx": "^5.1.0",
|
||||||
|
"ts-node": "^10.9.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"prettier": "3.3.3",
|
"@types/node": "^22.7.5",
|
||||||
"@types/node": "^22.7.5"
|
"prettier": "3.3.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@modelcontextprotocol/inspector-server",
|
"name": "@modelcontextprotocol/inspector-server",
|
||||||
"version": "0.2.2",
|
"version": "0.2.4",
|
||||||
"description": "Server-side application for the Model Context Protocol inspector",
|
"description": "Server-side application for the Model Context Protocol inspector",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"author": "Anthropic, PBC (https://anthropic.com)",
|
"author": "Anthropic, PBC (https://anthropic.com)",
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
"typescript": "^5.6.2"
|
"typescript": "^5.6.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@modelcontextprotocol/sdk": "0.7.0",
|
"@modelcontextprotocol/sdk": "^1.0.1",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"eventsource": "^2.0.2",
|
"eventsource": "^2.0.2",
|
||||||
"express": "^4.21.0",
|
"express": "^4.21.0",
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
||||||
import express from "express";
|
import express from "express";
|
||||||
import mcpProxy from "./mcpProxy.js";
|
import mcpProxy from "./mcpProxy.js";
|
||||||
|
import { findActualExecutable } from "spawn-rx";
|
||||||
|
|
||||||
// Polyfill EventSource for an SSE client in Node.js
|
// Polyfill EventSource for an SSE client in Node.js
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
@@ -37,25 +38,33 @@ const createTransport = async (query: express.Request["query"]) => {
|
|||||||
|
|
||||||
if (transportType === "stdio") {
|
if (transportType === "stdio") {
|
||||||
const command = query.command as string;
|
const command = query.command as string;
|
||||||
const args = (query.args as string).split(/\s+/);
|
const origArgs = (query.args as string).split(/\s+/);
|
||||||
const env = query.env ? JSON.parse(query.env as string) : undefined;
|
const env = query.env ? JSON.parse(query.env as string) : undefined;
|
||||||
|
|
||||||
|
const { cmd, args } = findActualExecutable(command, origArgs);
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`Stdio transport: command=${command}, args=${args}, env=${JSON.stringify(env)}`,
|
`Stdio transport: command=${cmd}, args=${args}, env=${JSON.stringify(env)}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const transport = new StdioClientTransport({
|
const transport = new StdioClientTransport({
|
||||||
command,
|
command: cmd,
|
||||||
args,
|
args,
|
||||||
env,
|
env,
|
||||||
stderr: "pipe",
|
stderr: "pipe",
|
||||||
});
|
});
|
||||||
|
|
||||||
await transport.start();
|
await transport.start();
|
||||||
|
|
||||||
console.log("Spawned stdio transport");
|
console.log("Spawned stdio transport");
|
||||||
return transport;
|
return transport;
|
||||||
} else if (transportType === "sse") {
|
} else if (transportType === "sse") {
|
||||||
const url = query.url as string;
|
const url = query.url as string;
|
||||||
console.log(`SSE transport: url=${url}`);
|
console.log(`SSE transport: url=${url}`);
|
||||||
|
|
||||||
const transport = new SSEClientTransport(new URL(url));
|
const transport = new SSEClientTransport(new URL(url));
|
||||||
await transport.start();
|
await transport.start();
|
||||||
|
|
||||||
console.log("Connected to SSE transport");
|
console.log("Connected to SSE transport");
|
||||||
return transport;
|
return transport;
|
||||||
} else {
|
} else {
|
||||||
@@ -99,6 +108,7 @@ app.get("/sse", async (req, res) => {
|
|||||||
console.error(error);
|
console.error(error);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("Set up MCP proxy");
|
console.log("Set up MCP proxy");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error in /sse route:", error);
|
console.error("Error in /sse route:", error);
|
||||||
@@ -126,6 +136,7 @@ app.post("/message", async (req, res) => {
|
|||||||
app.get("/config", (req, res) => {
|
app.get("/config", (req, res) => {
|
||||||
try {
|
try {
|
||||||
const defaultEnvironment = getDefaultEnvironment();
|
const defaultEnvironment = getDefaultEnvironment();
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
defaultEnvironment,
|
defaultEnvironment,
|
||||||
defaultCommand: values.env,
|
defaultCommand: values.env,
|
||||||
@@ -138,4 +149,4 @@ app.get("/config", (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const PORT = process.env.PORT || 3000;
|
const PORT = process.env.PORT || 3000;
|
||||||
app.listen(PORT, () => { });
|
app.listen(PORT, () => {});
|
||||||
|
|||||||
Reference in New Issue
Block a user