-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Separate error states per tab, clear errors when clicking around #52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
733d2a6
0716ada
9d0c643
3bae267
da2ac8d
7a35078
c1c8fc2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,10 @@ | ||
| import { Client } from "@modelcontextprotocol/sdk/client/index.js"; | ||
| import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"; | ||
| import { | ||
| CompatibilityCallToolResultSchema, | ||
| ClientNotification, | ||
| ClientRequest, | ||
| CompatibilityCallToolResult, | ||
| CompatibilityCallToolResultSchema, | ||
| CreateMessageRequestSchema, | ||
| CreateMessageResult, | ||
| EmptyResultSchema, | ||
|
|
@@ -19,8 +21,6 @@ import { | |
| Root, | ||
| ServerNotification, | ||
| Tool, | ||
| CompatibilityCallToolResult, | ||
| ClientNotification, | ||
| } from "@modelcontextprotocol/sdk/types.js"; | ||
| import { useEffect, useRef, useState } from "react"; | ||
| // Add dark mode class based on system preference | ||
|
|
@@ -40,25 +40,25 @@ import { | |
| import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; | ||
| import { | ||
| Bell, | ||
| ChevronDown, | ||
| ChevronRight, | ||
| Files, | ||
| FolderTree, | ||
| Hammer, | ||
| Hash, | ||
| MessageSquare, | ||
| Play, | ||
| Send, | ||
| Terminal, | ||
| FolderTree, | ||
| ChevronDown, | ||
| ChevronRight, | ||
| } from "lucide-react"; | ||
|
|
||
| import { toast } from "react-toastify"; | ||
| import { ZodType } from "zod"; | ||
| import "./App.css"; | ||
| import ConsoleTab from "./components/ConsoleTab"; | ||
| import HistoryAndNotifications from "./components/History"; | ||
| import PingTab from "./components/PingTab"; | ||
| import PromptsTab, { Prompt } from "./components/PromptsTab"; | ||
| import RequestsTab from "./components/RequestsTabs"; | ||
| import ResourcesTab from "./components/ResourcesTab"; | ||
| import RootsTab from "./components/RootsTab"; | ||
| import SamplingTab, { PendingRequest } from "./components/SamplingTab"; | ||
|
|
@@ -79,7 +79,15 @@ const App = () => { | |
| const [tools, setTools] = useState<Tool[]>([]); | ||
| const [toolResult, setToolResult] = | ||
| useState<CompatibilityCallToolResult | null>(null); | ||
| const [error, setError] = useState<string | null>(null); | ||
| const [errors, setErrors] = useState<Record<string, string | null>>({ | ||
| resources: null, | ||
| prompts: null, | ||
| tools: null, | ||
| }); | ||
|
|
||
| const clearError = (tabKey: keyof typeof errors) => { | ||
| setErrors((prev) => ({ ...prev, [tabKey]: null })); | ||
| }; | ||
| const [command, setCommand] = useState<string>(() => { | ||
| return localStorage.getItem("lastCommand") || "mcp-server-everything"; | ||
| }); | ||
|
|
@@ -175,6 +183,7 @@ const App = () => { | |
| const makeRequest = async <T extends ZodType<object>>( | ||
| request: ClientRequest, | ||
| schema: T, | ||
| tabKey?: keyof typeof errors, | ||
| ) => { | ||
| if (!mcpClient) { | ||
| throw new Error("MCP client not connected"); | ||
|
|
@@ -183,9 +192,19 @@ const App = () => { | |
| try { | ||
| const response = await mcpClient.request(request, schema); | ||
| pushHistory(request, response); | ||
|
|
||
| if (tabKey !== undefined) { | ||
| setErrors((prev) => ({ ...prev, [tabKey]: null })); | ||
| } | ||
|
|
||
| return response; | ||
| } catch (e: unknown) { | ||
| setError((e as Error).message); | ||
| if (tabKey === undefined) { | ||
| toast.error((e as Error).message); | ||
| } else { | ||
| setErrors((prev) => ({ ...prev, [tabKey]: (e as Error).message })); | ||
| } | ||
|
Comment on lines
+234
to
+236
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. since we have a
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would only be used in this and |
||
|
|
||
| throw e; | ||
| } | ||
| }; | ||
|
|
@@ -199,7 +218,7 @@ const App = () => { | |
| await mcpClient.notification(notification); | ||
| pushHistory(notification); | ||
| } catch (e: unknown) { | ||
| setError((e as Error).message); | ||
| toast.error((e as Error).message); | ||
| throw e; | ||
| } | ||
| }; | ||
|
|
@@ -211,6 +230,7 @@ const App = () => { | |
| params: nextResourceCursor ? { cursor: nextResourceCursor } : {}, | ||
| }, | ||
| ListResourcesResultSchema, | ||
| "resources", | ||
| ); | ||
| setResources(resources.concat(response.resources ?? [])); | ||
| setNextResourceCursor(response.nextCursor); | ||
|
|
@@ -225,6 +245,7 @@ const App = () => { | |
| : {}, | ||
| }, | ||
| ListResourceTemplatesResultSchema, | ||
| "resources", | ||
| ); | ||
| setResourceTemplates( | ||
| resourceTemplates.concat(response.resourceTemplates ?? []), | ||
|
|
@@ -239,6 +260,7 @@ const App = () => { | |
| params: { uri }, | ||
| }, | ||
| ReadResourceResultSchema, | ||
| "resources", | ||
| ); | ||
| setResourceContent(JSON.stringify(response, null, 2)); | ||
| }; | ||
|
|
@@ -250,6 +272,7 @@ const App = () => { | |
| params: nextPromptCursor ? { cursor: nextPromptCursor } : {}, | ||
| }, | ||
| ListPromptsResultSchema, | ||
| "prompts", | ||
| ); | ||
| setPrompts(response.prompts); | ||
| setNextPromptCursor(response.nextCursor); | ||
|
|
@@ -262,6 +285,7 @@ const App = () => { | |
| params: { name, arguments: args }, | ||
| }, | ||
| GetPromptResultSchema, | ||
| "prompts", | ||
| ); | ||
| setPromptContent(JSON.stringify(response, null, 2)); | ||
| }; | ||
|
|
@@ -273,6 +297,7 @@ const App = () => { | |
| params: nextToolCursor ? { cursor: nextToolCursor } : {}, | ||
| }, | ||
| ListToolsResultSchema, | ||
| "tools", | ||
| ); | ||
| setTools(response.tools); | ||
| setNextToolCursor(response.nextCursor); | ||
|
|
@@ -291,6 +316,7 @@ const App = () => { | |
| }, | ||
| }, | ||
| CompatibilityCallToolResultSchema, | ||
| "tools", | ||
| ); | ||
| setToolResult(response); | ||
| }; | ||
|
|
@@ -507,39 +533,66 @@ const App = () => { | |
| <ResourcesTab | ||
| resources={resources} | ||
| resourceTemplates={resourceTemplates} | ||
| listResources={listResources} | ||
| listResourceTemplates={listResourceTemplates} | ||
| readResource={readResource} | ||
| listResources={() => { | ||
| clearError("resources"); | ||
| listResources(); | ||
| }} | ||
| listResourceTemplates={() => { | ||
| clearError("resources"); | ||
| listResourceTemplates(); | ||
| }} | ||
| readResource={(uri) => { | ||
| clearError("resources"); | ||
| readResource(uri); | ||
| }} | ||
| selectedResource={selectedResource} | ||
| setSelectedResource={setSelectedResource} | ||
| setSelectedResource={(resource) => { | ||
| clearError("resources"); | ||
| setSelectedResource(resource); | ||
| }} | ||
| resourceContent={resourceContent} | ||
| nextCursor={nextResourceCursor} | ||
| nextTemplateCursor={nextResourceTemplateCursor} | ||
| error={error} | ||
| error={errors.resources} | ||
| /> | ||
| <PromptsTab | ||
| prompts={prompts} | ||
| listPrompts={listPrompts} | ||
| getPrompt={getPrompt} | ||
| listPrompts={() => { | ||
| clearError("prompts"); | ||
| listPrompts(); | ||
| }} | ||
| getPrompt={(name, args) => { | ||
| clearError("prompts"); | ||
| getPrompt(name, args); | ||
| }} | ||
| selectedPrompt={selectedPrompt} | ||
| setSelectedPrompt={setSelectedPrompt} | ||
| setSelectedPrompt={(prompt) => { | ||
| clearError("prompts"); | ||
| setSelectedPrompt(prompt); | ||
| }} | ||
| promptContent={promptContent} | ||
| nextCursor={nextPromptCursor} | ||
| error={error} | ||
| error={errors.prompts} | ||
| /> | ||
| <RequestsTab /> | ||
| <ToolsTab | ||
| tools={tools} | ||
| listTools={listTools} | ||
| callTool={callTool} | ||
| listTools={() => { | ||
| clearError("tools"); | ||
| listTools(); | ||
| }} | ||
| callTool={(name, params) => { | ||
| clearError("tools"); | ||
| callTool(name, params); | ||
| }} | ||
| selectedTool={selectedTool} | ||
| setSelectedTool={(tool) => { | ||
| clearError("tools"); | ||
| setSelectedTool(tool); | ||
| setToolResult(null); | ||
| }} | ||
| toolResult={toolResult} | ||
| nextCursor={nextToolCursor} | ||
| error={error} | ||
| error={errors.tools} | ||
| /> | ||
| <ConsoleTab /> | ||
| <PingTab | ||
|
|
||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,13 @@ | ||
| import { StrictMode } from "react"; | ||
| import { createRoot } from "react-dom/client"; | ||
| import { ToastContainer } from 'react-toastify'; | ||
| import 'react-toastify/dist/ReactToastify.css'; | ||
| import App from "./App.tsx"; | ||
| import "./index.css"; | ||
|
|
||
| createRoot(document.getElementById("root")!).render( | ||
| <StrictMode> | ||
| <App /> | ||
| <ToastContainer /> | ||
| </StrictMode>, | ||
| ); |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.