Skip to main content
Glama

Superglue MCP

Official
by superglue-ai
ToolResultsView.tsx11.8 kB
import { Button } from '@/src/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/src/components/ui/card'; import { Textarea } from '@/src/components/ui/textarea'; import { Workflow as Tool, WorkflowResult as ToolResult } from '@superglue/client'; import { Check, Copy, Loader2 } from 'lucide-react'; import { useMemo, useState } from 'react'; import { AutoSizer, List } from 'react-virtualized'; import { ToolCreateSuccess } from './ToolCreateSuccess'; import { truncateForDisplay } from './ToolMiniStepCards'; const MAX_LINES = 10000; const getResponseLines = (response: any): { lines: string[], truncated: boolean } => { if (!response) return { lines: ['No results yet...'], truncated: false }; const display = truncateForDisplay(response); const lines = display.value.split('\n'); if (lines.length > MAX_LINES) { return { lines: [...lines.slice(0, MAX_LINES), '... [Output truncated - too many lines]'], truncated: true }; } return { lines, truncated: display.truncated }; }; interface ToolResultsViewProps { activeTab: 'results' | 'transform' | 'final' | 'instructions'; setActiveTab: (tab: 'results' | 'transform' | 'final' | 'instructions') => void; executionResult: ToolResult | null; finalTransform: string; setFinalTransform: (transform: string) => void; finalResult: any; isExecuting: boolean; executionError: string | null; showInstructionsTab?: boolean; currentTool?: Tool; credentials?: Record<string, string>; payload?: Record<string, any>; } export function ToolResultsView({ activeTab, setActiveTab, executionResult, finalTransform, setFinalTransform, finalResult, isExecuting, executionError, showInstructionsTab = false, currentTool, credentials, payload }: ToolResultsViewProps) { // Memoize the line processing to avoid recalculation on every render const rawResultsData = useMemo(() => getResponseLines(executionResult?.stepResults), [executionResult?.stepResults] ); const finalResultsData = useMemo(() => getResponseLines(finalResult), [finalResult] ); const [rawCopied, setRawCopied] = useState(false); const [finalCopied, setFinalCopied] = useState(false); const handleCopyRaw = () => { if (executionResult?.stepResults) { navigator.clipboard.writeText(JSON.stringify(executionResult.stepResults, null, 2)); setRawCopied(true); setTimeout(() => setRawCopied(false), 1000); } }; const handleCopyFinal = () => { if (finalResult) { navigator.clipboard.writeText(JSON.stringify(finalResult, null, 2)); setFinalCopied(true); setTimeout(() => setFinalCopied(false), 1000); } }; return ( <Card className="flex flex-col h-full"> <CardHeader className="py-3 px-4 flex-shrink-0"> <div className="flex justify-between items-center"> <CardTitle>Results</CardTitle> <div className="flex gap-2"> <Button variant={activeTab === 'results' ? 'default' : 'ghost'} size="sm" onClick={() => setActiveTab('results')} > Raw Results </Button> <Button variant={activeTab === 'final' ? 'default' : 'ghost'} size="sm" onClick={() => setActiveTab('final')} > Final Results </Button> <Button variant={activeTab === 'transform' ? 'default' : 'ghost'} size="sm" onClick={() => setActiveTab('transform')} > Transformation </Button> {showInstructionsTab && ( <Button variant={activeTab === 'instructions' ? 'default' : 'ghost'} size="sm" onClick={() => setActiveTab('instructions')} > Instructions </Button> )} </div> </div> </CardHeader> <CardContent className="p-0 flex-grow flex flex-col overflow-hidden"> {executionResult && ( <div className="p-3 bg-muted border-b flex-shrink-0"> <div className="flex flex-col gap-1"> <div className="flex items-center"> <span className="font-semibold mr-2">Status:</span> <span className={executionResult.success ? "text-green-600" : "text-red-600"}> {executionResult.success ? "Success" : "Failed"} </span> </div> {executionResult.startedAt && ( <div className="flex items-center"> <span className="font-semibold mr-2">Time:</span> <span className="text-sm"> {new Date(executionResult.startedAt).toLocaleString()} {executionResult.completedAt && ` • Duration: ${((new Date(executionResult.completedAt).getTime() - new Date(executionResult.startedAt).getTime()) / 1000).toFixed(2)}s`} </span> </div> )} {executionError && ( <div className="text-red-600"> <span className="font-semibold mr-2">Error:</span> <span>{executionError}</span> </div> )} </div> </div> )} {activeTab === 'results' ? ( executionResult ? ( <div className="flex-grow overflow-hidden p-1 relative"> {executionResult.stepResults && ( <Button variant="ghost" size="icon" className="absolute top-2 right-2 z-10 h-8 w-8" onClick={handleCopyRaw}> {rawCopied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />} </Button> )} {rawResultsData.truncated && ( <div className="text-xs text-amber-800 dark:text-amber-300 flex items-center gap-1.5 bg-amber-500/10 py-1 px-2 rounded mb-2"> <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> <path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" /> <line x1="12" y1="9" x2="12" y2="13" /> <line x1="12" y1="17" x2="12.01" y2="17" /> </svg> Large dataset detected - display has been truncated for performance </div> )} <AutoSizer> {({ height, width }) => ( <List width={width} height={height} rowCount={rawResultsData.lines.length} rowHeight={18} rowRenderer={({ index, key, style }) => { const line = rawResultsData.lines[index]; const indentMatch = line?.match(/^(\s*)/); const indentLevel = indentMatch ? indentMatch[0].length : 0; return ( <div key={key} style={{ ...style, whiteSpace: 'pre', paddingLeft: `${indentLevel * 8}px`, }} className="font-mono text-xs overflow-hidden text-ellipsis px-4" > {line?.trimLeft()} </div> ); }} overscanRowCount={20} className="overflow-auto" /> )} </AutoSizer> </div> ) : ( <div className="h-full flex items-center justify-center p-4"> <p className="text-gray-500 italic flex items-center gap-2"> {isExecuting && <Loader2 className="h-4 w-4 animate-spin" />} {isExecuting ? 'Executing...' : 'No results yet. Test the tool to see results here.'} </p> </div> ) ) : activeTab === 'transform' ? ( <div className="flex-grow overflow-auto p-4"> <Textarea value={finalTransform} onChange={(e) => setFinalTransform(e.target.value)} className="font-mono text-xs w-full h-full min-h-[300px]" spellCheck={false} /> </div> ) : activeTab === 'instructions' ? ( showInstructionsTab && ( <div className="p-4"> <ToolCreateSuccess currentTool={currentTool} payload={payload || {}} credentials={credentials} /> </div> ) ) : ( // activeTab === 'final' finalResult ? ( <div className="flex-grow overflow-hidden p-1 relative"> <Button variant="ghost" size="icon" className="absolute top-2 right-2 z-10 h-8 w-8" onClick={handleCopyFinal}> {finalCopied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />} </Button> {finalResultsData.truncated && ( <div className="text-xs text-amber-800 dark:text-amber-300 flex items-center gap-1.5 bg-amber-500/10 py-1 px-2 rounded mb-2"> <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> <path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" /> <line x1="12" y1="9" x2="12" y2="13" /> <line x1="12" y1="17" x2="12.01" y2="17" /> </svg> Large dataset detected - display has been truncated for performance </div> )} <AutoSizer> {({ height, width }) => ( <List width={width} height={height} rowCount={finalResultsData.lines.length} rowHeight={18} rowRenderer={({ index, key, style }) => { const line = finalResultsData.lines[index]; const indentMatch = line?.match(/^(\s*)/); const indentLevel = indentMatch ? indentMatch[0].length : 0; return ( <div key={key} style={{ ...style, whiteSpace: 'pre', paddingLeft: `${indentLevel * 8}px`, }} className="font-mono text-xs overflow-hidden text-ellipsis px-4" > {line?.trimLeft()} </div> ); }} overscanRowCount={20} className="overflow-auto" /> )} </AutoSizer> </div> ) : ( <div className="h-full flex items-center justify-center p-4"> <p className="text-gray-500 italic flex items-center gap-2"> {isExecuting && <Loader2 className="h-4 w-4 animate-spin" />} {isExecuting ? 'Executing...' : 'No final results yet. Test the tool to see results here.'} </p> </div> ) )} </CardContent> </Card> ); }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/superglue-ai/superglue'

If you have feedback or need assistance with the MCP directory API, please join our Discord server