diff --git a/README.md b/README.md index b312f71c..7c41764d 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,74 @@ CLIENT_PORT=8080 SERVER_PORT=9000 npx @modelcontextprotocol/inspector node build For more details on ways to use the inspector, see the [Inspector section of the MCP docs site](https://modelcontextprotocol.io/docs/tools/inspector). For help with debugging, see the [Debugging guide](https://modelcontextprotocol.io/docs/tools/debugging). +### Servers File Export + +The MCP Inspector provides convenient buttons to export server launch configurations for use in clients such as Cursor, Claude Code, or the Inspector's CLI. The file is usually called `mcp.json`. + +- **Server Entry** - Copies a single server configuration entry to your clipboard. This can be added to your `mcp.json` file inside the `mcpServers` object with your preferred server name. + + **STDIO transport example:** + + ```json + { + "command": "node", + "args": ["build/index.js", "--debug"], + "env": { + "API_KEY": "your-api-key", + "DEBUG": "true" + } + } + ``` + + **SSE transport example:** + + ```json + { + "type": "sse", + "url": "http://localhost:3000/events", + "note": "For SSE connections, add this URL directly in Client" + } + ``` + +- **Servers File** - Copies a complete MCP configuration file structure to your clipboard, with your current server configuration added as `default-server`. This can be saved directly as `mcp.json`. + + **STDIO transport example:** + + ```json + { + "mcpServers": { + "default-server": { + "command": "node", + "args": ["build/index.js", "--debug"], + "env": { + "API_KEY": "your-api-key", + "DEBUG": "true" + } + } + } + } + ``` + + **SSE transport example:** + + ```json + { + "mcpServers": { + "default-server": { + "type": "sse", + "url": "http://localhost:3000/events", + "note": "For SSE connections, add this URL directly in Client" + } + } + } + ``` + +These buttons appear in the Inspector UI after you've configured your server settings, making it easy to save and reuse your configurations. + +For SSE transport connections, the Inspector provides similar functionality for both buttons. The "Server Entry" button copies the SSE URL configuration that can be added to your existing configuration file, while the "Servers File" button creates a complete configuration file containing the SSE URL for direct use in clients. + +You can paste the Server Entry into your existing `mcp.json` file under your chosen server name, or use the complete Servers File payload to create a new configuration file. + ### Authentication The inspector supports bearer token authentication for SSE connections. Enter your token in the UI when connecting to an MCP server, and it will be sent in the Authorization header. You can override the header name using the input field in the sidebar. @@ -93,6 +161,8 @@ Example server configuration file: } ``` +> **Tip:** You can easily generate this configuration format using the **Server Entry** and **Servers File** buttons in the Inspector UI, as described in the Servers File Export section above. + ### From this repository If you're working on the inspector itself: diff --git a/client/src/components/Sidebar.tsx b/client/src/components/Sidebar.tsx index 6c95daa8..180e8a22 100644 --- a/client/src/components/Sidebar.tsx +++ b/client/src/components/Sidebar.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useState, useCallback } from "react"; import { Play, ChevronDown, @@ -12,6 +12,8 @@ import { Settings, HelpCircle, RefreshCwOff, + Copy, + CheckCheck, } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; @@ -36,6 +38,7 @@ import { TooltipTrigger, TooltipContent, } from "@/components/ui/tooltip"; +import { useToast } from "../lib/hooks/useToast"; interface SidebarProps { connectionStatus: ConnectionStatus; @@ -95,6 +98,115 @@ const Sidebar = ({ const [showBearerToken, setShowBearerToken] = useState(false); const [showConfig, setShowConfig] = useState(false); const [shownEnvVars, setShownEnvVars] = useState>(new Set()); + const [copiedServerEntry, setCopiedServerEntry] = useState(false); + const [copiedServerFile, setCopiedServerFile] = useState(false); + const { toast } = useToast(); + + // Shared utility function to generate server config + const generateServerConfig = useCallback(() => { + if (transportType === "stdio") { + return { + command, + args: args.trim() ? args.split(/\s+/) : [], + env: { ...env }, + }; + } else { + return { + type: "sse", + url: sseUrl, + note: "For SSE connections, add this URL directly in Client", + }; + } + }, [transportType, command, args, env, sseUrl]); + + // Memoized config entry generator + const generateMCPServerEntry = useCallback(() => { + return JSON.stringify(generateServerConfig(), null, 2); + }, [generateServerConfig]); + + // Memoized config file generator + const generateMCPServerFile = useCallback(() => { + return JSON.stringify( + { + mcpServers: { + "default-server": generateServerConfig(), + }, + }, + null, + 2, + ); + }, [generateServerConfig]); + + // Memoized copy handlers + const handleCopyServerEntry = useCallback(() => { + try { + const configJson = generateMCPServerEntry(); + navigator.clipboard + .writeText(configJson) + .then(() => { + setCopiedServerEntry(true); + + toast({ + title: "Config entry copied", + description: + transportType === "stdio" + ? "Server configuration has been copied to clipboard. Add this to your mcp.json inside the 'mcpServers' object with your preferred server name." + : "SSE URL has been copied. Use this URL in Cursor directly.", + }); + + setTimeout(() => { + setCopiedServerEntry(false); + }, 2000); + }) + .catch((error) => { + toast({ + title: "Error", + description: `Failed to copy config: ${error instanceof Error ? error.message : String(error)}`, + variant: "destructive", + }); + }); + } catch (error) { + toast({ + title: "Error", + description: `Failed to copy config: ${error instanceof Error ? error.message : String(error)}`, + variant: "destructive", + }); + } + }, [generateMCPServerEntry, transportType, toast]); + + const handleCopyServerFile = useCallback(() => { + try { + const configJson = generateMCPServerFile(); + navigator.clipboard + .writeText(configJson) + .then(() => { + setCopiedServerFile(true); + + toast({ + title: "Servers file copied", + description: + "Servers configuration has been copied to clipboard. Add this to your mcp.json file. Current testing server will be added as 'default-server'", + }); + + setTimeout(() => { + setCopiedServerFile(false); + }, 2000); + }) + .catch((error) => { + toast({ + title: "Error", + description: `Failed to copy config: ${error instanceof Error ? error.message : String(error)}`, + variant: "destructive", + }); + }); + } catch (error) { + toast({ + title: "Error", + description: `Failed to copy config: ${error instanceof Error ? error.message : String(error)}`, + variant: "destructive", + }); + } + }, [generateMCPServerFile, toast]); return (
@@ -161,6 +273,44 @@ const Sidebar = ({ className="font-mono" />
+
+ + + + + Copy Server Entry + + + + + + Copy Servers File + +
) : ( <> @@ -176,6 +326,22 @@ const Sidebar = ({ className="font-mono" /> +
+ +