UNPKG

@bottobot/td-mcp

Version:

TouchDesigner MCP Server v2.8.0 - 21 MCP tools, 629 operators with clean parameter data, 69 Python API classes, 14 tutorials, 32 workflow patterns. Includes version system, experimental techniques KB, core tool enhancements, and experimental build support

304 lines (256 loc) 10.3 kB
#!/usr/bin/env node // TD-MCP v2 - Pure MCP TouchDesigner Documentation Server // Following Claude.md principles: POC first, no premature abstraction // Phase 5: Code Organization - Modular structure // // Wiki system fully integrated - all tools use OperatorDataManager for data access import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { promises as fs } from 'fs'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; // Import operator data manager import OperatorDataManager from './wiki/operator-data-manager.js'; // Import tools import * as getOperatorTool from './tools/get_operator.js'; import * as searchOperatorsTool from './tools/search_operators.js'; import * as suggestWorkflowTool from './tools/suggest_workflow.js'; import * as listOperatorsTool from './tools/list_operators.js'; import * as getTutorialTool from './tools/get_tutorial.js'; import * as listTutorialsTool from './tools/list_tutorials.js'; import * as getPythonApiTool from './tools/get_python_api.js'; import * as searchPythonApiTool from './tools/search_python_api.js'; import * as searchTutorialsTool from './tools/search_tutorials.js'; import * as getOperatorExamplesTool from './tools/get_operator_examples.js'; import * as listPythonClassesTool from './tools/list_python_classes.js'; import * as compareOperatorsTool from './tools/compare_operators.js'; import * as getVersionInfoTool from './tools/get_version_info.js'; import * as listVersionsTool from './tools/list_versions.js'; // Import experimental techniques tools (v2.9) import * as getExperimentalTechniquesTool from './tools/get_experimental_techniques.js'; import * as searchExperimentalTool from './tools/search_experimental.js'; import * as getGlslPatternTool from './tools/get_glsl_pattern.js'; // Import core enhancement tools (v2.10) import * as getOperatorConnectionsTool from './tools/get_operator_connections.js'; import * as getNetworkTemplateTool from './tools/get_network_template.js'; // Import experimental build tools (v2.11) import * as getExperimentalBuildTool from './tools/get_experimental_build.js'; import * as listExperimentalBuildsTool from './tools/list_experimental_builds.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const PATTERNS_PATH = join(__dirname, 'data', 'patterns.json'); // Load package.json to get version const packageJson = JSON.parse(await fs.readFile(join(__dirname, 'package.json'), 'utf-8')); const VERSION = packageJson.version; // Create MCP server instance const server = new McpServer({ name: "td-mcp", version: VERSION }); // Initialize operator data manager with TouchDesigner documentation path const operatorDataManager = new OperatorDataManager({ wikiPath: join(__dirname, 'wiki'), dataPath: join(__dirname, 'wiki', 'data'), processedPath: join(__dirname, 'wiki', 'data', 'processed'), // Point to processed directory with all 649 operators searchIndexPath: join(__dirname, 'wiki', 'data', 'search-index'), enablePersistence: true, autoIndex: true, // TouchDesigner documentation path - points to downloaded docs tdDocsPath: join(__dirname, 'wiki', 'docs', 'python'), // Progress reporting progressCallback: (progress) => { if (progress.processed % 100 === 0 || progress.complete) { console.log(`[Wiki] Processing progress: ${progress.percentage}% (${progress.processed}/${progress.total})`); } }, progressInterval: 100 // Report every 100 files }); // Web server functionality removed - not needed for MCP server operation // Workflow patterns storage (will be integrated with wiki system) let workflowPatterns = null; // Load workflow patterns async function loadPatterns() { try { console.log(`[Patterns] Loading from: ${PATTERNS_PATH}`); const content = await fs.readFile(PATTERNS_PATH, 'utf-8'); workflowPatterns = JSON.parse(content); console.log(`[Patterns] Loaded ${workflowPatterns.patterns.length} workflow patterns`); } catch (error) { console.error('[Patterns] Failed to load patterns:', error); workflowPatterns = { patterns: [], common_transitions: {} }; } } // Register tools server.registerTool( "get_operator", getOperatorTool.schema, async (params) => await getOperatorTool.handler(params, { operatorDataManager }) ); server.registerTool( "search_operators", searchOperatorsTool.schema, async (params) => await searchOperatorsTool.handler(params, { operatorDataManager }) ); server.registerTool( "suggest_workflow", suggestWorkflowTool.schema, async (params) => await suggestWorkflowTool.handler(params, { operatorDataManager, workflowPatterns }) ); server.registerTool( "list_operators", listOperatorsTool.schema, async (params) => await listOperatorsTool.handler(params, { operatorDataManager }) ); // Register tutorial tools server.registerTool( "get_tutorial", getTutorialTool.schema, async (params) => await getTutorialTool.handler(params, { operatorDataManager }) ); server.registerTool( "list_tutorials", listTutorialsTool.schema, async (params) => await listTutorialsTool.handler(params, { operatorDataManager }) ); // Register Python API tools server.registerTool( "get_python_api", getPythonApiTool.schema, async (params) => await getPythonApiTool.handler(params, { operatorDataManager }) ); server.registerTool( "search_python_api", searchPythonApiTool.schema, async (params) => await searchPythonApiTool.handler(params, { operatorDataManager }) ); // Register new v2.7 tools server.registerTool( "search_tutorials", searchTutorialsTool.schema, async (params) => await searchTutorialsTool.handler(params, { operatorDataManager }) ); server.registerTool( "get_operator_examples", getOperatorExamplesTool.schema, async (params) => await getOperatorExamplesTool.handler(params, { operatorDataManager }) ); server.registerTool( "list_python_classes", listPythonClassesTool.schema, async (params) => await listPythonClassesTool.handler(params, { operatorDataManager }) ); server.registerTool( "compare_operators", compareOperatorsTool.schema, async (params) => await compareOperatorsTool.handler(params, { operatorDataManager }) ); // Register version system tools (v2.8) server.registerTool( "get_version_info", getVersionInfoTool.schema, async (params) => await getVersionInfoTool.handler(params, {}) ); server.registerTool( "list_versions", listVersionsTool.schema, async (params) => await listVersionsTool.handler(params, {}) ); // Register experimental techniques tools (v2.9) server.registerTool( "get_experimental_techniques", getExperimentalTechniquesTool.schema, async (params) => await getExperimentalTechniquesTool.handler(params) ); server.registerTool( "search_experimental", searchExperimentalTool.schema, async (params) => await searchExperimentalTool.handler(params) ); server.registerTool( "get_glsl_pattern", getGlslPatternTool.schema, async (params) => await getGlslPatternTool.handler(params) ); // Register core enhancement tools (v2.10) server.registerTool( "get_operator_connections", getOperatorConnectionsTool.schema, async (params) => await getOperatorConnectionsTool.handler(params, {}) ); server.registerTool( "get_network_template", getNetworkTemplateTool.schema, async (params) => await getNetworkTemplateTool.handler(params, {}) ); // Register experimental build tools (v2.11) server.registerTool( "get_experimental_build", getExperimentalBuildTool.schema, async (params) => await getExperimentalBuildTool.handler(params, {}) ); server.registerTool( "list_experimental_builds", listExperimentalBuildsTool.schema, async (params) => await listExperimentalBuildsTool.handler(params, {}) ); // Main startup async function main() { console.log(`TD-MCP v${VERSION} Server Starting...`); console.log('================================'); console.log('TouchDesigner MCP Server for VS Code/Codium'); console.log('Pure MCP server - stdio transport\n'); try { // Initialize wiki system console.log('[Server] Initializing operator data manager...'); const initStartTime = Date.now(); await operatorDataManager.initialize(); const initDuration = Date.now() - initStartTime; console.log(`[Server] Initialization took ${initDuration}ms (${(initDuration/1000).toFixed(2)}s)`); await loadPatterns(); console.log(`\n[Server] TD MCP v${VERSION} initialized successfully`); const stats = operatorDataManager.getSystemStats(); const pythonApiStats = stats.pythonApiStats || { totalClasses: 0 }; console.log(`[Server] Ready with ${stats.totalEntries} operators, ${stats.totalTutorials} tutorials, and ${pythonApiStats.totalClasses} Python classes`); console.log(`[Server] All 21 tools registered`); } catch (error) { console.error('[Server] Initialization error:', error); // Continue startup even if wiki system fails to initialize console.log('[Server] Continuing startup with limited functionality'); } // Connect to stdio transport const transport = new StdioServerTransport(); await server.connect(transport); console.log(`\nTD-MCP v${VERSION} Server is now running`); } // Graceful shutdown handling process.on('SIGINT', async () => { console.log('\n[Server] Received SIGINT, shutting down gracefully...'); try { if (operatorDataManager) { console.log('[Server] Cleaning up operator data manager...'); operatorDataManager.destroy(); } console.log('[Server] Shutdown complete'); process.exit(0); } catch (error) { console.error('[Server] Error during shutdown:', error); process.exit(1); } }); process.on('SIGTERM', async () => { console.log('\n[Server] Received SIGTERM, shutting down gracefully...'); try { if (operatorDataManager) { operatorDataManager.destroy(); } process.exit(0); } catch (error) { console.error('[Server] Error during shutdown:', error); process.exit(1); } }); // Start the server main().catch(error => { console.error('Server failed to start:', error); process.exit(1); });