@@ -17,11 +17,20 @@ import {
1717 ListToolsResult ,
1818 Tool ,
1919} 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" ;
2128import { useEffect , useState } from "react" ;
2229import ListPane from "./ListPane" ;
2330import JsonView from "./JsonView" ;
2431import ToolResults from "./ToolResults" ;
32+ import { useToast } from "@/lib/hooks/useToast" ;
33+ import useCopy from "@/lib/hooks/useCopy" ;
2534
2635// Type guard to safely detect the optional _meta field without using `any`
2736const hasMeta = ( tool : Tool ) : tool is Tool & { _meta : unknown } =>
@@ -55,6 +64,8 @@ const ToolsTab = ({
5564 const [ isToolRunning , setIsToolRunning ] = useState ( false ) ;
5665 const [ isOutputSchemaExpanded , setIsOutputSchemaExpanded ] = useState ( false ) ;
5766 const [ isMetaExpanded , setIsMetaExpanded ] = useState ( false ) ;
67+ const { toast } = useToast ( ) ;
68+ const { copied, setCopied } = useCopy ( ) ;
5869
5970 useEffect ( ( ) => {
6071 const params = Object . entries (
@@ -291,29 +302,54 @@ const ToolsTab = ({
291302 </ div >
292303 </ div >
293304 ) }
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 >
317353 < ToolResults
318354 toolResult = { toolResult }
319355 selectedTool = { selectedTool }
0 commit comments