Skip to main content
Glama

Superglue MCP

Official
by superglue-ai
AddStepDialog.tsx10.3 kB
import { useConfig } from '@/src/app/config-context'; import { Button } from '@/src/components/ui/button'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/src/components/ui/dialog'; import { Input } from '@/src/components/ui/input'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/src/components/ui/tabs'; import { cn } from '@/src/lib/utils'; import { ExecutionStep, SuperglueClient, Workflow as Tool } from '@superglue/client'; import { Loader2 } from 'lucide-react'; import { useEffect, useState } from 'react'; interface AddStepDialogProps { open: boolean; onOpenChange: (open: boolean) => void; onConfirm: (stepId: string) => void; onConfirmTool?: (steps: ExecutionStep[]) => void; existingStepIds: string[]; defaultId?: string; } export function AddStepDialog({ open, onOpenChange, onConfirm, onConfirmTool, existingStepIds, defaultId }: AddStepDialogProps) { const [stepId, setStepId] = useState(defaultId || ''); const [error, setError] = useState(''); const [activeTab, setActiveTab] = useState<'scratch' | 'tool'>('scratch'); const [tools, setTools] = useState<Tool[]>([]); const [loadingTools, setLoadingTools] = useState(false); const [selectedToolId, setSelectedToolId] = useState<string | null>(null); const [searchQuery, setSearchQuery] = useState(''); const config = useConfig(); const client = new SuperglueClient({ endpoint: config.superglueEndpoint, apiKey: config.superglueApiKey, }); useEffect(() => { if (open && defaultId) { setStepId(defaultId); setError(''); } }, [open, defaultId]); useEffect(() => { if (open && activeTab === 'tool' && tools.length === 0) { loadTools(); } }, [open, activeTab]); const loadTools = async () => { setLoadingTools(true); try { const result = await client.listWorkflows(1000, 0); setTools(result.items?.filter(tool => tool.steps?.length > 0) || []); } catch (error) { console.error('Error loading tools:', error); setError('Failed to load tools'); } finally { setLoadingTools(false); } }; const handleOpenChange = (newOpen: boolean) => { if (!newOpen) { setError(''); setSelectedToolId(null); setActiveTab('scratch'); setSearchQuery(''); } onOpenChange(newOpen); }; const filteredTools = tools.filter(tool => { if (!searchQuery.trim()) return true; const query = searchQuery.toLowerCase(); return ( tool.id?.toLowerCase().includes(query) || tool.instruction?.toLowerCase().includes(query) ); }); const handleConfirmScratch = () => { const trimmedId = stepId.trim(); if (!trimmedId) { setError('Step ID cannot be empty'); return; } if (existingStepIds.includes(trimmedId)) { setError(`Step with ID "${trimmedId}" already exists`); return; } onConfirm(trimmedId); setError(''); }; const handleConfirmTool = () => { if (!selectedToolId) { setError('Please select a tool'); return; } const selectedTool = tools.find(t => t.id === selectedToolId); if (!selectedTool || !selectedTool.steps) { setError('Selected tool has no steps'); return; } if (onConfirmTool) { onConfirmTool(selectedTool.steps); } setError(''); setSelectedToolId(null); }; return ( <Dialog open={open} onOpenChange={handleOpenChange}> <DialogContent className="max-w-2xl"> <DialogHeader> <DialogTitle>Add</DialogTitle> <DialogDescription> Create a new step from scratch or import steps from an existing tool </DialogDescription> </DialogHeader> <Tabs value={activeTab} onValueChange={(v) => setActiveTab(v as 'scratch' | 'tool')} className="overflow-hidden w-full"> <TabsList className="grid w-full grid-cols-2"> <TabsTrigger value="scratch">Add new step</TabsTrigger> <TabsTrigger value="tool">Import tool</TabsTrigger> </TabsList> <TabsContent value="scratch" className="space-y-4 py-4 overflow-hidden"> <div className="space-y-2"> <label htmlFor="step-id" className="text-sm font-medium"> Step ID </label> <Input id="step-id" value={stepId} onChange={(e) => { setStepId(e.target.value); setError(''); }} onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); handleConfirmScratch(); } }} placeholder="e.g., fetch_users" className={cn(error && "border-destructive")} autoFocus /> {error && ( <p className="text-sm text-destructive">{error}</p> )} </div> </TabsContent> <TabsContent value="tool" className="space-y-4 py-4"> {loadingTools ? ( <div className="flex items-center justify-center py-8"> <Loader2 className="h-6 w-6 animate-spin" /> </div> ) : tools.length === 0 ? ( <div className="text-center py-8 text-muted-foreground"> No tools found </div> ) : ( <div className="space-y-3"> <div className="space-y-2"> <Input placeholder="Search by ID or instruction..." value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} className="w-full" /> </div> <div className="border rounded-lg max-h-[400px] overflow-y-auto overflow-x-hidden"> {filteredTools.length === 0 ? ( <div className="text-center py-8 text-muted-foreground"> No tools match your search </div> ) : ( filteredTools.map((tool) => ( <button key={tool.id} onClick={() => { setSelectedToolId(tool.id); setError(''); }} className={cn( "w-full text-left px-4 py-3 border-b last:border-b-0 hover:bg-accent transition-colors overflow-hidden", selectedToolId === tool.id && "bg-accent" )} > <div className="font-medium truncate">{tool.id}</div> {tool.instruction && ( <div className="text-sm text-muted-foreground truncate mt-1"> {tool.instruction} </div> )} <div className="text-xs text-muted-foreground mt-1"> {tool.steps?.length || 0} step{tool.steps?.length !== 1 ? 's' : ''} </div> </button> )) )} </div> {error && ( <p className="text-sm text-destructive">{error}</p> )} </div> )} </TabsContent> </Tabs> <DialogFooter> <Button variant="outline" onClick={() => handleOpenChange(false)} > Cancel </Button> <Button onClick={activeTab === 'scratch' ? handleConfirmScratch : handleConfirmTool} disabled={loadingTools} > {activeTab === 'scratch' ? 'Add Step' : 'Import Steps'} </Button> </DialogFooter> </DialogContent> </Dialog> ); }

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