@@ -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" ;
1724import { useEffect , useState } from "react" ;
1825import ListPane from "./ListPane" ;
1926import JsonView from "./JsonView" ;
2027import 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`
2332const 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