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 01/29] 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 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 02/29] 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 03/29] 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 04/29] 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 05/29] 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 06/29] 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 07/29] 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 && ( +