UNPKG

tryaii-mcp-server

Version:

TryAII MCP Server - 15+ AI models with comparison, cost tracking, and collective intelligence

588 lines (578 loc) • 22.1 kB
#!/usr/bin/env node /** * TryAII MCP Server - NPM Package Bridge * * This connects Claude Desktop to the hosted TryAII server using standardized * API calls with clear naming conventions and proper error handling. */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, InitializeRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import { createRequire } from 'node:module'; const require = createRequire(import.meta.url); const pkg = require('../package.json'); // ======================================== // STANDARDIZED DEFINITIONS // ======================================== const STANDARDIZED_TOOLS = [ { name: "get_available_models", description: "Get comprehensive list of all available AI models with capabilities, pricing, and provider information", inputSchema: { type: "object", properties: { provider: { type: "string", description: "Filter models by provider (optional)", enum: ["openai", "anthropic", "google", "deepseek", "xai", "mistral"] } } } }, { name: "chat_with_model", description: "Execute conversation with a specific AI model with full conversation history support", inputSchema: { type: "object", properties: { modelId: { type: "string", description: "The ID of the model to use (e.g., 'gpt-4', 'claude-3-5-sonnet')" }, message: { type: "string", description: "The message to send to the model" }, conversationHistory: { type: "array", description: "Previous conversation messages (optional)", items: { type: "object", properties: { role: { type: "string", enum: ["user", "assistant", "system"] }, content: { type: "string" } }, required: ["role", "content"] } }, enableWebSearch: { type: "boolean", description: "Enable web search for enhanced responses (default: false)" }, temperature: { type: "number", description: "Control response creativity (0.0-2.0, default: 0.7)", minimum: 0, maximum: 2 }, maxTokens: { type: "number", description: "Maximum tokens to generate (default: 12000)", minimum: 1, maximum: 200000 } }, required: ["modelId", "message"] } }, { name: "brains", description: "Execute collective intelligence query using 5 top AI models (o3, Claude Opus 4, DeepSeek Chat, Gemini 2.5 Pro, Grok 3). Creates beautiful HTML report saved to file with clickable URL for instant browser viewing - always return the URL to the user", inputSchema: { type: "object", properties: { question: { type: "string", description: "Question to ask the collective intelligence of 5 top models" }, enableWebSearch: { type: "boolean", description: "Enable web search for enhanced responses (default: false)" }, temperature: { type: "number", description: "Control response creativity (0.0-2.0, default: 0.7)", minimum: 0, maximum: 2 }, maxTokens: { type: "number", description: "Maximum tokens per model (default: 12000)", minimum: 1, maximum: 200000 } }, required: ["question"] } }, { name: "compare_models", description: "Execute side-by-side comparison of multiple AI models for the same prompt with detailed analysis", inputSchema: { type: "object", properties: { modelIds: { type: "array", description: "Array of model IDs to compare (2-10 models)", items: { type: "string" }, minItems: 2, maxItems: 10 }, message: { type: "string", description: "The prompt/message to test with all models" }, enableWebSearch: { type: "boolean", description: "Enable web search for enhanced responses (default: false)" }, temperature: { type: "number", description: "Control response creativity (0.0-2.0, default: 0.7)", minimum: 0, maximum: 2 }, maxTokens: { type: "number", description: "Maximum tokens per model (default: 12000)", minimum: 1, maximum: 200000 } }, required: ["modelIds", "message"] } }, { name: "get_model_info", description: "Get detailed information about a specific AI model including capabilities, pricing, and specifications", inputSchema: { type: "object", properties: { modelId: { type: "string", description: "The ID of the model to get information about" } }, required: ["modelId"] } } ]; const STANDARDIZED_RESOURCES = [ { uri: 'model_registry', name: 'AI Model Registry', description: 'Complete registry of available AI models across all providers with capabilities and pricing', mimeType: 'application/json', }, { uri: 'server_status', name: 'Server Health Status', description: 'Current server status, uptime, system health metrics, and operational information', mimeType: 'application/json', }, { uri: 'mcp_manifest', name: 'MCP Server Capabilities', description: 'MCP protocol capabilities, tool definitions, and server manifest information', mimeType: 'application/json', }, ]; // ======================================== // STANDARDIZED API CLIENT // ======================================== /** * TryAII API Client - Standardized interface for all server interactions * * Naming Convention: {action}_{subject}_{qualifier?} * - get_*: Retrieve data * - execute_*: Perform actions * - check_*: Status checks */ class TryAIIApiClient { baseUrl; apiKey; constructor(baseUrl, apiKey) { this.baseUrl = baseUrl; this.apiKey = apiKey; } /** * Make HTTP request with standardized headers and error handling */ async makeRequest(endpoint, options = {}) { const url = `${this.baseUrl}${endpoint}`; const headers = { 'Content-Type': 'application/json', 'User-Agent': 'tryaii-mcp-bridge/1.1.0', 'X-Client': 'npm-package', ...(options.headers || {}), }; // Add API key authentication if (this.apiKey) { headers['Authorization'] = `Bearer ${this.apiKey}`; headers['X-API-Key'] = this.apiKey; } const response = await fetch(url, { ...options, headers, }); if (!response.ok) { const errorText = await response.text().catch(() => 'Unknown error'); throw new Error(`TryAII API Error [${response.status}]: ${errorText}`); } return response.json(); } // ======================================== // CORE AI API METHODS // ======================================== /** * Get available AI models with filtering capabilities * Route: GET /api/models */ async get_available_models(filters) { const params = new URLSearchParams(); if (filters?.provider) { params.append('provider', filters.provider); } const endpoint = `/api/models${params.toString() ? `?${params.toString()}` : ''}`; return this.makeRequest(endpoint, { method: 'GET' }); } /** * Execute chat conversation with a specific model * Route: POST /api/chat */ async execute_chat_conversation(params) { return this.makeRequest('/api/chat', { method: 'POST', body: JSON.stringify(params), }); } /** * Execute brains query (multi-model intelligence) */ async execute_brains_query(params) { return this.makeRequest('/mcp/tools/brains', { method: 'POST', body: JSON.stringify({ arguments: params, requestId: Math.random().toString(36).substring(7) }), }); } /** * Execute model comparison analysis * Route: POST /api/compare */ async execute_model_comparison(params) { return this.makeRequest('/api/compare', { method: 'POST', body: JSON.stringify(params), }); } /** * Get detailed information about a specific model * Route: GET /api/models/{modelId} */ async get_model_information(modelId) { return this.makeRequest(`/api/models/${modelId}`, { method: 'GET' }); } // ======================================== // SYSTEM & HEALTH API METHODS // ======================================== /** * Check server health and status * Route: GET /health */ async check_server_health() { return this.makeRequest('/health', { method: 'GET' }); } /** * Get MCP manifest and capabilities * Route: GET /mcp/manifest */ async get_mcp_capabilities() { return this.makeRequest('/mcp/manifest', { method: 'GET' }); } // ======================================== // RESOURCE ACCESS METHODS // ======================================== /** * Get model registry data */ async get_model_registry() { return this.get_available_models(); } /** * Get server status information */ async get_server_status() { return this.check_server_health(); } } // ======================================== // MCP BRIDGE SERVER // ======================================== class TryAIIMCPBridge { server; apiClient; constructor() { const toolMap = STANDARDIZED_TOOLS.reduce((acc, tool) => { acc[tool.name] = tool; return acc; }, {}); const resourceMap = STANDARDIZED_RESOURCES.reduce((acc, resource) => { acc[resource.uri] = resource; return acc; }, {}); this.server = new Server({ protocolVersion: '2024-11-05', serverInfo: { name: 'tryaii-mcp-server', version: pkg.version, }, capabilities: { tools: toolMap, resources: resourceMap, }, }, { capabilities: { tools: {}, resources: {}, }, }); // Initialize API client with environment configuration const apiKey = process.env.TRYAII_API_KEY || process.env.USER_TRYAII_API_KEY; const baseUrl = process.env.TRYAII_BASE_URL || 'https://tryaii-mcp.onrender.com'; this.apiClient = new TryAIIApiClient(baseUrl, apiKey); this.setupHandlers(); } setupHandlers() { // Override the default initialize handler to send a correctly structured response this.server.setRequestHandler(InitializeRequestSchema, async (request) => { const toolMap = STANDARDIZED_TOOLS.reduce((acc, tool) => { acc[tool.name] = tool; return acc; }, {}); const resourceMap = STANDARDIZED_RESOURCES.reduce((acc, resource) => { acc[resource.uri] = resource; return acc; }, {}); return { protocolVersion: '2024-11-05', serverInfo: { name: 'tryaii-mcp-server', version: pkg.version, }, capabilities: { tools: toolMap, resources: resourceMap, }, }; }); // List tools handler this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: STANDARDIZED_TOOLS, }; }); // List resources handler this.server.setRequestHandler(ListResourcesRequestSchema, async () => { return { resources: STANDARDIZED_RESOURCES, }; }); // Read resource handler this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => { try { const response = await this.handleResourceRead(request.params.uri); return { contents: [ { type: 'text', text: JSON.stringify(response, null, 2), }, ], }; } catch (error) { return { contents: [ { type: 'text', text: `Error reading resource: ${error instanceof Error ? error.message : String(error)}`, }, ], }; } }); // Call tool handler - route to standardized API methods this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { const response = await this.handleToolCall(name, args); return { content: [ { type: 'text', text: typeof response === 'string' ? response : JSON.stringify(response, null, 2), }, ], }; } catch (error) { return this.getFallbackResponse(name, args, error); } }); } /** * Handle tool calls with standardized routing */ async handleToolCall(toolName, args) { switch (toolName) { case 'get_available_models': return this.apiClient.get_available_models(args); case 'chat_with_model': return this.apiClient.execute_chat_conversation({ modelId: args.modelId, message: args.message, conversationHistory: args.conversationHistory, enableWebSearch: args.enableWebSearch, temperature: args.temperature, maxTokens: args.maxTokens, }); case 'brains': return this.apiClient.execute_brains_query({ question: args.question, enableWebSearch: args.enableWebSearch, temperature: args.temperature, maxTokens: args.maxTokens, }); case 'compare_models': return this.apiClient.execute_model_comparison({ modelIds: args.modelIds, message: args.message, enableWebSearch: args.enableWebSearch, temperature: args.temperature, maxTokens: args.maxTokens, }); case 'get_model_info': return this.apiClient.get_model_information(args.modelId); default: throw new Error(`Unknown tool: ${toolName}`); } } /** * Handle resource reads with standardized routing */ async handleResourceRead(resourceUri) { switch (resourceUri) { case 'model_registry': return this.apiClient.get_model_registry(); case 'server_status': return this.apiClient.get_server_status(); case 'mcp_manifest': return this.apiClient.get_mcp_capabilities(); default: throw new Error(`Unknown resource: ${resourceUri}`); } } /** * Provide helpful fallback responses when server is unavailable */ getFallbackResponse(toolName, args, error) { const statusMessage = this.apiClient['apiKey'] ? `Connected to ${this.apiClient['baseUrl']} with API key ${this.apiClient['apiKey'].substring(0, 8)}...` : `Connected to ${this.apiClient['baseUrl']} (using server fallback keys)`; const errorMessage = error instanceof Error ? error.message : String(error); switch (toolName) { case 'get_available_models': return { content: [ { type: 'text', text: `šŸ¤– Available AI Models (15+): • **OpenAI**: gpt-4, gpt-4-turbo, gpt-3.5-turbo, gpt-4o, gpt-4o-mini, o1-preview, o1-mini, o3-mini • **Anthropic**: claude-3-5-sonnet-20241022, claude-3-5-haiku-20241022, claude-3-opus-20240229 • **Google**: gemini-pro, gemini-1.5-pro, gemini-2.0-flash-exp • **DeepSeek**: deepseek-chat, deepseek-coder, deepseek-reasoner • **xAI**: grok-beta, grok-2-1212 • **Mistral**: mistral-large, mistral-medium, mistral-small šŸ“” **Status**: ${statusMessage} āš ļø **Note**: Server temporarily unavailable (${errorMessage}). Please try again or check https://tryaii-mcp.onrender.com/health`, }, ], }; case 'brains': return { content: [ { type: 'text', text: `🧠 Brains Tool - Collective Intelligence **Question**: ${args.question || 'Not provided'} šŸ”„ **Status**: ${statusMessage} āš ļø **Error**: ${errorMessage} šŸ’” **What Brains Does**: - Queries 5 top AI models simultaneously (o3, Claude Opus 4, DeepSeek Chat, Gemini 2.5 Pro, Grok 3) - Creates beautiful HTML report with responses - Provides clickable URL for instant browser viewing - Includes cost tracking and performance analysis šŸ”§ **Troubleshooting**: - Check your API key: process.env.TRYAII_API_KEY - Verify server status: https://tryaii-mcp.onrender.com/health - Ensure sufficient balance for multi-model query Please try again in a moment.`, }, ], }; default: return { content: [ { type: 'text', text: `šŸ› ļø Tool: ${toolName} āš ļø **Error**: ${errorMessage} šŸ“” **Status**: ${statusMessage} šŸ”§ **Troubleshooting**: - Verify your API key is set: process.env.TRYAII_API_KEY - Check server health: https://tryaii-mcp.onrender.com/health - Review tool parameters and try again Available tools: get_available_models, chat_with_model, brains, compare_models, get_model_info`, }, ], }; } } async start() { const transport = new StdioServerTransport(); await this.server.connect(transport); // Log to stderr (won't interfere with MCP protocol) console.error(`šŸš€ TryAII MCP Bridge v${pkg.version} started successfully`); console.error(`šŸ“” API Endpoint: ${this.apiClient['baseUrl']}`); console.error(`šŸ”‘ Authentication: ${this.apiClient['apiKey'] ? `API key configured (${this.apiClient['apiKey'].substring(0, 8)}...)` : 'Using server fallback keys'}`); console.error(`šŸ› ļø Available tools: ${STANDARDIZED_TOOLS.map(t => t.name).join(', ')}`); console.error(`šŸ“š Available resources: ${STANDARDIZED_RESOURCES.map(r => r.uri).join(', ')}`); console.error('āœ… Ready for MCP requests from Claude Desktop/Cursor'); // Keep the process alive with a heartbeat const heartbeatInterval = setInterval(() => { // This log goes to stderr and does not interfere with MCP console.error(`[${new Date().toISOString()}] ā¤ļø Heartbeat: Process is alive.`); }, 60 * 1000); // every 60 seconds // Graceful shutdown const cleanup = () => { clearInterval(heartbeatInterval); console.error('\nšŸ›‘ Shutting down TryAII MCP Bridge gracefully.'); process.exit(0); }; process.on('SIGINT', cleanup); process.on('SIGTERM', cleanup); } } // ======================================== // STARTUP // ======================================== const bridge = new TryAIIMCPBridge(); bridge.start().catch((error) => { console.error('āŒ Failed to start TryAII MCP Bridge:', error); process.exit(1); }); export { TryAIIApiClient, TryAIIMCPBridge }; //# sourceMappingURL=mcp-standalone.js.map