touchdesigner-mcp-server 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +20 -0
- package/README.md +20 -0
- package/dist/cli.js +1 -1
- package/dist/core/constants.js +5 -3
- package/dist/core/errorHandling.js +18 -3
- package/dist/core/logger.js +10 -32
- package/dist/core/result.js +2 -2
- package/dist/core/version.js +4 -0
- package/dist/features/prompts/handlers/td_prompts.js +24 -18
- package/dist/features/tools/handlers/tdTools.js +75 -18
- package/dist/features/tools/metadata/touchDesignerToolMetadata.js +246 -172
- package/dist/features/tools/presenter/classListFormatter.js +22 -22
- package/dist/features/tools/presenter/index.js +2 -0
- package/dist/features/tools/presenter/moduleHelpFormatter.js +315 -0
- package/dist/features/tools/presenter/nodeDetailsFormatter.js +17 -17
- package/dist/features/tools/presenter/nodeErrorsFormatter.js +68 -0
- package/dist/features/tools/presenter/nodeListFormatter.js +12 -12
- package/dist/features/tools/presenter/operationFormatter.js +10 -10
- package/dist/features/tools/presenter/responseFormatter.js +4 -4
- package/dist/features/tools/presenter/scriptResultFormatter.js +14 -14
- package/dist/features/tools/presenter/toolMetadataFormatter.js +8 -8
- package/dist/gen/endpoints/TouchDesignerAPI.js +22 -4
- package/dist/gen/mcp/touchDesignerAPI.zod.js +52 -12
- package/dist/server/connectionManager.js +11 -28
- package/dist/server/touchDesignerServer.js +4 -3
- package/dist/tdClient/touchDesignerClient.js +127 -26
- package/package.json +24 -17
|
@@ -1,27 +1,34 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createErrorResult, createSuccessResult } from "../core/result.js";
|
|
2
|
+
import { PACKAGE_VERSION } from "../core/version.js";
|
|
3
|
+
import { createNode as apiCreateNode, deleteNode as apiDeleteNode, execNodeMethod as apiExecNodeMethod, execPythonScript as apiExecPythonScript, getModuleHelp as apiGetModuleHelp, getNodeDetail as apiGetNodeDetail, getNodeErrors as apiGetNodeErrors, getNodes as apiGetNodes, getTdInfo as apiGetTdInfo, getTdPythonClassDetails as apiGetTdPythonClassDetails, getTdPythonClasses as apiGetTdPythonClasses, updateNode as apiUpdateNode, } from "../gen/endpoints/TouchDesignerAPI.js";
|
|
4
|
+
const updateGuide = `
|
|
5
|
+
1. Download the latest [touchdesigner-mcp-td.zip](https://github.com/8beeeaaat/touchdesigner-mcp/releases/latest/download/touchdesigner-mcp-td.zip) from the releases page.
|
|
6
|
+
2. Delete the existing \`touchdesigner-mcp-td\` folder and replace it with the newly extracted contents.
|
|
7
|
+
3. Remove the old \`mcp_webserver_base\` component from your TouchDesigner project and import the \`.tox\` from the new folder.
|
|
8
|
+
4. Restart TouchDesigner and the AI agent running the MCP server (e.g., Claude Desktop).
|
|
9
|
+
`;
|
|
2
10
|
/**
|
|
3
11
|
* Default implementation of ITouchDesignerApi using generated API clients
|
|
4
12
|
*/
|
|
5
13
|
const defaultApiClient = {
|
|
14
|
+
createNode: apiCreateNode,
|
|
15
|
+
deleteNode: apiDeleteNode,
|
|
6
16
|
execNodeMethod: apiExecNodeMethod,
|
|
7
17
|
execPythonScript: apiExecPythonScript,
|
|
8
|
-
|
|
9
|
-
getNodes: apiGetNodes,
|
|
18
|
+
getModuleHelp: apiGetModuleHelp,
|
|
10
19
|
getNodeDetail: apiGetNodeDetail,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
getTdPythonClasses: apiGetTdPythonClasses,
|
|
20
|
+
getNodeErrors: apiGetNodeErrors,
|
|
21
|
+
getNodes: apiGetNodes,
|
|
22
|
+
getTdInfo: apiGetTdInfo,
|
|
15
23
|
getTdPythonClassDetails: apiGetTdPythonClassDetails,
|
|
24
|
+
getTdPythonClasses: apiGetTdPythonClasses,
|
|
25
|
+
updateNode: apiUpdateNode,
|
|
16
26
|
};
|
|
17
27
|
/**
|
|
18
28
|
* Null logger implementation that discards all logs
|
|
19
29
|
*/
|
|
20
30
|
const nullLogger = {
|
|
21
|
-
|
|
22
|
-
log: () => { },
|
|
23
|
-
warn: () => { },
|
|
24
|
-
error: () => { },
|
|
31
|
+
sendLog: () => { },
|
|
25
32
|
};
|
|
26
33
|
/**
|
|
27
34
|
* Handle API error response
|
|
@@ -31,9 +38,9 @@ const nullLogger = {
|
|
|
31
38
|
function handleError(response) {
|
|
32
39
|
if (response.error) {
|
|
33
40
|
const errorMessage = response.error;
|
|
34
|
-
return {
|
|
41
|
+
return { error: new Error(errorMessage), success: false };
|
|
35
42
|
}
|
|
36
|
-
return {
|
|
43
|
+
return { error: new Error("Unknown error occurred"), success: false };
|
|
37
44
|
}
|
|
38
45
|
/**
|
|
39
46
|
* Handle API response and return a structured result
|
|
@@ -46,12 +53,12 @@ function handleApiResponse(response) {
|
|
|
46
53
|
return handleError(response);
|
|
47
54
|
}
|
|
48
55
|
if (data === null) {
|
|
49
|
-
return {
|
|
56
|
+
return { error: new Error("No data received"), success: false };
|
|
50
57
|
}
|
|
51
58
|
if (data === undefined) {
|
|
52
|
-
return {
|
|
59
|
+
return { error: new Error("No data received"), success: false };
|
|
53
60
|
}
|
|
54
|
-
return { success: true
|
|
61
|
+
return { data, success: true };
|
|
55
62
|
}
|
|
56
63
|
/**
|
|
57
64
|
* TouchDesigner client implementation with dependency injection
|
|
@@ -60,18 +67,47 @@ function handleApiResponse(response) {
|
|
|
60
67
|
export class TouchDesignerClient {
|
|
61
68
|
logger;
|
|
62
69
|
api;
|
|
70
|
+
verifiedCompatibilityError;
|
|
71
|
+
logDebug(message, context) {
|
|
72
|
+
const data = context ? { message, ...context } : { message };
|
|
73
|
+
this.logger.sendLog({
|
|
74
|
+
data,
|
|
75
|
+
level: "debug",
|
|
76
|
+
logger: "TouchDesignerClient",
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Verify compatibility with the TouchDesigner server
|
|
81
|
+
*/
|
|
82
|
+
async verifyCompatibility() {
|
|
83
|
+
if (this.verifiedCompatibilityError) {
|
|
84
|
+
throw this.verifiedCompatibilityError;
|
|
85
|
+
}
|
|
86
|
+
const result = await this.verifyVersionCompatibility();
|
|
87
|
+
if (result.success) {
|
|
88
|
+
this.verifiedCompatibilityError = null;
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
this.verifiedCompatibilityError = result.error;
|
|
92
|
+
throw result.error;
|
|
93
|
+
}
|
|
63
94
|
/**
|
|
64
95
|
* Initialize TouchDesigner client with optional dependencies
|
|
65
96
|
*/
|
|
66
97
|
constructor(params = {}) {
|
|
67
98
|
this.logger = params.logger || nullLogger;
|
|
68
99
|
this.api = params.httpClient || defaultApiClient;
|
|
100
|
+
this.verifiedCompatibilityError = null;
|
|
69
101
|
}
|
|
70
102
|
/**
|
|
71
103
|
* Execute a node method
|
|
72
104
|
*/
|
|
73
105
|
async execNodeMethod(params) {
|
|
74
|
-
this.
|
|
106
|
+
this.logDebug("Executing node method", {
|
|
107
|
+
method: params.method,
|
|
108
|
+
nodePath: params.nodePath,
|
|
109
|
+
});
|
|
110
|
+
await this.verifyCompatibility();
|
|
75
111
|
const result = await this.api.execNodeMethod(params);
|
|
76
112
|
return handleApiResponse(result);
|
|
77
113
|
}
|
|
@@ -79,7 +115,8 @@ export class TouchDesignerClient {
|
|
|
79
115
|
* Execute a script in TouchDesigner
|
|
80
116
|
*/
|
|
81
117
|
async execPythonScript(params) {
|
|
82
|
-
this.
|
|
118
|
+
this.logDebug("Executing Python script", { params });
|
|
119
|
+
await this.verifyCompatibility();
|
|
83
120
|
const result = await this.api.execPythonScript(params);
|
|
84
121
|
return handleApiResponse(result);
|
|
85
122
|
}
|
|
@@ -87,7 +124,8 @@ export class TouchDesignerClient {
|
|
|
87
124
|
* Get TouchDesigner server information
|
|
88
125
|
*/
|
|
89
126
|
async getTdInfo() {
|
|
90
|
-
this.
|
|
127
|
+
this.logDebug("Getting server info");
|
|
128
|
+
await this.verifyCompatibility();
|
|
91
129
|
const result = await this.api.getTdInfo();
|
|
92
130
|
return handleApiResponse(result);
|
|
93
131
|
}
|
|
@@ -95,7 +133,10 @@ export class TouchDesignerClient {
|
|
|
95
133
|
* Get list of nodes
|
|
96
134
|
*/
|
|
97
135
|
async getNodes(params) {
|
|
98
|
-
this.
|
|
136
|
+
this.logDebug("Getting nodes for parent", {
|
|
137
|
+
parentPath: params.parentPath,
|
|
138
|
+
});
|
|
139
|
+
await this.verifyCompatibility();
|
|
99
140
|
const result = await this.api.getNodes(params);
|
|
100
141
|
return handleApiResponse(result);
|
|
101
142
|
}
|
|
@@ -103,15 +144,34 @@ export class TouchDesignerClient {
|
|
|
103
144
|
* Get node properties
|
|
104
145
|
*/
|
|
105
146
|
async getNodeDetail(params) {
|
|
106
|
-
this.
|
|
147
|
+
this.logDebug("Getting properties for node", {
|
|
148
|
+
nodePath: params.nodePath,
|
|
149
|
+
});
|
|
150
|
+
await this.verifyCompatibility();
|
|
107
151
|
const result = await this.api.getNodeDetail(params);
|
|
108
152
|
return handleApiResponse(result);
|
|
109
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* Get node error information
|
|
156
|
+
*/
|
|
157
|
+
async getNodeErrors(params) {
|
|
158
|
+
this.logDebug("Checking node errors", {
|
|
159
|
+
nodePath: params.nodePath,
|
|
160
|
+
});
|
|
161
|
+
await this.verifyCompatibility();
|
|
162
|
+
const result = await this.api.getNodeErrors(params);
|
|
163
|
+
return handleApiResponse(result);
|
|
164
|
+
}
|
|
110
165
|
/**
|
|
111
166
|
* Create a new node
|
|
112
167
|
*/
|
|
113
168
|
async createNode(params) {
|
|
114
|
-
this.
|
|
169
|
+
this.logDebug("Creating node", {
|
|
170
|
+
nodeName: params.nodeName,
|
|
171
|
+
nodeType: params.nodeType,
|
|
172
|
+
parentPath: params.parentPath,
|
|
173
|
+
});
|
|
174
|
+
await this.verifyCompatibility();
|
|
115
175
|
const result = await this.api.createNode(params);
|
|
116
176
|
return handleApiResponse(result);
|
|
117
177
|
}
|
|
@@ -119,7 +179,8 @@ export class TouchDesignerClient {
|
|
|
119
179
|
* Update node properties
|
|
120
180
|
*/
|
|
121
181
|
async updateNode(params) {
|
|
122
|
-
this.
|
|
182
|
+
this.logDebug("Updating node", { nodePath: params.nodePath });
|
|
183
|
+
await this.verifyCompatibility();
|
|
123
184
|
const result = await this.api.updateNode(params);
|
|
124
185
|
return handleApiResponse(result);
|
|
125
186
|
}
|
|
@@ -127,7 +188,8 @@ export class TouchDesignerClient {
|
|
|
127
188
|
* Delete a node
|
|
128
189
|
*/
|
|
129
190
|
async deleteNode(params) {
|
|
130
|
-
this.
|
|
191
|
+
this.logDebug("Deleting node", { nodePath: params.nodePath });
|
|
192
|
+
await this.verifyCompatibility();
|
|
131
193
|
const result = await this.api.deleteNode(params);
|
|
132
194
|
return handleApiResponse(result);
|
|
133
195
|
}
|
|
@@ -135,7 +197,8 @@ export class TouchDesignerClient {
|
|
|
135
197
|
* Get list of available Python classes/modules in TouchDesigner
|
|
136
198
|
*/
|
|
137
199
|
async getClasses() {
|
|
138
|
-
this.
|
|
200
|
+
this.logDebug("Getting Python classes");
|
|
201
|
+
await this.verifyCompatibility();
|
|
139
202
|
const result = await this.api.getTdPythonClasses();
|
|
140
203
|
return handleApiResponse(result);
|
|
141
204
|
}
|
|
@@ -143,8 +206,46 @@ export class TouchDesignerClient {
|
|
|
143
206
|
* Get details of a specific class/module
|
|
144
207
|
*/
|
|
145
208
|
async getClassDetails(className) {
|
|
146
|
-
this.
|
|
209
|
+
this.logDebug("Getting class details", { className });
|
|
210
|
+
await this.verifyCompatibility();
|
|
147
211
|
const result = await this.api.getTdPythonClassDetails(className);
|
|
148
212
|
return handleApiResponse(result);
|
|
149
213
|
}
|
|
214
|
+
/**
|
|
215
|
+
* Retrieve Python help() documentation for modules/classes
|
|
216
|
+
*/
|
|
217
|
+
async getModuleHelp(params) {
|
|
218
|
+
this.logDebug("Getting module help", { moduleName: params.moduleName });
|
|
219
|
+
await this.verifyCompatibility();
|
|
220
|
+
const result = await this.api.getModuleHelp(params);
|
|
221
|
+
return handleApiResponse(result);
|
|
222
|
+
}
|
|
223
|
+
async verifyVersionCompatibility() {
|
|
224
|
+
const tdInfoResult = await this.api.getTdInfo();
|
|
225
|
+
if (!tdInfoResult.success) {
|
|
226
|
+
return createErrorResult(new Error(`Failed to retrieve TouchDesigner info for version check: ${tdInfoResult.error}`));
|
|
227
|
+
}
|
|
228
|
+
const apiVersion = tdInfoResult.data?.mcpApiVersion?.trim();
|
|
229
|
+
if (!apiVersion) {
|
|
230
|
+
return createErrorResult(new Error(`TouchDesigner API server did not report its version. Please reinstall the TouchDesigner components from the latest release.\n${updateGuide}`));
|
|
231
|
+
}
|
|
232
|
+
const normalizedServerVersion = this.normalizeVersion(PACKAGE_VERSION);
|
|
233
|
+
const normalizedApiVersion = this.normalizeVersion(apiVersion);
|
|
234
|
+
if (normalizedServerVersion !== normalizedApiVersion) {
|
|
235
|
+
this.logger.sendLog({
|
|
236
|
+
data: {
|
|
237
|
+
message: "MCP server and TouchDesigner API server versions are incompatible",
|
|
238
|
+
touchDesignerApiVersion: normalizedApiVersion,
|
|
239
|
+
touchDesignerServerVersion: normalizedServerVersion,
|
|
240
|
+
},
|
|
241
|
+
level: "error",
|
|
242
|
+
logger: "TouchDesignerClient",
|
|
243
|
+
});
|
|
244
|
+
return createErrorResult(new Error(`Version mismatch detected between MCP server (${normalizedServerVersion}) and TouchDesigner API server (${normalizedApiVersion}). Update both components to the same release.\n${updateGuide}`));
|
|
245
|
+
}
|
|
246
|
+
return createSuccessResult(undefined);
|
|
247
|
+
}
|
|
248
|
+
normalizeVersion(version) {
|
|
249
|
+
return version.trim().replace(/^v/i, "");
|
|
250
|
+
}
|
|
150
251
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "touchdesigner-mcp-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "MCP server for TouchDesigner",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -23,30 +23,31 @@
|
|
|
23
23
|
"touchdesigner-mcp-server": "dist/cli.js"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
26
|
+
"@modelcontextprotocol/sdk": "^1.23.0",
|
|
27
27
|
"@mozilla/readability": "^0.6.0",
|
|
28
28
|
"@types/axios": "^0.14.4",
|
|
29
29
|
"@types/ws": "^8.18.1",
|
|
30
|
-
"@types/yargs": "^17.0.
|
|
31
|
-
"axios": "^1.
|
|
30
|
+
"@types/yargs": "^17.0.35",
|
|
31
|
+
"axios": "^1.13.2",
|
|
32
32
|
"mustache": "^4.2.0",
|
|
33
|
-
"yaml": "^2.8.
|
|
34
|
-
"zod": "
|
|
33
|
+
"yaml": "^2.8.2",
|
|
34
|
+
"zod": "4.1.13"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@biomejs/biome": "2.
|
|
38
|
-
"@openapitools/openapi-generator-cli": "^2.
|
|
39
|
-
"@types/jsdom": "^
|
|
37
|
+
"@biomejs/biome": "2.3.8",
|
|
38
|
+
"@openapitools/openapi-generator-cli": "^2.25.2",
|
|
39
|
+
"@types/jsdom": "^27.0.0",
|
|
40
40
|
"@types/mustache": "^4.2.6",
|
|
41
|
-
"@types/node": "^24.
|
|
42
|
-
"@vitest/coverage-v8": "^
|
|
41
|
+
"@types/node": "^24.10.1",
|
|
42
|
+
"@vitest/coverage-v8": "^4.0.14",
|
|
43
43
|
"archiver": "^7.0.1",
|
|
44
|
-
"msw": "^2.
|
|
44
|
+
"msw": "^2.12.3",
|
|
45
45
|
"npm-run-all": "^4.1.5",
|
|
46
|
-
"orval": "^7.
|
|
46
|
+
"orval": "^7.17.0",
|
|
47
|
+
"prettier": "^3.7.3",
|
|
47
48
|
"shx": "^0.4.0",
|
|
48
|
-
"typescript": "^5.9.
|
|
49
|
-
"vitest": "^
|
|
49
|
+
"typescript": "^5.9.3",
|
|
50
|
+
"vitest": "^4.0.14"
|
|
50
51
|
},
|
|
51
52
|
"type": "module",
|
|
52
53
|
"exports": {
|
|
@@ -67,13 +68,19 @@
|
|
|
67
68
|
"lint": "run-p lint:*",
|
|
68
69
|
"lint:biome": "biome check",
|
|
69
70
|
"lint:tsc": "tsc --noEmit",
|
|
70
|
-
"
|
|
71
|
+
"lint:python": "ruff check td/",
|
|
72
|
+
"lint:yaml": "prettier --check \"**/*.{yml,yaml}\"",
|
|
73
|
+
"format": "run-p format:*",
|
|
74
|
+
"format:biome": "biome check --fix",
|
|
75
|
+
"format:python": "ruff format td/ && ruff check --fix td/",
|
|
76
|
+
"format:yaml": "prettier --write \"**/*.{yml,yaml}\"",
|
|
71
77
|
"dev": "npx @modelcontextprotocol/inspector node dist/cli.js --stdio",
|
|
72
78
|
"test": "run-p test:*",
|
|
73
79
|
"test:integration": "vitest run ./tests/integration",
|
|
74
80
|
"test:unit": "vitest run ./tests/unit",
|
|
75
81
|
"coverage": "vitest run --coverage",
|
|
76
82
|
"gen": "run-s gen:*",
|
|
83
|
+
"gen:version": "node ./scripts/syncVersions.ts",
|
|
77
84
|
"gen:webserver": "openapi-generator-cli generate -i ./src/api/index.yml -g python-flask -o ./td/modules/td_server",
|
|
78
85
|
"gen:handlers": "node td/genHandlers.js",
|
|
79
86
|
"gen:mcp": "orval --config ./orval.config.ts"
|
|
@@ -87,6 +94,6 @@
|
|
|
87
94
|
]
|
|
88
95
|
},
|
|
89
96
|
"overrides": {
|
|
90
|
-
"axios": "^1.
|
|
97
|
+
"axios": "^1.13.2"
|
|
91
98
|
}
|
|
92
99
|
}
|