Skip to main content
Glama
cli.ts6.86 kB
#!/usr/bin/env node import { ConsoleLogger } from "./core/logger.js"; import { TouchDesignerServer } from "./server/touchDesignerServer.js"; import type { StreamableHttpTransportConfig, TransportConfig, } from "./transport/config.js"; import { isStreamableHttpTransportConfig } from "./transport/config.js"; import { ExpressHttpManager } from "./transport/expressHttpManager.js"; import { TransportFactory } from "./transport/factory.js"; import { SessionManager } from "./transport/sessionManager.js"; // Note: Environment variables should be set by the MCP Bundle runtime or CLI arguments const DEFAULT_HOST = "http://127.0.0.1"; const DEFAULT_PORT = 9981; const DEFAULT_MCP_ENDPOINT = "/mcp"; /** * Parse command line arguments for TouchDesigner connection */ export function parseArgs(args?: string[]) { const argsToProcess = args || process.argv.slice(2); const parsed = { host: DEFAULT_HOST, port: DEFAULT_PORT, }; for (let i = 0; i < argsToProcess.length; i++) { const arg = argsToProcess[i]; if (arg.startsWith("--host=")) { parsed.host = arg.split("=")[1]; } else if (arg.startsWith("--port=")) { parsed.port = Number.parseInt(arg.split("=")[1], 10); } } return parsed; } /** * Parse transport configuration from command line arguments * * Detects if HTTP mode is requested via --mcp-http-port flag. * If not specified, defaults to stdio mode. * * @param args - Command line arguments (defaults to process.argv.slice(2)) * @returns Transport configuration (stdio or streamable-http) * * @example * ```bash * # Stdio mode (default) * touchdesigner-mcp-server --host=http://localhost --port=9981 * * # HTTP mode * touchdesigner-mcp-server --mcp-http-port=6280 --mcp-http-host=127.0.0.1 * ``` */ export function parseTransportConfig(args?: string[]): TransportConfig { const argsToProcess = args || process.argv.slice(2); // Check for HTTP mode const httpPortArg = argsToProcess.find((arg) => arg.startsWith("--mcp-http-port="), ); if (httpPortArg) { const portStr = httpPortArg.split("=")[1]; const port = Number.parseInt(portStr, 10); if (Number.isNaN(port) || port < 1 || port > 65535) { console.error( `Invalid value for --mcp-http-port: "${portStr}". Please specify a valid port number (1-65535).`, ); process.exit(1); } const hostArg = argsToProcess.find((arg) => arg.startsWith("--mcp-http-host="), ); const host = hostArg ? hostArg.split("=")[1] : "127.0.0.1"; const config: StreamableHttpTransportConfig = { endpoint: DEFAULT_MCP_ENDPOINT, host, port, sessionConfig: { enabled: true }, type: "streamable-http", }; return config; } // Default to stdio mode return { type: "stdio" }; } /** * Start TouchDesigner MCP server * * Supports both stdio and HTTP transport modes based on command line arguments. * * @param params - Server startup parameters * @param params.argv - Command line arguments * @param params.nodeEnv - Node environment * * @example * ```bash * # Stdio mode (default) * touchdesigner-mcp-server --host=http://localhost --port=9981 * * # HTTP mode * touchdesigner-mcp-server --mcp-http-port=6280 --host=http://localhost --port=9981 * ``` */ export async function startServer(params?: { nodeEnv?: string; argv?: string[]; }): Promise<void> { try { // Parse transport configuration const transportConfig = parseTransportConfig(params?.argv); // Parse TouchDesigner connection arguments const args = parseArgs(params?.argv); process.env.TD_WEB_SERVER_HOST = args.host; process.env.TD_WEB_SERVER_PORT = args.port.toString(); // Create MCP server const server = new TouchDesignerServer(); // Handle stdio mode if (transportConfig.type === "stdio") { const transportResult = TransportFactory.create(transportConfig); if (!transportResult.success) { throw transportResult.error; } const result = await server.connect(transportResult.data); if (!result.success) { throw new Error(`Failed to connect: ${result.error.message}`); } console.error("MCP server started in stdio mode"); return; } // Handle HTTP mode if (isStreamableHttpTransportConfig(transportConfig)) { // Use ConsoleLogger for HTTP manager and session manager // This avoids "Not connected" errors since HTTP mode doesn't have a global MCP connection const logger = new ConsoleLogger(); // Create session manager if enabled const sessionManager = transportConfig.sessionConfig?.enabled ? new SessionManager(transportConfig.sessionConfig, logger) : null; // Server factory for creating per-session instances // Each session gets its own TouchDesignerServer with independent MCP protocol state const serverFactory = () => TouchDesignerServer.create(); // Create Express HTTP manager with server factory const httpManager = new ExpressHttpManager( transportConfig, serverFactory, sessionManager, logger, ); // Start HTTP server const startResult = await httpManager.start(); if (!startResult.success) { throw startResult.error; } console.error( `MCP server started in HTTP mode on ${transportConfig.host}:${transportConfig.port}${transportConfig.endpoint}`, ); // Start session cleanup if enabled if (sessionManager) { sessionManager.startTTLCleanup(); } // Set up graceful shutdown const shutdown = async () => { console.error("\nShutting down server..."); // Stop session cleanup if (sessionManager) { sessionManager.stopTTLCleanup(); } // Stop HTTP server const stopResult = await httpManager.stop(); if (!stopResult.success) { console.error(`Error during shutdown: ${stopResult.error.message}`); } console.error("Server shutdown complete"); process.exit(0); }; process.on("SIGINT", shutdown); process.on("SIGTERM", shutdown); return; } // Type-safe exhaustive check using never type // This ensures all cases of the TransportConfig discriminated union are handled // If a new transport type is added, TypeScript will error at compile time assertNever(transportConfig); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Failed to initialize server: ${errorMessage}`); } } /** * Helper function for exhaustive type checking * TypeScript will error if called with a non-never type, ensuring all cases are handled */ function assertNever(value: never): never { throw new Error( `Unsupported transport type: ${(value as { type: string }).type}`, ); } // Start server if this file is executed directly startServer({ argv: process.argv, nodeEnv: process.env.NODE_ENV, }).catch((error) => { console.error("Failed to start server:", error); if (process.env.NODE_ENV === "test") return; process.exit(1); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/8beeeaaat/touchdesigner-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server