adding two buttons for configs: copy entry, copy file. also adding some tooltip and optimizations.

This commit is contained in:
sumeetpardeshi
2025-05-01 15:42:16 -07:00
parent 114df8ac30
commit 163356f855

View File

@@ -1,4 +1,4 @@
import { useState } from "react"; import { useState, useCallback } from "react";
import { import {
Play, Play,
ChevronDown, ChevronDown,
@@ -98,47 +98,62 @@ const Sidebar = ({
const [showBearerToken, setShowBearerToken] = useState(false); const [showBearerToken, setShowBearerToken] = useState(false);
const [showConfig, setShowConfig] = useState(false); const [showConfig, setShowConfig] = useState(false);
const [shownEnvVars, setShownEnvVars] = useState<Set<string>>(new Set()); const [shownEnvVars, setShownEnvVars] = useState<Set<string>>(new Set());
const [copiedConfig, setCopiedConfig] = useState(false); const [copiedConfigEntry, setCopiedConfigEntry] = useState(false);
const [copiedConfigFile, setCopiedConfigFile] = useState(false);
const { toast } = useToast(); const { toast } = useToast();
// Generate MCP configuration JSON // Shared utility function to generate server config
const generateMCPConfig = () => { const generateServerConfig = useCallback(() => {
if (transportType === "stdio") { if (transportType === "stdio") {
// Generate only the server config without the mcpServers wrapper return {
const serverConfig = {
command, command,
args: args.trim() ? args.split(/\s+/) : [], args: args.trim() ? args.split(/\s+/) : [],
env: { ...env }, env: { ...env }
}; };
return JSON.stringify(serverConfig, null, 2);
} else { } else {
// For SSE connections return {
return JSON.stringify({ type: "sse",
// SSE configuration doesn't go in mcp.json, but provide the URL info url: sseUrl,
"type": "sse", note: "For SSE connections, add this URL directly in Client"
"url": sseUrl, };
"note": "For SSE connections, add this URL directly in Cursor"
}, null, 2);
} }
}; }, [transportType, command, args, env, sseUrl]);
// Handle copy config // Memoized config entry generator
const handleCopyConfig = () => { const generateMCPConfigEntry = useCallback(() => {
return JSON.stringify(generateServerConfig(), null, 2);
}, [generateServerConfig]);
// Memoized config file generator
const generateMCPConfigFile = useCallback(() => {
return JSON.stringify(
{
mcpServers: {
"default-server": generateServerConfig()
}
},
null,
2
);
}, [generateServerConfig]);
// Memoized copy handlers
const handleCopyConfigEntry = useCallback(() => {
try { try {
const configJson = generateMCPConfig(); const configJson = generateMCPConfigEntry();
navigator.clipboard.writeText(configJson); navigator.clipboard.writeText(configJson);
setCopiedConfig(true); setCopiedConfigEntry(true);
toast({ toast({
title: "Config copied", title: "Config entry copied",
description: transportType === "stdio" description:
? "Server configuration has been copied to clipboard. Add this to your mcp.json inside the 'mcpServers' object with your preferred server name." transportType === "stdio"
: "SSE URL has been copied. Use this URL in Cursor directly.", ? "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(() => { setTimeout(() => {
setCopiedConfig(false); setCopiedConfigEntry(false);
}, 2000); }, 2000);
} catch (error) { } catch (error) {
toast({ toast({
@@ -147,7 +162,30 @@ const Sidebar = ({
variant: "destructive", variant: "destructive",
}); });
} }
}; }, [generateMCPConfigEntry, transportType, toast]);
const handleCopyConfigFile = useCallback(() => {
try {
const configJson = generateMCPConfigFile();
navigator.clipboard.writeText(configJson);
setCopiedConfigFile(true);
toast({
title: "Config file copied",
description: "Server configuration has been copied to clipboard. Add this to your mcp.json file. Current testing server will be added as 'default-server'",
});
setTimeout(() => {
setCopiedConfigFile(false);
}, 2000);
} catch (error) {
toast({
title: "Error",
description: `Failed to copy config: ${error instanceof Error ? error.message : String(error)}`,
variant: "destructive",
});
}
}, [generateMCPConfigFile, toast]);
return ( return (
<div className="w-80 bg-card border-r border-border flex flex-col h-full"> <div className="w-80 bg-card border-r border-border flex flex-col h-full">
@@ -213,21 +251,48 @@ const Sidebar = ({
className="font-mono" className="font-mono"
/> />
</div> </div>
<div className="flex justify-end"> <div className="flex gap-2 justify-end">
<Button
variant="outline" <Tooltip>
size="sm" <TooltipTrigger asChild>
onClick={handleCopyConfig} <Button
className="mt-1" variant="outline"
title="Copy Server Configuration for mcp.json" size="sm"
> onClick={handleCopyConfigEntry}
{copiedConfig ? ( className="mt-1"
<CheckCheck className="h-4 w-4 mr-2" /> >
) : ( {copiedConfigEntry ? (
<Copy className="h-4 w-4 mr-2" /> <CheckCheck className="h-4 w-4 mr-2" />
)} ) : (
Copy Config <Copy className="h-4 w-4 mr-2" />
</Button> )}
Config Entry
</Button>
</TooltipTrigger>
<TooltipContent>
Copy Config Entry
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="outline"
size="sm"
onClick={handleCopyConfigFile}
className="mt-1"
>
{copiedConfigFile ? (
<CheckCheck className="h-4 w-4 mr-2" />
) : (
<Copy className="h-4 w-4 mr-2" />
)}
Config File
</Button>
</TooltipTrigger>
<TooltipContent>
Copy Config File
</TooltipContent>
</Tooltip>
</div> </div>
</> </>
) : ( ) : (
@@ -248,11 +313,11 @@ const Sidebar = ({
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
onClick={handleCopyConfig} onClick={handleCopyConfigFile}
className="mt-1" className="mt-1"
title="Copy SSE URL Configuration" title="Copy SSE URL Configuration"
> >
{copiedConfig ? ( {copiedConfigFile ? (
<CheckCheck className="h-4 w-4 mr-2" /> <CheckCheck className="h-4 w-4 mr-2" />
) : ( ) : (
<Copy className="h-4 w-4 mr-2" /> <Copy className="h-4 w-4 mr-2" />