touchdesigner-mcp-server
Version:
MCP server for TouchDesigner
151 lines (150 loc) • 5.06 kB
JavaScript
import { createNode as apiCreateNode, deleteNode as apiDeleteNode, execNodeMethod as apiExecNodeMethod, execPythonScript as apiExecPythonScript, getNodeDetail as apiGetNodeDetail, getNodes as apiGetNodes, getTdInfo as apiGetTdInfo, getTdPythonClassDetails as apiGetTdPythonClassDetails, getTdPythonClasses as apiGetTdPythonClasses, updateNode as apiUpdateNode, } from "../gen/endpoints/TouchDesignerAPI.js";
/**
* Default implementation of ITouchDesignerApi using generated API clients
*/
const defaultApiClient = {
execNodeMethod: apiExecNodeMethod,
execPythonScript: apiExecPythonScript,
getTdInfo: apiGetTdInfo,
getNodes: apiGetNodes,
getNodeDetail: apiGetNodeDetail,
createNode: apiCreateNode,
updateNode: apiUpdateNode,
deleteNode: apiDeleteNode,
getTdPythonClasses: apiGetTdPythonClasses,
getTdPythonClassDetails: apiGetTdPythonClassDetails,
};
/**
* Null logger implementation that discards all logs
*/
const nullLogger = {
debug: () => { },
log: () => { },
warn: () => { },
error: () => { },
};
/**
* Handle API error response
* @param response - API response object
* @returns ErrorResult object indicating failure
*/
function handleError(response) {
if (response.error) {
const errorMessage = response.error;
return { success: false, error: new Error(errorMessage) };
}
return { success: false, error: new Error("Unknown error occurred") };
}
/**
* Handle API response and return a structured result
* @param response - API response object
* @returns Result object indicating success or failure
*/
function handleApiResponse(response) {
const { success, data } = response;
if (!success) {
return handleError(response);
}
if (data === null) {
return { success: false, error: new Error("No data received") };
}
if (data === undefined) {
return { success: false, error: new Error("No data received") };
}
return { success: true, data };
}
/**
* TouchDesigner client implementation with dependency injection
* for better testability and separation of concerns
*/
export class TouchDesignerClient {
logger;
api;
/**
* Initialize TouchDesigner client with optional dependencies
*/
constructor(params = {}) {
this.logger = params.logger || nullLogger;
this.api = params.httpClient || defaultApiClient;
}
/**
* Execute a node method
*/
async execNodeMethod(params) {
this.logger.debug(`Executing node method: ${params.method} on ${params.nodePath}`);
const result = await this.api.execNodeMethod(params);
return handleApiResponse(result);
}
/**
* Execute a script in TouchDesigner
*/
async execPythonScript(params) {
this.logger.debug(`Executing Python script: ${params}`);
const result = await this.api.execPythonScript(params);
return handleApiResponse(result);
}
/**
* Get TouchDesigner server information
*/
async getTdInfo() {
this.logger.debug("Getting server info");
const result = await this.api.getTdInfo();
return handleApiResponse(result);
}
/**
* Get list of nodes
*/
async getNodes(params) {
this.logger.debug(`Getting nodes for parent: ${params.parentPath}`);
const result = await this.api.getNodes(params);
return handleApiResponse(result);
}
/**
* Get node properties
*/
async getNodeDetail(params) {
this.logger.debug(`Getting properties for node: ${params.nodePath}`);
const result = await this.api.getNodeDetail(params);
return handleApiResponse(result);
}
/**
* Create a new node
*/
async createNode(params) {
this.logger.debug(`Creating node: ${params.nodeName} of type ${params.nodeType} under ${params.parentPath}`);
const result = await this.api.createNode(params);
return handleApiResponse(result);
}
/**
* Update node properties
*/
async updateNode(params) {
this.logger.debug(`Updating node: ${params.nodePath}`);
const result = await this.api.updateNode(params);
return handleApiResponse(result);
}
/**
* Delete a node
*/
async deleteNode(params) {
this.logger.debug(`Deleting node: ${params.nodePath}`);
const result = await this.api.deleteNode(params);
return handleApiResponse(result);
}
/**
* Get list of available Python classes/modules in TouchDesigner
*/
async getClasses() {
this.logger.debug("Getting Python classes");
const result = await this.api.getTdPythonClasses();
return handleApiResponse(result);
}
/**
* Get details of a specific class/module
*/
async getClassDetails(className) {
this.logger.debug(`Getting class details for: ${className}`);
const result = await this.api.getTdPythonClassDetails(className);
return handleApiResponse(result);
}
}