Skip to main content
Glama

Context Pods

by conorluddy
list-mcps.tsโ€ข10.8 kB
/** * List MCP servers tool */ import { getRegistryOperations, MCPServerStatus, type MCPServerFilters, type MCPServerMetadata, type RegistryOperations, } from '../registry/index.js'; import { BaseTool, type ToolResult } from './base-tool.js'; /** * Arguments for list-mcps tool */ interface ListMCPsArgs extends Record<string, unknown> { filter?: string; status?: string; template?: string; language?: string; search?: string; format?: 'table' | 'json' | 'summary'; } /** * List MCP servers tool implementation */ export class ListMCPsTool extends BaseTool { constructor() { super('list-mcps'); } /** * Validate list-mcps arguments */ protected async validateArguments(args: unknown): Promise<string | null> { const typedArgs = args as ListMCPsArgs; // Validate optional arguments if (typedArgs.filter !== undefined) { const error = this.validateStringArgument(typedArgs, 'filter', false); if (error) return error; } if (typedArgs.status !== undefined) { const error = this.validateStringArgument(typedArgs, 'status', false); if (error) return error; // Validate status value const validStatuses = Object.values(MCPServerStatus); if (!validStatuses.includes(typedArgs.status as MCPServerStatus)) { return `Invalid status. Valid values: ${validStatuses.join(', ')}`; } } if (typedArgs.template !== undefined) { const error = this.validateStringArgument(typedArgs, 'template', false); if (error) return error; } if (typedArgs.language !== undefined) { const error = this.validateStringArgument(typedArgs, 'language', false); if (error) return error; } if (typedArgs.search !== undefined) { const error = this.validateStringArgument(typedArgs, 'search', false); if (error) return error; } if (typedArgs.format !== undefined) { const validFormats = ['table', 'json', 'summary']; if (!validFormats.includes(typedArgs.format)) { return `Invalid format. Valid values: ${validFormats.join(', ')}`; } } return null; } /** * Execute list-mcps tool */ protected async execute(args: unknown): Promise<ToolResult> { const typedArgs = args as ListMCPsArgs; try { // Build filters const filters = this.buildFilters(typedArgs); // Get servers from registry const registry = await getRegistryOperations(); const servers = await registry.listServers(filters); // Format output based on requested format const format = typedArgs.format || 'table'; let output: string; switch (format) { case 'json': output = this.formatAsJSON(servers); break; case 'summary': output = await this.formatAsSummary(servers, registry); break; case 'table': default: output = this.formatAsTable(servers); break; } return { success: true, data: output, }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : String(error), }; } } /** * Build filters from arguments */ private buildFilters(args: ListMCPsArgs): MCPServerFilters { const filters: MCPServerFilters = {}; if (args.status) { filters.status = args.status as MCPServerStatus; } if (args.template) { filters.template = args.template; } if (args.language) { filters.language = args.language; } if (args.search) { filters.search = args.search; } // Handle legacy 'filter' parameter if (args.filter) { // Try to parse as status first const validStatuses = Object.values(MCPServerStatus); if (validStatuses.includes(args.filter as MCPServerStatus)) { filters.status = args.filter as MCPServerStatus; } else { // Use as search term filters.search = args.filter; } } return filters; } /** * Format servers as table */ private formatAsTable(servers: MCPServerMetadata[]): string { if (servers.length === 0) { return '๐Ÿ“ฆ No MCP servers found.\n\nUse the "create-mcp" or "wrap-script" tools to generate your first MCP server.'; } let output = `๐Ÿ“ฆ Managed MCP Servers (${servers.length} total)\n\n`; // Table header output += 'โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”\n'; output += 'โ”‚ Name โ”‚ Status โ”‚ Template โ”‚ Language โ”‚ Created โ”‚\n'; output += 'โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค\n'; // Table rows for (const server of servers) { const name = this.truncate(server.name, 15); const status = this.getStatusEmoji(server.status) + ' ' + this.truncate(server.status, 10); const template = this.truncate(server.template, 15); const language = this.truncate(server.metadata.language || 'unknown', 16); const created = this.formatDate(server.createdAt); output += `โ”‚ ${this.pad(name, 15)} โ”‚ ${this.pad(status, 12)} โ”‚ ${this.pad(template, 15)} โ”‚ ${this.pad(language, 16)} โ”‚ ${this.pad(created, 15)} โ”‚\n`; } output += 'โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜\n'; // Add usage information output += '\n๐Ÿ’ก Use "list-mcps --format=json" for detailed information'; output += '\n๐Ÿ’ก Filter by status: "list-mcps --status=ready"'; output += '\n๐Ÿ’ก Search: "list-mcps --search=my-server"'; return output; } /** * Format servers as JSON */ private formatAsJSON(servers: MCPServerMetadata[]): string { if (servers.length === 0) { return JSON.stringify( { servers: [], count: 0, message: 'No MCP servers found', }, null, 2, ); } return JSON.stringify( { servers: servers.map((server) => ({ id: server.id, name: server.name, status: server.status, template: server.template, path: server.path, language: server.metadata.language, description: server.metadata.description, tags: server.metadata.tags, turboOptimized: server.metadata.turboOptimized, buildCommand: server.metadata.buildCommand, devCommand: server.metadata.devCommand, lastBuildStatus: server.metadata.lastBuildStatus, lastBuildTime: server.metadata.lastBuildTime, errorMessage: server.metadata.errorMessage, createdAt: server.createdAt, updatedAt: server.updatedAt, })), count: servers.length, }, null, 2, ); } /** * Format servers as summary */ private async formatAsSummary( servers: MCPServerMetadata[], registry: RegistryOperations, ): Promise<string> { const stats = await registry.getStatistics(); let output = `๐Ÿ“Š MCP Servers Summary\n\n`; // Overall statistics output += `๐Ÿ“ˆ Statistics:\n`; output += `- Total servers: ${stats.total}\n`; output += `- Recently created: ${stats.recentlyCreated} (last 24h)\n\n`; // Status breakdown output += `๐Ÿ“‹ By Status:\n`; for (const [status, count] of Object.entries(stats.byStatus)) { if (typeof count === 'number' && count > 0) { const emoji = this.getStatusEmoji(status as MCPServerStatus); output += `- ${emoji} ${status}: ${count}\n`; } } // Template breakdown if (Object.keys(stats.byTemplate).length > 0) { output += `\n๐ŸŽจ By Template:\n`; const sortedTemplates = Object.entries(stats.byTemplate) .sort(([, a], [, b]) => (typeof b === 'number' ? b : 0) - (typeof a === 'number' ? a : 0)) .slice(0, 5); // Top 5 templates for (const [template, count] of sortedTemplates) { output += `- ${template}: ${typeof count === 'number' ? count : 0}\n`; } if (Object.keys(stats.byTemplate).length > 5) { output += `- ... and ${Object.keys(stats.byTemplate).length - 5} more\n`; } } // Recent servers if (servers.length > 0) { output += `\n๐Ÿ•’ Recent Servers:\n`; const recent = servers.sort((a, b) => b.createdAt - a.createdAt).slice(0, 3); for (const server of recent) { const emoji = this.getStatusEmoji(server.status); const date = this.formatDate(server.createdAt); output += `- ${emoji} ${server.name} (${server.template}) - ${date}\n`; } } return output; } /** * Get emoji for server status */ private getStatusEmoji(status: MCPServerStatus): string { switch (status) { case MCPServerStatus.READY: return 'โœ…'; case MCPServerStatus.BUILDING: return '๐Ÿ”จ'; case MCPServerStatus.CREATED: return '๐Ÿ†•'; case MCPServerStatus.ERROR: return 'โŒ'; case MCPServerStatus.ARCHIVED: return '๐Ÿ“ฆ'; default: return 'โ“'; } } /** * Truncate string to specified length */ private truncate(str: string, length: number): string { if (str.length <= length) { return str; } return str.substring(0, length - 3) + '...'; } /** * Pad string to specified length */ private pad(str: string, length: number): string { return str.padEnd(length, ' '); } /** * Format timestamp as relative date */ private formatDate(timestamp: number): string { const now = Date.now(); const diff = now - timestamp; const minutes = Math.floor(diff / (1000 * 60)); const hours = Math.floor(diff / (1000 * 60 * 60)); const days = Math.floor(diff / (1000 * 60 * 60 * 24)); if (minutes < 1) { return 'just now'; } else if (minutes < 60) { return `${minutes}m ago`; } else if (hours < 24) { return `${hours}h ago`; } else if (days < 7) { return `${days}d ago`; } else { return new Date(timestamp).toLocaleDateString(); } } }

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/conorluddy/ContextPods'

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