Compare commits

...

13 Commits

Author SHA1 Message Date
Ashwin Bhat
6f62066d34 display inspector version in UI 2024-11-27 10:52:38 -05:00
ashwin-ant
c770d217e7 Merge pull request #86 from modelcontextprotocol/ani/debuggability
Make debugging Inspector easier for users
2024-11-27 10:04:00 -05:00
Ani Betts
98470a12f9 Make stdout/error echo for client and server 2024-11-27 15:57:02 +01:00
Ani Betts
a00564fafa Disable minification on production build, we don't need it here and it makes debugging annoying 2024-11-27 15:55:11 +01:00
Ashwin Bhat
62546dec58 bump version to 0.2.4 2024-11-27 09:33:15 -05:00
ashwin-ant
886ac5fc7b Merge pull request #81 from modelcontextprotocol/ashwin/serverport
Respect custom server port
2024-11-27 08:59:59 -05:00
ashwin-ant
722df4d798 Merge pull request #82 from jacksteamdev/fix-1
Add Runtime Type Validation for Tool Results
2024-11-26 15:32:58 -05:00
Jack Steam
407e304585 Merge branch 'main' into fix-1 2024-11-26 13:31:12 -07:00
Jack Steam
60578314aa Update client/src/components/ToolsTab.tsx
Co-authored-by: ashwin-ant <ashwin@anthropic.com>
2024-11-26 13:30:43 -07:00
Jack Steam
fbac5b78bc feat: add data validation message 2024-11-26 12:47:39 -06:00
Ashwin Bhat
f876b1ec0d consolidate server URL configuration 2024-11-26 13:40:28 -05:00
Jack Steam
aecfa21d47 fix: add static type validation 2024-11-26 11:14:55 -07:00
Ashwin Bhat
a3d542c0a3 make server port configurable via URL query param 2024-11-26 13:12:45 -05:00
10 changed files with 79 additions and 36 deletions

View File

@@ -51,18 +51,24 @@ async function main() {
...(command ? [`--env`, command] : []), ...(command ? [`--env`, command] : []),
...(mcpServerArgs ? ["--args", mcpServerArgs.join(" ")] : []), ...(mcpServerArgs ? ["--args", mcpServerArgs.join(" ")] : []),
], ],
{ env: { ...process.env, PORT: SERVER_PORT }, signal: abort.signal }, {
env: { ...process.env, PORT: SERVER_PORT },
signal: abort.signal,
echoOutput: true,
},
); );
const client = spawnPromise("node", [inspectorClientPath], { const client = spawnPromise("node", [inspectorClientPath], {
env: { ...process.env, PORT: CLIENT_PORT }, env: { ...process.env, PORT: CLIENT_PORT },
signal: abort.signal, signal: abort.signal,
echoOutput: true,
}); });
// Make sure our server/client didn't immediately fail // Make sure our server/client didn't immediately fail
await Promise.any([server, client, delay(2 * 1000)]); await Promise.any([server, client, delay(2 * 1000)]);
const portParam = SERVER_PORT === "3000" ? "" : `?proxyPort=${SERVER_PORT}`;
console.log( console.log(
`\n🔍 MCP Inspector is up and running at http://localhost:${CLIENT_PORT} 🚀`, `\n🔍 MCP Inspector is up and running at http://localhost:${CLIENT_PORT}${portParam} 🚀`,
); );
try { try {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@modelcontextprotocol/inspector-client", "name": "@modelcontextprotocol/inspector-client",
"version": "0.2.3", "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)",

View File

@@ -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}

View File

@@ -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>
)} )}

View File

@@ -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 (

View File

@@ -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"]
} }

View File

@@ -10,4 +10,12 @@ export default defineConfig({
"@": path.resolve(__dirname, "./src"), "@": path.resolve(__dirname, "./src"),
}, },
}, },
build: {
minify: false,
rollupOptions: {
output: {
manualChunks: undefined
}
}
}
}); });

20
package-lock.json generated
View File

@@ -1,22 +1,22 @@
{ {
"name": "@modelcontextprotocol/inspector", "name": "@modelcontextprotocol/inspector",
"version": "0.2.3", "version": "0.2.4",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@modelcontextprotocol/inspector", "name": "@modelcontextprotocol/inspector",
"version": "0.2.3", "version": "0.2.4",
"license": "MIT", "license": "MIT",
"workspaces": [ "workspaces": [
"client", "client",
"server" "server"
], ],
"dependencies": { "dependencies": {
"@modelcontextprotocol/inspector-client": "0.2.3", "@modelcontextprotocol/inspector-client": "0.2.4",
"@modelcontextprotocol/inspector-server": "0.2.3", "@modelcontextprotocol/inspector-server": "0.2.4",
"concurrently": "^9.0.1", "concurrently": "^9.0.1",
"spawn-rx": "^5.0.4", "spawn-rx": "^5.1.0",
"ts-node": "^10.9.2" "ts-node": "^10.9.2"
}, },
"bin": { "bin": {
@@ -29,7 +29,7 @@
}, },
"client": { "client": {
"name": "@modelcontextprotocol/inspector-client", "name": "@modelcontextprotocol/inspector-client",
"version": "0.2.3", "version": "0.2.4",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@modelcontextprotocol/sdk": "^1.0.1", "@modelcontextprotocol/sdk": "^1.0.1",
@@ -5737,9 +5737,9 @@
} }
}, },
"node_modules/spawn-rx": { "node_modules/spawn-rx": {
"version": "5.0.4", "version": "5.1.0",
"resolved": "https://registry.npmjs.org/spawn-rx/-/spawn-rx-5.0.4.tgz", "resolved": "https://registry.npmjs.org/spawn-rx/-/spawn-rx-5.1.0.tgz",
"integrity": "sha512-Do11ahkHLlqN9G/J6fs10gdx25BU33NrpkyN3/DFXIIUVojBiJysl12nC0iGUkE+msJAPflzyfpLWWHGHw/6Xg==", "integrity": "sha512-b4HX44hI0usMiHu6LNaZUVg0BGqHuBcl+81iEhZwhvKHz1efTqD/CHBcUbm/uIe5TARy9pJolxU2NMfh6GuQBA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"debug": "^4.3.7", "debug": "^4.3.7",
@@ -6926,7 +6926,7 @@
}, },
"server": { "server": {
"name": "@modelcontextprotocol/inspector-server", "name": "@modelcontextprotocol/inspector-server",
"version": "0.2.3", "version": "0.2.4",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@modelcontextprotocol/sdk": "^1.0.1", "@modelcontextprotocol/sdk": "^1.0.1",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@modelcontextprotocol/inspector", "name": "@modelcontextprotocol/inspector",
"version": "0.2.3", "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)",
@@ -33,10 +33,10 @@
"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.3", "@modelcontextprotocol/inspector-client": "0.2.4",
"@modelcontextprotocol/inspector-server": "0.2.3", "@modelcontextprotocol/inspector-server": "0.2.4",
"concurrently": "^9.0.1", "concurrently": "^9.0.1",
"spawn-rx": "^5.0.4", "spawn-rx": "^5.1.0",
"ts-node": "^10.9.2" "ts-node": "^10.9.2"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@modelcontextprotocol/inspector-server", "name": "@modelcontextprotocol/inspector-server",
"version": "0.2.3", "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)",