@@ -17,11 +17,20 @@ import {
17
17
ListToolsResult ,
18
18
Tool ,
19
19
} from "@modelcontextprotocol/sdk/types.js" ;
20
- import { Loader2 , Send , ChevronDown , ChevronUp } from "lucide-react" ;
20
+ import {
21
+ Loader2 ,
22
+ Send ,
23
+ ChevronDown ,
24
+ ChevronUp ,
25
+ Copy ,
26
+ CheckCheck ,
27
+ } from "lucide-react" ;
21
28
import { useEffect , useState } from "react" ;
22
29
import ListPane from "./ListPane" ;
23
30
import JsonView from "./JsonView" ;
24
31
import ToolResults from "./ToolResults" ;
32
+ import { useToast } from "@/lib/hooks/useToast" ;
33
+ import useCopy from "@/lib/hooks/useCopy" ;
25
34
26
35
// Type guard to safely detect the optional _meta field without using `any`
27
36
const hasMeta = ( tool : Tool ) : tool is Tool & { _meta : unknown } =>
@@ -55,6 +64,8 @@ const ToolsTab = ({
55
64
const [ isToolRunning , setIsToolRunning ] = useState ( false ) ;
56
65
const [ isOutputSchemaExpanded , setIsOutputSchemaExpanded ] = useState ( false ) ;
57
66
const [ isMetaExpanded , setIsMetaExpanded ] = useState ( false ) ;
67
+ const { toast } = useToast ( ) ;
68
+ const { copied, setCopied } = useCopy ( ) ;
58
69
59
70
useEffect ( ( ) => {
60
71
const params = Object . entries (
@@ -291,29 +302,54 @@ const ToolsTab = ({
291
302
</ div >
292
303
</ div >
293
304
) }
294
- < Button
295
- onClick = { async ( ) => {
296
- try {
297
- setIsToolRunning ( true ) ;
298
- await callTool ( selectedTool . name , params ) ;
299
- } finally {
300
- setIsToolRunning ( false ) ;
301
- }
302
- } }
303
- disabled = { isToolRunning }
304
- >
305
- { isToolRunning ? (
306
- < >
307
- < Loader2 className = "w-4 h-4 mr-2 animate-spin" />
308
- Running...
309
- </ >
310
- ) : (
311
- < >
312
- < Send className = "w-4 h-4 mr-2" />
313
- Run Tool
314
- </ >
315
- ) }
316
- </ Button >
305
+ < div className = "flex gap-2" >
306
+ < Button
307
+ onClick = { async ( ) => {
308
+ try {
309
+ setIsToolRunning ( true ) ;
310
+ await callTool ( selectedTool . name , params ) ;
311
+ } finally {
312
+ setIsToolRunning ( false ) ;
313
+ }
314
+ } }
315
+ disabled = { isToolRunning }
316
+ >
317
+ { isToolRunning ? (
318
+ < >
319
+ < Loader2 className = "w-4 h-4 mr-2 animate-spin" />
320
+ Running...
321
+ </ >
322
+ ) : (
323
+ < >
324
+ < Send className = "w-4 h-4 mr-2" />
325
+ Run Tool
326
+ </ >
327
+ ) }
328
+ </ Button >
329
+ < Button
330
+ onClick = { async ( ) => {
331
+ try {
332
+ navigator . clipboard . writeText (
333
+ JSON . stringify ( params , null , 2 ) ,
334
+ ) ;
335
+ setCopied ( true ) ;
336
+ } catch ( error ) {
337
+ toast ( {
338
+ title : "Error" ,
339
+ description : `There was an error copying input to the clipboard: ${ error instanceof Error ? error . message : String ( error ) } ` ,
340
+ variant : "destructive" ,
341
+ } ) ;
342
+ }
343
+ } }
344
+ >
345
+ { copied ? (
346
+ < CheckCheck className = "h-4 w-4 mr-2 dark:text-green-700 text-green-600" />
347
+ ) : (
348
+ < Copy className = "h-4 w-4 mr-2" />
349
+ ) }
350
+ Copy Input
351
+ </ Button >
352
+ </ div >
317
353
< ToolResults
318
354
toolResult = { toolResult }
319
355
selectedTool = { selectedTool }
0 commit comments