Skip to main content
Glama

Agent MCP

project-picker.tsx9.15 kB
"use client" import React, { useState, useEffect } from "react" import { Plus, Server, Settings, Wifi, WifiOff, AlertCircle, Loader2 } from "lucide-react" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Badge } from "@/components/ui/badge" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { useServerStore, MCPServer } from "@/lib/stores/server-store" export function ProjectPicker() { const { servers, activeServerId, isConnecting, connectionError, setActiveServer, refreshAllServers, disconnectServer } = useServerStore() const [isOpen, setIsOpen] = useState(false) const activeServer = servers.find(s => s.id === activeServerId) useEffect(() => { // Auto-refresh server status every 30 seconds const interval = setInterval(() => { refreshAllServers() }, 30000) return () => clearInterval(interval) }, [refreshAllServers]) const handleServerSelect = async (serverId: string) => { await setActiveServer(serverId) setIsOpen(false) } const getStatusIcon = (status: MCPServer['status']) => { switch (status) { case 'connected': return <Wifi className="h-4 w-4 text-primary" /> case 'connecting': return <Loader2 className="h-4 w-4 text-info animate-spin" /> case 'error': return <AlertCircle className="h-4 w-4 text-destructive" /> default: return <WifiOff className="h-4 w-4 text-muted-foreground" /> } } const getStatusColor = (status: MCPServer['status']) => { switch (status) { case 'connected': return 'bg-primary' case 'connecting': return 'bg-info' case 'error': return 'bg-destructive' default: return 'bg-muted-foreground' } } return ( <DropdownMenu open={isOpen} onOpenChange={setIsOpen}> <DropdownMenuTrigger asChild> <Button variant="outline" className="justify-between min-w-[200px]"> <div className="flex items-center space-x-2"> {isConnecting ? ( <Loader2 className="h-4 w-4 animate-spin" /> ) : ( <div className={`w-2 h-2 rounded-full ${activeServer ? getStatusColor(activeServer.status) : 'bg-muted-foreground'}`} /> )} <span className="truncate"> {activeServer ? activeServer.name : 'Select Project'} </span> </div> <Settings className="h-4 w-4 ml-2 opacity-50" /> </Button> </DropdownMenuTrigger> <DropdownMenuContent className="w-80" align="start"> <DropdownMenuLabel className="flex items-center justify-between"> MCP Server Projects <Button variant="ghost" size="sm" onClick={() => refreshAllServers()} className="h-6 w-6 p-0" > <Server className="h-3 w-3" /> </Button> </DropdownMenuLabel> <DropdownMenuSeparator /> {connectionError && ( <div className="p-2"> <Card className="border-destructive/20 bg-destructive/10"> <CardContent className="p-3"> <div className="flex items-center space-x-2"> <AlertCircle className="h-4 w-4 text-destructive" /> <span className="text-sm text-destructive"> {connectionError} </span> </div> </CardContent> </Card> </div> )} <div className="max-h-[300px] overflow-y-auto"> {servers.map((server) => ( <DropdownMenuItem key={server.id} onClick={() => handleServerSelect(server.id)} className="flex items-center justify-between p-3 cursor-pointer" disabled={isConnecting} > <div className="flex items-center space-x-3"> {getStatusIcon(server.status)} <div className="flex flex-col"> <span className="font-medium">{server.name}</span> <span className="text-xs text-muted-foreground"> {server.host}:{server.port} </span> {server.description && ( <span className="text-xs text-muted-foreground"> {server.description} </span> )} </div> </div> <div className="flex items-center space-x-2"> {server.id === activeServerId && ( <Badge variant="secondary" className="text-xs"> Active </Badge> )} <Badge variant={server.status === 'connected' ? 'default' : 'secondary'} className="text-xs" > {server.status} </Badge> </div> </DropdownMenuItem> ))} </div> <DropdownMenuSeparator /> <div className="p-2 space-y-2"> <AddServerDialog /> {activeServerId && ( <Button variant="outline" size="sm" onClick={disconnectServer} className="w-full" > Disconnect </Button> )} </div> </DropdownMenuContent> </DropdownMenu> ) } function AddServerDialog() { const { addServer } = useServerStore() const [isOpen, setIsOpen] = useState(false) const [formData, setFormData] = useState({ name: '', host: 'localhost', port: 8080, description: '' }) const handleSubmit = (e: React.FormEvent) => { e.preventDefault() if (!formData.name.trim()) return addServer({ name: formData.name.trim(), host: formData.host.trim() || 'localhost', port: formData.port, description: formData.description.trim() }) setFormData({ name: '', host: 'localhost', port: 8080, description: '' }) setIsOpen(false) } return ( <Dialog open={isOpen} onOpenChange={setIsOpen}> <DialogTrigger asChild> <Button variant="outline" size="sm" className="w-full"> <Plus className="h-4 w-4 mr-2" /> Add Server </Button> </DialogTrigger> <DialogContent className="sm:max-w-[425px]"> <DialogHeader> <DialogTitle>Add MCP Server</DialogTitle> <DialogDescription> Add a new MCP server to connect to. This could be a local development server or a remote instance. </DialogDescription> </DialogHeader> <form onSubmit={handleSubmit} className="space-y-4"> <div className="grid gap-2"> <Label htmlFor="name">Project Name</Label> <Input id="name" value={formData.name} onChange={(e) => setFormData(prev => ({ ...prev, name: e.target.value }))} placeholder="My Project" required /> </div> <div className="grid grid-cols-3 gap-2"> <div className="col-span-2"> <Label htmlFor="host">Host</Label> <Input id="host" value={formData.host} onChange={(e) => setFormData(prev => ({ ...prev, host: e.target.value }))} placeholder="localhost" /> </div> <div> <Label htmlFor="port">Port</Label> <Input id="port" type="number" value={formData.port} onChange={(e) => setFormData(prev => ({ ...prev, port: parseInt(e.target.value) || 8080 }))} placeholder="8080" /> </div> </div> <div className="grid gap-2"> <Label htmlFor="description">Description (Optional)</Label> <Input id="description" value={formData.description} onChange={(e) => setFormData(prev => ({ ...prev, description: e.target.value }))} placeholder="Brief description of this server" /> </div> <DialogFooter> <Button type="button" variant="outline" onClick={() => setIsOpen(false)}> Cancel </Button> <Button type="submit">Add Server</Button> </DialogFooter> </form> </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/rinadelph/Agent-MCP'

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