From 764f02310d262faee11c1c8ce40d5f39c0a04779 Mon Sep 17 00:00:00 2001 From: Ashwin Bhat Date: Fri, 29 Nov 2024 16:26:56 -0500 Subject: [PATCH 01/34] link to MCP docs site in readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 3f48b1a..c5e6b2e 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,8 @@ The inspector runs both a client UI (default port 5173) and an MCP proxy server CLIENT_PORT=8080 SERVER_PORT=9000 npx @modelcontextprotocol/inspector build/index.js ``` +For more details on ways to use the inspector, see the [Inspector section of the MCP docs site](https://modelcontextprotocol.io/docs/tools/inspector). + ### From this repository If you're working on the inspector itself: From cc17ba8d569297414eac28f7e08bb3580ab98bb0 Mon Sep 17 00:00:00 2001 From: Kees Heuperman Date: Sun, 1 Dec 2024 09:50:53 +0100 Subject: [PATCH 02/34] feat: Add button to clear loaded items Add a button to the ListPane component that clears loaded items. This will allow the user to clear and reload resources, resource templates, prompts or tools when they expect the available items to have changed. --- client/src/App.tsx | 16 ++++++++++++++++ client/src/components/ListPane.tsx | 10 ++++++++++ client/src/components/PromptsTab.tsx | 3 +++ client/src/components/ResourcesTab.tsx | 6 ++++++ client/src/components/ToolsTab.tsx | 5 ++++- 5 files changed, 39 insertions(+), 1 deletion(-) diff --git a/client/src/App.tsx b/client/src/App.tsx index 81ce70a..0ee234b 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -525,10 +525,18 @@ const App = () => { clearError("resources"); listResources(); }} + clearResources={() => { + setResources([]); + setNextResourceCursor(undefined); + }} listResourceTemplates={() => { clearError("resources"); listResourceTemplates(); }} + clearResourceTemplates={() => { + setResourceTemplates([]); + setNextResourceTemplateCursor(undefined); + }} readResource={(uri) => { clearError("resources"); readResource(uri); @@ -549,6 +557,10 @@ const App = () => { clearError("prompts"); listPrompts(); }} + clearPrompts={() => { + setPrompts([]); + setNextPromptCursor(undefined); + }} getPrompt={(name, args) => { clearError("prompts"); getPrompt(name, args); @@ -568,6 +580,10 @@ const App = () => { clearError("tools"); listTools(); }} + clearTools={() => { + setTools([]); + setNextToolCursor(undefined); + }} callTool={(name, params) => { clearError("tools"); callTool(name, params); diff --git a/client/src/components/ListPane.tsx b/client/src/components/ListPane.tsx index 5fca6a4..90693dd 100644 --- a/client/src/components/ListPane.tsx +++ b/client/src/components/ListPane.tsx @@ -3,6 +3,7 @@ import { Button } from "./ui/button"; type ListPaneProps = { items: T[]; listItems: () => void; + clearItems: () => void; setSelectedItem: (item: T) => void; renderItem: (item: T) => React.ReactNode; title: string; @@ -13,6 +14,7 @@ type ListPaneProps = { const ListPane = ({ items, listItems, + clearItems, setSelectedItem, renderItem, title, @@ -32,6 +34,14 @@ const ListPane = ({ > {buttonText} +
{items.map((item, index) => (
void; + clearPrompts: () => void; getPrompt: (name: string, args: Record) => void; selectedPrompt: Prompt | null; setSelectedPrompt: (prompt: Prompt) => void; @@ -55,6 +57,7 @@ const PromptsTab = ({ { setSelectedPrompt(prompt); setPromptArgs({}); diff --git a/client/src/components/ResourcesTab.tsx b/client/src/components/ResourcesTab.tsx index b1224f1..e948881 100644 --- a/client/src/components/ResourcesTab.tsx +++ b/client/src/components/ResourcesTab.tsx @@ -16,7 +16,9 @@ const ResourcesTab = ({ resources, resourceTemplates, listResources, + clearResources, listResourceTemplates, + clearResourceTemplates, readResource, selectedResource, setSelectedResource, @@ -28,7 +30,9 @@ const ResourcesTab = ({ resources: Resource[]; resourceTemplates: ResourceTemplate[]; listResources: () => void; + clearResources: () => void; listResourceTemplates: () => void; + clearResourceTemplates: () => void; readResource: (uri: string) => void; selectedResource: Resource | null; setSelectedResource: (resource: Resource | null) => void; @@ -68,6 +72,7 @@ const ResourcesTab = ({ { setSelectedResource(resource); readResource(resource.uri); @@ -90,6 +95,7 @@ const ResourcesTab = ({ { setSelectedTemplate(template); setSelectedResource(null); diff --git a/client/src/components/ToolsTab.tsx b/client/src/components/ToolsTab.tsx index 7b85ccc..1113ebc 100644 --- a/client/src/components/ToolsTab.tsx +++ b/client/src/components/ToolsTab.tsx @@ -18,6 +18,7 @@ import { CompatibilityCallToolResult } from "@modelcontextprotocol/sdk/types.js" const ToolsTab = ({ tools, listTools, + clearTools, callTool, selectedTool, setSelectedTool, @@ -27,6 +28,7 @@ const ToolsTab = ({ }: { tools: Tool[]; listTools: () => void; + clearTools: () => void; callTool: (name: string, params: Record) => void; selectedTool: Tool | null; setSelectedTool: (tool: Tool) => void; @@ -50,7 +52,7 @@ const ToolsTab = ({

Errors:

{parsedResult.error.errors.map((error, idx) => ( -
@@ -108,6 +110,7 @@ const ToolsTab = ({
        (
           <>

From b845444fabf2f893e3014a7673005c2188271393 Mon Sep 17 00:00:00 2001
From: Ashwin Bhat 
Date: Wed, 4 Dec 2024 09:56:04 -0800
Subject: [PATCH 03/34] update sdk to 1.0.3

---
 client/package.json |  2 +-
 package-lock.json   | 11 +++++------
 server/package.json |  2 +-
 3 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/client/package.json b/client/package.json
index 91bc418..5ed07ee 100644
--- a/client/package.json
+++ b/client/package.json
@@ -21,7 +21,7 @@
     "preview": "vite preview"
   },
   "dependencies": {
-    "@modelcontextprotocol/sdk": "^1.0.1",
+    "@modelcontextprotocol/sdk": "^1.0.3",
     "@radix-ui/react-icons": "^1.3.0",
     "@radix-ui/react-label": "^2.1.0",
     "@radix-ui/react-select": "^2.1.2",
diff --git a/package-lock.json b/package-lock.json
index a6dee35..fb35e2c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -32,7 +32,7 @@
       "version": "0.2.7",
       "license": "MIT",
       "dependencies": {
-        "@modelcontextprotocol/sdk": "^1.0.1",
+        "@modelcontextprotocol/sdk": "^1.0.3",
         "@radix-ui/react-icons": "^1.3.0",
         "@radix-ui/react-label": "^2.1.0",
         "@radix-ui/react-select": "^2.1.2",
@@ -1203,10 +1203,9 @@
       "link": true
     },
     "node_modules/@modelcontextprotocol/sdk": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.0.1.tgz",
-      "integrity": "sha512-slLdFaxQJ9AlRg+hw28iiTtGvShAOgOKXcD0F91nUcRYiOMuS9ZBYjcdNZRXW9G5JQ511GRTdUy1zQVZDpJ+4w==",
-      "license": "MIT",
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.0.3.tgz",
+      "integrity": "sha512-2as3cX/VJ0YBHGmdv3GFyTpoM8q2gqE98zh3Vf1NwnsSY0h3mvoO07MUzfygCKkWsFjcZm4otIiqD6Xh7kiSBQ==",
       "dependencies": {
         "content-type": "^1.0.5",
         "raw-body": "^3.0.0",
@@ -6929,7 +6928,7 @@
       "version": "0.2.7",
       "license": "MIT",
       "dependencies": {
-        "@modelcontextprotocol/sdk": "^1.0.1",
+        "@modelcontextprotocol/sdk": "^1.0.3",
         "cors": "^2.8.5",
         "eventsource": "^2.0.2",
         "express": "^4.21.0",
diff --git a/server/package.json b/server/package.json
index 41b0a61..d7864a4 100644
--- a/server/package.json
+++ b/server/package.json
@@ -27,7 +27,7 @@
     "typescript": "^5.6.2"
   },
   "dependencies": {
-    "@modelcontextprotocol/sdk": "^1.0.1",
+    "@modelcontextprotocol/sdk": "^1.0.3",
     "cors": "^2.8.5",
     "eventsource": "^2.0.2",
     "express": "^4.21.0",

From f04b161411eb8071189bd49f77145511e8d83e45 Mon Sep 17 00:00:00 2001
From: = <1936278+evalstate@users.noreply.github.com>
Date: Thu, 5 Dec 2024 08:11:35 +0000
Subject: [PATCH 04/34] Allow setting timeout via "timeout" URL parameter

---
 client/src/App.tsx | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/client/src/App.tsx b/client/src/App.tsx
index 0ee234b..2c84377 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -59,6 +59,7 @@ const DEFAULT_REQUEST_TIMEOUT_MSEC = 10000;
 
 const params = new URLSearchParams(window.location.search);
 const PROXY_PORT = params.get("proxyPort") ?? "3000";
+const REQUEST_TIMEOUT = parseInt(params.get("timeout") ?? "") || DEFAULT_REQUEST_TIMEOUT_MSEC;
 const PROXY_SERVER_URL = `http://localhost:${PROXY_PORT}`;
 
 const App = () => {
@@ -243,7 +244,7 @@ const App = () => {
       const abortController = new AbortController();
       const timeoutId = setTimeout(() => {
         abortController.abort("Request timed out");
-      }, DEFAULT_REQUEST_TIMEOUT_MSEC);
+      }, REQUEST_TIMEOUT);
 
       let response;
       try {

From ed5017d73ed108e8f99ec4a16a7f3bb977d7de0e Mon Sep 17 00:00:00 2001
From: evalstate <1936278+evalstate@users.noreply.github.com>
Date: Thu, 5 Dec 2024 15:32:55 +0000
Subject: [PATCH 05/34] Two fixes to the Tools Tab: 1) Tool Parameters were
 stale when switching between Tools causing incorrect messages to be sent. 2)
 Tool List is emptied when "Clear" is selected, so invalid messages can't be
 sent.

