touchdesigner-mcp-server 1.4.8 → 1.4.9
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/dist/features/tools/handlers/tdTools.js +23 -256
- package/dist/features/tools/metadata/touchDesignerToolMetadata.js +96 -474
- package/dist/features/tools/toolDefinitions.js +317 -0
- package/dist/gen/endpoints/TouchDesignerAPI.js +18 -18
- package/dist/gen/mcp/touchDesignerAPI.zod.js +27 -27
- package/package.json +6 -21
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { REFERENCE_COMMENT, TOOL_NAMES } from "../../core/constants.js";
|
|
2
|
+
import { CreateNodeBody, DeleteNodeQueryParams, ExecNodeMethodBody, ExecPythonScriptBody, GetModuleHelpQueryParams, GetNodeDetailQueryParams, GetNodeErrorsQueryParams, GetNodesQueryParams, GetTdPythonClassDetailsParams, UpdateNodeBody, } from "../../gen/mcp/touchDesignerAPI.zod.js";
|
|
3
|
+
import { formatClassDetails, formatClassList, formatCreateNodeResult, formatDeleteNodeResult, formatExecNodeMethodResult, formatModuleHelp, formatNodeDetails, formatNodeErrors, formatNodeList, formatScriptResult, formatTdInfo, formatUpdateNodeResult, } from "./presenter/index.js";
|
|
4
|
+
import { detailOnlyFormattingSchema, formattingOptionsSchema, } from "./types.js";
|
|
5
|
+
/**
|
|
6
|
+
* Authoring helper that infers `params` from the tool's schema while erasing to
|
|
7
|
+
* the uniform {@link ToolDefinition} shape for storage in the table.
|
|
8
|
+
*/
|
|
9
|
+
function defineTool(def) {
|
|
10
|
+
// `run` has a narrower param type (z.infer<S>) than ToolDefinition exposes
|
|
11
|
+
// (Record<string, unknown>). Function parameters are contravariant, so a
|
|
12
|
+
// direct `as ToolDefinition` is rejected; the double cast is required. Safe
|
|
13
|
+
// because params are validated by the MCP SDK before reaching run().
|
|
14
|
+
return def;
|
|
15
|
+
}
|
|
16
|
+
export const TOOL_DEFINITIONS = [
|
|
17
|
+
defineTool({
|
|
18
|
+
category: "system",
|
|
19
|
+
description: "Get server information from TouchDesigner",
|
|
20
|
+
example: `import { getTdInfo } from './servers/touchdesigner/getTdInfo';
|
|
21
|
+
|
|
22
|
+
const info = await getTdInfo();
|
|
23
|
+
console.log(\`\${info.server} \${info.version}\`);`,
|
|
24
|
+
name: TOOL_NAMES.GET_TD_INFO,
|
|
25
|
+
returns: "TouchDesigner build metadata (server, version, operating system).",
|
|
26
|
+
run: async ({ params, tdClient }) => {
|
|
27
|
+
const result = await tdClient.getTdInfo();
|
|
28
|
+
if (!result.success) {
|
|
29
|
+
throw result.error;
|
|
30
|
+
}
|
|
31
|
+
return formatTdInfo(result.data, {
|
|
32
|
+
detailLevel: params.detailLevel ?? "summary",
|
|
33
|
+
responseFormat: params.responseFormat,
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
schema: detailOnlyFormattingSchema,
|
|
37
|
+
}),
|
|
38
|
+
defineTool({
|
|
39
|
+
category: "python",
|
|
40
|
+
description: "Execute a Python script in TouchDesigner (detailLevel=minimal|summary|detailed, responseFormat=json|yaml|markdown)",
|
|
41
|
+
example: `import { executePythonScript } from './servers/touchdesigner/executePythonScript';
|
|
42
|
+
|
|
43
|
+
await executePythonScript({
|
|
44
|
+
script: "op('/project1/text1').par.text = 'Hello MCP'",
|
|
45
|
+
});`,
|
|
46
|
+
name: TOOL_NAMES.EXECUTE_PYTHON_SCRIPT,
|
|
47
|
+
notes: "Wrap long-running scripts with logging so the agent can stream intermediate checkpoints.",
|
|
48
|
+
returns: "Result payload that mirrors `result` from the executed script (if set).",
|
|
49
|
+
run: async ({ params, tdClient, logger }) => {
|
|
50
|
+
const { detailLevel, responseFormat, ...scriptParams } = params;
|
|
51
|
+
logger.sendLog({
|
|
52
|
+
data: `Executing script: ${scriptParams.script}`,
|
|
53
|
+
level: "debug",
|
|
54
|
+
});
|
|
55
|
+
const result = await tdClient.execPythonScript(scriptParams);
|
|
56
|
+
if (!result.success) {
|
|
57
|
+
throw result.error;
|
|
58
|
+
}
|
|
59
|
+
return formatScriptResult(result, scriptParams.script, {
|
|
60
|
+
detailLevel: detailLevel ?? "summary",
|
|
61
|
+
responseFormat,
|
|
62
|
+
});
|
|
63
|
+
},
|
|
64
|
+
schema: ExecPythonScriptBody.extend(detailOnlyFormattingSchema.shape),
|
|
65
|
+
}),
|
|
66
|
+
defineTool({
|
|
67
|
+
category: "nodes",
|
|
68
|
+
description: "List nodes under a path with token-optimized output (detailLevel+limit supported)",
|
|
69
|
+
errorComment: REFERENCE_COMMENT,
|
|
70
|
+
example: `import { getTdNodes } from './servers/touchdesigner/getTdNodes';
|
|
71
|
+
|
|
72
|
+
const nodes = await getTdNodes({
|
|
73
|
+
parentPath: '/project1',
|
|
74
|
+
pattern: 'geo*',
|
|
75
|
+
});
|
|
76
|
+
console.log(nodes.nodes?.map(node => node.path));`,
|
|
77
|
+
name: TOOL_NAMES.GET_TD_NODES,
|
|
78
|
+
returns: "Set of nodes (id, opType, name, path, optional properties) under parentPath.",
|
|
79
|
+
run: async ({ params, tdClient }) => {
|
|
80
|
+
const { detailLevel, limit, responseFormat, ...queryParams } = params;
|
|
81
|
+
const result = await tdClient.getNodes(queryParams);
|
|
82
|
+
if (!result.success) {
|
|
83
|
+
throw result.error;
|
|
84
|
+
}
|
|
85
|
+
const fallbackMode = queryParams.includeProperties
|
|
86
|
+
? "detailed"
|
|
87
|
+
: "summary";
|
|
88
|
+
return formatNodeList(result.data, {
|
|
89
|
+
detailLevel: detailLevel ?? fallbackMode,
|
|
90
|
+
limit,
|
|
91
|
+
responseFormat,
|
|
92
|
+
});
|
|
93
|
+
},
|
|
94
|
+
schema: GetNodesQueryParams.extend(formattingOptionsSchema.shape),
|
|
95
|
+
}),
|
|
96
|
+
defineTool({
|
|
97
|
+
category: "nodes",
|
|
98
|
+
description: "Get node parameters with concise/detailed formatting (detailLevel+limit supported)",
|
|
99
|
+
errorComment: REFERENCE_COMMENT,
|
|
100
|
+
example: `import { getTdNodeParameters } from './servers/touchdesigner/getTdNodeParameters';
|
|
101
|
+
|
|
102
|
+
const node = await getTdNodeParameters({ nodePath: '/project1/text1' });
|
|
103
|
+
console.log(node.properties?.Text);`,
|
|
104
|
+
name: TOOL_NAMES.GET_TD_NODE_PARAMETERS,
|
|
105
|
+
returns: "Full node record with parameters, paths, and metadata.",
|
|
106
|
+
run: async ({ params, tdClient }) => {
|
|
107
|
+
const { detailLevel, limit, responseFormat, ...queryParams } = params;
|
|
108
|
+
const result = await tdClient.getNodeDetail(queryParams);
|
|
109
|
+
if (!result.success) {
|
|
110
|
+
throw result.error;
|
|
111
|
+
}
|
|
112
|
+
return formatNodeDetails(result.data, {
|
|
113
|
+
detailLevel: detailLevel ?? "summary",
|
|
114
|
+
limit,
|
|
115
|
+
responseFormat,
|
|
116
|
+
});
|
|
117
|
+
},
|
|
118
|
+
schema: GetNodeDetailQueryParams.extend(formattingOptionsSchema.shape),
|
|
119
|
+
}),
|
|
120
|
+
defineTool({
|
|
121
|
+
category: "nodes",
|
|
122
|
+
description: "Check node and descendant errors reported by TouchDesigner",
|
|
123
|
+
errorComment: REFERENCE_COMMENT,
|
|
124
|
+
example: `import { getTdNodeErrors } from './servers/touchdesigner/getTdNodeErrors';
|
|
125
|
+
|
|
126
|
+
const report = await getTdNodeErrors({
|
|
127
|
+
nodePath: '/project1/text1',
|
|
128
|
+
});
|
|
129
|
+
if (report.hasErrors) {
|
|
130
|
+
console.log(report.errors?.map(err => err.message));
|
|
131
|
+
}`,
|
|
132
|
+
name: TOOL_NAMES.GET_TD_NODE_ERRORS,
|
|
133
|
+
returns: "Error report outlining offending nodes, messages, and counts.",
|
|
134
|
+
run: async ({ params, tdClient }) => {
|
|
135
|
+
const { detailLevel, limit, responseFormat, ...queryParams } = params;
|
|
136
|
+
const result = await tdClient.getNodeErrors(queryParams);
|
|
137
|
+
if (!result.success) {
|
|
138
|
+
throw result.error;
|
|
139
|
+
}
|
|
140
|
+
return formatNodeErrors(result.data, {
|
|
141
|
+
detailLevel: detailLevel ?? "summary",
|
|
142
|
+
limit,
|
|
143
|
+
responseFormat,
|
|
144
|
+
});
|
|
145
|
+
},
|
|
146
|
+
schema: GetNodeErrorsQueryParams.extend(formattingOptionsSchema.shape),
|
|
147
|
+
}),
|
|
148
|
+
defineTool({
|
|
149
|
+
category: "nodes",
|
|
150
|
+
description: "Create a new node in TouchDesigner",
|
|
151
|
+
errorComment: REFERENCE_COMMENT,
|
|
152
|
+
example: `import { createTdNode } from './servers/touchdesigner/createTdNode';
|
|
153
|
+
|
|
154
|
+
const created = await createTdNode({
|
|
155
|
+
parentPath: '/project1',
|
|
156
|
+
nodeType: 'textTOP',
|
|
157
|
+
nodeName: 'title',
|
|
158
|
+
});
|
|
159
|
+
console.log(created.result?.path);`,
|
|
160
|
+
name: TOOL_NAMES.CREATE_TD_NODE,
|
|
161
|
+
returns: "Created node metadata including resolved path and properties.",
|
|
162
|
+
run: async ({ params, tdClient }) => {
|
|
163
|
+
const { detailLevel, responseFormat, ...createParams } = params;
|
|
164
|
+
const result = await tdClient.createNode(createParams);
|
|
165
|
+
if (!result.success) {
|
|
166
|
+
throw result.error;
|
|
167
|
+
}
|
|
168
|
+
return formatCreateNodeResult(result.data, {
|
|
169
|
+
detailLevel: detailLevel ?? "summary",
|
|
170
|
+
responseFormat,
|
|
171
|
+
});
|
|
172
|
+
},
|
|
173
|
+
schema: CreateNodeBody.extend(detailOnlyFormattingSchema.shape),
|
|
174
|
+
}),
|
|
175
|
+
defineTool({
|
|
176
|
+
category: "nodes",
|
|
177
|
+
description: "Update parameters of a specific node in TouchDesigner",
|
|
178
|
+
errorComment: REFERENCE_COMMENT,
|
|
179
|
+
example: `import { updateTdNodeParameters } from './servers/touchdesigner/updateTdNodeParameters';
|
|
180
|
+
|
|
181
|
+
await updateTdNodeParameters({
|
|
182
|
+
nodePath: '/project1/text1',
|
|
183
|
+
properties: { text: 'Hello TouchDesigner' },
|
|
184
|
+
});`,
|
|
185
|
+
name: TOOL_NAMES.UPDATE_TD_NODE_PARAMETERS,
|
|
186
|
+
returns: "Lists of updated vs failed parameters so the agent can retry selectively.",
|
|
187
|
+
run: async ({ params, tdClient }) => {
|
|
188
|
+
const { detailLevel, responseFormat, ...updateParams } = params;
|
|
189
|
+
const result = await tdClient.updateNode(updateParams);
|
|
190
|
+
if (!result.success) {
|
|
191
|
+
throw result.error;
|
|
192
|
+
}
|
|
193
|
+
return formatUpdateNodeResult(result.data, {
|
|
194
|
+
detailLevel: detailLevel ?? "summary",
|
|
195
|
+
responseFormat,
|
|
196
|
+
});
|
|
197
|
+
},
|
|
198
|
+
schema: UpdateNodeBody.extend(detailOnlyFormattingSchema.shape),
|
|
199
|
+
}),
|
|
200
|
+
defineTool({
|
|
201
|
+
category: "nodes",
|
|
202
|
+
description: "Delete an existing node in TouchDesigner",
|
|
203
|
+
errorComment: REFERENCE_COMMENT,
|
|
204
|
+
example: `import { deleteTdNode } from './servers/touchdesigner/deleteTdNode';
|
|
205
|
+
|
|
206
|
+
const result = await deleteTdNode({ nodePath: '/project1/tmp1' });
|
|
207
|
+
console.log(result.deleted);`,
|
|
208
|
+
name: TOOL_NAMES.DELETE_TD_NODE,
|
|
209
|
+
returns: "Deletion status plus previous node metadata when available.",
|
|
210
|
+
run: async ({ params, tdClient }) => {
|
|
211
|
+
const { detailLevel, responseFormat, ...deleteParams } = params;
|
|
212
|
+
const result = await tdClient.deleteNode(deleteParams);
|
|
213
|
+
if (!result.success) {
|
|
214
|
+
throw result.error;
|
|
215
|
+
}
|
|
216
|
+
return formatDeleteNodeResult(result.data, {
|
|
217
|
+
detailLevel: detailLevel ?? "summary",
|
|
218
|
+
responseFormat,
|
|
219
|
+
});
|
|
220
|
+
},
|
|
221
|
+
schema: DeleteNodeQueryParams.extend(detailOnlyFormattingSchema.shape),
|
|
222
|
+
}),
|
|
223
|
+
defineTool({
|
|
224
|
+
category: "nodes",
|
|
225
|
+
description: "Execute a method on a specific node in TouchDesigner",
|
|
226
|
+
errorComment: REFERENCE_COMMENT,
|
|
227
|
+
example: `import { execNodeMethod } from './servers/touchdesigner/execNodeMethod';
|
|
228
|
+
|
|
229
|
+
const renderStatus = await execNodeMethod({
|
|
230
|
+
nodePath: '/project1/render1',
|
|
231
|
+
method: 'par',
|
|
232
|
+
kwargs: { enable: true },
|
|
233
|
+
});
|
|
234
|
+
console.log(renderStatus.result);`,
|
|
235
|
+
name: TOOL_NAMES.EXECUTE_NODE_METHOD,
|
|
236
|
+
returns: "Raw method return payload including any serializable values.",
|
|
237
|
+
run: async ({ params, tdClient }) => {
|
|
238
|
+
const { detailLevel, responseFormat, ...execParams } = params;
|
|
239
|
+
const { nodePath, method, args, kwargs } = execParams;
|
|
240
|
+
const result = await tdClient.execNodeMethod(execParams);
|
|
241
|
+
if (!result.success) {
|
|
242
|
+
throw result.error;
|
|
243
|
+
}
|
|
244
|
+
return formatExecNodeMethodResult(result.data, { args, kwargs, method, nodePath }, { detailLevel: detailLevel ?? "summary", responseFormat });
|
|
245
|
+
},
|
|
246
|
+
schema: ExecNodeMethodBody.extend(detailOnlyFormattingSchema.shape),
|
|
247
|
+
}),
|
|
248
|
+
defineTool({
|
|
249
|
+
category: "classes",
|
|
250
|
+
description: "List TouchDesigner Python classes/modules (detailLevel+limit supported)",
|
|
251
|
+
errorComment: REFERENCE_COMMENT,
|
|
252
|
+
example: `import { getTdClasses } from './servers/touchdesigner/getTdClasses';
|
|
253
|
+
|
|
254
|
+
const classes = await getTdClasses({ limit: 20 });
|
|
255
|
+
console.log(classes.classes?.map(cls => cls.name));`,
|
|
256
|
+
name: TOOL_NAMES.GET_TD_CLASSES,
|
|
257
|
+
returns: "Python class catalogue with names, types, and optional summaries.",
|
|
258
|
+
run: async ({ params, tdClient }) => {
|
|
259
|
+
const result = await tdClient.getClasses();
|
|
260
|
+
if (!result.success) {
|
|
261
|
+
throw result.error;
|
|
262
|
+
}
|
|
263
|
+
return formatClassList(result.data, {
|
|
264
|
+
detailLevel: params.detailLevel ?? "summary",
|
|
265
|
+
limit: params.limit ?? 50,
|
|
266
|
+
responseFormat: params.responseFormat,
|
|
267
|
+
});
|
|
268
|
+
},
|
|
269
|
+
schema: formattingOptionsSchema,
|
|
270
|
+
}),
|
|
271
|
+
defineTool({
|
|
272
|
+
category: "classes",
|
|
273
|
+
description: "Get information about a TouchDesigner class/module (detailLevel+limit supported)",
|
|
274
|
+
errorComment: REFERENCE_COMMENT,
|
|
275
|
+
example: `import { getTdClassDetails } from './servers/touchdesigner/getTdClassDetails';
|
|
276
|
+
|
|
277
|
+
const textTop = await getTdClassDetails({ className: 'textTOP' });
|
|
278
|
+
console.log(textTop.methods?.length);`,
|
|
279
|
+
name: TOOL_NAMES.GET_TD_CLASS_DETAILS,
|
|
280
|
+
returns: "Deep description of a Python class including methods and properties.",
|
|
281
|
+
run: async ({ params, tdClient }) => {
|
|
282
|
+
const { className, detailLevel, limit, responseFormat } = params;
|
|
283
|
+
const result = await tdClient.getClassDetails(className);
|
|
284
|
+
if (!result.success) {
|
|
285
|
+
throw result.error;
|
|
286
|
+
}
|
|
287
|
+
return formatClassDetails(result.data, {
|
|
288
|
+
detailLevel: detailLevel ?? "summary",
|
|
289
|
+
limit: limit ?? 30,
|
|
290
|
+
responseFormat,
|
|
291
|
+
});
|
|
292
|
+
},
|
|
293
|
+
schema: GetTdPythonClassDetailsParams.extend(formattingOptionsSchema.shape),
|
|
294
|
+
}),
|
|
295
|
+
defineTool({
|
|
296
|
+
category: "classes",
|
|
297
|
+
description: "Retrieve Python help() text for a TouchDesigner module or class",
|
|
298
|
+
example: `import { getTdModuleHelp } from './servers/touchdesigner/getTdModuleHelp';
|
|
299
|
+
|
|
300
|
+
const docs = await getTdModuleHelp({ moduleName: 'noiseCHOP' });
|
|
301
|
+
console.log(docs.helpText?.slice(0, 200));`,
|
|
302
|
+
name: TOOL_NAMES.GET_TD_MODULE_HELP,
|
|
303
|
+
returns: "Captured Python help() output with formatter context.",
|
|
304
|
+
run: async ({ params, tdClient }) => {
|
|
305
|
+
const { detailLevel, moduleName, responseFormat } = params;
|
|
306
|
+
const result = await tdClient.getModuleHelp({ moduleName });
|
|
307
|
+
if (!result.success) {
|
|
308
|
+
throw result.error;
|
|
309
|
+
}
|
|
310
|
+
return formatModuleHelp(result.data, {
|
|
311
|
+
detailLevel: detailLevel ?? "summary",
|
|
312
|
+
responseFormat,
|
|
313
|
+
});
|
|
314
|
+
},
|
|
315
|
+
schema: GetModuleHelpQueryParams.extend(detailOnlyFormattingSchema.shape),
|
|
316
|
+
}),
|
|
317
|
+
];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Generated by orval v8.
|
|
2
|
+
* Generated by orval v8.17.0 🍺
|
|
3
3
|
* Do not edit manually.
|
|
4
4
|
* TouchDesigner API
|
|
5
5
|
* OpenAPI schema for generating TouchDesigner API client code
|
|
@@ -28,16 +28,8 @@ export const TdPythonClassInfoType = {
|
|
|
28
28
|
object: 'object',
|
|
29
29
|
};
|
|
30
30
|
/**
|
|
31
|
-
* @summary
|
|
31
|
+
* @summary Get nodes in the path
|
|
32
32
|
*/
|
|
33
|
-
export const deleteNode = (params, options) => {
|
|
34
|
-
return customInstance({ url: `${process.env.TD_WEB_SERVER_HOST}:${process.env.TD_WEB_SERVER_PORT}/api/nodes`, method: 'DELETE',
|
|
35
|
-
params
|
|
36
|
-
}, options);
|
|
37
|
-
};
|
|
38
|
-
/**
|
|
39
|
-
* @summary Get nodes in the path
|
|
40
|
-
*/
|
|
41
33
|
export const getNodes = (params, options) => {
|
|
42
34
|
return customInstance({ url: `${process.env.TD_WEB_SERVER_HOST}:${process.env.TD_WEB_SERVER_PORT}/api/nodes`, method: 'GET',
|
|
43
35
|
params
|
|
@@ -46,10 +38,18 @@ export const getNodes = (params, options) => {
|
|
|
46
38
|
/**
|
|
47
39
|
* @summary Create a new node
|
|
48
40
|
*/
|
|
49
|
-
export const createNode = (
|
|
41
|
+
export const createNode = (createNodeBody, options) => {
|
|
50
42
|
return customInstance({ url: `${process.env.TD_WEB_SERVER_HOST}:${process.env.TD_WEB_SERVER_PORT}/api/nodes`, method: 'POST',
|
|
51
43
|
headers: { 'Content-Type': 'application/json', },
|
|
52
|
-
data:
|
|
44
|
+
data: createNodeBody
|
|
45
|
+
}, options);
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* @summary Delete an existing node
|
|
49
|
+
*/
|
|
50
|
+
export const deleteNode = (params, options) => {
|
|
51
|
+
return customInstance({ url: `${process.env.TD_WEB_SERVER_HOST}:${process.env.TD_WEB_SERVER_PORT}/api/nodes`, method: 'DELETE',
|
|
52
|
+
params
|
|
53
53
|
}, options);
|
|
54
54
|
};
|
|
55
55
|
/**
|
|
@@ -64,10 +64,10 @@ export const getNodeDetail = (params, options) => {
|
|
|
64
64
|
/**
|
|
65
65
|
* @summary Update node properties
|
|
66
66
|
*/
|
|
67
|
-
export const updateNode = (
|
|
67
|
+
export const updateNode = (updateNodeBody, options) => {
|
|
68
68
|
return customInstance({ url: `${process.env.TD_WEB_SERVER_HOST}:${process.env.TD_WEB_SERVER_PORT}/api/nodes/detail`, method: 'PATCH',
|
|
69
69
|
headers: { 'Content-Type': 'application/json', },
|
|
70
|
-
data:
|
|
70
|
+
data: updateNodeBody
|
|
71
71
|
}, options);
|
|
72
72
|
};
|
|
73
73
|
/**
|
|
@@ -110,10 +110,10 @@ export const getModuleHelp = (params, options) => {
|
|
|
110
110
|
* `parent_comp = op('/project1')` and `parent_comp.create('textTOP', 'myText')`.
|
|
111
111
|
* @summary Call a method of the specified node
|
|
112
112
|
*/
|
|
113
|
-
export const execNodeMethod = (
|
|
113
|
+
export const execNodeMethod = (execNodeMethodBody, options) => {
|
|
114
114
|
return customInstance({ url: `${process.env.TD_WEB_SERVER_HOST}:${process.env.TD_WEB_SERVER_PORT}/api/td/nodes/exec`, method: 'POST',
|
|
115
115
|
headers: { 'Content-Type': 'application/json', },
|
|
116
|
-
data:
|
|
116
|
+
data: execNodeMethodBody
|
|
117
117
|
}, options);
|
|
118
118
|
};
|
|
119
119
|
/**
|
|
@@ -123,10 +123,10 @@ export const execNodeMethod = (execNodeMethodRequest, options) => {
|
|
|
123
123
|
* This endpoint allows you to interact with TouchDesigner nodes programmatically.
|
|
124
124
|
* @summary Execute python code on the server
|
|
125
125
|
*/
|
|
126
|
-
export const execPythonScript = (
|
|
126
|
+
export const execPythonScript = (execPythonScriptBody, options) => {
|
|
127
127
|
return customInstance({ url: `${process.env.TD_WEB_SERVER_HOST}:${process.env.TD_WEB_SERVER_PORT}/api/td/server/exec`, method: 'POST',
|
|
128
128
|
headers: { 'Content-Type': 'application/json', },
|
|
129
|
-
data:
|
|
129
|
+
data: execPythonScriptBody
|
|
130
130
|
}, options);
|
|
131
131
|
};
|
|
132
132
|
/**
|
|
@@ -1,31 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Generated by orval v8.
|
|
2
|
+
* Generated by orval v8.17.0 🍺
|
|
3
3
|
* Do not edit manually.
|
|
4
4
|
* TouchDesigner API
|
|
5
5
|
* OpenAPI schema for generating TouchDesigner API client code
|
|
6
6
|
* OpenAPI spec version: 1.4.3
|
|
7
7
|
*/
|
|
8
8
|
import * as zod from 'zod';
|
|
9
|
-
/**
|
|
10
|
-
* @summary Delete an existing node
|
|
11
|
-
*/
|
|
12
|
-
export const DeleteNodeQueryParams = zod.object({
|
|
13
|
-
"nodePath": zod.string().describe('Path to the node to delete. e.g., \"\/project1\/geo1\"')
|
|
14
|
-
});
|
|
15
|
-
export const DeleteNodeResponse = zod.object({
|
|
16
|
-
"success": zod.boolean().describe('Whether the operation was successful'),
|
|
17
|
-
"data": zod.object({
|
|
18
|
-
"deleted": zod.boolean().optional().describe('Whether the node was successfully deleted'),
|
|
19
|
-
"node": zod.object({
|
|
20
|
-
"id": zod.number(),
|
|
21
|
-
"opType": zod.string(),
|
|
22
|
-
"name": zod.string(),
|
|
23
|
-
"path": zod.string(),
|
|
24
|
-
"properties": zod.record(zod.string(), zod.unknown())
|
|
25
|
-
}).optional().describe('Information about a TouchDesigner node')
|
|
26
|
-
}).nullable(),
|
|
27
|
-
"error": zod.string().nullable().describe('Error message if the operation was not successful')
|
|
28
|
-
});
|
|
29
9
|
/**
|
|
30
10
|
* @summary Get nodes in the path
|
|
31
11
|
*/
|
|
@@ -66,7 +46,27 @@ export const CreateNodeResponse = zod.object({
|
|
|
66
46
|
"name": zod.string(),
|
|
67
47
|
"path": zod.string(),
|
|
68
48
|
"properties": zod.record(zod.string(), zod.unknown())
|
|
69
|
-
}).optional().describe('
|
|
49
|
+
}).optional().describe('Result of the execution')
|
|
50
|
+
}).nullable(),
|
|
51
|
+
"error": zod.string().nullable().describe('Error message if the operation was not successful')
|
|
52
|
+
});
|
|
53
|
+
/**
|
|
54
|
+
* @summary Delete an existing node
|
|
55
|
+
*/
|
|
56
|
+
export const DeleteNodeQueryParams = zod.object({
|
|
57
|
+
"nodePath": zod.string().describe('Path to the node to delete. e.g., \"\/project1\/geo1\"')
|
|
58
|
+
});
|
|
59
|
+
export const DeleteNodeResponse = zod.object({
|
|
60
|
+
"success": zod.boolean().describe('Whether the operation was successful'),
|
|
61
|
+
"data": zod.object({
|
|
62
|
+
"deleted": zod.boolean().optional().describe('Whether the node was successfully deleted'),
|
|
63
|
+
"node": zod.object({
|
|
64
|
+
"id": zod.number(),
|
|
65
|
+
"opType": zod.string(),
|
|
66
|
+
"name": zod.string(),
|
|
67
|
+
"path": zod.string(),
|
|
68
|
+
"properties": zod.record(zod.string(), zod.unknown())
|
|
69
|
+
}).optional().describe('Information about the deleted node')
|
|
70
70
|
}).nullable(),
|
|
71
71
|
"error": zod.string().nullable().describe('Error message if the operation was not successful')
|
|
72
72
|
});
|
|
@@ -85,7 +85,7 @@ export const GetNodeDetailResponse = zod.object({
|
|
|
85
85
|
"name": zod.string(),
|
|
86
86
|
"path": zod.string(),
|
|
87
87
|
"properties": zod.record(zod.string(), zod.unknown())
|
|
88
|
-
}).describe('Information about a TouchDesigner node'),
|
|
88
|
+
}).nullable().describe('Information about a TouchDesigner node'),
|
|
89
89
|
"error": zod.string().nullable().describe('Error message if the operation was not successful')
|
|
90
90
|
});
|
|
91
91
|
/**
|
|
@@ -170,7 +170,7 @@ export const GetTdPythonClassDetailsResponse = zod.object({
|
|
|
170
170
|
"type": zod.string().describe('Type of the property'),
|
|
171
171
|
"value": zod.looseObject({}).nullish().describe('Current value of the property (if serializable)')
|
|
172
172
|
}).describe('Information about a Python property')).describe('List of properties available in the class or module')
|
|
173
|
-
}).describe('Detailed information about a Python class or module'),
|
|
173
|
+
}).nullable().describe('Detailed information about a Python class or module'),
|
|
174
174
|
"error": zod.string().nullable().describe('Error message if the operation was not successful')
|
|
175
175
|
});
|
|
176
176
|
/**
|
|
@@ -185,7 +185,7 @@ export const GetModuleHelpResponse = zod.object({
|
|
|
185
185
|
"data": zod.object({
|
|
186
186
|
"moduleName": zod.string().describe('Normalized module\/class name the help text was generated for'),
|
|
187
187
|
"helpText": zod.string().describe('Captured output from Python\'s help() function')
|
|
188
|
-
}).describe('Raw Python help() output for a TouchDesigner module or class'),
|
|
188
|
+
}).nullable().describe('Raw Python help() output for a TouchDesigner module or class'),
|
|
189
189
|
"error": zod.string().nullable().describe('Error message if the operation was not successful')
|
|
190
190
|
});
|
|
191
191
|
/**
|
|
@@ -203,7 +203,7 @@ export const ExecNodeMethodBody = zod.object({
|
|
|
203
203
|
export const ExecNodeMethodResponse = zod.object({
|
|
204
204
|
"success": zod.boolean().describe('Whether the operation was successful'),
|
|
205
205
|
"data": zod.object({
|
|
206
|
-
"result": zod.
|
|
206
|
+
"result": zod.unknown().describe('Result of the method call. Can be any type (equivalent to unknown in TypeScript).')
|
|
207
207
|
}).nullable(),
|
|
208
208
|
"error": zod.string().nullable().describe('Error message if the operation was not successful')
|
|
209
209
|
});
|
|
@@ -221,7 +221,7 @@ export const ExecPythonScriptResponse = zod.object({
|
|
|
221
221
|
"success": zod.boolean().describe('Whether the operation was successful'),
|
|
222
222
|
"data": zod.object({
|
|
223
223
|
"result": zod.object({
|
|
224
|
-
"value": zod.
|
|
224
|
+
"value": zod.unknown().optional().describe('Return value of the executed script, can be any serializable value')
|
|
225
225
|
}).describe('Result of the executed script')
|
|
226
226
|
}).nullable(),
|
|
227
227
|
"error": zod.string().nullable().describe('Error message if the operation was not successful')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "touchdesigner-mcp-server",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.9",
|
|
4
4
|
"description": "MCP server for TouchDesigner",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -27,9 +27,6 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@modelcontextprotocol/sdk": "1.29.0",
|
|
30
|
-
"@mozilla/readability": "0.6.0",
|
|
31
|
-
"@types/ws": "8.18.1",
|
|
32
|
-
"@types/yargs": "17.0.35",
|
|
33
30
|
"axios": "1.17.0",
|
|
34
31
|
"express": "5.2.1",
|
|
35
32
|
"mustache": "4.2.0",
|
|
@@ -38,17 +35,15 @@
|
|
|
38
35
|
"zod": "4.4.3"
|
|
39
36
|
},
|
|
40
37
|
"devDependencies": {
|
|
41
|
-
"@biomejs/biome": "2.
|
|
42
|
-
"@
|
|
38
|
+
"@biomejs/biome": "2.5.0",
|
|
39
|
+
"@redocly/cli": "2.32.2",
|
|
43
40
|
"@types/express": "5.0.6",
|
|
44
|
-
"@types/jsdom": "28.0.3",
|
|
45
41
|
"@types/mustache": "4.2.6",
|
|
46
42
|
"@types/node": "25.9.3",
|
|
47
43
|
"@types/semver": "7.7.1",
|
|
48
44
|
"@vitest/coverage-v8": "4.1.8",
|
|
49
|
-
"msw": "2.14.6",
|
|
50
45
|
"npm-run-all": "4.1.5",
|
|
51
|
-
"orval": "8.
|
|
46
|
+
"orval": "8.17.0",
|
|
52
47
|
"prettier": "3.8.4",
|
|
53
48
|
"shx": "0.4.0",
|
|
54
49
|
"typescript": "6.0.3",
|
|
@@ -89,21 +84,11 @@
|
|
|
89
84
|
"version:api": "node ./scripts/syncApiServerVersions.ts",
|
|
90
85
|
"version:mcp": "node ./scripts/syncMcpServerVersions.ts",
|
|
91
86
|
"gen": "run-s gen:*",
|
|
92
|
-
"gen:
|
|
87
|
+
"gen:openapi": "redocly bundle ./src/api/index.yml -o ./td/modules/td_server/openapi_server/openapi/openapi.yaml",
|
|
93
88
|
"gen:handlers": "node td/genHandlers.js",
|
|
94
89
|
"gen:mcp": "orval --config ./orval.config.ts"
|
|
95
90
|
},
|
|
96
91
|
"files": [
|
|
97
92
|
"dist/**/*"
|
|
98
|
-
]
|
|
99
|
-
"overrides": {
|
|
100
|
-
"concurrently": {
|
|
101
|
-
"shell-quote": "1.8.4"
|
|
102
|
-
}
|
|
103
|
-
},
|
|
104
|
-
"msw": {
|
|
105
|
-
"workerDirectory": [
|
|
106
|
-
"public"
|
|
107
|
-
]
|
|
108
|
-
}
|
|
93
|
+
]
|
|
109
94
|
}
|