spa/.claude/skills/thread-manager/dist/server.js

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();
}