tryaii-mcp-server
Version:
TryAII MCP Server - 15+ AI models with comparison, cost tracking, and collective intelligence
588 lines (578 loc) ⢠22.1 kB
JavaScript
/**
* 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