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
|
@@ -1,22 +1,10 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { TOOL_NAMES } from "../../../core/constants.js";
|
|
3
3
|
import { handleToolError } from "../../../core/errorHandling.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { detailOnlyFormattingSchema
|
|
8
|
-
const execPythonScriptToolSchema = ExecPythonScriptBody.extend(detailOnlyFormattingSchema.shape);
|
|
9
|
-
const tdInfoToolSchema = detailOnlyFormattingSchema;
|
|
10
|
-
const getNodesToolSchema = GetNodesQueryParams.extend(formattingOptionsSchema.shape);
|
|
11
|
-
const getNodeDetailToolSchema = GetNodeDetailQueryParams.extend(formattingOptionsSchema.shape);
|
|
12
|
-
const getNodeErrorsToolSchema = GetNodeErrorsQueryParams.extend(formattingOptionsSchema.shape);
|
|
13
|
-
const createNodeToolSchema = CreateNodeBody.extend(detailOnlyFormattingSchema.shape);
|
|
14
|
-
const updateNodeToolSchema = UpdateNodeBody.extend(detailOnlyFormattingSchema.shape);
|
|
15
|
-
const deleteNodeToolSchema = DeleteNodeQueryParams.extend(detailOnlyFormattingSchema.shape);
|
|
16
|
-
const classListToolSchema = formattingOptionsSchema;
|
|
17
|
-
const classDetailToolSchema = GetTdPythonClassDetailsParams.extend(formattingOptionsSchema.shape);
|
|
18
|
-
const moduleHelpToolSchema = GetModuleHelpQueryParams.extend(detailOnlyFormattingSchema.shape);
|
|
19
|
-
const execNodeMethodToolSchema = ExecNodeMethodBody.extend(detailOnlyFormattingSchema.shape);
|
|
4
|
+
import { buildToolMetadata, } from "../metadata/touchDesignerToolMetadata.js";
|
|
5
|
+
import { formatToolMetadata } from "../presenter/index.js";
|
|
6
|
+
import { TOOL_DEFINITIONS } from "../toolDefinitions.js";
|
|
7
|
+
import { detailOnlyFormattingSchema } from "../types.js";
|
|
20
8
|
const describeToolsSchema = detailOnlyFormattingSchema.extend({
|
|
21
9
|
filter: z
|
|
22
10
|
.string()
|
|
@@ -25,7 +13,21 @@ const describeToolsSchema = detailOnlyFormattingSchema.extend({
|
|
|
25
13
|
.optional(),
|
|
26
14
|
});
|
|
27
15
|
export function registerTdTools(server, logger, tdClient) {
|
|
28
|
-
|
|
16
|
+
// Register every TouchDesigner operation from the single source of truth.
|
|
17
|
+
for (const definition of TOOL_DEFINITIONS) {
|
|
18
|
+
server.tool(definition.name, definition.description, definition.schema.strict().shape, async (params = {}) => {
|
|
19
|
+
try {
|
|
20
|
+
const text = await definition.run({ logger, params, tdClient });
|
|
21
|
+
return createToolResult(tdClient, text);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
return handleToolError(error, logger, definition.name, definition.errorComment);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
// `describe_td_tools` is the meta tool: it documents the tools above rather
|
|
29
|
+
// than calling TouchDesigner, so it is registered on its own.
|
|
30
|
+
const toolMetadataEntries = buildToolMetadata(TOOL_DEFINITIONS);
|
|
29
31
|
server.tool(TOOL_NAMES.DESCRIBE_TD_TOOLS, "Generate a filesystem-oriented manifest of available TouchDesigner tools", describeToolsSchema.strict().shape, async (params = {}) => {
|
|
30
32
|
try {
|
|
31
33
|
const { detailLevel, responseFormat, filter } = params;
|
|
@@ -38,12 +40,7 @@ export function registerTdTools(server, logger, tdClient) {
|
|
|
38
40
|
? `No TouchDesigner tools matched filter "${filter}".`
|
|
39
41
|
: "No TouchDesigner tools are registered.";
|
|
40
42
|
return {
|
|
41
|
-
content: [
|
|
42
|
-
{
|
|
43
|
-
text: message,
|
|
44
|
-
type: "text",
|
|
45
|
-
},
|
|
46
|
-
],
|
|
43
|
+
content: [{ text: message, type: "text" }],
|
|
47
44
|
};
|
|
48
45
|
}
|
|
49
46
|
const formattedText = formatToolMetadata(filteredEntries, {
|
|
@@ -52,247 +49,17 @@ export function registerTdTools(server, logger, tdClient) {
|
|
|
52
49
|
responseFormat,
|
|
53
50
|
});
|
|
54
51
|
return {
|
|
55
|
-
content: [
|
|
56
|
-
{
|
|
57
|
-
text: formattedText,
|
|
58
|
-
type: "text",
|
|
59
|
-
},
|
|
60
|
-
],
|
|
52
|
+
content: [{ text: formattedText, type: "text" }],
|
|
61
53
|
};
|
|
62
54
|
}
|
|
63
55
|
catch (error) {
|
|
64
56
|
return handleToolError(error, logger, TOOL_NAMES.DESCRIBE_TD_TOOLS);
|
|
65
57
|
}
|
|
66
58
|
});
|
|
67
|
-
server.tool(TOOL_NAMES.GET_TD_INFO, "Get server information from TouchDesigner", tdInfoToolSchema.strict().shape, async (params = {}) => {
|
|
68
|
-
try {
|
|
69
|
-
const { detailLevel, responseFormat } = params;
|
|
70
|
-
const result = await tdClient.getTdInfo();
|
|
71
|
-
if (!result.success) {
|
|
72
|
-
throw result.error;
|
|
73
|
-
}
|
|
74
|
-
const formattedText = formatTdInfo(result.data, {
|
|
75
|
-
detailLevel: detailLevel ?? "summary",
|
|
76
|
-
responseFormat,
|
|
77
|
-
});
|
|
78
|
-
return createToolResult(tdClient, formattedText);
|
|
79
|
-
}
|
|
80
|
-
catch (error) {
|
|
81
|
-
return handleToolError(error, logger, TOOL_NAMES.GET_TD_INFO);
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
server.tool(TOOL_NAMES.EXECUTE_PYTHON_SCRIPT, "Execute a Python script in TouchDesigner (detailLevel=minimal|summary|detailed, responseFormat=json|yaml|markdown)", execPythonScriptToolSchema.strict().shape, async (params) => {
|
|
85
|
-
try {
|
|
86
|
-
const { detailLevel, responseFormat, ...scriptParams } = params;
|
|
87
|
-
logger.sendLog({
|
|
88
|
-
data: `Executing script: ${scriptParams.script}`,
|
|
89
|
-
level: "debug",
|
|
90
|
-
});
|
|
91
|
-
const result = await tdClient.execPythonScript(scriptParams);
|
|
92
|
-
if (!result.success) {
|
|
93
|
-
throw result.error;
|
|
94
|
-
}
|
|
95
|
-
// Use formatter for token-optimized response
|
|
96
|
-
const formattedText = formatScriptResult(result, scriptParams.script, {
|
|
97
|
-
detailLevel: detailLevel ?? "summary",
|
|
98
|
-
responseFormat,
|
|
99
|
-
});
|
|
100
|
-
return createToolResult(tdClient, formattedText);
|
|
101
|
-
}
|
|
102
|
-
catch (error) {
|
|
103
|
-
return handleToolError(error, logger, TOOL_NAMES.EXECUTE_PYTHON_SCRIPT);
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
server.tool(TOOL_NAMES.CREATE_TD_NODE, "Create a new node in TouchDesigner", createNodeToolSchema.strict().shape, async (params) => {
|
|
107
|
-
try {
|
|
108
|
-
const { detailLevel, responseFormat, ...createParams } = params;
|
|
109
|
-
const result = await tdClient.createNode(createParams);
|
|
110
|
-
if (!result.success) {
|
|
111
|
-
throw result.error;
|
|
112
|
-
}
|
|
113
|
-
const formattedText = formatCreateNodeResult(result.data, {
|
|
114
|
-
detailLevel: detailLevel ?? "summary",
|
|
115
|
-
responseFormat,
|
|
116
|
-
});
|
|
117
|
-
return createToolResult(tdClient, formattedText);
|
|
118
|
-
}
|
|
119
|
-
catch (error) {
|
|
120
|
-
return handleToolError(error, logger, TOOL_NAMES.CREATE_TD_NODE, REFERENCE_COMMENT);
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
server.tool(TOOL_NAMES.DELETE_TD_NODE, "Delete an existing node in TouchDesigner", deleteNodeToolSchema.strict().shape, async (params) => {
|
|
124
|
-
try {
|
|
125
|
-
const { detailLevel, responseFormat, ...deleteParams } = params;
|
|
126
|
-
const result = await tdClient.deleteNode(deleteParams);
|
|
127
|
-
if (!result.success) {
|
|
128
|
-
throw result.error;
|
|
129
|
-
}
|
|
130
|
-
const formattedText = formatDeleteNodeResult(result.data, {
|
|
131
|
-
detailLevel: detailLevel ?? "summary",
|
|
132
|
-
responseFormat,
|
|
133
|
-
});
|
|
134
|
-
return createToolResult(tdClient, formattedText);
|
|
135
|
-
}
|
|
136
|
-
catch (error) {
|
|
137
|
-
return handleToolError(error, logger, TOOL_NAMES.DELETE_TD_NODE, REFERENCE_COMMENT);
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
|
-
server.tool(TOOL_NAMES.GET_TD_NODES, "List nodes under a path with token-optimized output (detailLevel+limit supported)", getNodesToolSchema.strict().shape, async (params) => {
|
|
141
|
-
try {
|
|
142
|
-
const { detailLevel, limit, responseFormat, ...queryParams } = params;
|
|
143
|
-
const result = await tdClient.getNodes(queryParams);
|
|
144
|
-
if (!result.success) {
|
|
145
|
-
throw result.error;
|
|
146
|
-
}
|
|
147
|
-
// Use formatter for token-optimized response
|
|
148
|
-
const fallbackMode = queryParams.includeProperties
|
|
149
|
-
? "detailed"
|
|
150
|
-
: "summary";
|
|
151
|
-
const formattedText = formatNodeList(result.data, {
|
|
152
|
-
detailLevel: detailLevel ?? fallbackMode,
|
|
153
|
-
limit,
|
|
154
|
-
responseFormat,
|
|
155
|
-
});
|
|
156
|
-
return createToolResult(tdClient, formattedText);
|
|
157
|
-
}
|
|
158
|
-
catch (error) {
|
|
159
|
-
return handleToolError(error, logger, TOOL_NAMES.GET_TD_NODES, REFERENCE_COMMENT);
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
server.tool(TOOL_NAMES.GET_TD_NODE_PARAMETERS, "Get node parameters with concise/detailed formatting (detailLevel+limit supported)", getNodeDetailToolSchema.strict().shape, async (params) => {
|
|
163
|
-
try {
|
|
164
|
-
const { detailLevel, limit, responseFormat, ...queryParams } = params;
|
|
165
|
-
const result = await tdClient.getNodeDetail(queryParams);
|
|
166
|
-
if (!result.success) {
|
|
167
|
-
throw result.error;
|
|
168
|
-
}
|
|
169
|
-
// Use formatter for token-optimized response
|
|
170
|
-
const formattedText = formatNodeDetails(result.data, {
|
|
171
|
-
detailLevel: detailLevel ?? "summary",
|
|
172
|
-
limit,
|
|
173
|
-
responseFormat,
|
|
174
|
-
});
|
|
175
|
-
return createToolResult(tdClient, formattedText);
|
|
176
|
-
}
|
|
177
|
-
catch (error) {
|
|
178
|
-
return handleToolError(error, logger, TOOL_NAMES.GET_TD_NODE_PARAMETERS, REFERENCE_COMMENT);
|
|
179
|
-
}
|
|
180
|
-
});
|
|
181
|
-
server.tool(TOOL_NAMES.GET_TD_NODE_ERRORS, "Check node and descendant errors reported by TouchDesigner", getNodeErrorsToolSchema.strict().shape, async (params) => {
|
|
182
|
-
try {
|
|
183
|
-
const { detailLevel, limit, responseFormat, ...queryParams } = params;
|
|
184
|
-
const result = await tdClient.getNodeErrors(queryParams);
|
|
185
|
-
if (!result.success) {
|
|
186
|
-
throw result.error;
|
|
187
|
-
}
|
|
188
|
-
const formattedText = formatNodeErrors(result.data, {
|
|
189
|
-
detailLevel: detailLevel ?? "summary",
|
|
190
|
-
limit,
|
|
191
|
-
responseFormat,
|
|
192
|
-
});
|
|
193
|
-
return createToolResult(tdClient, formattedText);
|
|
194
|
-
}
|
|
195
|
-
catch (error) {
|
|
196
|
-
return handleToolError(error, logger, TOOL_NAMES.GET_TD_NODE_ERRORS, REFERENCE_COMMENT);
|
|
197
|
-
}
|
|
198
|
-
});
|
|
199
|
-
server.tool(TOOL_NAMES.UPDATE_TD_NODE_PARAMETERS, "Update parameters of a specific node in TouchDesigner", updateNodeToolSchema.strict().shape, async (params) => {
|
|
200
|
-
try {
|
|
201
|
-
const { detailLevel, responseFormat, ...updateParams } = params;
|
|
202
|
-
const result = await tdClient.updateNode(updateParams);
|
|
203
|
-
if (!result.success) {
|
|
204
|
-
throw result.error;
|
|
205
|
-
}
|
|
206
|
-
const formattedText = formatUpdateNodeResult(result.data, {
|
|
207
|
-
detailLevel: detailLevel ?? "summary",
|
|
208
|
-
responseFormat,
|
|
209
|
-
});
|
|
210
|
-
return createToolResult(tdClient, formattedText);
|
|
211
|
-
}
|
|
212
|
-
catch (error) {
|
|
213
|
-
return handleToolError(error, logger, TOOL_NAMES.UPDATE_TD_NODE_PARAMETERS, REFERENCE_COMMENT);
|
|
214
|
-
}
|
|
215
|
-
});
|
|
216
|
-
server.tool(TOOL_NAMES.EXECUTE_NODE_METHOD, "Execute a method on a specific node in TouchDesigner", execNodeMethodToolSchema.strict().shape, async (params) => {
|
|
217
|
-
try {
|
|
218
|
-
const { detailLevel, responseFormat, ...execParams } = params;
|
|
219
|
-
const { nodePath, method, args, kwargs } = execParams;
|
|
220
|
-
const result = await tdClient.execNodeMethod(execParams);
|
|
221
|
-
if (!result.success) {
|
|
222
|
-
throw result.error;
|
|
223
|
-
}
|
|
224
|
-
const formattedText = formatExecNodeMethodResult(result.data, { args, kwargs, method, nodePath }, { detailLevel: detailLevel ?? "summary", responseFormat });
|
|
225
|
-
return createToolResult(tdClient, formattedText);
|
|
226
|
-
}
|
|
227
|
-
catch (error) {
|
|
228
|
-
logger.sendLog({
|
|
229
|
-
data: error,
|
|
230
|
-
level: "error",
|
|
231
|
-
});
|
|
232
|
-
return handleToolError(error, logger, TOOL_NAMES.EXECUTE_NODE_METHOD, REFERENCE_COMMENT);
|
|
233
|
-
}
|
|
234
|
-
});
|
|
235
|
-
server.tool(TOOL_NAMES.GET_TD_CLASSES, "List TouchDesigner Python classes/modules (detailLevel+limit supported)", classListToolSchema.strict().shape, async (params = {}) => {
|
|
236
|
-
try {
|
|
237
|
-
const result = await tdClient.getClasses();
|
|
238
|
-
if (!result.success) {
|
|
239
|
-
throw result.error;
|
|
240
|
-
}
|
|
241
|
-
// Use formatter for token-optimized response
|
|
242
|
-
const formattedText = formatClassList(result.data, {
|
|
243
|
-
detailLevel: params.detailLevel ?? "summary",
|
|
244
|
-
limit: params.limit ?? 50,
|
|
245
|
-
responseFormat: params.responseFormat,
|
|
246
|
-
});
|
|
247
|
-
return createToolResult(tdClient, formattedText);
|
|
248
|
-
}
|
|
249
|
-
catch (error) {
|
|
250
|
-
return handleToolError(error, logger, TOOL_NAMES.GET_TD_CLASSES, REFERENCE_COMMENT);
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
server.tool(TOOL_NAMES.GET_TD_CLASS_DETAILS, "Get information about a TouchDesigner class/module (detailLevel+limit supported)", classDetailToolSchema.strict().shape, async (params) => {
|
|
254
|
-
try {
|
|
255
|
-
const { className, detailLevel, limit, responseFormat } = params;
|
|
256
|
-
const result = await tdClient.getClassDetails(className);
|
|
257
|
-
if (!result.success) {
|
|
258
|
-
throw result.error;
|
|
259
|
-
}
|
|
260
|
-
// Use formatter for token-optimized response
|
|
261
|
-
const formattedText = formatClassDetails(result.data, {
|
|
262
|
-
detailLevel: detailLevel ?? "summary",
|
|
263
|
-
limit: limit ?? 30,
|
|
264
|
-
responseFormat,
|
|
265
|
-
});
|
|
266
|
-
return createToolResult(tdClient, formattedText);
|
|
267
|
-
}
|
|
268
|
-
catch (error) {
|
|
269
|
-
return handleToolError(error, logger, TOOL_NAMES.GET_TD_CLASS_DETAILS, REFERENCE_COMMENT);
|
|
270
|
-
}
|
|
271
|
-
});
|
|
272
|
-
server.tool(TOOL_NAMES.GET_TD_MODULE_HELP, "Retrieve Python help() text for a TouchDesigner module or class", moduleHelpToolSchema.strict().shape, async (params) => {
|
|
273
|
-
try {
|
|
274
|
-
const { detailLevel, moduleName, responseFormat } = params;
|
|
275
|
-
const result = await tdClient.getModuleHelp({ moduleName });
|
|
276
|
-
if (!result.success) {
|
|
277
|
-
throw result.error;
|
|
278
|
-
}
|
|
279
|
-
const formattedText = formatModuleHelp(result.data, {
|
|
280
|
-
detailLevel: detailLevel ?? "summary",
|
|
281
|
-
responseFormat,
|
|
282
|
-
});
|
|
283
|
-
return createToolResult(tdClient, formattedText);
|
|
284
|
-
}
|
|
285
|
-
catch (error) {
|
|
286
|
-
return handleToolError(error, logger, TOOL_NAMES.GET_TD_MODULE_HELP);
|
|
287
|
-
}
|
|
288
|
-
});
|
|
289
59
|
}
|
|
290
60
|
const createToolResult = (tdClient, text) => {
|
|
291
61
|
const content = [
|
|
292
|
-
{
|
|
293
|
-
text,
|
|
294
|
-
type: "text",
|
|
295
|
-
},
|
|
62
|
+
{ text, type: "text" },
|
|
296
63
|
];
|
|
297
64
|
const additionalContents = tdClient.getAdditionalToolResultContents();
|
|
298
65
|
if (additionalContents) {
|