diff --git a/client/src/App.tsx b/client/src/App.tsx index e90090f..433e45a 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -22,21 +22,12 @@ import { CompatibilityCallToolResult, ClientNotification, } from "@modelcontextprotocol/sdk/types.js"; -import { useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; // Add dark mode class based on system preference if (window.matchMedia("(prefers-color-scheme: dark)").matches) { document.documentElement.classList.add("dark"); } -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, @@ -44,12 +35,9 @@ import { Hammer, Hash, MessageSquare, - Play, Send, Terminal, FolderTree, - ChevronDown, - ChevronRight, } from "lucide-react"; import { ZodType } from "zod"; @@ -95,7 +83,6 @@ const App = () => { const [notifications, setNotifications] = useState([]); const [roots, setRoots] = useState([]); const [env, setEnv] = useState>({}); - const [showEnvVars, setShowEnvVars] = useState(false); const [pendingSampleRequests, setPendingSampleRequests] = useState< Array< @@ -140,6 +127,49 @@ const App = () => { >(); const [nextToolCursor, setNextToolCursor] = useState(); const progressTokenRef = useRef(0); + const [historyPaneHeight, setHistoryPaneHeight] = useState(300); + const [isDragging, setIsDragging] = useState(false); + const dragStartY = useRef(0); + const dragStartHeight = useRef(0); + + const handleDragStart = useCallback( + (e: React.MouseEvent) => { + setIsDragging(true); + dragStartY.current = e.clientY; + dragStartHeight.current = historyPaneHeight; + document.body.style.userSelect = "none"; + }, + [historyPaneHeight], + ); + + const handleDragMove = useCallback( + (e: MouseEvent) => { + if (!isDragging) return; + const deltaY = dragStartY.current - e.clientY; + const newHeight = Math.max( + 100, + Math.min(800, dragStartHeight.current + deltaY), + ); + setHistoryPaneHeight(newHeight); + }, + [isDragging], + ); + + const handleDragEnd = useCallback(() => { + setIsDragging(false); + document.body.style.userSelect = ""; + }, []); + + useEffect(() => { + if (isDragging) { + window.addEventListener("mousemove", handleDragMove); + window.addEventListener("mouseup", handleDragEnd); + return () => { + window.removeEventListener("mousemove", handleDragMove); + window.removeEventListener("mouseup", handleDragEnd); + }; + } + }, [isDragging, handleDragMove, handleDragEnd]); useEffect(() => { localStorage.setItem("lastCommand", command); @@ -353,231 +383,153 @@ const App = () => { return (
- +
-

MCP Inspector

-
-
-
-

Connect MCP Server

-
- - {transportType === "stdio" ? ( - <> - setCommand(e.target.value)} - /> - setArgs(e.target.value)} - /> - - ) : ( - setUrl(e.target.value)} - /> - )} - -
- {transportType === "stdio" && ( -
- - {showEnvVars && ( -
- {Object.entries(env).map(([key, value]) => ( -
- - setEnv((prev) => ({ - ...prev, - [e.target.value]: value, - })) - } - /> - - setEnv((prev) => ({ - ...prev, - [key]: e.target.value, - })) - } - /> - -
- ))} - -
+
+ {mcpClient ? ( + + + + + Resources + + + + Prompts + + + + Requests + + + + Tools + + + + Console + + + + Ping + + + + Sampling + {pendingSampleRequests.length > 0 && ( + + {pendingSampleRequests.length} + )} -
- )} -
- {mcpClient ? ( - - - - - Resources - - - - Prompts - - - - Requests - - - - Tools - - - - Console - - - - Ping - - - - Sampling - {pendingSampleRequests.length > 0 && ( - - {pendingSampleRequests.length} - - )} - - - - Roots - - - -
- - - - { - setSelectedTool(tool); - setToolResult(null); - }} - toolResult={toolResult} - nextCursor={nextToolCursor} - error={error} - /> - - { - void makeRequest( - { - method: "ping" as const, - }, - EmptyResultSchema, - ); - }} - /> - - -
-
- ) : ( -
-

- Connect to an MCP server to start inspecting -

+ + + + Roots + + + +
+ + + + { + setSelectedTool(tool); + setToolResult(null); + }} + toolResult={toolResult} + nextCursor={nextToolCursor} + error={error} + /> + + { + void makeRequest( + { + method: "ping" as const, + }, + EmptyResultSchema, + ); + }} + /> + +
- )} + + ) : ( +
+

+ Connect to an MCP server to start inspecting +

+
+ )} +
+
+
+
+
+
+
-
); }; diff --git a/client/src/components/History.tsx b/client/src/components/History.tsx index aaa94fa..532d3b1 100644 --- a/client/src/components/History.tsx +++ b/client/src/components/History.tsx @@ -29,8 +29,8 @@ const HistoryAndNotifications = ({ }; return ( -
-
+
+

History

{requestHistory.length === 0 ? (

No history yet

@@ -107,7 +107,7 @@ const HistoryAndNotifications = ({ )}
-
+

Server Notifications

{serverNotifications.length === 0 ? (

No notifications yet

diff --git a/client/src/components/HistoryAndNotifications.tsx b/client/src/components/HistoryAndNotifications.tsx deleted file mode 100644 index e69de29..0000000 diff --git a/client/src/components/Sidebar.tsx b/client/src/components/Sidebar.tsx index e6e31a4..544a4de 100644 --- a/client/src/components/Sidebar.tsx +++ b/client/src/components/Sidebar.tsx @@ -1,39 +1,196 @@ -import { Menu, Settings } from "lucide-react"; +import { useState } from "react"; + +import { Play, ChevronDown, ChevronRight, Settings } from "lucide-react"; import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; -const Sidebar = ({ connectionStatus }: { connectionStatus: string }) => ( -
-
- -

MCP Inspector

-
+interface SidebarProps { + connectionStatus: "disconnected" | "connected" | "error"; + transportType: "stdio" | "sse"; + setTransportType: (type: "stdio" | "sse") => void; + command: string; + setCommand: (command: string) => void; + args: string; + setArgs: (args: string) => void; + url: string; + setUrl: (url: string) => void; + env: Record; + setEnv: (env: Record) => void; + onConnect: () => void; +} + +const Sidebar = ({ + connectionStatus, + transportType, + setTransportType, + command, + setCommand, + args, + setArgs, + url, + setUrl, + env, + setEnv, + onConnect, +}: SidebarProps) => { + const [showEnvVars, setShowEnvVars] = useState(false); -
-
-
- - {connectionStatus === "connected" - ? "Connected" - : connectionStatus === "error" - ? "Connection Error" - : "Disconnected"} - + return ( +
+
+ +

MCP Inspector

- +
+
+
+ + +
+ {transportType === "stdio" ? ( + <> +
+ + setCommand(e.target.value)} + /> +
+
+ + setArgs(e.target.value)} + /> +
+ + ) : ( +
+ + setUrl(e.target.value)} + /> +
+ )} + {transportType === "stdio" && ( +
+ + {showEnvVars && ( +
+ {Object.entries(env).map(([key, value]) => ( +
+
+ { + const newEnv = { ...env }; + newEnv[e.target.value] = value; + setEnv(newEnv); + }} + /> + { + const newEnv = { ...env }; + newEnv[key] = e.target.value; + setEnv(newEnv); + }} + /> +
+ +
+ ))} + +
+ )} +
+ )} + +
+ + +
+
+ + {connectionStatus === "connected" + ? "Connected" + : connectionStatus === "error" + ? "Connection Error" + : "Disconnected"} + +
+
+
+
-
-); + ); +}; export default Sidebar;