touchdesigner-mcp-server 1.1.0 → 1.1.1

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.
Files changed (27) hide show
  1. package/README.ja.md +26 -75
  2. package/README.md +25 -74
  3. package/dist/core/constants.js +0 -1
  4. package/dist/features/tools/handlers/tdTools.js +50 -162
  5. package/dist/features/tools/types.js +1 -26
  6. package/dist/gen/endpoints/TouchDesignerAPI.js +1 -1
  7. package/dist/gen/mcp/touchDesignerAPI.zod.js +1 -1
  8. package/dist/server/touchDesignerServer.js +1 -1
  9. package/package.json +3 -4
  10. package/dist/features/tools/metadata/touchDesignerToolMetadata.js +0 -402
  11. package/dist/features/tools/presenter/classListFormatter.js +0 -187
  12. package/dist/features/tools/presenter/index.js +0 -11
  13. package/dist/features/tools/presenter/markdownRenderer.js +0 -25
  14. package/dist/features/tools/presenter/nodeDetailsFormatter.js +0 -140
  15. package/dist/features/tools/presenter/nodeListFormatter.js +0 -124
  16. package/dist/features/tools/presenter/operationFormatter.js +0 -117
  17. package/dist/features/tools/presenter/presenter.js +0 -62
  18. package/dist/features/tools/presenter/responseFormatter.js +0 -66
  19. package/dist/features/tools/presenter/scriptResultFormatter.js +0 -171
  20. package/dist/features/tools/presenter/templates/markdown/classDetailsSummary.md +0 -13
  21. package/dist/features/tools/presenter/templates/markdown/classListSummary.md +0 -7
  22. package/dist/features/tools/presenter/templates/markdown/default.md +0 -3
  23. package/dist/features/tools/presenter/templates/markdown/detailedPayload.md +0 -5
  24. package/dist/features/tools/presenter/templates/markdown/nodeDetailsSummary.md +0 -10
  25. package/dist/features/tools/presenter/templates/markdown/nodeListSummary.md +0 -8
  26. package/dist/features/tools/presenter/templates/markdown/scriptSummary.md +0 -15
  27. package/dist/features/tools/presenter/toolMetadataFormatter.js +0 -118
@@ -1,83 +1,18 @@
1
- import { z } from "zod";
2
1
  import { REFERENCE_COMMENT, TOOL_NAMES } from "../../../core/constants.js";
3
2
  import { handleToolError } from "../../../core/errorHandling.js";
4
3
  import { createNodeBody, deleteNodeQueryParams, execNodeMethodBody, execPythonScriptBody, getNodeDetailQueryParams, getNodesQueryParams, getTdPythonClassDetailsParams, updateNodeBody, } from "../../../gen/mcp/touchDesignerAPI.zod.js";
