Skip to content

Commit 05e41d2

Browse files
authored
Merge pull request #334 from sumeetpardeshi/main
feat: Add copy json config button
2 parents 5e8e78c + 1e48310 commit 05e41d2

File tree

4 files changed

+477
-2
lines changed

4 files changed

+477
-2
lines changed

README.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,74 @@ CLIENT_PORT=8080 SERVER_PORT=9000 npx @modelcontextprotocol/inspector node build
4242

4343
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).
4444

45+
### Servers File Export
46+
47+
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`.
48+
49+
- **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.
50+
51+
**STDIO transport example:**
52+
53+
```json
54+
{
55+
"command": "node",
56+
"args": ["build/index.js", "--debug"],
57+
"env": {
58+
"API_KEY": "your-api-key",
59+
"DEBUG": "true"
60+
}
61+
}
62+
```
63+
64+
**SSE transport example:**
65+
66+
```json
67+
{
68+
"type": "sse",
69+
"url": "http://localhost:3000/events",
70+
"note": "For SSE connections, add this URL directly in Client"
71+
}
72+
```
73+
74+
- **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`.
75+
76+
**STDIO transport example:**
77+
78+
```json
79+
{
80+
"mcpServers": {
81+
"default-server": {
82+
"command": "node",
83+
"args": ["build/index.js", "--debug"],
84+
"env": {
85+
"API_KEY": "your-api-key",
86+
"DEBUG": "true"
87+
}
88+
}
89+
}
90+
}
91+
```
92+
93+
**SSE transport example:**
94+
95+
```json
96+
{
97+
"mcpServers": {
98+
"default-server": {
99+
"type": "sse",
100+
"url": "http://localhost:3000/events",
101+
"note": "For SSE connections, add this URL directly in Client"
102+
}
103+
}
104+
}
105+
```
106+
107+
These buttons appear in the Inspector UI after you've configured your server settings, making it easy to save and reuse your configurations.
108+
109+
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.
110+
111+
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.
112+
45113
### Authentication
46114

47115
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.
@@ -94,6 +162,8 @@ Example server configuration file:
94162
}
95163
```
96164

165+
> **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.
166+
97167
You can also set the initial `transport` type, `serverUrl`, `serverCommand`, and `serverArgs` via query params, for example:
98168

99169
```

client/src/components/Sidebar.tsx

