import { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"; import { CallToolResultSchema, ClientRequest, CreateMessageRequestSchema, CreateMessageResult, EmptyResultSchema, GetPromptResultSchema, ListPromptsResultSchema, ListResourcesResultSchema, ListToolsResultSchema, ProgressNotificationSchema, ReadResourceResultSchema, Resource, ServerNotification, Tool, } from "@modelcontextprotocol/sdk/types.js"; import { useEffect, useRef, useState } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Bell, Files, Hammer, Hash, MessageSquare, Play, Send, Terminal, } from "lucide-react"; import { AnyZodObject } 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 SamplingTab, { PendingRequest } from "./components/SamplingTab"; import Sidebar from "./components/Sidebar"; import ToolsTab from "./components/ToolsTab"; const App = () => { const [connectionStatus, setConnectionStatus] = useState< "disconnected" | "connected" | "error" >("disconnected"); const [resources, setResources] = useState([]); const [resourceContent, setResourceContent] = useState(""); const [prompts, setPrompts] = useState([]); const [promptContent, setPromptContent] = useState(""); const [tools, setTools] = useState([]); const [toolResult, setToolResult] = useState(""); const [error, setError] = useState(null); const [command, setCommand] = useState(() => { return ( localStorage.getItem("lastCommand") || "/Users/ashwin/.nvm/versions/node/v18.20.4/bin/node" ); }); const [args, setArgs] = useState(() => { return ( localStorage.getItem("lastArgs") || "/Users/ashwin/code/mcp/example-servers/build/everything/stdio.js" ); }); const [url, setUrl] = useState("http://localhost:3001/sse"); const [transportType, setTransportType] = useState<"stdio" | "sse">("stdio"); const [requestHistory, setRequestHistory] = useState< { request: string; response: string }[] >([]); const [mcpClient, setMcpClient] = useState(null); const [notifications, setNotifications] = useState([]); const [pendingSampleRequests, setPendingSampleRequests] = useState< Array< PendingRequest & { resolve: (result: CreateMessageResult) => void; reject: (error: Error) => void; } > >([]); const nextRequestId = useRef(0); const handleApproveSampling = (id: number, result: CreateMessageResult) => { setPendingSampleRequests((prev) => { const request = prev.find((r) => r.id === id); request?.resolve(result); return prev.filter((r) => r.id !== id); }); }; const handleRejectSampling = (id: number) => { setPendingSampleRequests((prev) => { const request = prev.find((r) => r.id === id); request?.reject(new Error("Sampling request rejected")); return prev.filter((r) => r.id !== id); }); }; const [selectedResource, setSelectedResource] = useState( null, ); const [selectedPrompt, setSelectedPrompt] = useState(null); const [selectedTool, setSelectedTool] = useState(null); const [nextResourceCursor, setNextResourceCursor] = useState< string | undefined >(); const [nextPromptCursor, setNextPromptCursor] = useState< string | undefined >(); const [nextToolCursor, setNextToolCursor] = useState(); const progressTokenRef = useRef(0); useEffect(() => { localStorage.setItem("lastCommand", command); }, [command]); useEffect(() => { localStorage.setItem("lastArgs", args); }, [args]); const pushHistory = (request: object, response: object) => { setRequestHistory((prev) => [ ...prev, { request: JSON.stringify(request), response: JSON.stringify(response) }, ]); }; const makeRequest = async ( request: ClientRequest, schema: T, ) => { if (!mcpClient) { throw new Error("MCP client not connected"); } try { const response = await mcpClient.request(request, schema); pushHistory(request, response); return response; } catch (e: unknown) { setError((e as Error).message); throw e; } }; const listResources = async () => { const response = await makeRequest( { method: "resources/list" as const, params: nextResourceCursor ? { cursor: nextResourceCursor } : {}, }, ListResourcesResultSchema, ); setResources(resources.concat(response.resources ?? [])); setNextResourceCursor(response.nextCursor); }; const readResource = async (uri: string) => { const response = await makeRequest( { method: "resources/read" as const, params: { uri }, }, ReadResourceResultSchema, ); setResourceContent(JSON.stringify(response, null, 2)); }; const listPrompts = async () => { const response = await makeRequest( { method: "prompts/list" as const, params: nextPromptCursor ? { cursor: nextPromptCursor } : {}, }, ListPromptsResultSchema, ); setPrompts(response.prompts); setNextPromptCursor(response.nextCursor); }; const getPrompt = async (name: string, args: Record = {}) => { const response = await makeRequest( { method: "prompts/get" as const, params: { name, arguments: args }, }, GetPromptResultSchema, ); setPromptContent(JSON.stringify(response, null, 2)); }; const listTools = async () => { const response = await makeRequest( { method: "tools/list" as const, params: nextToolCursor ? { cursor: nextToolCursor } : {}, }, ListToolsResultSchema, ); setTools(response.tools); setNextToolCursor(response.nextCursor); }; const callTool = async (name: string, params: Record) => { const response = await makeRequest( { method: "tools/call" as const, params: { name, arguments: params, _meta: { progressToken: progressTokenRef.current++, }, }, }, CallToolResultSchema, ); setToolResult(JSON.stringify(response.toolResult, null, 2)); }; const connectMcpServer = async () => { try { const client = new Client({ name: "mcp-inspector", version: "0.0.1", }); const backendUrl = new URL("http://localhost:3000/sse"); backendUrl.searchParams.append("transportType", transportType); if (transportType === "stdio") { backendUrl.searchParams.append("command", command); backendUrl.searchParams.append("args", args); } else { backendUrl.searchParams.append("url", url); } const clientTransport = new SSEClientTransport(backendUrl); await client.connect(clientTransport); client.setNotificationHandler( ProgressNotificationSchema, (notification) => { setNotifications((prevNotifications) => [ ...prevNotifications, notification, ]); }, ); client.setRequestHandler(CreateMessageRequestSchema, (request) => { return new Promise((resolve, reject) => { setPendingSampleRequests((prev) => [ ...prev, { id: nextRequestId.current++, request, resolve, reject }, ]); }); }); setMcpClient(client); setConnectionStatus("connected"); } catch (e) { console.error(e); setConnectionStatus("error"); } }; return (

MCP Inspector

Connect MCP Server

{transportType === "stdio" ? ( <> setCommand(e.target.value)} /> setArgs(e.target.value)} /> ) : ( setUrl(e.target.value)} /> )}
{mcpClient ? ( Resources Prompts Requests Tools Console Ping Sampling {pendingSampleRequests.length > 0 && ( {pendingSampleRequests.length} )}
{ setSelectedTool(tool); setToolResult(""); }} toolResult={toolResult} nextCursor={nextToolCursor} error={error} /> { void makeRequest( { method: "ping" as const, }, EmptyResultSchema, ); }} />
) : (

Connect to an MCP server to start inspecting

)}
); }; export default App;