5
- import { getTouchDesignerToolMetadata } from "../metadata/touchDesignerToolMetadata.js";
6
- import { formatClassDetails, formatClassList, formatCreateNodeResult, formatDeleteNodeResult, formatExecNodeMethodResult, formatNodeDetails, formatNodeList, formatScriptResult, formatTdInfo, formatToolMetadata, formatUpdateNodeResult, } from "../presenter/index.js";
7
- import { detailOnlyFormattingSchema, formattingOptionsSchema, } from "../types.js";
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 createNodeToolSchema = createNodeBody.extend(detailOnlyFormattingSchema.shape);
13
- const updateNodeToolSchema = updateNodeBody.extend(detailOnlyFormattingSchema.shape);
14
- const deleteNodeToolSchema = deleteNodeQueryParams.extend(detailOnlyFormattingSchema.shape);
15
- const classListToolSchema = formattingOptionsSchema;
16
- const classDetailToolSchema = getTdPythonClassDetailsParams.extend(formattingOptionsSchema.shape);
17
- const execNodeMethodToolSchema = execNodeMethodBody.extend(detailOnlyFormattingSchema.shape);
18
- const describeToolsSchema = detailOnlyFormattingSchema.extend({
19
- filter: z
20
- .string()
21
- .min(1)
22
- .describe("Optional keyword to filter by tool name, module path, or parameter description")
23
- .optional(),
24
- });
25
4
  export function registerTdTools(server, logger, tdClient) {
26
- const toolMetadataEntries = getTouchDesignerToolMetadata();
27
- server.tool(TOOL_NAMES.DESCRIBE_TD_TOOLS, "Generate a filesystem-oriented manifest of available TouchDesigner tools", describeToolsSchema.strict().shape, async (params = {}) => {
5
+ server.tool(TOOL_NAMES.GET_TD_INFO, "Get server information from TouchDesigner", async () => {
28
6
  try {
29
- const { detailLevel, responseFormat, filter } = params;
30
- const normalizedFilter = filter?.trim().toLowerCase();
31
- const filteredEntries = normalizedFilter
32
- ? toolMetadataEntries.filter((entry) => matchesMetadataFilter(entry, normalizedFilter))
33
- : toolMetadataEntries;
34
- if (filteredEntries.length === 0) {
35
- const message = filter
36
- ? `No TouchDesigner tools matched filter "${filter}".`
37
- : "No TouchDesigner tools are registered.";
38
- return {
39
- content: [
40
- {
41
- type: "text",
42
- text: message,
43
- },
44
- ],
45
- };
46
- }
47
- const formattedText = formatToolMetadata(filteredEntries, {
48
- detailLevel: detailLevel ?? (filter ? "summary" : "minimal"),
49
- responseFormat,
50
- filter: normalizedFilter,
51
- });
52
- return {
53
- content: [
54
- {
55
- type: "text",
56
- text: formattedText,
57
- },
58
- ],
59
- };
60
- }
61
- catch (error) {
62
- return handleToolError(error, logger, TOOL_NAMES.DESCRIBE_TD_TOOLS);
63
- }
64
- });
65
- server.tool(TOOL_NAMES.GET_TD_INFO, "Get server information from TouchDesigner", tdInfoToolSchema.strict().shape, async (params = {}) => {
66
- try {
67
- const { detailLevel, responseFormat } = params;
68
7
  const result = await tdClient.getTdInfo();
69
8
  if (!result.success) {
70
9
  throw result.error;
71
10
  }
72
- const formattedText = formatTdInfo(result.data, {
73
- detailLevel: detailLevel ?? "summary",
74
- responseFormat,
75
- });
76
11
  return {
77
12
  content: [
78
13
  {
79
14
  type: "text",
80
- text: formattedText,
15
+ text: `Server information: ${JSON.stringify(result, null, 2)}`,
81
16
  },
82
17
  ],
83
18
  };
@@ -86,24 +21,19 @@ export function registerTdTools(server, logger, tdClient) {
86
21
  return handleToolError(error, logger, TOOL_NAMES.GET_TD_INFO);
87
22
  }
88
23
  });
89
- 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) => {
24
+ server.tool(TOOL_NAMES.EXECUTE_PYTHON_SCRIPT, "Execute Python script directly in TouchDesigner", execPythonScriptBody.strict().shape, async (params) => {
90
25
  try {
91
- const { detailLevel, responseFormat, ...scriptParams } = params;
92
- logger.debug(`Executing script: ${scriptParams.script}`);
93
- const result = await tdClient.execPythonScript(scriptParams);
26
+ const { script } = params;
27
+ logger.debug(`Executing script: ${script}`);
28
+ const result = await tdClient.execPythonScript(params);
94
29
  if (!result.success) {
95
30
  throw result.error;
96
31
  }
97
- // Use formatter for token-optimized response
98
- const formattedText = formatScriptResult(result, scriptParams.script, {
99
- detailLevel: detailLevel ?? "summary",
100
- responseFormat,
101
- });
102
32
  return {
103
33
  content: [
104
34
  {
105
35
  type: "text",
106
- text: formattedText,
36
+ text: `Script executed successfully. Result: ${JSON.stringify(result, null, 2)}`,
107
37
  },
108
38
  ],
109
39
  };
@@ -112,22 +42,22 @@ export function registerTdTools(server, logger, tdClient) {
112
42
  return handleToolError(error, logger, TOOL_NAMES.EXECUTE_PYTHON_SCRIPT);
113
43
  }
114
44
  });
115
- server.tool(TOOL_NAMES.CREATE_TD_NODE, "Create a new node in TouchDesigner", createNodeToolSchema.strict().shape, async (params) => {
45
+ server.tool(TOOL_NAMES.CREATE_TD_NODE, "Create a new node in TouchDesigner", createNodeBody.strict().shape, async (params) => {
116
46
  try {
117
- const { detailLevel, responseFormat, ...createParams } = params;
118
- const result = await tdClient.createNode(createParams);
47
+ const { parentPath, nodeType, nodeName } = params;
48
+ const result = await tdClient.createNode({
49
+ parentPath,
50
+ nodeType,
51
+ nodeName,
52
+ });
119
53
  if (!result.success) {
120
54
  throw result.error;
121
55
  }
122
- const formattedText = formatCreateNodeResult(result.data, {
123
- detailLevel: detailLevel ?? "summary",
124
- responseFormat,
125
- });
126
56
  return {
127
57
  content: [
128
58
  {
129
59
  type: "text",
130
- text: formattedText,
60
+ text: `Node created successfully: ${JSON.stringify(result, null, 2)}`,
131
61
  },
132
62
  ],
133
63
  };
@@ -136,22 +66,17 @@ export function registerTdTools(server, logger, tdClient) {
136
66
  return handleToolError(error, logger, TOOL_NAMES.CREATE_TD_NODE, REFERENCE_COMMENT);
137
67
  }
138
68
  });
139
- server.tool(TOOL_NAMES.DELETE_TD_NODE, "Delete an existing node in TouchDesigner", deleteNodeToolSchema.strict().shape, async (params) => {
69
+ server.tool(TOOL_NAMES.DELETE_TD_NODE, "Delete an existing node in TouchDesigner", deleteNodeQueryParams.strict().shape, async (params) => {
140
70
  try {
141
- const { detailLevel, responseFormat, ...deleteParams } = params;
142
- const result = await tdClient.deleteNode(deleteParams);
71
+ const result = await tdClient.deleteNode(params);
143
72
  if (!result.success) {
144
73
  throw result.error;
145
74
  }
146
- const formattedText = formatDeleteNodeResult(result.data, {
147
- detailLevel: detailLevel ?? "summary",
148
- responseFormat,
149
- });
150
75
  return {
151
76
  content: [
152
77
  {
153
78
  type: "text",
154
- text: formattedText,
79
+ text: `Node deleted successfully: ${JSON.stringify(result, null, 2)}`,
155
80
  },
156
81
  ],
157
82
  };
@@ -160,27 +85,22 @@ export function registerTdTools(server, logger, tdClient) {
160
85
  return handleToolError(error, logger, TOOL_NAMES.DELETE_TD_NODE, REFERENCE_COMMENT);
161
86
  }
162
87
  });
163
- server.tool(TOOL_NAMES.GET_TD_NODES, "List nodes under a path with token-optimized output (detailLevel+limit supported)", getNodesToolSchema.strict().shape, async (params) => {
88
+ server.tool(TOOL_NAMES.GET_TD_NODES, "Get all nodes in the parent path (lightweight by default for better performance)", getNodesQueryParams.strict().shape, async (params) => {
164
89
  try {
165
- const { detailLevel, limit, responseFormat, ...queryParams } = params;
166
- const result = await tdClient.getNodes(queryParams);
90
+ const result = await tdClient.getNodes(params);
167
91
  if (!result.success) {
168
92
  throw result.error;
169
93
  }
170
- // Use formatter for token-optimized response
171
- const fallbackMode = queryParams.includeProperties
172
- ? "detailed"
173
- : "summary";
174
- const formattedText = formatNodeList(result.data, {
175
- detailLevel: detailLevel ?? fallbackMode,
176
- limit,
177
- responseFormat,
178
- });
94
+ const nodeCount = result.data?.nodes?.length || 0;
95
+ const isLightweight = !params.includeProperties;
96
+ const performanceNote = isLightweight
97
+ ? " (lightweight mode - set includeProperties=true for full node details)"
98
+ : " (full mode with properties)";
179
99
  return {
180
100
  content: [
181
101
  {
182
102
  type: "text",
183
- text: formattedText,
103
+ text: `Project nodes retrieved (${nodeCount} nodes)${performanceNote}: ${JSON.stringify(result, null, 2)}`,
184
104
  },
185
105
  ],
186
106
  };
@@ -189,24 +109,18 @@ export function registerTdTools(server, logger, tdClient) {
189
109
  return handleToolError(error, logger, TOOL_NAMES.GET_TD_NODES, REFERENCE_COMMENT);
190
110
  }
191
111
  });
192
- server.tool(TOOL_NAMES.GET_TD_NODE_PARAMETERS, "Get node parameters with concise/detailed formatting (detailLevel+limit supported)", getNodeDetailToolSchema.strict().shape, async (params) => {
112
+ server.tool(TOOL_NAMES.GET_TD_NODE_PARAMETERS, "Get parameters of a specific node in TouchDesigner", getNodeDetailQueryParams.strict().shape, async (params) => {
193
113
  try {
194
- const { detailLevel, limit, responseFormat, ...queryParams } = params;
195
- const result = await tdClient.getNodeDetail(queryParams);
114
+ const { nodePath } = params;
115
+ const result = await tdClient.getNodeDetail(params);
196
116
  if (!result.success) {
197
117
  throw result.error;
198
118
  }
199
- // Use formatter for token-optimized response
200
- const formattedText = formatNodeDetails(result.data, {
201
- detailLevel: detailLevel ?? "summary",
202
- limit,
203
- responseFormat,
204
- });
205
119
  return {
206
120
  content: [
207
121
  {
208
122
  type: "text",
209
- text: formattedText,
123
+ text: `Node parameters for node at path ${nodePath}: ${JSON.stringify(result, null, 2)}`,
210
124
  },
211
125
  ],
212
126
  };
@@ -215,22 +129,17 @@ export function registerTdTools(server, logger, tdClient) {
215
129
  return handleToolError(error, logger, TOOL_NAMES.GET_TD_NODE_PARAMETERS, REFERENCE_COMMENT);
216
130
  }
217
131
  });
218
- server.tool(TOOL_NAMES.UPDATE_TD_NODE_PARAMETERS, "Update parameters of a specific node in TouchDesigner", updateNodeToolSchema.strict().shape, async (params) => {
132
+ server.tool(TOOL_NAMES.UPDATE_TD_NODE_PARAMETERS, "Update parameters of a specific node in TouchDesigner", updateNodeBody.strict().shape, async (params) => {
219
133
  try {
220
- const { detailLevel, responseFormat, ...updateParams } = params;
221
- const result = await tdClient.updateNode(updateParams);
134
+ const result = await tdClient.updateNode(params);
222
135
  if (!result.success) {
223
136
  throw result.error;
224
137
  }
225
- const formattedText = formatUpdateNodeResult(result.data, {
226
- detailLevel: detailLevel ?? "summary",
227
- responseFormat,
228
- });
229
138
  return {
230
139
  content: [
231
140
  {
232
141
  type: "text",
233
- text: formattedText,
142
+ text: `Node parameters updated successfully: ${JSON.stringify(result, null, 2)}`,
234
143
  },
235
144
  ],
236
145
  };
@@ -239,20 +148,26 @@ export function registerTdTools(server, logger, tdClient) {
239
148
  return handleToolError(error, logger, TOOL_NAMES.UPDATE_TD_NODE_PARAMETERS, REFERENCE_COMMENT);
240
149
  }
241
150
  });
242
- server.tool(TOOL_NAMES.EXECUTE_NODE_METHOD, "Execute a method on a specific node in TouchDesigner", execNodeMethodToolSchema.strict().shape, async (params) => {
151
+ server.tool(TOOL_NAMES.EXECUTE_NODE_METHOD, "Execute a method on a specific node in TouchDesigner", execNodeMethodBody.strict().shape, async (params) => {
243
152
  try {
244
- const { detailLevel, responseFormat, ...execParams } = params;
245
- const { nodePath, method, args, kwargs } = execParams;
246
- const result = await tdClient.execNodeMethod(execParams);
153
+ const { nodePath, method, args } = params;
154
+ const result = await tdClient.execNodeMethod(params);
247
155
  if (!result.success) {
248
156
  throw result.error;
249
157
  }
250
- const formattedText = formatExecNodeMethodResult(result.data, { nodePath, method, args, kwargs }, { detailLevel: detailLevel ?? "summary", responseFormat });
251
158
  return {
252
159
  content: [
253
160
  {
254
161
  type: "text",
255
- text: formattedText,
162
+ text: `op('${nodePath}').${method}(${args?.map((v) => `"${v}"`).join(",")}${params.kwargs
163
+ ? Object.entries(params.kwargs)
164
+ .map(([key, value]) => `${key}=${JSON.stringify(value)}`)
165
+ .join(", ")
166
+ : ""})`,
167
+ },
168
+ {
169
+ type: "text",
170
+ text: JSON.stringify(result, null, 2),
256
171
  },
257
172
  ],
258
173
  };
@@ -262,23 +177,17 @@ export function registerTdTools(server, logger, tdClient) {
262
177
  return handleToolError(error, logger, TOOL_NAMES.EXECUTE_NODE_METHOD, REFERENCE_COMMENT);
263
178
  }
264
179
  });
265
- server.tool(TOOL_NAMES.GET_TD_CLASSES, "List TouchDesigner Python classes/modules (detailLevel+limit supported)", classListToolSchema.strict().shape, async (params = {}) => {
180
+ server.tool(TOOL_NAMES.GET_TD_CLASSES, "Get list of classes and modules in TouchDesigner", async () => {
266
181
  try {
267
182
  const result = await tdClient.getClasses();
268
183
  if (!result.success) {
269
184
  throw result.error;
270
185
  }
271
- // Use formatter for token-optimized response
272
- const formattedText = formatClassList(result.data, {
273
- detailLevel: params.detailLevel ?? "summary",
274
- limit: params.limit ?? 50,
275
- responseFormat: params.responseFormat,
276
- });
277
186
  return {
278
187
  content: [
279
188
  {
280
189
  type: "text",
281
- text: formattedText,
190
+ text: `TouchDesigner classes list: ${JSON.stringify(result, null, 2)}`,
282
191
  },
283
192
  ],
284
193
  };
@@ -287,24 +196,18 @@ export function registerTdTools(server, logger, tdClient) {
287
196
  return handleToolError(error, logger, TOOL_NAMES.GET_TD_CLASSES, REFERENCE_COMMENT);
288
197
  }
289
198
  });
290
- server.tool(TOOL_NAMES.GET_TD_CLASS_DETAILS, "Get information about a TouchDesigner class/module (detailLevel+limit supported)", classDetailToolSchema.strict().shape, async (params) => {
199
+ server.tool(TOOL_NAMES.GET_TD_CLASS_DETAILS, "Get detailed information about a specific TouchDesigner class or module", getTdPythonClassDetailsParams.strict().shape, async (params) => {
291
200
  try {
292
- const { className, detailLevel, limit, responseFormat } = params;
201
+ const { className } = params;
293
202
  const result = await tdClient.getClassDetails(className);
294
203
  if (!result.success) {
295
204
  throw result.error;
296
205
  }
297
- // Use formatter for token-optimized response
298
- const formattedText = formatClassDetails(result.data, {
299
- detailLevel: detailLevel ?? "summary",
300
- limit: limit ?? 30,
301
- responseFormat,
302
- });
303
206
  return {
304
207
  content: [
305
208
  {
306
209
  type: "text",
307
- text: formattedText,
210
+ text: `Details for ${className}: ${JSON.stringify(result, null, 2)}`,
308
211
  },
309
212
  ],
310
213
  };
@@ -314,18 +217,3 @@ export function registerTdTools(server, logger, tdClient) {
314
217
  }
315
218
  });
316
219
  }
317
- function matchesMetadataFilter(entry, keyword) {
318
- const normalizedKeyword = keyword.toLowerCase();
319
- const haystacks = [
320
- entry.functionName,
321
- entry.modulePath,
322
- entry.description,
323
- entry.category,
324
- entry.tool,
325
- entry.notes ?? "",
326
- ];
327
- if (haystacks.some((value) => value.toLowerCase().includes(normalizedKeyword))) {
328
- return true;
329
- }
330
- return entry.parameters.some((param) => [param.name, param.type, param.description ?? ""].some((value) => value.toLowerCase().includes(normalizedKeyword)));
331
- }
@@ -1,26 +1 @@
1
- import { z } from "zod";
2
- /**
3
- * Shared Zod schemas for MCP tool formatting parameters.
4
- * These stay on the TypeScript side only and are not sent to TouchDesigner.
5
- */
6
- export const detailLevelSchema = z
7
- .enum(["minimal", "summary", "detailed"])
8
- .describe("Response detail level for tool output (minimal, summary, or detailed)");
9
- export const limitSchema = z
10
- .number()
11
- .int()
12
- .min(1)
13
- .max(500)
14
- .describe("Maximum number of items to include in formatted output");
15
- export const presenterFormatSchema = z
16
- .enum(["json", "yaml", "markdown"])
17
- .describe("Structured output format for formatted responses");
18
- export const detailOnlyFormattingSchema = z.object({
19
- detailLevel: detailLevelSchema.optional(),
20
- responseFormat: presenterFormatSchema.optional(),
21
- });
22
- export const formattingOptionsSchema = z.object({
23
- detailLevel: detailLevelSchema.optional(),
24
- limit: limitSchema.optional(),
25
- responseFormat: presenterFormatSchema.optional(),
26
- });
1
+ "use strict";
@@ -3,7 +3,7 @@
3
3
  * Do not edit manually.
4
4
  * TouchDesigner API
5
5
  * OpenAPI schema for generating TouchDesigner API client code
6
- * OpenAPI spec version: 1.1.0
6
+ * OpenAPI spec version: 1.1.1
7
7
  */
8
8
  import { customInstance } from '../../api/customInstance.js';
9
9
  // eslint-disable-next-line @typescript-eslint/no-redeclare
@@ -3,7 +3,7 @@
3
3
  * Do not edit manually.
4
4
  * TouchDesigner API
5
5
  * OpenAPI schema for generating TouchDesigner API client code
6
- * OpenAPI spec version: 1.1.0
6
+ * OpenAPI spec version: 1.1.1
7
7
  */
8
8
  import { z as zod } from 'zod';
9
9
  /**
@@ -18,7 +18,7 @@ export class TouchDesignerServer {
18
18
  constructor() {
19
19
  this.server = new McpServer({
20
20
  name: "TouchDesigner",
21
- version: "1.1.0",
21
+ version: "1.1.1",
22
22
  }, {
23
23
  capabilities: {
24
24
  prompts: {},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "touchdesigner-mcp-server",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "MCP server for TouchDesigner",
5
5
  "repository": {
6
6
  "type": "git",
@@ -29,18 +29,17 @@
29
29
  "@types/ws": "^8.18.1",
30
30
  "@types/yargs": "^17.0.33",
31
31
  "axios": "^1.12.2",
32
- "mustache": "^4.2.0",
33
32
  "zod": "3.25.76"
34
33
  },
35
34
  "devDependencies": {
36
35
  "@biomejs/biome": "2.2.4",
37
36
  "@openapitools/openapi-generator-cli": "^2.23.1",
38
37
  "@types/jsdom": "^21.1.7",
39
- "@types/mustache": "^4.2.6",
40
38
  "@types/node": "^24.4.0",
41
39
  "@vitest/coverage-v8": "^3.2.4",
42
40
  "archiver": "^7.0.1",
43
41
  "msw": "^2.11.2",
42
+ "mustache": "^4.2.0",
44
43
  "npm-run-all": "^4.1.5",
45
44
  "orval": "^7.11.2",
46
45
  "shx": "^0.4.0",
@@ -62,7 +61,7 @@
62
61
  "scripts": {
63
62
  "build": "run-s build:*",
64
63
  "build:gen": "npm run gen",
65
- "build:dist": "tsc && shx chmod +x dist/*.js && shx cp -r src/features/tools/presenter/templates dist/features/tools/presenter/",
64
+ "build:dist": "tsc && shx chmod +x dist/*.js",
66
65
  "build:dxt": "npx @anthropic-ai/dxt pack dxt/ touchdesigner-mcp.dxt",
67
66
  "lint": "run-p lint:*",
68
67
  "lint:biome": "biome check",