---
 client/src/components/ToolsTab.tsx | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/client/src/components/ToolsTab.tsx b/client/src/components/ToolsTab.tsx
index 1113ebc..55dff83 100644
--- a/client/src/components/ToolsTab.tsx
+++ b/client/src/components/ToolsTab.tsx
@@ -10,7 +10,7 @@ import {
   CallToolResultSchema,
 } from "@modelcontextprotocol/sdk/types.js";
 import { AlertCircle, Send } from "lucide-react";
-import { useState } from "react";
+import { useEffect, useState } from "react";
 import ListPane from "./ListPane";
 
 import { CompatibilityCallToolResult } from "@modelcontextprotocol/sdk/types.js";
@@ -31,12 +31,15 @@ const ToolsTab = ({
   clearTools: () => void;
   callTool: (name: string, params: Record) => void;
   selectedTool: Tool | null;
-  setSelectedTool: (tool: Tool) => void;
+  setSelectedTool: (tool: Tool | null) => void;
   toolResult: CompatibilityCallToolResult | null;
   nextCursor: ListToolsResult["nextCursor"];
   error: string | null;
 }) => {
   const [params, setParams] = useState>({});
+  useEffect(() => {
+    setParams({});
+  }, [selectedTool]);
 
   const renderToolResult = () => {
     if (!toolResult) return null;
@@ -110,7 +113,10 @@ const ToolsTab = ({
        {
+          clearTools();
+          setSelectedTool(null);
+        }}
         setSelectedItem={setSelectedTool}
         renderItem={(tool) => (
           <>

From 576ff0043a8f2c3e01430ea4e7aeb57e9e571266 Mon Sep 17 00:00:00 2001
From: Ashwin Bhat 
Date: Thu, 5 Dec 2024 08:01:57 -0800
Subject: [PATCH 06/34] bump version to 0.3.0

---
 client/package.json |  2 +-
 package-lock.json   | 12 ++++++------
 package.json        |  6 +++---
 server/package.json |  2 +-
 4 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/client/package.json b/client/package.json
index 5ed07ee..888495c 100644
--- a/client/package.json
+++ b/client/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@modelcontextprotocol/inspector-client",
-  "version": "0.2.7",
+  "version": "0.3.0",
   "description": "Client-side application for the Model Context Protocol inspector",
   "license": "MIT",
   "author": "Anthropic, PBC (https://anthropic.com)",
diff --git a/package-lock.json b/package-lock.json
index fb35e2c..26f07ff 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,20 +1,20 @@
 {
   "name": "@modelcontextprotocol/inspector",
-  "version": "0.2.7",
+  "version": "0.3.0",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "@modelcontextprotocol/inspector",
-      "version": "0.2.7",
+      "version": "0.3.0",
       "license": "MIT",
       "workspaces": [
         "client",
         "server"
       ],
       "dependencies": {
-        "@modelcontextprotocol/inspector-client": "0.2.7",
-        "@modelcontextprotocol/inspector-server": "0.2.7",
+        "@modelcontextprotocol/inspector-client": "0.3.0",
+        "@modelcontextprotocol/inspector-server": "0.3.0",
         "concurrently": "^9.0.1",
         "spawn-rx": "^5.1.0",
         "ts-node": "^10.9.2"
@@ -29,7 +29,7 @@
     },
     "client": {
       "name": "@modelcontextprotocol/inspector-client",
-      "version": "0.2.7",
+      "version": "0.3.0",
       "license": "MIT",
       "dependencies": {
         "@modelcontextprotocol/sdk": "^1.0.3",
@@ -6925,7 +6925,7 @@
     },
     "server": {
       "name": "@modelcontextprotocol/inspector-server",
-      "version": "0.2.7",
+      "version": "0.3.0",
       "license": "MIT",
       "dependencies": {
         "@modelcontextprotocol/sdk": "^1.0.3",
diff --git a/package.json b/package.json
index a86624f..9061a7a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@modelcontextprotocol/inspector",
-  "version": "0.2.7",
+  "version": "0.3.0",
   "description": "Model Context Protocol inspector",
   "license": "MIT",
   "author": "Anthropic, PBC (https://anthropic.com)",
@@ -33,8 +33,8 @@
     "publish-all": "npm publish --workspaces --access public && npm publish --access public"
   },
   "dependencies": {
-    "@modelcontextprotocol/inspector-client": "0.2.7",
-    "@modelcontextprotocol/inspector-server": "0.2.7",
+    "@modelcontextprotocol/inspector-client": "0.3.0",
+    "@modelcontextprotocol/inspector-server": "0.3.0",
     "concurrently": "^9.0.1",
     "spawn-rx": "^5.1.0",
     "ts-node": "^10.9.2"
diff --git a/server/package.json b/server/package.json
index d7864a4..c79e7d2 100644
--- a/server/package.json
+++ b/server/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@modelcontextprotocol/inspector-server",
-  "version": "0.2.7",
+  "version": "0.3.0",
   "description": "Server-side application for the Model Context Protocol inspector",
   "license": "MIT",
   "author": "Anthropic, PBC (https://anthropic.com)",

From 9ea77a729cfc9894e3e8d0c5c4fc3f10a0d69c65 Mon Sep 17 00:00:00 2001
From: "devin-ai-integration[bot]"
 <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Date: Fri, 6 Dec 2024 19:33:08 +0000
Subject: [PATCH 07/34] chore: add shell-quote package and types

---
 package-lock.json | 20 ++++++++++++++++----
 package.json      |  4 +++-
 2 files changed, 19 insertions(+), 5 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 26f07ff..1e81bd0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -24,7 +24,9 @@
       },
       "devDependencies": {
         "@types/node": "^22.7.5",
-        "prettier": "3.3.3"
+        "@types/shell-quote": "^1.7.5",
+        "prettier": "3.3.3",
+        "shell-quote": "^1.8.2"
       }
     },
     "client": {
@@ -2393,6 +2395,13 @@
         "@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": {
       "version": "8.5.13",
       "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz",
@@ -5688,10 +5697,13 @@
       }
     },
     "node_modules/shell-quote": {
-      "version": "1.8.1",
-      "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz",
-      "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==",
+      "version": "1.8.2",
+      "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz",
+      "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==",
       "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
       "funding": {
         "url": "https://github.com/sponsors/ljharb"
       }
diff --git a/package.json b/package.json
index 9061a7a..bdc835a 100644
--- a/package.json
+++ b/package.json
@@ -41,6 +41,8 @@
   },
   "devDependencies": {
     "@types/node": "^22.7.5",
-    "prettier": "3.3.3"
+    "@types/shell-quote": "^1.7.5",
+    "prettier": "3.3.3",
+    "shell-quote": "^1.8.2"
   }
 }

From cc1ae05f9d2fcbca82f1380d0df15ffd449a4ab7 Mon Sep 17 00:00:00 2001
From: "devin-ai-integration[bot]"
 <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Date: Fri, 6 Dec 2024 19:34:03 +0000
Subject: [PATCH 08/34] fix: use shell-quote for proper argument parsing

---
 server/src/index.ts | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/server/src/index.ts b/server/src/index.ts
index a217f44..940a008 100644
--- a/server/src/index.ts
+++ b/server/src/index.ts
@@ -3,6 +3,7 @@
 import cors from "cors";
 import EventSource from "eventsource";
 import { parseArgs } from "node:util";
+import { parse as shellParseArgs } from 'shell-quote';
 
 import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
 import {
@@ -38,7 +39,7 @@ const createTransport = async (query: express.Request["query"]) => {
 
   if (transportType === "stdio") {
     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 { cmd, args } = findActualExecutable(command, origArgs);

From c340e5f1ed69b32abd18ae255c5b677339b82166 Mon Sep 17 00:00:00 2001
From: "devin-ai-integration[bot]"
 <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Date: Fri, 6 Dec 2024 19:43:11 +0000
Subject: [PATCH 09/34] chore: move shell-quote to main dependencies

---
 package-lock.json | 4 ++--
 package.json      | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 1e81bd0..13f4248 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,6 +16,7 @@
         "@modelcontextprotocol/inspector-client": "0.3.0",
         "@modelcontextprotocol/inspector-server": "0.3.0",
         "concurrently": "^9.0.1",
+        "shell-quote": "^1.8.2",
         "spawn-rx": "^5.1.0",
         "ts-node": "^10.9.2"
       },
@@ -25,8 +26,7 @@
       "devDependencies": {
         "@types/node": "^22.7.5",
         "@types/shell-quote": "^1.7.5",
-        "prettier": "3.3.3",
-        "shell-quote": "^1.8.2"
+        "prettier": "3.3.3"
       }
     },
     "client": {
diff --git a/package.json b/package.json
index bdc835a..c262588 100644
--- a/package.json
+++ b/package.json
@@ -36,13 +36,13 @@
     "@modelcontextprotocol/inspector-client": "0.3.0",
     "@modelcontextprotocol/inspector-server": "0.3.0",
     "concurrently": "^9.0.1",
+    "shell-quote": "^1.8.2",
     "spawn-rx": "^5.1.0",
     "ts-node": "^10.9.2"
   },
   "devDependencies": {
     "@types/node": "^22.7.5",
     "@types/shell-quote": "^1.7.5",
-    "prettier": "3.3.3",
-    "shell-quote": "^1.8.2"
+    "prettier": "3.3.3"
   }
 }

From bd6586bbad77af75391296cf3bfaed7dc6941c55 Mon Sep 17 00:00:00 2001
From: "devin-ai-integration[bot]"
 <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Date: Fri, 6 Dec 2024 19:43:48 +0000
Subject: [PATCH 10/34] style: apply prettier formatting

---
 CODE_OF_CONDUCT.md    | 22 +++++++++++-----------
 SECURITY.md           |  1 +
 client/src/App.tsx    |  3 ++-
 client/src/main.tsx   |  4 ++--
 client/vite.config.ts |  8 ++++----
 server/src/index.ts   |  2 +-
 6 files changed, 21 insertions(+), 19 deletions(-)

diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 05c32c6..7e832b3 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -17,23 +17,23 @@ diverse, inclusive, and healthy community.
 Examples of behavior that contributes to a positive environment for our
 community include:
 
-* Demonstrating empathy and kindness toward other people
-* Being respectful of differing opinions, viewpoints, and experiences
-* Giving and gracefully accepting constructive feedback
-* Accepting responsibility and apologizing to those affected by our mistakes,
+- Demonstrating empathy and kindness toward other people
+- Being respectful of differing opinions, viewpoints, and experiences
+- Giving and gracefully accepting constructive feedback
+- Accepting responsibility and apologizing to those affected by our mistakes,
   and learning from the experience
-* Focusing on what is best not just for us as individuals, but for the
+- Focusing on what is best not just for us as individuals, but for the
   overall community
 
 Examples of unacceptable behavior include:
 
-* The use of sexualized language or imagery, and sexual attention or
+- The use of sexualized language or imagery, and sexual attention or
   advances of any kind
-* Trolling, insulting or derogatory comments, and personal or political attacks
-* Public or private harassment
-* Publishing others' private information, such as a physical or email
+- Trolling, insulting or derogatory comments, and personal or political attacks
+- Public or private harassment
+- Publishing others' private information, such as a physical or email
   address, without their explicit permission
-* Other conduct which could reasonably be considered inappropriate in a
+- Other conduct which could reasonably be considered inappropriate in a
   professional setting
 
 ## Enforcement Responsibilities
@@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban.
 ### 4. Permanent Ban
 
 **Community Impact**: Demonstrating a pattern of violation of community
-standards, including sustained inappropriate behavior,  harassment of an
+standards, including sustained inappropriate behavior, harassment of an
 individual, or aggression toward or disparagement of classes of individuals.
 
 **Consequence**: A permanent ban from any sort of public interaction within
diff --git a/SECURITY.md b/SECURITY.md
index 21f8e59..9d54767 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -1,4 +1,5 @@
 # Security Policy
+
 Thank you for helping us keep the inspector secure.
 
 ## Reporting Security Issues
diff --git a/client/src/App.tsx b/client/src/App.tsx
index 2c84377..8a4df79 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -59,7 +59,8 @@ const DEFAULT_REQUEST_TIMEOUT_MSEC = 10000;
 
 const params = new URLSearchParams(window.location.search);
 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 App = () => {
diff --git a/client/src/main.tsx b/client/src/main.tsx
index 8ed57a0..450213d 100644
--- a/client/src/main.tsx
+++ b/client/src/main.tsx
@@ -1,7 +1,7 @@
 import { StrictMode } from "react";
 import { createRoot } from "react-dom/client";
-import { ToastContainer } from 'react-toastify';
-import 'react-toastify/dist/ReactToastify.css';
+import { ToastContainer } from "react-toastify";
+import "react-toastify/dist/ReactToastify.css";
 import App from "./App.tsx";
 import "./index.css";
 
diff --git a/client/vite.config.ts b/client/vite.config.ts
index 3b6c407..dd3bd01 100644
--- a/client/vite.config.ts
+++ b/client/vite.config.ts
@@ -14,8 +14,8 @@ export default defineConfig({
     minify: false,
     rollupOptions: {
       output: {
-        manualChunks: undefined
-      }
-    }
-  }
+        manualChunks: undefined,
+      },
+    },
+  },
 });
diff --git a/server/src/index.ts b/server/src/index.ts
index 940a008..b82b17d 100644
--- a/server/src/index.ts
+++ b/server/src/index.ts
@@ -3,7 +3,7 @@
 import cors from "cors";
 import EventSource from "eventsource";
 import { parseArgs } from "node:util";
-import { parse as shellParseArgs } from 'shell-quote';
+import { parse as shellParseArgs } from "shell-quote";
 
 import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
 import {

From fdc521646fa97d6d70da984e538024c77171848e Mon Sep 17 00:00:00 2001
From: Jeffrey Ling 
Date: Fri, 6 Dec 2024 12:48:48 -0700
Subject: [PATCH 11/34] no need to prettier format everything right now

---
 CODE_OF_CONDUCT.md    | 22 +++++++++++-----------
 SECURITY.md           |  1 -
 client/src/App.tsx    |  3 +--
 client/src/main.tsx   |  4 ++--
 client/vite.config.ts |  8 ++++----
 5 files changed, 18 insertions(+), 20 deletions(-)

diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 7e832b3..05c32c6 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -17,23 +17,23 @@ diverse, inclusive, and healthy community.
 Examples of behavior that contributes to a positive environment for our
 community include:
 
-- Demonstrating empathy and kindness toward other people
-- Being respectful of differing opinions, viewpoints, and experiences
-- Giving and gracefully accepting constructive feedback
-- Accepting responsibility and apologizing to those affected by our mistakes,
+* Demonstrating empathy and kindness toward other people
+* Being respectful of differing opinions, viewpoints, and experiences
+* Giving and gracefully accepting constructive feedback
+* Accepting responsibility and apologizing to those affected by our mistakes,
   and learning from the experience
-- Focusing on what is best not just for us as individuals, but for the
+* Focusing on what is best not just for us as individuals, but for the
   overall community
 
 Examples of unacceptable behavior include:
 
-- The use of sexualized language or imagery, and sexual attention or
+* The use of sexualized language or imagery, and sexual attention or
   advances of any kind
-- Trolling, insulting or derogatory comments, and personal or political attacks
-- Public or private harassment
-- Publishing others' private information, such as a physical or email
+* Trolling, insulting or derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or email
   address, without their explicit permission
-- Other conduct which could reasonably be considered inappropriate in a
+* Other conduct which could reasonably be considered inappropriate in a
   professional setting
 
 ## Enforcement Responsibilities
@@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban.
 ### 4. Permanent Ban
 
 **Community Impact**: Demonstrating a pattern of violation of community
-standards, including sustained inappropriate behavior, harassment of an
+standards, including sustained inappropriate behavior,  harassment of an
 individual, or aggression toward or disparagement of classes of individuals.
 
 **Consequence**: A permanent ban from any sort of public interaction within
diff --git a/SECURITY.md b/SECURITY.md
index 9d54767..21f8e59 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -1,5 +1,4 @@
 # Security Policy
-
 Thank you for helping us keep the inspector secure.
 
 ## Reporting Security Issues
diff --git a/client/src/App.tsx b/client/src/App.tsx
index 8a4df79..2c84377 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -59,8 +59,7 @@ const DEFAULT_REQUEST_TIMEOUT_MSEC = 10000;
 
 const params = new URLSearchParams(window.location.search);
 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 App = () => {
diff --git a/client/src/main.tsx b/client/src/main.tsx
index 450213d..8ed57a0 100644
--- a/client/src/main.tsx
+++ b/client/src/main.tsx
@@ -1,7 +1,7 @@
 import { StrictMode } from "react";
 import { createRoot } from "react-dom/client";
-import { ToastContainer } from "react-toastify";
-import "react-toastify/dist/ReactToastify.css";
+import { ToastContainer } from 'react-toastify';
+import 'react-toastify/dist/ReactToastify.css';
 import App from "./App.tsx";
 import "./index.css";
 
diff --git a/client/vite.config.ts b/client/vite.config.ts
index dd3bd01..3b6c407 100644
--- a/client/vite.config.ts
+++ b/client/vite.config.ts
@@ -14,8 +14,8 @@ export default defineConfig({
     minify: false,
     rollupOptions: {
       output: {
-        manualChunks: undefined,
-      },
-    },
-  },
+        manualChunks: undefined
+      }
+    }
+  }
 });

From e96b3be15941c36d51fcb5f1c88b430a1eb5fefb Mon Sep 17 00:00:00 2001
From: "devin-ai-integration[bot]"
 <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Date: Sat, 7 Dec 2024 06:15:21 +0000
Subject: [PATCH 12/34] feat: implement capability negotiation for UI tabs

- Add CapabilityContext to manage server capabilities
- Disable tabs when server doesn't support feature
- Show error message in tab content when capability missing
- Implements #85
---
 client/src/App.tsx                     | 287 +++++++++++++------------
 client/src/components/PromptsTab.tsx   | 164 +++++++-------
 client/src/components/ResourcesTab.tsx | 267 ++++++++++++-----------
 client/src/components/ToolsTab.tsx     | 220 ++++++++++---------
 client/src/lib/contexts.ts             |  12 ++
 5 files changed, 509 insertions(+), 441 deletions(-)
 create mode 100644 client/src/lib/contexts.ts

diff --git a/client/src/App.tsx b/client/src/App.tsx
index 2c84377..2f36450 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -55,6 +55,8 @@ import SamplingTab, { PendingRequest } from "./components/SamplingTab";
 import Sidebar from "./components/Sidebar";
 import ToolsTab from "./components/ToolsTab";
 
+import { CapabilityContext, ServerCapabilities } from "@/lib/contexts";
+
 const DEFAULT_REQUEST_TIMEOUT_MSEC = 10000;
 
 const params = new URLSearchParams(window.location.search);
@@ -66,6 +68,7 @@ const App = () => {
   const [connectionStatus, setConnectionStatus] = useState<
     "disconnected" | "connected" | "error"
   >("disconnected");
+  const [serverCapabilities, setServerCapabilities] = useState(null);
   const [resources, setResources] = useState([]);
   const [resourceTemplates, setResourceTemplates] = useState<
     ResourceTemplate[]
@@ -217,6 +220,13 @@ const App = () => {
     rootsRef.current = roots;
   }, [roots]);
 
+  useEffect(() => {
+    if (mcpClient) {
+      const capabilities = mcpClient.getServerCapabilities();
+      setServerCapabilities(capabilities ?? null);
+    }
+  }, [mcpClient]);
+
   const pushHistory = (request: object, response?: object) => {
     setRequestHistory((prev) => [
       ...prev,
@@ -444,6 +454,9 @@ const App = () => {
 
       await client.connect(clientTransport);
 
+      const capabilities = client.getServerCapabilities();
+      setServerCapabilities(capabilities ?? null);
+
       client.setRequestHandler(CreateMessageRequestSchema, (request) => {
         return new Promise((resolve, reject) => {
           setPendingSampleRequests((prev) => [
@@ -485,143 +498,145 @@ const App = () => {
       
{mcpClient ? ( - - - - - Resources - - - - Prompts - - - - Tools - - - - Ping - - - - Sampling - {pendingSampleRequests.length > 0 && ( - - {pendingSampleRequests.length} - - )} - - - - Roots - - + + + + + + Resources + + + + Prompts + + + + Tools + + + + Ping + + + + Sampling + {pendingSampleRequests.length > 0 && ( + + {pendingSampleRequests.length} + + )} + + + + Roots + + -
- { - clearError("resources"); - listResources(); - }} - clearResources={() => { - setResources([]); - setNextResourceCursor(undefined); - }} - listResourceTemplates={() => { - clearError("resources"); - listResourceTemplates(); - }} - clearResourceTemplates={() => { - setResourceTemplates([]); - setNextResourceTemplateCursor(undefined); - }} - readResource={(uri) => { - clearError("resources"); - readResource(uri); - }} - selectedResource={selectedResource} - setSelectedResource={(resource) => { - clearError("resources"); - setSelectedResource(resource); - }} - resourceContent={resourceContent} - nextCursor={nextResourceCursor} - nextTemplateCursor={nextResourceTemplateCursor} - error={errors.resources} - /> - { - clearError("prompts"); - listPrompts(); - }} - clearPrompts={() => { - setPrompts([]); - setNextPromptCursor(undefined); - }} - getPrompt={(name, args) => { - clearError("prompts"); - getPrompt(name, args); - }} - selectedPrompt={selectedPrompt} - setSelectedPrompt={(prompt) => { - clearError("prompts"); - setSelectedPrompt(prompt); - }} - promptContent={promptContent} - nextCursor={nextPromptCursor} - error={errors.prompts} - /> - { - clearError("tools"); - listTools(); - }} - clearTools={() => { - setTools([]); - setNextToolCursor(undefined); - }} - callTool={(name, params) => { - clearError("tools"); - callTool(name, params); - }} - selectedTool={selectedTool} - setSelectedTool={(tool) => { - clearError("tools"); - setSelectedTool(tool); - setToolResult(null); - }} - toolResult={toolResult} - nextCursor={nextToolCursor} - error={errors.tools} - /> - - { - void makeRequest( - { - method: "ping" as const, - }, - EmptyResultSchema, - ); - }} - /> - - -
-
+
+ { + clearError("resources"); + listResources(); + }} + clearResources={() => { + setResources([]); + setNextResourceCursor(undefined); + }} + listResourceTemplates={() => { + clearError("resources"); + listResourceTemplates(); + }} + clearResourceTemplates={() => { + setResourceTemplates([]); + setNextResourceTemplateCursor(undefined); + }} + readResource={(uri) => { + clearError("resources"); + readResource(uri); + }} + selectedResource={selectedResource} + setSelectedResource={(resource) => { + clearError("resources"); + setSelectedResource(resource); + }} + resourceContent={resourceContent} + nextCursor={nextResourceCursor} + nextTemplateCursor={nextResourceTemplateCursor} + error={errors.resources} + /> + { + clearError("prompts"); + listPrompts(); + }} + clearPrompts={() => { + setPrompts([]); + setNextPromptCursor(undefined); + }} + getPrompt={(name, args) => { + clearError("prompts"); + getPrompt(name, args); + }} + selectedPrompt={selectedPrompt} + setSelectedPrompt={(prompt) => { + clearError("prompts"); + setSelectedPrompt(prompt); + }} + promptContent={promptContent} + nextCursor={nextPromptCursor} + error={errors.prompts} + /> + { + clearError("tools"); + listTools(); + }} + clearTools={() => { + setTools([]); + setNextToolCursor(undefined); + }} + callTool={(name, params) => { + clearError("tools"); + callTool(name, params); + }} + selectedTool={selectedTool} + setSelectedTool={(tool) => { + clearError("tools"); + setSelectedTool(tool); + setToolResult(null); + }} + toolResult={toolResult} + nextCursor={nextToolCursor} + error={errors.tools} + /> + + { + void makeRequest( + { + method: "ping" as const, + }, + EmptyResultSchema, + ); + }} + /> + + +
+
+ ) : (

diff --git a/client/src/components/PromptsTab.tsx b/client/src/components/PromptsTab.tsx index df8b8a5..5882e7e 100644 --- a/client/src/components/PromptsTab.tsx +++ b/client/src/components/PromptsTab.tsx @@ -6,8 +6,9 @@ import { TabsContent } from "@/components/ui/tabs"; import { Textarea } from "@/components/ui/textarea"; import { ListPromptsResult } from "@modelcontextprotocol/sdk/types.js"; import { AlertCircle } from "lucide-react"; -import { useState } from "react"; +import { useState, useContext } from "react"; import ListPane from "./ListPane"; +import { CapabilityContext } from "@/lib/contexts"; export type Prompt = { name: string; @@ -40,6 +41,7 @@ const PromptsTab = ({ nextCursor: ListPromptsResult["nextCursor"]; error: string | null; }) => { + const capabilities = useContext(CapabilityContext); const [promptArgs, setPromptArgs] = useState>({}); const handleInputChange = (argName: string, value: string) => { @@ -54,86 +56,98 @@ const PromptsTab = ({ return ( - { - setSelectedPrompt(prompt); - setPromptArgs({}); - }} - renderItem={(prompt) => ( - <> - {prompt.name} - {prompt.description} - - )} - title="Prompts" - buttonText={nextCursor ? "List More Prompts" : "List Prompts"} - isButtonDisabled={!nextCursor && prompts.length > 0} - /> + {!capabilities?.prompts ? ( + + + Feature Not Available + + The connected server does not support prompts. + + + ) : ( + <> + { + setSelectedPrompt(prompt); + setPromptArgs({}); + }} + renderItem={(prompt) => ( + <> + {prompt.name} + {prompt.description} + + )} + title="Prompts" + buttonText={nextCursor ? "List More Prompts" : "List Prompts"} + isButtonDisabled={!nextCursor && prompts.length > 0} + /> -

-
-

- {selectedPrompt ? selectedPrompt.name : "Select a prompt"} -

-
-
- {error ? ( - - - Error - {error} - - ) : selectedPrompt ? ( -
- {selectedPrompt.description && ( -

- {selectedPrompt.description} -

- )} - {selectedPrompt.arguments?.map((arg) => ( -
- - - handleInputChange(arg.name, e.target.value) - } - /> - {arg.description && ( -

- {arg.description} - {arg.required && ( - (Required) - )} +

+
+

+ {selectedPrompt ? selectedPrompt.name : "Select a prompt"} +

+
+
+ {error ? ( + + + Error + {error} + + ) : selectedPrompt ? ( +
+ {selectedPrompt.description && ( +

+ {selectedPrompt.description}

)} + {selectedPrompt.arguments?.map((arg) => ( +
+ + + handleInputChange(arg.name, e.target.value) + } + /> + {arg.description && ( +

+ {arg.description} + {arg.required && ( + (Required) + )} +

+ )} +
+ ))} + + {promptContent && ( +