/**
* MCP Server setup and configuration
*/
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import type { BaseTool } from './tools/base.js';
import { logger } from './utils/logger.js';
import * as z from 'zod';
/**
* MCP Server class that manages tools registration and lifecycle
*/
export class MCPServer {
private server: McpServer;
private tools: Map<string, BaseTool> = new Map();
constructor() {
this.server = new McpServer({
name: 'mcp-server-custom-tools',
version: '1.0.0',
});
// Setup error handling
this.server.server.onerror = (error) => {
logger.error('MCP Server error:', { error: error.message, stack: error.stack });
};
logger.info('MCP Server initialized');
}
/**
* Register a tool with the MCP server
*/
registerTool(tool: BaseTool): void {
this.tools.set(tool.name, tool);
// Register with MCP server using the tool's schema
// McpServer expects inputSchema to be a Zod object schema
if (!(tool.schema instanceof z.ZodObject)) {
throw new Error(`Tool ${tool.name} must have a ZodObject schema`);
}
// Use the schema directly - McpServer accepts ZodObject
this.server.registerTool(
tool.name,
{
title: tool.name,
description: tool.description,
inputSchema: tool.schema.shape,
},
async (params: Record<string, unknown>) => {
logger.info(`Calling tool: ${tool.name}`, { params });
const result = await tool.run(params);
// Convert ToolResult to MCP format
return {
content: result.content.map(item => ({
type: 'text' as const,
text: item.text || '',
})),
};
}
);
logger.info(`Registered tool: ${tool.name}`);
}
/**
* Register multiple tools at once
*/
registerTools(tools: BaseTool[]): void {
for (const tool of tools) {
this.registerTool(tool);
}
}
/**
* Start the MCP server with STDIO transport
*/
async start(): Promise<void> {
const transport = new StdioServerTransport();
await this.server.connect(transport);
logger.info('MCP Server started with STDIO transport');
}
/**
* Get the underlying server instance (for advanced usage)
*/
getServer(): McpServer {
return this.server;
}
}