Merge remote-tracking branch 'origin/main' into devin/1733551277-capability-negotiation

This commit is contained in:
Jeffrey Ling
2024-12-09 04:31:45 -07:00
5 changed files with 35 additions and 7 deletions

View File

@@ -62,7 +62,8 @@ const DEFAULT_REQUEST_TIMEOUT_MSEC = 10000;
const params = new URLSearchParams(window.location.search); const params = new URLSearchParams(window.location.search);
const PROXY_PORT = params.get("proxyPort") ?? "3000"; const PROXY_PORT = params.get("proxyPort") ?? "3000";
const REQUEST_TIMEOUT = parseInt(params.get("timeout") ?? "") || DEFAULT_REQUEST_TIMEOUT_MSEC; const REQUEST_TIMEOUT =
parseInt(params.get("timeout") ?? "") || DEFAULT_REQUEST_TIMEOUT_MSEC;
const PROXY_SERVER_URL = `http://localhost:${PROXY_PORT}`; const PROXY_SERVER_URL = `http://localhost:${PROXY_PORT}`;
const App = () => { const App = () => {
@@ -221,6 +222,12 @@ const App = () => {
rootsRef.current = roots; rootsRef.current = roots;
}, [roots]); }, [roots]);
useEffect(() => {
if (!window.location.hash) {
window.location.hash = "resources";
}
}, []);
const pushHistory = (request: object, response?: object) => { const pushHistory = (request: object, response?: object) => {
setRequestHistory((prev) => [ setRequestHistory((prev) => [
...prev, ...prev,
@@ -255,10 +262,14 @@ const App = () => {
response = await mcpClient.request(request, schema, { response = await mcpClient.request(request, schema, {
signal: abortController.signal, signal: abortController.signal,
}); });
pushHistory(request, response);
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
pushHistory(request, { error: errorMessage });
throw error;
} finally { } finally {
clearTimeout(timeoutId); clearTimeout(timeoutId);
} }
pushHistory(request, response);
if (tabKey !== undefined) { if (tabKey !== undefined) {
clearError(tabKey); clearError(tabKey);
@@ -494,12 +505,14 @@ const App = () => {
{mcpClient ? ( {mcpClient ? (
<Tabs <Tabs
defaultValue={ defaultValue={
window.location.hash.slice(1) ||
serverCapabilities?.resources ? "resources" : serverCapabilities?.resources ? "resources" :
serverCapabilities?.prompts ? "prompts" : serverCapabilities?.prompts ? "prompts" :
serverCapabilities?.tools ? "tools" : serverCapabilities?.tools ? "tools" :
"ping" "ping"
} }
className="w-full p-4" className="w-full p-4"
onValueChange={(value) => (window.location.hash = value)}
> >
<TabsList className="mb-4 p-0"> <TabsList className="mb-4 p-0">
<TabsTrigger value="resources" disabled={!serverCapabilities?.resources}> <TabsTrigger value="resources" disabled={!serverCapabilities?.resources}>

View File

@@ -100,7 +100,7 @@ const ToolsTab = ({
return ( return (
<> <>
<h4 className="font-semibold mb-2">Tool Result (Legacy):</h4> <h4 className="font-semibold mb-2">Tool Result (Legacy):</h4>
<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 p-4 rounded text-sm overflow-auto max-h-64">
{JSON.stringify(toolResult.toolResult, null, 2)} {JSON.stringify(toolResult.toolResult, null, 2)}
</pre> </pre>
</> </>

18
package-lock.json generated
View File

@@ -16,6 +16,7 @@
"@modelcontextprotocol/inspector-client": "0.3.0", "@modelcontextprotocol/inspector-client": "0.3.0",
"@modelcontextprotocol/inspector-server": "0.3.0", "@modelcontextprotocol/inspector-server": "0.3.0",
"concurrently": "^9.0.1", "concurrently": "^9.0.1",
"shell-quote": "^1.8.2",
"spawn-rx": "^5.1.0", "spawn-rx": "^5.1.0",
"ts-node": "^10.9.2" "ts-node": "^10.9.2"
}, },
@@ -24,6 +25,7 @@
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^22.7.5", "@types/node": "^22.7.5",
"@types/shell-quote": "^1.7.5",
"prettier": "3.3.3" "prettier": "3.3.3"
} }
}, },
@@ -2393,6 +2395,13 @@
"@types/send": "*" "@types/send": "*"
} }
}, },
"node_modules/@types/shell-quote": {
"version": "1.7.5",
"resolved": "https://registry.npmjs.org/@types/shell-quote/-/shell-quote-1.7.5.tgz",
"integrity": "sha512-+UE8GAGRPbJVQDdxi16dgadcBfQ+KG2vgZhV1+3A1XmHbmwcdwhCUwIdy+d3pAGrbvgRoVSjeI9vOWyq376Yzw==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/ws": { "node_modules/@types/ws": {
"version": "8.5.13", "version": "8.5.13",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz",
@@ -5688,10 +5697,13 @@
} }
}, },
"node_modules/shell-quote": { "node_modules/shell-quote": {
"version": "1.8.1", "version": "1.8.2",
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz",
"integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==",
"license": "MIT", "license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": { "funding": {
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }

View File

@@ -36,11 +36,13 @@
"@modelcontextprotocol/inspector-client": "0.3.0", "@modelcontextprotocol/inspector-client": "0.3.0",
"@modelcontextprotocol/inspector-server": "0.3.0", "@modelcontextprotocol/inspector-server": "0.3.0",
"concurrently": "^9.0.1", "concurrently": "^9.0.1",
"shell-quote": "^1.8.2",
"spawn-rx": "^5.1.0", "spawn-rx": "^5.1.0",
"ts-node": "^10.9.2" "ts-node": "^10.9.2"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^22.7.5", "@types/node": "^22.7.5",
"@types/shell-quote": "^1.7.5",
"prettier": "3.3.3" "prettier": "3.3.3"
} }
} }

View File

@@ -3,6 +3,7 @@
import cors from "cors"; import cors from "cors";
import EventSource from "eventsource"; import EventSource from "eventsource";
import { parseArgs } from "node:util"; import { parseArgs } from "node:util";
import { parse as shellParseArgs } from "shell-quote";
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"; import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
import { import {
@@ -38,7 +39,7 @@ 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 origArgs = (query.args as string).split(/\s+/); const origArgs = shellParseArgs(query.args as string) as string[];
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); const { cmd, args } = findActualExecutable(command, origArgs);