trading-platform-mcp-mt4-co.../src/index.ts
2026-01-16 08:33:13 -06:00

292 lines
8.0 KiB
TypeScript

/**
* MCP Server: MT4 Connector
*
* Exposes MT4 trading capabilities as MCP tools for AI agents.
* Communicates with mt4-gateway service to execute trading operations.
*
* @version 0.1.0
* @author Trading Platform Trading Platform
*/
import express, { Request, Response, NextFunction } from 'express';
import dotenv from 'dotenv';
import { mcpToolSchemas, toolHandlers } from './tools';
import { getMT4Client } from './services/mt4-client';
// Load environment variables
dotenv.config();
const app = express();
const PORT = process.env.PORT || 3605;
const SERVICE_NAME = 'mcp-mt4-connector';
const VERSION = '0.1.0';
// ==========================================
// Middleware
// ==========================================
app.use(express.json());
// Request logging
app.use((req: Request, _res: Response, next: NextFunction) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.path}`);
next();
});
// ==========================================
// Health & Status Endpoints
// ==========================================
/**
* Health check endpoint
*/
app.get('/health', async (_req: Request, res: Response) => {
try {
const client = getMT4Client();
const mt4Connected = await client.isConnected();
res.json({
status: 'ok',
service: SERVICE_NAME,
version: VERSION,
timestamp: new Date().toISOString(),
dependencies: {
mt4Gateway: mt4Connected ? 'connected' : 'disconnected',
},
});
} catch (error) {
res.json({
status: 'degraded',
service: SERVICE_NAME,
version: VERSION,
timestamp: new Date().toISOString(),
dependencies: {
mt4Gateway: 'error',
},
error: error instanceof Error ? error.message : 'Unknown error',
});
}
});
/**
* List available MCP tools
*/
app.get('/tools', (_req: Request, res: Response) => {
res.json({
tools: mcpToolSchemas,
count: mcpToolSchemas.length,
});
});
/**
* Get specific tool schema
*/
app.get('/tools/:toolName', (req: Request, res: Response) => {
const { toolName } = req.params;
const tool = mcpToolSchemas.find(t => t.name === toolName);
if (!tool) {
res.status(404).json({
error: `Tool '${toolName}' not found`,
availableTools: mcpToolSchemas.map(t => t.name),
});
return;
}
res.json(tool);
});
// ==========================================
// MCP Tool Execution Endpoints
// ==========================================
/**
* Execute an MCP tool
* POST /tools/:toolName
* Body: { parameters: {...} }
*/
app.post('/tools/:toolName', async (req: Request, res: Response) => {
const { toolName } = req.params;
const { parameters = {} } = req.body;
// Validate tool exists
const handler = toolHandlers[toolName];
if (!handler) {
res.status(404).json({
success: false,
error: `Tool '${toolName}' not found`,
availableTools: Object.keys(toolHandlers),
});
return;
}
try {
console.log(`[${new Date().toISOString()}] Executing tool: ${toolName}`);
console.log(`Parameters: ${JSON.stringify(parameters)}`);
const result = await handler(parameters);
res.json({
success: true,
tool: toolName,
result,
});
} catch (error) {
console.error(`[${new Date().toISOString()}] Tool error: ${toolName}`, error);
// Handle Zod validation errors
if (error && typeof error === 'object' && 'issues' in error) {
res.status(400).json({
success: false,
error: 'Validation error',
details: (error as { issues: unknown[] }).issues,
});
return;
}
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
});
}
});
// ==========================================
// MCP Protocol Endpoints (Standard)
// ==========================================
/**
* MCP Initialize
* Returns server capabilities
*/
app.post('/mcp/initialize', (_req: Request, res: Response) => {
res.json({
protocolVersion: '2024-11-05',
capabilities: {
tools: {},
},
serverInfo: {
name: SERVICE_NAME,
version: VERSION,
},
});
});
/**
* MCP List Tools
* Returns all available tools in MCP format
*/
app.post('/mcp/tools/list', (_req: Request, res: Response) => {
res.json({
tools: mcpToolSchemas.map(tool => ({
name: tool.name,
description: tool.description,
inputSchema: tool.inputSchema,
})),
});
});
/**
* MCP Call Tool
* Execute a tool with parameters
*/
app.post('/mcp/tools/call', async (req: Request, res: Response) => {
const { name, arguments: args = {} } = req.body;
if (!name) {
res.status(400).json({
error: {
code: 'invalid_request',
message: 'Tool name is required',
},
});
return;
}
const handler = toolHandlers[name];
if (!handler) {
res.status(404).json({
error: {
code: 'unknown_tool',
message: `Tool '${name}' not found`,
},
});
return;
}
try {
const result = await handler(args);
res.json(result);
} catch (error) {
// Handle Zod validation errors
if (error && typeof error === 'object' && 'issues' in error) {
res.status(400).json({
error: {
code: 'invalid_params',
message: 'Invalid tool parameters',
data: (error as { issues: unknown[] }).issues,
},
});
return;
}
res.status(500).json({
error: {
code: 'internal_error',
message: error instanceof Error ? error.message : 'Unknown error',
},
});
}
});
// ==========================================
// Error Handler
// ==========================================
app.use((err: Error, _req: Request, res: Response, _next: NextFunction) => {
console.error(`[${new Date().toISOString()}] Unhandled error:`, err);
res.status(500).json({
error: 'Internal server error',
message: err.message,
});
});
// ==========================================
// Start Server
// ==========================================
app.listen(PORT, () => {
console.log('');
console.log('╔══════════════════════════════════════════════════════════╗');
console.log('║ MCP MT4 Connector - Trading Platform ║');
console.log('╠══════════════════════════════════════════════════════════╣');
console.log(`║ Service: ${SERVICE_NAME.padEnd(45)}`);
console.log(`║ Version: ${VERSION.padEnd(45)}`);
console.log(`║ Port: ${String(PORT).padEnd(48)}`);
console.log('╠══════════════════════════════════════════════════════════╣');
console.log('║ Endpoints: ║');
console.log(`║ - Health: http://localhost:${PORT}/health`.padEnd(63) + '║');
console.log(`║ - Tools: http://localhost:${PORT}/tools`.padEnd(63) + '║');
console.log('╠══════════════════════════════════════════════════════════╣');
console.log('║ MCP Tools Available: ║');
mcpToolSchemas.forEach(tool => {
console.log(`║ - ${tool.name.padEnd(54)}`);
});
console.log('╚══════════════════════════════════════════════════════════╝');
console.log('');
});
// ==========================================
// Graceful Shutdown
// ==========================================
process.on('SIGTERM', () => {
console.log('Received SIGTERM, shutting down gracefully...');
process.exit(0);
});
process.on('SIGINT', () => {
console.log('Received SIGINT, shutting down gracefully...');
process.exit(0);
});