Lines changed: 159 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState } from "react";
1+
import { useState, useCallback } from "react";
22
import {
33
Play,
44
ChevronDown,
@@ -12,6 +12,8 @@ import {
1212
Settings,
1313
HelpCircle,
1414
RefreshCwOff,
15+
Copy,
16+
CheckCheck,
1517
} from "lucide-react";
1618
import { Button } from "@/components/ui/button";
1719
import { Input } from "@/components/ui/input";
@@ -36,6 +38,7 @@ import {
3638
TooltipTrigger,
3739
TooltipContent,
3840
} from "@/components/ui/tooltip";
41+
import { useToast } from "../lib/hooks/useToast";
3942

4043
interface SidebarProps {
4144
connectionStatus: ConnectionStatus;
@@ -95,6 +98,120 @@ const Sidebar = ({
9598
const [showBearerToken, setShowBearerToken] = useState(false);
9699
const [showConfig, setShowConfig] = useState(false);
97100
const [shownEnvVars, setShownEnvVars] = useState<Set<string>>(new Set());
101+
const [copiedServerEntry, setCopiedServerEntry] = useState(false);
102+
const [copiedServerFile, setCopiedServerFile] = useState(false);
103+
const { toast } = useToast();
104+
105+
// Reusable error reporter for copy actions
106+
const reportError = useCallback(
107+
(error: unknown) => {
108+
toast({
109+
title: "Error",
110+
description: `Failed to copy config: ${error instanceof Error ? error.message : String(error)}`,
111+
variant: "destructive",
112+
});
113+
},
114+
[toast],
115+
);
116+
117+
// Shared utility function to generate server config
118+
const generateServerConfig = useCallback(() => {
119+
if (transportType === "stdio") {
120+
return {
121+
command,
122+
args: args.trim() ? args.split(/\s+/) : [],
123+
env: { ...env },
124+
};
125+
}
126+
if (transportType === "sse") {
127+
return {
128+
type: "sse",
129+
url: sseUrl,
130+
note: "For SSE connections, add this URL directly in Client",
131+
};
132+
}
133+
if (transportType === "streamable-http") {
134+
return {
135+
type: "streamable-http",
136+
url: sseUrl,
137+
note: "For Streamable HTTP connections, add this URL directly in Client",
138+
};
139+
}
140+
return {};
141+
}, [transportType, command, args, env, sseUrl]);
142+
143+
// Memoized config entry generator
144+
const generateMCPServerEntry = useCallback(() => {
145+
return JSON.stringify(generateServerConfig(), null, 4);
146+
}, [generateServerConfig]);
147+
148+
// Memoized config file generator
149+
const generateMCPServerFile = useCallback(() => {
150+
return JSON.stringify(
151+
{
152+
mcpServers: {
153+
"default-server": generateServerConfig(),
154+
},
155+
},
156+
null,
157+
4,
158+
);
159+
}, [generateServerConfig]);
160+
161+
// Memoized copy handlers
162+
const handleCopyServerEntry = useCallback(() => {
163+
try {
164+
const configJson = generateMCPServerEntry();
165+
navigator.clipboard
166+
.writeText(configJson)
167+
.then(() => {
168+
setCopiedServerEntry(true);
169+
170+
toast({
171+
title: "Config entry copied",
172+
description:
173+
transportType === "stdio"
174+
? "Server configuration has been copied to clipboard. Add this to your mcp.json inside the 'mcpServers' object with your preferred server name."
175+
: "SSE URL has been copied. Use this URL in Cursor directly.",
176+
});
177+
178+
setTimeout(() => {
179+
setCopiedServerEntry(false);
180+
}, 2000);
181+
})
182+
.catch((error) => {
183+
reportError(error);
184+
});
185+
} catch (error) {
186+
reportError(error);
187+
}
188+
}, [generateMCPServerEntry, transportType, toast, reportError]);
189+
190+
const handleCopyServerFile = useCallback(() => {
191+
try {
192+
const configJson = generateMCPServerFile();
193+
navigator.clipboard
194+
.writeText(configJson)
195+
.then(() => {
196+
setCopiedServerFile(true);
197+
198+
toast({
199+
title: "Servers file copied",
200+
description:
201+
"Servers configuration has been copied to clipboard. Add this to your mcp.json file. Current testing server will be added as 'default-server'",
202+
});
203+
204+
setTimeout(() => {
205+
setCopiedServerFile(false);
206+
}, 2000);
207+
})
208+
.catch((error) => {
209+
reportError(error);
210+
});
211+
} catch (error) {
212+
reportError(error);
213+
}
214+
}, [generateMCPServerFile, toast, reportError]);
98215

99216
return (
100217
<div className="w-80 bg-card border-r border-border flex flex-col h-full">
@@ -223,6 +340,7 @@ const Sidebar = ({
223340
</div>
224341
</>
225342
)}
343+
226344
{transportType === "stdio" && (
227345
<div className="space-y-2">
228346
<Button
@@ -348,6 +466,46 @@ const Sidebar = ({
348466
</div>
349467
)}
350468

469+
{/* Always show both copy buttons for all transport types */}
470+
<div className="grid grid-cols-2 gap-2 mt-2">
471+
<Tooltip>
472+
<TooltipTrigger asChild>
473+
<Button
474+
variant="outline"
475+
size="sm"
476+
onClick={handleCopyServerEntry}
477+
className="w-full"
478+
>
479+
{copiedServerEntry ? (
480+
<CheckCheck className="h-4 w-4 mr-2" />
481+
) : (
482+
<Copy className="h-4 w-4 mr-2" />
483+
)}
484+
Server Entry
485+
</Button>
486+
</TooltipTrigger>
487+
<TooltipContent>Copy Server Entry</TooltipContent>
488+
</Tooltip>
489+
<Tooltip>
490+
<TooltipTrigger asChild>
491+
<Button
492+
variant="outline"
493+
size="sm"
494+
onClick={handleCopyServerFile}
495+
className="w-full"
496+
>
497+
{copiedServerFile ? (
498+
<CheckCheck className="h-4 w-4 mr-2" />
499+
) : (
500+
<Copy className="h-4 w-4 mr-2" />
501+
)}
502+
Servers File
503+
</Button>
504+
</TooltipTrigger>
505+
<TooltipContent>Copy Servers File</TooltipContent>
506+
</Tooltip>
507+
</div>
508+
351509
{/* Configuration */}
352510
<div className="space-y-2">
353511
<Button

0 commit comments

Comments
 (0)