Skip to content

Commit 5bbd9f7

Browse files
committed
feat: Copy the input parameters of tools
1 parent 8589faa commit 5bbd9f7

File tree

1 file changed

+60
-24
lines changed

1 file changed

+60
-24
lines changed

client/src/components/ToolsTab.tsx

Lines changed: 60 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,20 @@ import {
1313
ListToolsResult,
1414
Tool,
1515
} from "@modelcontextprotocol/sdk/types.js";
16-
import { Loader2, Send, ChevronDown, ChevronUp } from "lucide-react";
16+
import {
17+
Loader2,
18+
Send,
19+
ChevronDown,
20+
ChevronUp,
21+
Copy,
22+
CheckCheck,
23+
} from "lucide-react";
1724
import { useEffect, useState } from "react";
1825
import ListPane from "./ListPane";
1926
import JsonView from "./JsonView";
2027
import ToolResults from "./ToolResults";
28+
import { useToast } from "@/lib/hooks/useToast";
29+
import useCopy from "@/lib/hooks/useCopy";
2130

2231
// Type guard to safely detect the optional _meta field without using `any`
2332
const hasMeta = (tool: Tool): tool is Tool & { _meta: unknown } =>
@@ -51,6 +60,8 @@ const ToolsTab = ({
5160
const [isToolRunning, setIsToolRunning] = useState(false);
5261
const [isOutputSchemaExpanded, setIsOutputSchemaExpanded] = useState(false);
5362
const [isMetaExpanded, setIsMetaExpanded] = useState(false);
63+
const { toast } = useToast();
64+
const { copied, setCopied } = useCopy();
5465

5566
useEffect(() => {
5667
const params = Object.entries(
@@ -284,29 +295,54 @@ const ToolsTab = ({
284295
</div>
285296
</div>
286297
)}
287-
<Button
288-
onClick={async () => {
289-
try {
290-
setIsToolRunning(true);
291-
await callTool(selectedTool.name, params);
292-
} finally {
293-
setIsToolRunning(false);
294-
}
295-
}}
296-
disabled={isToolRunning}
297-
>
298-
{isToolRunning ? (
299-
<>
300-
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
301-
Running...
302-
</>
303-
) : (
304-
<>
305-
<Send className="w-4 h-4 mr-2" />
306-
Run Tool
307-
</>
308-
)}
309-
</Button>
298+
<div className="flex gap-2">
299+
<Button
300+
onClick={async () => {
301+
try {
302+
setIsToolRunning(true);
303+
await callTool(selectedTool.name, params);
304+
} finally {
305+
setIsToolRunning(false);
306+
}
307+
}}
308+
disabled={isToolRunning}
309+
>
310+
{isToolRunning ? (
311+
<>
312+
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
313+
Running...
314+
</>
315+
) : (
316+
<>
317+
<Send className="w-4 h-4 mr-2" />
318+
Run Tool
319+
</>
320+
)}
321+
</Button>
322+
<Button
323+
onClick={async () => {
324+
try {
325+
navigator.clipboard.writeText(
326+
JSON.stringify(params, null, 2),
327+
);
328+
setCopied(true);
329+
} catch (error) {
330+
toast({
331+
title: "Error",
332+
description: `There was an error coping result into the clipboard: ${error instanceof Error ? error.message : String(error)}`,
333+
variant: "destructive",
334+
});
335+
}
336+
}}
337+
>
338+
{copied ? (
339+
<CheckCheck className="h-4 w-4 mr-2 dark:text-green-700 text-green-600" />
340+
) : (
341+
<Copy className="h-4 w-4 mr-2" />
342+
)}
343+
Copy Input
344+
</Button>
345+
</div>
310346
<ToolResults
311347
toolResult={toolResult}
312348
selectedTool={selectedTool}

0 commit comments

Comments
 (0)