@@ -13,11 +13,20 @@ import {
13
13
ListToolsResult ,
14
14
Tool ,
15
15
} 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" ;
17
24
import { useEffect , useState } from "react" ;
18
25
import ListPane from "./ListPane" ;
19
26
import JsonView from "./JsonView" ;
20
27
import ToolResults from "./ToolResults" ;
28
+ import { useToast } from "@/lib/hooks/useToast" ;
29
+ import useCopy from "@/lib/hooks/useCopy" ;
21
30
22
31
// Type guard to safely detect the optional _meta field without using `any`
23
32
const hasMeta = ( tool : Tool ) : tool is Tool & { _meta : unknown } =>
@@ -51,6 +60,8 @@ const ToolsTab = ({
51
60
const [ isToolRunning , setIsToolRunning ] = useState ( false ) ;
52
61
const [ isOutputSchemaExpanded , setIsOutputSchemaExpanded ] = useState ( false ) ;
53
62
const [ isMetaExpanded , setIsMetaExpanded ] = useState ( false ) ;
63
+ const { toast } = useToast ( ) ;
64
+ const { copied, setCopied } = useCopy ( ) ;
54
65
55
66
useEffect ( ( ) => {
56
67
const params = Object . entries (
@@ -284,29 +295,54 @@ const ToolsTab = ({
284
295
</ div >
285
296
</ div >
286
297
) }
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 >
310
346
< ToolResults
311
347
toolResult = { toolResult }
312
348
selectedTool = { selectedTool }
0 commit comments