"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MCPServer = void 0; exports.run = run; const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js"); const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js"); const thread_manager_1 = require("./core/thread-manager"); const database_1 = require("./database"); const path_1 = __importDefault(require("path")); const fs_extra_1 = __importDefault(require("fs-extra")); const zod_1 = require("zod"); class MCPServer { mcp; threadManager; toolHandlers; constructor(dbManager) { // Initialize McpServer with server info this.mcp = new mcp_js_1.McpServer({ name: 'thread-manager', version: '1.0.0' }, { capabilities: { tools: {} } }); this.threadManager = new thread_manager_1.ThreadManager(dbManager); this.toolHandlers = new Map(); this.registerCoreTools(); } registerCoreTools() { // Dynamically load tools for better modularity const toolsDir = path_1.default.join(__dirname, 'tools'); const toolFiles = fs_extra_1.default.readdirSync(toolsDir).filter(file => (file.endsWith('.ts') || file.endsWith('.js')) && !file.endsWith('.d.ts')); for (const file of toolFiles) { const toolModule = require(path_1.default.join(toolsDir, file)); if (toolModule.toolDefinition && toolModule.toolHandler) { const def = toolModule.toolDefinition; const handler = toolModule.toolHandler; // Convert JSON Schema to Zod schema for MCP SDK const inputSchema = this.jsonSchemaToZod(def.inputSchema); // Register tool with McpServer this.mcp.tool(def.name, def.description || '', inputSchema.shape, // Pass the shape of the ZodObject async (input) => { try { // Call the handler with the old invocation format for compatibility const result = await handler({ toolName: def.name, input }, this.threadManager); // console.error(`[DEBUG] Full result from handler for ${def.name}:`, JSON.stringify(result, null, 2)); let outputText = ""; if (typeof result.message === 'string') { outputText = result.message; } else if (result.message !== undefined && result.message !== null) { outputText = JSON.stringify(result.message, null, 2); } // Append info if available if (result.info) { if (outputText) outputText += "\n\n"; outputText += `Info: ${result.info}`; } // Fallback if (!outputText) { outputText = JSON.stringify(result, null, 2); } return { content: [ { type: 'text', text: outputText } ] }; } catch (error) { console.error(`Error handling tool ${def.name}:`, error); return { content: [ { type: 'text', text: JSON.stringify({ error: error.message || 'Unknown error' }, null, 2) } ], // Cast to any isError: true }; } }); this.toolHandlers.set(def.name, handler); console.log(`Registered MCP Tool: ${def.name}`); } } } jsonSchemaToZod(schema) { // Simple conversion from JSON Schema to Zod // This is a basic implementation - may need to be enhanced for complex schemas const shape = {}; if (schema.properties) { for (const [key, prop] of Object.entries(schema.properties)) { let zodType; switch (prop.type) { case 'string': zodType = zod_1.z.string(); if (prop.description) { zodType = zodType.describe(prop.description); } break; case 'number': zodType = zod_1.z.number(); if (prop.description) { zodType = zodType.describe(prop.description); } break; case 'boolean': zodType = zod_1.z.boolean(); if (prop.description) { zodType = zodType.describe(prop.description); } break; case 'array': if (prop.items?.type === 'string') { zodType = zod_1.z.array(zod_1.z.string()); } else { zodType = zod_1.z.array(zod_1.z.any()); } if (prop.description) { zodType = zodType.describe(prop.description); } break; default: zodType = zod_1.z.any(); } // Make optional if not in required array if (!schema.required || !schema.required.includes(key)) { zodType = zodType.optional(); } shape[key] = zodType; } } return zod_1.z.object(shape); } async start() { console.error('Starting Thread Manager MCP Server...'); // Connect to stdio transport const transport = new stdio_js_1.StdioServerTransport(); await this.mcp.connect(transport); console.error('Thread Manager MCP Server started and listening for tool calls.'); } async callTool(toolName, input) { const handler = this.toolHandlers.get(toolName); if (!handler) { throw new Error(`Tool ${toolName} not found`); } return handler({ toolName, input }, this.threadManager); } // For testing purposes, allows direct access to ThreadManager getThreadManager() { return this.threadManager; } } exports.MCPServer = MCPServer; // Main entry point for the skill (if run directly or by MCP CLI) const server = new MCPServer(database_1.dbManager); async function run() { await server.start(); } // If module is imported/required, the server won't start automatically. // Only starts if directly executed, e.g., `node dist/index.js` if (require.main === module) { run(); }