touchdesigner-mcp-server 1.4.0 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.ja.md CHANGED
@@ -18,6 +18,8 @@ TouchDesigner MCPは、AIモデルとTouchDesigner WebServer DAT 間のブリッ
18
18
 
19
19
  **[インストールガイド](docs/installation.ja.md)** を参照してください。
20
20
 
21
+ アップデートする場合は **[最新リリース](https://github.com/8beeeaaat/touchdesigner-mcp/releases/latest#for-updates-from-previous-versions)** の手順を参照してください。
22
+
21
23
  ## MCPサーバーの機能
22
24
 
23
25
  このサーバーは、Model Context Protocol (MCP) を通じてTouchDesigner への操作、および各種実装ドキュメントへの参照を可能にします。
@@ -87,9 +89,11 @@ TouchDesigner MCPは、AIモデルとTouchDesigner WebServer DAT 間のブリッ
87
89
 
88
90
  - **開発者向け:** ローカルで開発している場合は、`package.json` を編集した後に `npm run version` を実行してください(または単に `npm version ...` を使用してください)。これにより、Python API(`pyproject.toml` + `td/modules/utils/version.py`)、MCPバンドルマニフェスト、およびレジストリメタデータが同期され、ランタイム互換性チェックが成功するようになります。
89
91
 
92
+ 互換性チェックの内部動作については [Version Compatibility Verification](docs/architecture.md#version-compatibility-verification) も参照してください。
93
+
90
94
  ### 接続エラーのトラブルシューティング
91
95
 
92
- - `TouchDesignerClient` は接続に失敗した互換性チェック結果を **最大5秒間キャッシュ**し、その間のツール呼び出しでは同じエラーを再利用して TouchDesigner への無駄な負荷を避けます。TTL が切れると自動的に再試行します。
96
+ - `TouchDesignerClient` は接続に失敗した互換性チェック結果を **最大60秒間キャッシュ**し、その間のツール呼び出しでは同じエラーを再利用して TouchDesigner への無駄な負荷を避けます。TTL が切れると自動的に再試行します。
93
97
  - MCP サーバーが TouchDesigner に接続できない場合は、次のようなガイド付きメッセージが表示されます:
94
98
  - `ECONNREFUSED` / "connect refused": TouchDesigner を起動し、`mcp_webserver_base.tox` からインポートした WebServer DAT がアクティブか、ポート設定(デフォルト `9981`)が正しいか確認してください。
95
99
  - `ETIMEDOUT` / "timeout": TouchDesigner の応答が遅い、またはネットワークが詰まっています。TouchDesigner/ WebServer DAT の再起動やネットワーク状況の確認を行ってください。
package/README.md CHANGED
@@ -16,10 +16,9 @@ TouchDesigner MCP acts as a bridge between AI models and the TouchDesigner WebSe
16
16
 
17
17
  ## Installation
18
18
 
19
- Read the **[Installation Guide](docs/installation.md)**.
19
+ Please refer to the **[Installation Guide](docs/installation.md)**.
20
20
 
21
- The guide includes the required TouchDesigner preparation, per-agent setup, verification steps, and
22
- troubleshooting tips.
21
+ If you are updating, please refer to the procedure in the **[Latest Release](https://github.com/8beeeaaat/touchdesigner-mcp/releases/latest#for-updates-from-previous-versions)**.
23
22
 
24
23
  ## MCP Server Features
25
24
 
@@ -91,9 +90,11 @@ The MCP server uses **semantic versioning** for flexible compatibility checks
91
90
 
92
91
  - **For developers:** When developing locally, run `npm run version` after editing `package.json` (or simply use `npm version ...`). This keeps the Python API (`pyproject.toml` + `td/modules/utils/version.py`), MCP bundle manifest, and registry metadata in sync so that the runtime compatibility check succeeds.
93
92
 
93
+ For a deeper look at how the MCP server enforces these rules, see [Version Compatibility Verification](docs/architecture.md#version-compatibility-verification).
94
+
94
95
  ### Troubleshooting connection errors
95
96
 
96
- - `TouchDesignerClient` caches failed connection checks for **5 seconds**. Subsequent tool calls reuse the cached error to avoid spamming TouchDesigner and automatically retry after the TTL expires.
97
+ - `TouchDesignerClient` caches failed connection checks for **60 seconds**. Subsequent tool calls reuse the cached error to avoid spamming TouchDesigner and automatically retry after the TTL expires.
97
98
  - When the MCP server cannot reach TouchDesigner, you now get guided error messages with concrete fixes:
98
99
  - `ECONNREFUSED` / "connect refused": start TouchDesigner, ensure the WebServer DAT from `mcp_webserver_base.tox` is running, and confirm the configured port (default `9981`).
99
100
  - `ETIMEDOUT` / "timeout": TouchDesigner is responding slowly or the network is blocked. Restart TouchDesigner/WebServer DAT or check your network connection.
@@ -65,11 +65,11 @@ const COMPATIBILITY_POLICY = {
65
65
  },
66
66
  /**
67
67
  * Behavior when PATCH versions differ
68
- * - 'warning': Continue with warning
68
+ * - 'allow': Allow without logging
69
69
  */
70
70
  [COMPATIBILITY_POLICY_TYPES.PATCH_DIFF]: {
71
71
  compatible: true,
72
- level: COMPATIBILITY_POLICY_ERROR_LEVELS.WARNING,
72
+ level: COMPATIBILITY_POLICY_ERROR_LEVELS.ALLOW,
73
73
  message: generatePatchDiffMessage,
74
74
  },
75
75
  /**
@@ -112,14 +112,15 @@ export const getCompatibilityPolicy = (type) => {
112
112
  * Update guide template
113
113
  */
114
114
  const updateGuide = `
115
- Update Guide:
116
- 1. Download the latest release: https://github.com/8beeeaaat/touchdesigner-mcp/releases/latest
115
+ Update Guide: https://github.com/8beeeaaat/touchdesigner-mcp/releases/latest#for-updates-from-previous-versions
116
+ 1. Download the latest release files from the releases page
117
117
  2. Replace TouchDesigner components:
118
- - Delete the existing touchdesigner-mcp-td folder
119
- - Extract and import the new mcp_webserver_base.tox
118
+ 1. Delete the existing touchdesigner-mcp-td folder from your system
119
+ 2. Delete old mcp_webserver_base node from your TouchDesigner project
120
+ 3. Extract and import the new mcp_webserver_base.tox to your TouchDesigner project
120
121
  3. Restart TouchDesigner and the MCP client (e.g., Claude Desktop)
121
122
 
122
- For more details, see: https://github.com/8beeeaaat/touchdesigner-mcp#troubleshooting-version-compatibility
123
+ For more details on compatibility, see: https://github.com/8beeeaaat/touchdesigner-mcp#troubleshooting-version-compatibility
123
124
  `.trim();
124
125
  /**
125
126
  * Generate error message for unknown version information
@@ -131,8 +132,11 @@ export function generateNoVersionMessage(args) {
131
132
  MCP Server: ${args.mcpVersion || "Unknown"}
132
133
  API Server: ${args.apiVersion || "Unknown"}
133
134
 
135
+ ${args.apiVersion ? "" : "You might be using an old tox file from before v1.3.0 released on December 1, 2025, or the TouchDesigner setup might not be done correctly."}
136
+ ${args.mcpVersion ? "" : "The MCP server version could not be determined. You might be using an outdated MCP server."}
137
+
134
138
  Version information is required to ensure compatibility between the MCP server and TouchDesigner components.
135
- Please ensure both components are updated to compatible versions.
139
+ Please ensure both components are updated to latest versions.
136
140
 
137
141
  ${updateGuide}
138
142
  `.trim();
@@ -148,7 +152,7 @@ MCP Server: ${args.mcpVersion}
148
152
  API Server: ${args.apiVersion}
149
153
 
150
154
  MAJOR version mismatch indicates breaking changes.
151
- Both components must be updated to compatible versions.
155
+ Please ensure both components are updated to compatible versions.
152
156
 
153
157
  ${updateGuide}
154
158
  `.trim();
@@ -75,14 +75,7 @@ export function registerTdTools(server, logger, tdClient) {
75
75
  detailLevel: detailLevel ?? "summary",
76
76
  responseFormat,
77
77
  });
78
- return {
79
- content: [
80
- {
81
- text: formattedText,
82
- type: "text",
83
- },
84
- ],
85
- };
78
+ return createToolResult(tdClient, formattedText);
86
79
  }
87
80
  catch (error) {
88
81
  return handleToolError(error, logger, TOOL_NAMES.GET_TD_INFO);
@@ -104,14 +97,7 @@ export function registerTdTools(server, logger, tdClient) {
104
97
  detailLevel: detailLevel ?? "summary",
105
98
  responseFormat,
106
99
  });
107
- return {
108
- content: [
109
- {
110
- text: formattedText,
111
- type: "text",
112
- },
113
- ],
114
- };
100
+ return createToolResult(tdClient, formattedText);
115
101
  }
116
102
  catch (error) {
117
103
  return handleToolError(error, logger, TOOL_NAMES.EXECUTE_PYTHON_SCRIPT);
@@ -128,14 +114,7 @@ export function registerTdTools(server, logger, tdClient) {
128
114
  detailLevel: detailLevel ?? "summary",
129
115
  responseFormat,
130
116
  });
131
- return {
132
- content: [
133
- {
134
- text: formattedText,
135
- type: "text",
136
- },
137
- ],
138
- };
117
+ return createToolResult(tdClient, formattedText);
139
118
  }
140
119
  catch (error) {
141
120
  return handleToolError(error, logger, TOOL_NAMES.CREATE_TD_NODE, REFERENCE_COMMENT);
@@ -152,14 +131,7 @@ export function registerTdTools(server, logger, tdClient) {
152
131
  detailLevel: detailLevel ?? "summary",
153
132
  responseFormat,
154
133
  });
155
- return {
156
- content: [
157
- {
158
- text: formattedText,
159
- type: "text",
160
- },
161
- ],
162
- };
134
+ return createToolResult(tdClient, formattedText);
163
135
  }
164
136
  catch (error) {
165
137
  return handleToolError(error, logger, TOOL_NAMES.DELETE_TD_NODE, REFERENCE_COMMENT);
@@ -181,14 +153,7 @@ export function registerTdTools(server, logger, tdClient) {
181
153
  limit,
182
154
  responseFormat,
183
155
  });
184
- return {
185
- content: [
186
- {
187
- text: formattedText,
188
- type: "text",
189
- },
190
- ],
191
- };
156
+ return createToolResult(tdClient, formattedText);
192
157
  }
193
158
  catch (error) {
194
159
  return handleToolError(error, logger, TOOL_NAMES.GET_TD_NODES, REFERENCE_COMMENT);
@@ -207,14 +172,7 @@ export function registerTdTools(server, logger, tdClient) {
207
172
  limit,
208
173
  responseFormat,
209
174
  });
210
- return {
211
- content: [
212
- {
213
- text: formattedText,
214
- type: "text",
215
- },
216
- ],
217
- };
175
+ return createToolResult(tdClient, formattedText);
218
176
  }
219
177
  catch (error) {
220
178
  return handleToolError(error, logger, TOOL_NAMES.GET_TD_NODE_PARAMETERS, REFERENCE_COMMENT);
@@ -232,14 +190,7 @@ export function registerTdTools(server, logger, tdClient) {
232
190
  limit,
233
191
  responseFormat,
234
192
  });
235
- return {
236
- content: [
237
- {
238
- text: formattedText,
239
- type: "text",
240
- },
241
- ],
242
- };
193
+ return createToolResult(tdClient, formattedText);
243
194
  }
244
195
  catch (error) {
245
196
  return handleToolError(error, logger, TOOL_NAMES.GET_TD_NODE_ERRORS, REFERENCE_COMMENT);
@@ -256,14 +207,7 @@ export function registerTdTools(server, logger, tdClient) {
256
207
  detailLevel: detailLevel ?? "summary",
257
208
  responseFormat,
258
209
  });
259
- return {
260
- content: [
261
- {
262
- text: formattedText,
263
- type: "text",
264
- },
265
- ],
266
- };
210
+ return createToolResult(tdClient, formattedText);
267
211
  }
268
212
  catch (error) {
269
213
  return handleToolError(error, logger, TOOL_NAMES.UPDATE_TD_NODE_PARAMETERS, REFERENCE_COMMENT);
@@ -278,14 +222,7 @@ export function registerTdTools(server, logger, tdClient) {
278
222
  throw result.error;
279
223
  }
280
224
  const formattedText = formatExecNodeMethodResult(result.data, { args, kwargs, method, nodePath }, { detailLevel: detailLevel ?? "summary", responseFormat });
281
- return {
282
- content: [
283
- {
284
- text: formattedText,
285
- type: "text",
286
- },
287
- ],
288
- };
225
+ return createToolResult(tdClient, formattedText);
289
226
  }
290
227
  catch (error) {
291
228
  logger.sendLog({
@@ -307,14 +244,7 @@ export function registerTdTools(server, logger, tdClient) {
307
244
  limit: params.limit ?? 50,
308
245
  responseFormat: params.responseFormat,
309
246
  });
310
- return {
311
- content: [
312
- {
313
- text: formattedText,
314
- type: "text",
315
- },
316
- ],
317
- };
247
+ return createToolResult(tdClient, formattedText);
318
248
  }
319
249
  catch (error) {
320
250
  return handleToolError(error, logger, TOOL_NAMES.GET_TD_CLASSES, REFERENCE_COMMENT);
@@ -333,14 +263,7 @@ export function registerTdTools(server, logger, tdClient) {
333
263
  limit: limit ?? 30,
334
264
  responseFormat,
335
265
  });
336
- return {
337
- content: [
338
- {
339
- text: formattedText,
340
- type: "text",
341
- },
342
- ],
343
- };
266
+ return createToolResult(tdClient, formattedText);
344
267
  }
345
268
  catch (error) {
346
269
  return handleToolError(error, logger, TOOL_NAMES.GET_TD_CLASS_DETAILS, REFERENCE_COMMENT);
@@ -357,20 +280,26 @@ export function registerTdTools(server, logger, tdClient) {
357
280
  detailLevel: detailLevel ?? "summary",
358
281
  responseFormat,
359
282
  });
360
- return {
361
- content: [
362
- {
363
- text: formattedText,
364
- type: "text",
365
- },
366
- ],
367
- };
283
+ return createToolResult(tdClient, formattedText);
368
284
  }
369
285
  catch (error) {
370
286
  return handleToolError(error, logger, TOOL_NAMES.GET_TD_MODULE_HELP);
371
287
  }
372
288
  });
373
289
  }
290
+ const createToolResult = (tdClient, text) => {
291
+ const content = [
292
+ {
293
+ text,
294
+ type: "text",
295
+ },
296
+ ];
297
+ const additionalContents = tdClient.getAdditionalToolResultContents();
298
+ if (additionalContents) {
299
+ content.push(...additionalContents);
300
+ }
301
+ return { content };
302
+ };
374
303
  function matchesMetadataFilter(entry, keyword) {
375
304
  const normalizedKeyword = keyword.toLowerCase();
376
305
  const haystacks = [
@@ -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.3.0
6
+ * OpenAPI spec version: 1.4.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.3.0
6
+ * OpenAPI spec version: 1.4.1
7
7
  */
8
8
  import * as zod from 'zod';
9
9
  /**
@@ -20,7 +20,8 @@ const defaultApiClient = {
20
20
  getTdPythonClasses: apiGetTdPythonClasses,
21
21
  updateNode: apiUpdateNode,
22
22
  };
23
- export const ERROR_CACHE_TTL_MS = 5000; // 5 seconds
23
+ export const ERROR_CACHE_TTL_MS = 60 * 1000; // 60 seconds
24
+ export const SUCCESS_CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
24
25
  /**
25
26
  * Null logger implementation that discards all logs
26
27
  */
@@ -57,16 +58,14 @@ function handleApiResponse(response) {
57
58
  }
58
59
  return { data, success: true };
59
60
  }
60
- /**
61
- * TouchDesigner client implementation with dependency injection
62
- * for better testability and separation of concerns
63
- */
64
61
  export class TouchDesignerClient {
65
62
  logger;
66
63
  api;
67
64
  verifiedCompatibilityError;
68
65
  cachedCompatibilityCheck;
69
66
  errorCacheTimestamp;
67
+ successCacheTimestamp;
68
+ compatibilityNotice;
70
69
  /**
71
70
  * Initialize TouchDesigner client with optional dependencies
72
71
  */
@@ -76,6 +75,8 @@ export class TouchDesignerClient {
76
75
  this.verifiedCompatibilityError = null;
77
76
  this.cachedCompatibilityCheck = false;
78
77
  this.errorCacheTimestamp = null;
78
+ this.successCacheTimestamp = null;
79
+ this.compatibilityNotice = null;
79
80
  }
80
81
  /**
81
82
  * Log debug message
@@ -98,13 +99,56 @@ export class TouchDesignerClient {
98
99
  const now = Date.now();
99
100
  return now - this.errorCacheTimestamp >= ERROR_CACHE_TTL_MS;
100
101
  }
102
+ /**
103
+ * Check whether the cached successful compatibility check is still valid
104
+ */
105
+ hasValidSuccessCache() {
106
+ if (!this.cachedCompatibilityCheck || !this.successCacheTimestamp) {
107
+ return false;
108
+ }
109
+ const now = Date.now();
110
+ return now - this.successCacheTimestamp < SUCCESS_CACHE_TTL_MS;
111
+ }
112
+ /**
113
+ * Force the next API call to re-run compatibility verification.
114
+ * Useful when the user explicitly requests version information.
115
+ */
116
+ invalidateCompatibilityCache(reason) {
117
+ if (this.cachedCompatibilityCheck) {
118
+ this.logDebug("Invalidating cached compatibility check", { reason });
119
+ }
120
+ this.cachedCompatibilityCheck = false;
121
+ this.successCacheTimestamp = null;
122
+ this.verifiedCompatibilityError = null;
123
+ this.errorCacheTimestamp = null;
124
+ this.compatibilityNotice = null;
125
+ }
126
+ getAdditionalToolResultContents() {
127
+ if (!this.compatibilityNotice) {
128
+ return null;
129
+ }
130
+ return [
131
+ {
132
+ annotations: {
133
+ audience: ["user", "assistant"],
134
+ priority: this.compatibilityNotice.level === "warning" ? 0.2 : 0.1,
135
+ },
136
+ text: this.compatibilityNotice.message,
137
+ type: "text",
138
+ },
139
+ ];
140
+ }
101
141
  /**
102
142
  * Verify compatibility with the TouchDesigner server
103
143
  */
104
144
  async verifyCompatibility() {
105
145
  // If we've already verified compatibility successfully, skip re-verification
106
146
  if (this.cachedCompatibilityCheck && !this.verifiedCompatibilityError) {
107
- return;
147
+ if (this.hasValidSuccessCache()) {
148
+ return;
149
+ }
150
+ this.logDebug("Compatibility cache expired, re-verifying...");
151
+ this.invalidateCompatibilityCache("success cache expired");
108
152
  }
109
153
  // Clear cached error if TTL has expired
110
154
  if (this.verifiedCompatibilityError && this.shouldClearErrorCache()) {
@@ -129,9 +173,20 @@ export class TouchDesignerClient {
129
173
  }
130
174
  const result = await this.verifyVersionCompatibility();
131
175
  if (result.success) {
176
+ const compatibilityInfo = result.data;
132
177
  this.verifiedCompatibilityError = null;
133
178
  this.errorCacheTimestamp = null;
134
179
  this.cachedCompatibilityCheck = true;
180
+ this.successCacheTimestamp = Date.now();
181
+ if (compatibilityInfo.level === "warning" && compatibilityInfo.message) {
182
+ this.compatibilityNotice = {
183
+ level: compatibilityInfo.level,
184
+ message: compatibilityInfo.message,
185
+ };
186
+ }
187
+ else {
188
+ this.compatibilityNotice = null;
189
+ }
135
190
  this.logDebug("Compatibility verified successfully");
136
191
  return;
137
192
  }
@@ -142,6 +197,8 @@ export class TouchDesignerClient {
142
197
  this.verifiedCompatibilityError = result.error;
143
198
  this.errorCacheTimestamp = Date.now();
144
199
  this.cachedCompatibilityCheck = false;
200
+ this.successCacheTimestamp = null;
201
+ this.compatibilityNotice = null;
145
202
  throw result.error;
146
203
  }
147
204
  /**
@@ -173,6 +230,7 @@ export class TouchDesignerClient {
173
230
  * Get TouchDesigner server information
174
231
  */
175
232
  async getTdInfo() {
233
+ this.invalidateCompatibilityCache("tdInfo request");
176
234
  return this.apiCall("Getting server info", () => this.api.getTdInfo());
177
235
  }
178
236
  /**
@@ -295,7 +353,10 @@ export class TouchDesignerClient {
295
353
  if (result.level === "error") {
296
354
  return createErrorResult(new Error(result.message));
297
355
  }
298
- return createSuccessResult(undefined);
356
+ return createSuccessResult({
357
+ level: result.level,
358
+ message: result.message,
359
+ });
299
360
  }
300
361
  /**
301
362
  * Format connection errors with helpful messages
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "touchdesigner-mcp-server",
3
- "version": "1.4.0",
3
+ "version": "1.4.2",
4
4
  "description": "MCP server for TouchDesigner",
5
5
  "repository": {
6
6
  "type": "git",