tryaii-mcp-server
Version:
MCP Server for TryAII - Lightweight proxy to TryAII API service
313 lines (312 loc) • 13.9 kB
JavaScript
#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { ListToolsRequestSchema, CallToolRequestSchema, ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js";
// Import API client and models registry
import { TryAIIApiClient } from './clients/api-client.js';
import { AVAILABLE_MODELS } from './models/registry.js';
// ============================================================================================
// CONFIGURATION
// ============================================================================================
const API_BASE_URL = 'https://tryaii-mcp.onrender.com';
const API_KEY = process.env.TRYAII_API_KEY;
if (!API_KEY) {
console.error('❌ TRYAII_API_KEY environment variable is required');
process.exit(1);
}
// Initialize API client
const apiClient = new TryAIIApiClient({
baseUrl: API_BASE_URL,
apiKey: API_KEY
});
// ============================================================================================
// MCP SERVER CONFIGURATION
// ============================================================================================
const server = new Server({
name: "tryaii-server",
version: "2.0.0",
}, {
capabilities: {
tools: {},
},
});
// ============================================================================================
// TOOLS DEFINITION
// ============================================================================================
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "get_available_models",
description: "Get a list of all available AI models with their capabilities and pricing",
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: "Have a conversation with a specific AI model",
inputSchema: {
type: "object",
properties: {
modelId: {
type: "string",
description: "The ID of the model to use"
},
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 randomness (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: "compare_models",
description: "Compare responses from multiple AI models for the same prompt (OpenAI o3, Claude 4 Sonnet, Gemini 2.5 pro, DeepSeek R1, XAI Grok 3 and many more) and returns a tryaii URL report (https://tryaii.com/report/...) to the user",
inputSchema: {
type: "object",
properties: {
modelIds: {
type: "array",
description: "Array of model IDs to compare",
items: { type: "string" },
minItems: 2,
maxItems: 10
},
message: {
type: "string",
description: "The message/prompt to test with all models"
},
enableWebSearch: {
type: "boolean",
description: "Enable web search for enhanced responses (default: false)"
},
temperature: {
type: "number",
description: "Control randomness (0.0-2.0, default: 0.7)",
minimum: 0,
maximum: 2
},
maxTokens: {
type: "number",
description: "Maximum tokens to generate per model (default: 12000)",
minimum: 1000,
maximum: 20000
}
},
required: ["modelIds", "message"]
}
},
{
name: "brains",
description: "Get responses from multiple top AI models (OpenAI o3, Claude 4 Sonnet, Gemini 2.5 pro, DeepSeek R1, XAI Grok 3) simultaneously and returns a tryaii URL report (https://tryaii.com/report/...) to the user",
inputSchema: {
type: "object",
properties: {
question: {
type: "string",
description: "The question or prompt to ask all models"
},
enableWebSearch: {
type: "boolean",
description: "Enable web search for enhanced responses (default: false)"
},
temperature: {
type: "number",
description: "Control randomness (0.0-2.0, default: 0.7)",
minimum: 0.5,
maximum: 2
},
maxTokens: {
type: "number",
description: "Maximum tokens to generate per model (default: 12000)",
minimum: 1000,
maximum: 20000
}
},
required: ["question"]
}
},
{
name: "get_model_info",
description: "Get detailed information about a specific AI model",
inputSchema: {
type: "object",
properties: {
modelId: {
type: "string",
description: "The ID of the model to get information about"
}
},
required: ["modelId"]
}
},
]
};
});
// ============================================================================================
// TOOL HANDLERS
// ============================================================================================
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case "get_available_models": {
const { provider } = args;
try {
// Fetch models from the API server instead of local registry
const result = await apiClient.getAvailableModels(provider);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2)
}
]
};
}
catch (error) {
// Fallback to local registry if API fails
console.warn('Failed to fetch models from API, using local registry:', error instanceof Error ? error.message : String(error));
let models = AVAILABLE_MODELS;
if (provider) {
models = models.filter(model => model.provider.toLowerCase() === provider.toLowerCase());
}
return {
content: [
{
type: "text",
text: JSON.stringify({
models: models.map(model => ({
id: model.id,
name: model.name,
provider: model.provider,
description: model.description,
capabilities: model.capabilities,
pricing: {
inputCostPer1kTokens: model.inputCostPer1kTokens,
outputCostPer1kTokens: model.outputCostPer1kTokens
},
limits: {
maxContextTokens: model.maxContextTokens ? model.maxContextTokens < 2000 ? 2000 : model.maxContextTokens : 2000
},
features: {
webSearch: model.webSearch,
reasoning: model.reasoning,
latencySpeed: model.latencySpeed
}
})),
metadata: {
totalModels: models.length,
providers: [...new Set(models.map(m => m.provider))],
timestamp: new Date().toISOString(),
source: 'local_fallback'
}
}, null, 2)
}
]
};
}
}
case "chat_with_model": {
const result = await apiClient.chatWithModel(args);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2)
}
]
};
}
case "compare_models": {
const result = await apiClient.compareModels(args);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2)
}
]
};
}
case "brains": {
const result = await apiClient.brains(args);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2)
}
]
};
}
case "get_model_info": {
const result = await apiClient.getModelInfo(args);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2)
}
]
};
}
default:
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
}
}
catch (error) {
if (error instanceof McpError) {
throw error;
}
throw new McpError(ErrorCode.InternalError, `Tool execution failed: ${error instanceof Error ? error.message : String(error)}`);
}
});
// ============================================================================================
// SERVER STARTUP
// ============================================================================================
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
}
main().catch((error) => {
console.error('❌ Server startup failed:', error);
process.exit(1);
});