touchdesigner-mcp-server 1.4.7 → 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 +23 -25
- package/dist/gen/mcp/touchDesignerAPI.zod.js +32 -34
- package/package.json +23 -34
|
@@ -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
|
/**
|
|
@@ -106,29 +106,27 @@ export const getModuleHelp = (params, options) => {
|
|
|
106
106
|
};
|
|
107
107
|
/**
|
|
108
108
|
* Call a method on the node at the specified path (e.g., /project1).
|
|
109
|
-
This allows operations equivalent to TouchDesigner's Python API such as
|
|
110
|
-
`parent_comp = op('/project1')` and `parent_comp.create('textTOP', 'myText')`.
|
|
111
|
-
|
|
109
|
+
* This allows operations equivalent to TouchDesigner's Python API such as
|
|
110
|
+
* `parent_comp = op('/project1')` and `parent_comp.create('textTOP', 'myText')`.
|
|
112
111
|
* @summary Call a method of the specified node
|
|
113
112
|
*/
|
|
114
|
-
export const execNodeMethod = (
|
|
113
|
+
export const execNodeMethod = (execNodeMethodBody, options) => {
|
|
115
114
|
return customInstance({ url: `${process.env.TD_WEB_SERVER_HOST}:${process.env.TD_WEB_SERVER_PORT}/api/td/nodes/exec`, method: 'POST',
|
|
116
115
|
headers: { 'Content-Type': 'application/json', },
|
|
117
|
-
data:
|
|
116
|
+
data: execNodeMethodBody
|
|
118
117
|
}, options);
|
|
119
118
|
};
|
|
120
119
|
/**
|
|
121
120
|
* Execute a Python script directly in TouchDesigner.
|
|
122
|
-
Multiline scripts and scripts containing comments are supported.
|
|
123
|
-
The script can optionally set a `result` variable to explicitly return a value.
|
|
124
|
-
This endpoint allows you to interact with TouchDesigner nodes programmatically.
|
|
125
|
-
|
|
121
|
+
* Multiline scripts and scripts containing comments are supported.
|
|
122
|
+
* The script can optionally set a `result` variable to explicitly return a value.
|
|
123
|
+
* This endpoint allows you to interact with TouchDesigner nodes programmatically.
|
|
126
124
|
* @summary Execute python code on the server
|
|
127
125
|
*/
|
|
128
|
-
export const execPythonScript = (
|
|
126
|
+
export const execPythonScript = (execPythonScriptBody, options) => {
|
|
129
127
|
return customInstance({ url: `${process.env.TD_WEB_SERVER_HOST}:${process.env.TD_WEB_SERVER_PORT}/api/td/server/exec`, method: 'POST',
|
|
130
128
|
headers: { 'Content-Type': 'application/json', },
|
|
131
|
-
data:
|
|
129
|
+
data: execPythonScriptBody
|
|
132
130
|
}, options);
|
|
133
131
|
};
|
|
134
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,14 +185,13 @@ 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
|
/**
|
|
192
192
|
* Call a method on the node at the specified path (e.g., /project1).
|
|
193
|
-
This allows operations equivalent to TouchDesigner's Python API such as
|
|
194
|
-
`parent_comp = op('/project1')` and `parent_comp.create('textTOP', 'myText')`.
|
|
195
|
-
|
|
193
|
+
* This allows operations equivalent to TouchDesigner's Python API such as
|
|
194
|
+
* `parent_comp = op('/project1')` and `parent_comp.create('textTOP', 'myText')`.
|
|
196
195
|
* @summary Call a method of the specified node
|
|
197
196
|
*/
|
|
198
197
|
export const ExecNodeMethodBody = zod.object({
|
|
@@ -204,16 +203,15 @@ export const ExecNodeMethodBody = zod.object({
|
|
|
204
203
|
export const ExecNodeMethodResponse = zod.object({
|
|
205
204
|
"success": zod.boolean().describe('Whether the operation was successful'),
|
|
206
205
|
"data": zod.object({
|
|
207
|
-
"result": zod.
|
|
206
|
+
"result": zod.unknown().describe('Result of the method call. Can be any type (equivalent to unknown in TypeScript).')
|
|
208
207
|
}).nullable(),
|
|
209
208
|
"error": zod.string().nullable().describe('Error message if the operation was not successful')
|
|
210
209
|
});
|
|
211
210
|
/**
|
|
212
211
|
* Execute a Python script directly in TouchDesigner.
|
|
213
|
-
Multiline scripts and scripts containing comments are supported.
|
|
214
|
-
The script can optionally set a `result` variable to explicitly return a value.
|
|
215
|
-
This endpoint allows you to interact with TouchDesigner nodes programmatically.
|
|
216
|
-
|
|
212
|
+
* Multiline scripts and scripts containing comments are supported.
|
|
213
|
+
* The script can optionally set a `result` variable to explicitly return a value.
|
|
214
|
+
* This endpoint allows you to interact with TouchDesigner nodes programmatically.
|
|
217
215
|
* @summary Execute python code on the server
|
|
218
216
|
*/
|
|
219
217
|
export const ExecPythonScriptBody = zod.object({
|
|
@@ -223,7 +221,7 @@ export const ExecPythonScriptResponse = zod.object({
|
|
|
223
221
|
"success": zod.boolean().describe('Whether the operation was successful'),
|
|
224
222
|
"data": zod.object({
|
|
225
223
|
"result": zod.object({
|
|
226
|
-
"value": zod.
|
|
224
|
+
"value": zod.unknown().optional().describe('Return value of the executed script, can be any serializable value')
|
|
227
225
|
}).describe('Result of the executed script')
|
|
228
226
|
}).nullable(),
|
|
229
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",
|
|
@@ -26,34 +26,28 @@
|
|
|
26
26
|
"touchdesigner-mcp-server": "dist/cli.js"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@modelcontextprotocol/sdk": "
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"semver": "^7.7.4",
|
|
37
|
-
"yaml": "^2.8.3",
|
|
38
|
-
"zod": "4.3.6"
|
|
29
|
+
"@modelcontextprotocol/sdk": "1.29.0",
|
|
30
|
+
"axios": "1.17.0",
|
|
31
|
+
"express": "5.2.1",
|
|
32
|
+
"mustache": "4.2.0",
|
|
33
|
+
"semver": "7.8.4",
|
|
34
|
+
"yaml": "2.9.0",
|
|
35
|
+
"zod": "4.4.3"
|
|
39
36
|
},
|
|
40
37
|
"devDependencies": {
|
|
41
|
-
"@biomejs/biome": "2.
|
|
42
|
-
"@
|
|
43
|
-
"@types/express": "
|
|
44
|
-
"@types/
|
|
45
|
-
"@types/
|
|
46
|
-
"@types/
|
|
47
|
-
"@
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"shx": "^0.4.0",
|
|
55
|
-
"typescript": "^6.0.2",
|
|
56
|
-
"vitest": "^4.1.4"
|
|
38
|
+
"@biomejs/biome": "2.5.0",
|
|
39
|
+
"@redocly/cli": "2.32.2",
|
|
40
|
+
"@types/express": "5.0.6",
|
|
41
|
+
"@types/mustache": "4.2.6",
|
|
42
|
+
"@types/node": "25.9.3",
|
|
43
|
+
"@types/semver": "7.7.1",
|
|
44
|
+
"@vitest/coverage-v8": "4.1.8",
|
|
45
|
+
"npm-run-all": "4.1.5",
|
|
46
|
+
"orval": "8.17.0",
|
|
47
|
+
"prettier": "3.8.4",
|
|
48
|
+
"shx": "0.4.0",
|
|
49
|
+
"typescript": "6.0.3",
|
|
50
|
+
"vitest": "4.1.8"
|
|
57
51
|
},
|
|
58
52
|
"type": "module",
|
|
59
53
|
"exports": {
|
|
@@ -90,16 +84,11 @@
|
|
|
90
84
|
"version:api": "node ./scripts/syncApiServerVersions.ts",
|
|
91
85
|
"version:mcp": "node ./scripts/syncMcpServerVersions.ts",
|
|
92
86
|
"gen": "run-s gen:*",
|
|
93
|
-
"gen:
|
|
87
|
+
"gen:openapi": "redocly bundle ./src/api/index.yml -o ./td/modules/td_server/openapi_server/openapi/openapi.yaml",
|
|
94
88
|
"gen:handlers": "node td/genHandlers.js",
|
|
95
89
|
"gen:mcp": "orval --config ./orval.config.ts"
|
|
96
90
|
},
|
|
97
91
|
"files": [
|
|
98
92
|
"dist/**/*"
|
|
99
|
-
]
|
|
100
|
-
"msw": {
|
|
101
|
-
"workerDirectory": [
|
|
102
|
-
"public"
|
|
103
|
-
]
|
|
104
|
-
}
|
|
93
|
+
]
|
|
105
94
|
}
|