174 lines
7.3 KiB
JavaScript
174 lines
7.3 KiB
JavaScript
"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();
|
|
}
|