import React, { useState } from 'react'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Label } from '@/components/ui/label' import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { Loader2, AlertCircle, CheckCircle2, Zap, ChevronRight, Sparkles, Wifi, Server } from 'lucide-react'; import { ScrollArea } from '@/components/ui/scroll-area'; import { Checkbox } from '@/components/ui/checkbox'; import { cn } from '@/lib/utils'; import { createClient } from '@/lib/supabase/client'; import { Input } from '@/components/ui/input'; const API_URL = process.env.NEXT_PUBLIC_BACKEND_URL || ''; interface CustomMCPDialogProps { open: boolean; onOpenChange: (open: boolean) => void; onSave: (config: CustomMCPConfiguration) => void; } interface CustomMCPConfiguration { name: string; type: 'http' | 'sse'; config: any; enabledTools: string[]; selectedProfileId?: string; } interface MCPTool { name: string; description: string; inputSchema?: any; } export const CustomMCPDialog: React.FC = ({ open, onOpenChange, onSave }) => { const [step, setStep] = useState<'setup' | 'tools'>('setup'); const [serverType, setServerType] = useState<'http' | 'sse'>('sse'); const [configText, setConfigText] = useState(''); const [serverName, setServerName] = useState(''); const [manualServerName, setManualServerName] = useState(''); const [isValidating, setIsValidating] = useState(false); const [validationError, setValidationError] = useState(null); const [discoveredTools, setDiscoveredTools] = useState([]); const [selectedTools, setSelectedTools] = useState>(new Set()); const [processedConfig, setProcessedConfig] = useState(null); const validateAndDiscoverTools = async () => { setIsValidating(true); setValidationError(null); setDiscoveredTools([]); try { let parsedConfig: any; if (serverType === 'sse' || serverType === 'http') { const url = configText.trim(); if (!url) { throw new Error('Please enter the connection URL.'); } if (!manualServerName.trim()) { throw new Error('Please enter a name for this connection.'); } parsedConfig = { url }; setServerName(manualServerName.trim()); } const supabase = createClient(); const { data: { session } } = await supabase.auth.getSession(); if (!session) { throw new Error('You must be logged in to discover tools'); } const response = await fetch(`${API_URL}/mcp/discover-custom-tools`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${session.access_token}`, }, body: JSON.stringify({ type: serverType, config: parsedConfig }) }); if (!response.ok) { const error = await response.json(); throw new Error(error.message || 'Failed to connect to the service. Please check your configuration.'); } const data = await response.json(); if (!data.tools || data.tools.length === 0) { throw new Error('No tools found. Please check your configuration.'); } if (data.serverName) { setServerName(data.serverName); } if (data.processedConfig) { setProcessedConfig(data.processedConfig); } setDiscoveredTools(data.tools); setSelectedTools(new Set(data.tools.map((tool: MCPTool) => tool.name))); setStep('tools'); } catch (error: any) { setValidationError(error.message); } finally { setIsValidating(false); } }; const handleToolsNext = () => { if (selectedTools.size === 0) { setValidationError('Please select at least one tool to continue.'); return; } setValidationError(null); // Custom MCPs don't need credentials, so save directly handleSave(); }; const handleSave = () => { if (discoveredTools.length === 0 || selectedTools.size === 0) { setValidationError('Please select at least one tool to continue.'); return; } if (!serverName.trim()) { setValidationError('Please provide a name for this connection.'); return; } try { let configToSave: any = { url: configText.trim() }; onSave({ name: serverName, type: serverType, config: configToSave, enabledTools: Array.from(selectedTools), // Custom MCPs don't need credential profiles since they're just URLs selectedProfileId: undefined }); setConfigText(''); setManualServerName(''); setDiscoveredTools([]); setSelectedTools(new Set()); setServerName(''); setProcessedConfig(null); setValidationError(null); setStep('setup'); onOpenChange(false); } catch (error) { setValidationError('Invalid configuration format.'); } }; const handleToolToggle = (toolName: string) => { const newTools = new Set(selectedTools); if (newTools.has(toolName)) { newTools.delete(toolName); } else { newTools.add(toolName); } setSelectedTools(newTools); }; const handleBack = () => { if (step === 'tools') { setStep('setup'); } setValidationError(null); }; const handleReset = () => { setConfigText(''); setManualServerName(''); setDiscoveredTools([]); setSelectedTools(new Set()); setServerName(''); setProcessedConfig(null); setValidationError(null); setStep('setup'); }; const exampleConfigs = { http: `https://server.example.com/mcp`, sse: `https://mcp.composio.dev/partner/composio/gmail/sse?customerId=YOUR_CUSTOMER_ID` }; return ( { onOpenChange(open); if (!open) handleReset(); }}>
Connect New Service
{step === 'setup' ? 'Connect to external services to expand your capabilities with new tools and integrations.' : 'Choose which tools you\'d like to enable from this service connection.' }
1
Setup Connection
2
Select Tools
{step === 'setup' ? (
setServerType(value)} className="grid grid-cols-1 gap-3" >

Standard streamable HTTP connection

Real-time connection using Server-Sent Events for streaming updates

setManualServerName(e.target.value)} className="w-full px-4 py-3 border border-input bg-background rounded-lg text-base focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent" />

Give this connection a memorable name

setConfigText(e.target.value)} className="w-full px-4 py-3 border border-input bg-muted rounded-lg text-base focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent font-mono" />

Paste the complete connection URL provided by your service

{validationError && ( {validationError} )}
) : step === 'tools' ? (

Connection Successful!

Found {discoveredTools.length} available tools from {serverName}

Available Tools

Select the tools you want to enable

{discoveredTools.map((tool) => (
handleToolToggle(tool.name)} > handleToolToggle(tool.name)} className="mt-1" />
{tool.description && (

{tool.description}

)}
))}
{validationError && ( {validationError} )}
) : null}
{step === 'tools' ? ( <> ) : ( <> )}
); };