touchdesigner-mcp-server 1.1.2 → 1.3.0
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 +25 -5
- package/README.md +25 -5
- package/dist/cli.js +2 -2
- package/dist/core/constants.js +5 -3
- package/dist/core/errorHandling.js +18 -3
- package/dist/core/logger.js +10 -32
- package/dist/core/result.js +2 -2
- package/dist/core/version.js +4 -0
- package/dist/features/prompts/handlers/td_prompts.js +24 -18
- package/dist/features/tools/handlers/tdTools.js +75 -18
- package/dist/features/tools/metadata/touchDesignerToolMetadata.js +246 -172
- package/dist/features/tools/presenter/classListFormatter.js +22 -22
- package/dist/features/tools/presenter/index.js +2 -0
- package/dist/features/tools/presenter/moduleHelpFormatter.js +315 -0
- package/dist/features/tools/presenter/nodeDetailsFormatter.js +17 -17
- package/dist/features/tools/presenter/nodeErrorsFormatter.js +68 -0
- package/dist/features/tools/presenter/nodeListFormatter.js +12 -12
- package/dist/features/tools/presenter/operationFormatter.js +10 -10
- package/dist/features/tools/presenter/responseFormatter.js +4 -4
- package/dist/features/tools/presenter/scriptResultFormatter.js +14 -14
- package/dist/features/tools/presenter/toolMetadataFormatter.js +8 -8
- package/dist/gen/endpoints/TouchDesignerAPI.js +22 -4
- package/dist/gen/mcp/touchDesignerAPI.zod.js +52 -12
- package/dist/server/connectionManager.js +11 -28
- package/dist/server/touchDesignerServer.js +4 -3
- package/dist/tdClient/touchDesignerClient.js +127 -26
- package/package.json +25 -18
package/README.ja.md
CHANGED
|
@@ -52,14 +52,14 @@ flowchart LR
|
|
|
52
52
|
## 利用方法
|
|
53
53
|
|
|
54
54
|
<details>
|
|
55
|
-
<summary>方法1: Claude Desktop +
|
|
55
|
+
<summary>方法1: Claude Desktop + MCP Bundle(推奨)</summary>
|
|
56
56
|
|
|
57
57
|
##### 1. ファイルをダウンロード
|
|
58
58
|
|
|
59
59
|
[リリースページ](https://github.com/8beeeaaat/touchdesigner-mcp/releases/latest)から以下をダウンロード:
|
|
60
60
|
|
|
61
61
|
- **TouchDesigner Components**: `touchdesigner-mcp-td.zip`
|
|
62
|
-
- **
|
|
62
|
+
- **[MCP Bundle](https://github.com/modelcontextprotocol/mcpb) (.mcpb)**: `touchdesigner-mcp.mcpb`
|
|
63
63
|
|
|
64
64
|
##### 2. TouchDesignerコンポーネントを設置
|
|
65
65
|
|
|
@@ -73,13 +73,13 @@ flowchart LR
|
|
|
73
73
|
|
|
74
74
|

|
|
75
75
|
|
|
76
|
-
##### 3.
|
|
76
|
+
##### 3. MCP Bundleをインストール
|
|
77
77
|
|
|
78
|
-
`touchdesigner-mcp.
|
|
78
|
+
`touchdesigner-mcp.mcpb`ファイルをダブルクリックしてClaude DesktopにMCP Bundleをインストール
|
|
79
79
|
|
|
80
80
|
<https://github.com/user-attachments/assets/0786d244-8b82-4387-bbe4-9da048212854>
|
|
81
81
|
|
|
82
|
-
##### 4.
|
|
82
|
+
##### 4. MCP Bundleが自動的にTouchDesignerサーバー接続を処理
|
|
83
83
|
|
|
84
84
|
**⚠️ 重要:** TouchDesignerコンポーネントのディレクトリ構造は展開した状態を正確に保持してください。`mcp_webserver_base.tox`コンポーネントは`modules/`ディレクトリやその他のファイルへの相対パスを参照しています。
|
|
85
85
|
|
|
@@ -250,9 +250,11 @@ td/
|
|
|
250
250
|
| `delete_td_node` | 既存のノードを削除します。 |
|
|
251
251
|
| `exec_node_method` | ノードに対してPythonメソッドを呼び出します。 |
|
|
252
252
|
| `execute_python_script` | TD内で任意のPythonスクリプトを実行します。 |
|
|
253
|
+
| `get_module_help` | TouchDesignerモジュール/クラスのPython help()ドキュメントを取得します。 |
|
|
253
254
|
| `get_td_class_details` | TD Pythonクラス/モジュールの詳細情報を取得します。 |
|
|
254
255
|
| `get_td_classes` | TouchDesigner Pythonクラスのリストを取得します。 |
|
|
255
256
|
| `get_td_info` | TDサーバー環境に関する情報を取得します。 |
|
|
257
|
+
| `get_td_node_errors` | 指定されたノードとその子ノードのエラーをチェックします。 |
|
|
256
258
|
| `get_td_node_parameters` | 特定ノードのパラメータを取得します。 |
|
|
257
259
|
| `get_td_nodes` | 親パス内のノードを取得します(オプションでフィルタリング)。 |
|
|
258
260
|
| `update_td_node_parameters` | 特定ノードのパラメータを更新します。 |
|
|
@@ -354,6 +356,24 @@ td/
|
|
|
354
356
|
|
|
355
357
|
ビルドプロセス (`npm run build`) は、必要なすべての生成ステップ (`npm run gen`) を実行し、その後にTypeScriptコンパイル (`tsc`) を行います。
|
|
356
358
|
|
|
359
|
+
### バージョン管理
|
|
360
|
+
|
|
361
|
+
- `package.json` はすべてのコンポーネントバージョンの唯一の信頼できる情報源です(Node.js MCPサーバー、TouchDesigner Python API、MCPバンドル、および `server.json` メタデータ)。
|
|
362
|
+
- バージョンを更新する際は `npm version <patch|minor|major>`(または内部で使用される `npm run gen:version`)を実行してください。このスクリプトは `pyproject.toml`、`td/modules/utils/version.py`、`mcpb/manifest.json`、および `server.json` を書き換え、リリースワークフローがタグ値を信頼できるようにします。
|
|
363
|
+
- GitHubリリースワークフロー(`.github/workflows/release.yml`)はコミットを `v${version}` としてタグ付けし、同じバージョン番号から `touchdesigner-mcp-td.zip` / `touchdesigner-mcp.mcpb` を公開します。リリースをトリガーする前に必ず同期ステップを実行し、すべてのアーティファクトが整合するようにしてください。
|
|
364
|
+
|
|
365
|
+
## トラブルシューティング
|
|
366
|
+
|
|
367
|
+
### バージョン互換性のトラブルシューティング
|
|
368
|
+
|
|
369
|
+
- `ConnectionManager.connect` の実行中、MCPサーバーは自身のバージョンと `getTdInfo` が報告するTouchDesigner APIサーバーのバージョンを比較します。APIサーバーがバージョンを公開していない場合、またはバージョンが異なる場合(例:片方のみが更新された場合)、MCPサーバーはClaude/Codexコンソールおよび TouchDesigner ログ DAT に説明的なエラーメッセージを表示して接続を中止します。
|
|
370
|
+
- 不一致を解決するには、TouchDesignerコンポーネントを再インストールしてください:
|
|
371
|
+
1. リリースページから最新の [touchdesigner-mcp-td.zip](https://github.com/8beeeaaat/touchdesigner-mcp/releases/latest/download/touchdesigner-mcp-td.zip) をダウンロードします。
|
|
372
|
+
2. 既存の `touchdesigner-mcp-td` フォルダを削除し、新しく展開した内容に置き換えます。
|
|
373
|
+
3. TouchDesignerプロジェクトから古い `mcp_webserver_base` コンポーネントを削除し、新しいフォルダから `.tox` をインポートします。
|
|
374
|
+
4. TouchDesignerとMCPサーバーを実行しているAIエージェント(例:Claude Desktop)を再起動します。
|
|
375
|
+
- ローカルで開発している場合は、`package.json` を編集した後に `npm run gen:version` を実行してください(または単に `npm version ...` を使用してください)。これにより、Python API(`pyproject.toml` + `td/modules/utils/version.py`)、MCPバンドルマニフェスト、およびレジストリメタデータが同期され、ランタイム互換性チェックが成功するようになります。
|
|
376
|
+
|
|
357
377
|
## 開発で貢献
|
|
358
378
|
|
|
359
379
|
ぜひ一緒に改善しましょう!
|
package/README.md
CHANGED
|
@@ -52,14 +52,14 @@ flowchart LR
|
|
|
52
52
|
## Usage
|
|
53
53
|
|
|
54
54
|
<details>
|
|
55
|
-
<summary>Method 1: Using Claude Desktop and
|
|
55
|
+
<summary>Method 1: Using Claude Desktop and MCP Bundle (Recommended)</summary>
|
|
56
56
|
|
|
57
57
|
### 1. Download Files
|
|
58
58
|
|
|
59
59
|
Download the following from the [releases page](https://github.com/8beeeaaat/touchdesigner-mcp/releases/latest):
|
|
60
60
|
|
|
61
61
|
- **TouchDesigner Components**: `touchdesigner-mcp-td.zip`
|
|
62
|
-
- **
|
|
62
|
+
- **[MCP Bundle](https://github.com/modelcontextprotocol/mcpb) (.mcpb)**: `touchdesigner-mcp.mcpb`
|
|
63
63
|
|
|
64
64
|
### 2. Set up TouchDesigner Components
|
|
65
65
|
|
|
@@ -73,15 +73,15 @@ Download the following from the [releases page](https://github.com/8beeeaaat/tou
|
|
|
73
73
|
|
|
74
74
|

|
|
75
75
|
|
|
76
|
-
### 3. Install the
|
|
76
|
+
### 3. Install the MCP Bundle
|
|
77
77
|
|
|
78
|
-
Double-click the `touchdesigner-mcp.
|
|
78
|
+
Double-click the `touchdesigner-mcp.mcpb` file to install the bundle in Claude Desktop.
|
|
79
79
|
|
|
80
80
|
<https://github.com/user-attachments/assets/0786d244-8b82-4387-bbe4-9da048212854>
|
|
81
81
|
|
|
82
82
|
### 4. Connect to the Server
|
|
83
83
|
|
|
84
|
-
The
|
|
84
|
+
The MCP bundle will automatically handle the connection to the TouchDesigner server.
|
|
85
85
|
|
|
86
86
|
**⚠️ Important:** The directory structure must be preserved exactly as extracted. The `mcp_webserver_base.tox` component references relative paths to the `modules/` directory and other files.
|
|
87
87
|
|
|
@@ -250,9 +250,11 @@ Tools allow AI agents to perform actions in TouchDesigner.
|
|
|
250
250
|
| `delete_td_node` | Deletes an existing node. |
|
|
251
251
|
| `exec_node_method` | Calls a Python method on a node. |
|
|
252
252
|
| `execute_python_script` | Executes an arbitrary Python script in TouchDesigner. |
|
|
253
|
+
| `get_module_help` | Gets Python help() documentation for TouchDesigner modules/classes.|
|
|
253
254
|
| `get_td_class_details` | Gets details of a TouchDesigner Python class or module. |
|
|
254
255
|
| `get_td_classes` | Gets a list of TouchDesigner Python classes. |
|
|
255
256
|
| `get_td_info` | Gets information about the TouchDesigner server environment. |
|
|
257
|
+
| `get_td_node_errors` | Checks for errors on a specified node and its children. |
|
|
256
258
|
| `get_td_node_parameters`| Gets the parameters of a specific node. |
|
|
257
259
|
| `get_td_nodes` | Gets nodes under a parent path, with optional filtering. |
|
|
258
260
|
| `update_td_node_parameters` | Updates the parameters of a specific node. |
|
|
@@ -354,6 +356,24 @@ This project uses OpenAPI-based code generation tools (Orval and openapi-generat
|
|
|
354
356
|
|
|
355
357
|
The build process (`npm run build`) runs all necessary generation steps (`npm run gen`), followed by TypeScript compilation (`tsc`).
|
|
356
358
|
|
|
359
|
+
### Version management
|
|
360
|
+
|
|
361
|
+
- `package.json` is the single source of truth for every component version (Node.js MCP server, TouchDesigner Python API, MCP bundle, and `server.json` metadata).
|
|
362
|
+
- Run `npm version <patch|minor|major>` (or the underlying `npm run gen:version`) whenever you bump the version. The script rewrites `pyproject.toml`, `td/modules/utils/version.py`, `mcpb/manifest.json`, and `server.json` so that the release workflow can trust the tag value.
|
|
363
|
+
- The GitHub release workflow (`.github/workflows/release.yml`) tags the commit as `v${version}` and publishes `touchdesigner-mcp-td.zip` / `touchdesigner-mcp.mcpb` from the exact same version number. Always run the sync step before triggering a release so that every artifact stays aligned.
|
|
364
|
+
|
|
365
|
+
## Troubleshooting
|
|
366
|
+
|
|
367
|
+
### Troubleshooting version compatibility
|
|
368
|
+
|
|
369
|
+
- During `ConnectionManager.connect` the MCP server compares its own version with the TouchDesigner API server version reported by `getTdInfo`. If the API server does not expose a version or the versions differ (for example because only one side was updated), the MCP server aborts the connection with a descriptive error message in the Claude/Codex console and in the TouchDesigner log DAT.
|
|
370
|
+
- To resolve the mismatch, reinstall both the TouchDesigner components
|
|
371
|
+
1. Download the latest [touchdesigner-mcp-td.zip](https://github.com/8beeeaaat/touchdesigner-mcp/releases/latest/download/touchdesigner-mcp-td.zip) from the releases page.
|
|
372
|
+
2. Delete the existing \`touchdesigner-mcp-td\` folder and replace it with the newly extracted contents.
|
|
373
|
+
3. Remove the old \`mcp_webserver_base\` component from your TouchDesigner project and import the \`.tox\` from the new folder.
|
|
374
|
+
4. Restart TouchDesigner and the AI agent running the MCP server (e.g., Claude Desktop).
|
|
375
|
+
- When developing locally, run `npm run gen: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.
|
|
376
|
+
|
|
357
377
|
## Contributing
|
|
358
378
|
|
|
359
379
|
We welcome your contributions!
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
3
|
import { TouchDesignerServer } from "./server/touchDesignerServer.js";
|
|
4
|
-
// Note: Environment variables should be set by the
|
|
4
|
+
// Note: Environment variables should be set by the MCP Bundle runtime or CLI arguments
|
|
5
5
|
const DEFAULT_HOST = "http://127.0.0.1";
|
|
6
6
|
const DEFAULT_PORT = 9981;
|
|
7
7
|
/**
|
|
@@ -59,8 +59,8 @@ export async function startServer(params) {
|
|
|
59
59
|
}
|
|
60
60
|
// Start server if this file is executed directly
|
|
61
61
|
startServer({
|
|
62
|
-
nodeEnv: process.env.NODE_ENV,
|
|
63
62
|
argv: process.argv,
|
|
63
|
+
nodeEnv: process.env.NODE_ENV,
|
|
64
64
|
}).catch((error) => {
|
|
65
65
|
console.error("Failed to start server:", error);
|
|
66
66
|
if (process.env.NODE_ENV === "test")
|
package/dist/core/constants.js
CHANGED
|
@@ -10,18 +10,20 @@ export const TOOL_NAMES = {
|
|
|
10
10
|
CREATE_TD_NODE: "create_td_node",
|
|
11
11
|
DELETE_TD_NODE: "delete_td_node",
|
|
12
12
|
DESCRIBE_TD_TOOLS: "describe_td_tools",
|
|
13
|
-
EXECUTE_PYTHON_SCRIPT: "execute_python_script",
|
|
14
13
|
EXECUTE_NODE_METHOD: "exec_node_method",
|
|
15
|
-
|
|
14
|
+
EXECUTE_PYTHON_SCRIPT: "execute_python_script",
|
|
16
15
|
GET_TD_CLASS_DETAILS: "get_td_class_details",
|
|
17
16
|
GET_TD_CLASSES: "get_td_classes",
|
|
17
|
+
GET_TD_INFO: "get_td_info",
|
|
18
|
+
GET_TD_MODULE_HELP: "get_td_module_help",
|
|
19
|
+
GET_TD_NODE_ERRORS: "get_td_node_errors",
|
|
18
20
|
GET_TD_NODE_PARAMETERS: "get_td_node_parameters",
|
|
19
21
|
GET_TD_NODES: "get_td_nodes",
|
|
20
22
|
UPDATE_TD_NODE_PARAMETERS: "update_td_node_parameters",
|
|
21
23
|
};
|
|
22
24
|
export const REFERENCE_COMMENT = `Check reference resources: ${TD_PYTHON_CLASS_REFERENCE_INDEX_URL}`;
|
|
23
25
|
export const PROMPT_NAMES = {
|
|
24
|
-
SEARCH_NODE: "Search node",
|
|
25
26
|
CHECK_NODE_ERRORS: "Check node errors",
|
|
26
27
|
NODE_CONNECTION: "Node connection",
|
|
28
|
+
SEARCH_NODE: "Search node",
|
|
27
29
|
};
|
|
@@ -5,15 +5,30 @@ export function handleToolError(error, logger, toolName, referenceComment) {
|
|
|
5
5
|
const formattedError = error instanceof Error
|
|
6
6
|
? error
|
|
7
7
|
: new Error(error === null ? "Null error received" : String(error));
|
|
8
|
-
|
|
8
|
+
const logData = {
|
|
9
|
+
error: formattedError.message,
|
|
10
|
+
message: "Tool execution failed",
|
|
11
|
+
toolName,
|
|
12
|
+
};
|
|
13
|
+
if (referenceComment) {
|
|
14
|
+
logData.referenceComment = referenceComment;
|
|
15
|
+
}
|
|
16
|
+
if (formattedError.stack) {
|
|
17
|
+
logData.stack = formattedError.stack;
|
|
18
|
+
}
|
|
19
|
+
logger.sendLog({
|
|
20
|
+
data: logData,
|
|
21
|
+
level: "error",
|
|
22
|
+
logger: "ErrorHandling",
|
|
23
|
+
});
|
|
9
24
|
const errorMessage = `${toolName}: ${formattedError}${referenceComment ? `. ${referenceComment}` : ""}`;
|
|
10
25
|
return {
|
|
11
|
-
isError: true,
|
|
12
26
|
content: [
|
|
13
27
|
{
|
|
14
|
-
type: "text",
|
|
15
28
|
text: errorMessage,
|
|
29
|
+
type: "text",
|
|
16
30
|
},
|
|
17
31
|
],
|
|
32
|
+
isError: true,
|
|
18
33
|
};
|
|
19
34
|
}
|
package/dist/core/logger.js
CHANGED
|
@@ -7,39 +7,17 @@ export class McpLogger {
|
|
|
7
7
|
constructor(server) {
|
|
8
8
|
this.server = server;
|
|
9
9
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
warn(...args) {
|
|
20
|
-
this.sendLog("warning", args);
|
|
21
|
-
}
|
|
22
|
-
// biome-ignore lint/suspicious/noExplicitAny: logging accepts any type
|
|
23
|
-
error(...args) {
|
|
24
|
-
this.sendLog("error", args);
|
|
25
|
-
}
|
|
26
|
-
sendLog(level,
|
|
27
|
-
// biome-ignore lint/suspicious/noExplicitAny: logging accepts any type
|
|
28
|
-
args) {
|
|
29
|
-
for (const arg of args) {
|
|
30
|
-
try {
|
|
31
|
-
this.server.server.sendLoggingMessage({
|
|
32
|
-
level,
|
|
33
|
-
data: arg,
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
catch (error) {
|
|
37
|
-
if (error instanceof Error && error.message === "Not connected") {
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
console.error(`Failed to send log to MCP server: ${error instanceof Error ? error.message : String(error)}`);
|
|
41
|
-
console[level === "warning" ? "warn" : level]?.(arg);
|
|
10
|
+
sendLog(args) {
|
|
11
|
+
try {
|
|
12
|
+
this.server.server.sendLoggingMessage({
|
|
13
|
+
...args,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
if (error instanceof Error && error.message === "Not connected") {
|
|
18
|
+
return;
|
|
42
19
|
}
|
|
20
|
+
console.error(`Failed to send log to MCP server: ${error instanceof Error ? error.message : String(error)}`);
|
|
43
21
|
}
|
|
44
22
|
}
|
|
45
23
|
}
|
package/dist/core/result.js
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* Creates a success result with the provided data
|
|
3
3
|
*/
|
|
4
4
|
export function createSuccessResult(data) {
|
|
5
|
-
return { success: true
|
|
5
|
+
return { data, success: true };
|
|
6
6
|
}
|
|
7
7
|
/**
|
|
8
8
|
* Creates an error result with the provided error
|
|
9
9
|
*/
|
|
10
10
|
export function createErrorResult(error) {
|
|
11
|
-
return { success: false
|
|
11
|
+
return { error, success: false };
|
|
12
12
|
}
|
|
@@ -2,40 +2,40 @@ import { GetPromptRequestSchema, ListPromptsRequestSchema, } from "@modelcontext
|
|
|
2
2
|
import { PROMPT_NAMES, TOOL_NAMES } from "../../../core/constants.js";
|
|
3
3
|
const PROMPTS = [
|
|
4
4
|
{
|
|
5
|
-
name: PROMPT_NAMES.SEARCH_NODE,
|
|
6
|
-
description: "Fuzzy search for node",
|
|
7
5
|
arguments: [
|
|
8
6
|
{
|
|
9
|
-
name: "nodeName",
|
|
10
7
|
description: "Name of the node to check",
|
|
8
|
+
name: "nodeName",
|
|
11
9
|
required: true,
|
|
12
10
|
},
|
|
13
11
|
{
|
|
14
|
-
name: "nodeFamily",
|
|
15
12
|
description: "Family of the node to check",
|
|
13
|
+
name: "nodeFamily",
|
|
16
14
|
required: false,
|
|
17
15
|
},
|
|
18
16
|
{
|
|
19
|
-
name: "nodeType",
|
|
20
17
|
description: "Type of the node to check",
|
|
18
|
+
name: "nodeType",
|
|
21
19
|
required: false,
|
|
22
20
|
},
|
|
23
21
|
],
|
|
22
|
+
description: "Fuzzy search for node",
|
|
23
|
+
name: PROMPT_NAMES.SEARCH_NODE,
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
|
-
name: PROMPT_NAMES.CHECK_NODE_ERRORS,
|
|
27
|
-
description: "Fuzzy search for node and return errors in TouchDesigner.",
|
|
28
26
|
arguments: [
|
|
29
27
|
{
|
|
30
|
-
name: "nodePath",
|
|
31
28
|
description: "Path to the node to check",
|
|
29
|
+
name: "nodePath",
|
|
32
30
|
required: true,
|
|
33
31
|
},
|
|
34
32
|
],
|
|
33
|
+
description: "Fuzzy search for node and return errors in TouchDesigner.",
|
|
34
|
+
name: PROMPT_NAMES.CHECK_NODE_ERRORS,
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
|
-
name: PROMPT_NAMES.NODE_CONNECTION,
|
|
38
37
|
description: "Connect nodes between each other in TouchDesigner.",
|
|
38
|
+
name: PROMPT_NAMES.NODE_CONNECTION,
|
|
39
39
|
},
|
|
40
40
|
];
|
|
41
41
|
/**
|
|
@@ -49,7 +49,10 @@ export function registerTdPrompts(server, logger) {
|
|
|
49
49
|
});
|
|
50
50
|
server.server.setRequestHandler(GetPromptRequestSchema, (request) => {
|
|
51
51
|
try {
|
|
52
|
-
logger.
|
|
52
|
+
logger.sendLog({
|
|
53
|
+
data: `Handling GetPromptRequest: ${request.params.name}`,
|
|
54
|
+
level: "debug",
|
|
55
|
+
});
|
|
53
56
|
const prompt = getPrompt(request.params.name);
|
|
54
57
|
if (!prompt) {
|
|
55
58
|
throw new Error("Prompt name is required");
|
|
@@ -60,8 +63,8 @@ export function registerTdPrompts(server, logger) {
|
|
|
60
63
|
}
|
|
61
64
|
const { nodeName, nodeFamily, nodeType } = request.params.arguments;
|
|
62
65
|
const messages = handleSearchNodePrompt({
|
|
63
|
-
nodeName,
|
|
64
66
|
nodeFamily,
|
|
67
|
+
nodeName,
|
|
65
68
|
nodeType,
|
|
66
69
|
});
|
|
67
70
|
return { messages };
|
|
@@ -84,7 +87,10 @@ export function registerTdPrompts(server, logger) {
|
|
|
84
87
|
}
|
|
85
88
|
catch (error) {
|
|
86
89
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
87
|
-
logger.
|
|
90
|
+
logger.sendLog({
|
|
91
|
+
data: `Error handling prompt request: ${errorMessage}`,
|
|
92
|
+
level: "error",
|
|
93
|
+
});
|
|
88
94
|
throw new Error(errorMessage);
|
|
89
95
|
}
|
|
90
96
|
});
|
|
@@ -92,33 +98,33 @@ export function registerTdPrompts(server, logger) {
|
|
|
92
98
|
function handleSearchNodePrompt(params) {
|
|
93
99
|
return [
|
|
94
100
|
{
|
|
95
|
-
role: "user",
|
|
96
101
|
content: {
|
|
97
|
-
type: "text",
|
|
98
102
|
text: `Use the "${TOOL_NAMES.GET_TD_NODES}", "${TOOL_NAMES.GET_TD_NODE_PARAMETERS}" tools to search nodes what named "${params.nodeName}" in the TouchDesigner project.${params.nodeType ? ` Node Type: ${params.nodeType}.` : ""}${params.nodeFamily ? ` Node Family: ${params.nodeFamily}.` : ""}`,
|
|
103
|
+
type: "text",
|
|
99
104
|
},
|
|
105
|
+
role: "user",
|
|
100
106
|
},
|
|
101
107
|
];
|
|
102
108
|
}
|
|
103
109
|
function handleCheckNodeErrorsPrompt(params) {
|
|
104
110
|
return [
|
|
105
111
|
{
|
|
106
|
-
role: "user",
|
|
107
112
|
content: {
|
|
113
|
+
text: `Use the "${TOOL_NAMES.GET_TD_NODE_ERRORS}" tool to inspect "${params.nodePath}" (and optionally its children) for error messages. If errors are returned, examine the affected nodes' parameters and connections to resolve them.`,
|
|
108
114
|
type: "text",
|
|
109
|
-
text: `Use the "${TOOL_NAMES.EXECUTE_NODE_METHOD}" like "op('${params.nodePath}').errors()" tool to check node errors. If there are any errors, please check the node parameters and connections. If the node has children, please check the child nodes as well. Please check the node connections and parameters. If the node has children, please check the child nodes as well.`,
|
|
110
115
|
},
|
|
116
|
+
role: "user",
|
|
111
117
|
},
|
|
112
118
|
];
|
|
113
119
|
}
|
|
114
120
|
function handleNodeConnectionPrompt() {
|
|
115
121
|
return [
|
|
116
122
|
{
|
|
117
|
-
role: "user",
|
|
118
123
|
content: {
|
|
119
|
-
type: "text",
|
|
120
124
|
text: `Use the "${TOOL_NAMES.EXECUTE_PYTHON_SCRIPT}" tool e.g. op('/project1/text_over_image').outputConnectors[0].connect(op('/project1/out1'))`,
|
|
125
|
+
type: "text",
|
|
121
126
|
},
|
|
127
|
+
role: "user",
|
|
122
128
|
},
|
|
123
129
|
];
|
|
124
130
|
}
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { REFERENCE_COMMENT, TOOL_NAMES } from "../../../core/constants.js";
|
|
3
3
|
import { handleToolError } from "../../../core/errorHandling.js";
|
|
4
|
-
import { createNodeBody, deleteNodeQueryParams, execNodeMethodBody, execPythonScriptBody, getNodeDetailQueryParams, getNodesQueryParams, getTdPythonClassDetailsParams, updateNodeBody, } from "../../../gen/mcp/touchDesignerAPI.zod.js";
|
|
4
|
+
import { createNodeBody, deleteNodeQueryParams, execNodeMethodBody, execPythonScriptBody, getModuleHelpQueryParams, getNodeDetailQueryParams, getNodeErrorsQueryParams, getNodesQueryParams, getTdPythonClassDetailsParams, updateNodeBody, } from "../../../gen/mcp/touchDesignerAPI.zod.js";
|
|
5
5
|
import { getTouchDesignerToolMetadata } from "../metadata/touchDesignerToolMetadata.js";
|
|
6
|
-
import { formatClassDetails, formatClassList, formatCreateNodeResult, formatDeleteNodeResult, formatExecNodeMethodResult, formatNodeDetails, formatNodeList, formatScriptResult, formatTdInfo, formatToolMetadata, formatUpdateNodeResult, } from "../presenter/index.js";
|
|
6
|
+
import { formatClassDetails, formatClassList, formatCreateNodeResult, formatDeleteNodeResult, formatExecNodeMethodResult, formatModuleHelp, formatNodeDetails, formatNodeErrors, formatNodeList, formatScriptResult, formatTdInfo, formatToolMetadata, formatUpdateNodeResult, } from "../presenter/index.js";
|
|
7
7
|
import { detailOnlyFormattingSchema, formattingOptionsSchema, } from "../types.js";
|
|
8
8
|
const execPythonScriptToolSchema = execPythonScriptBody.extend(detailOnlyFormattingSchema.shape);
|
|
9
9
|
const tdInfoToolSchema = detailOnlyFormattingSchema;
|
|
10
10
|
const getNodesToolSchema = getNodesQueryParams.extend(formattingOptionsSchema.shape);
|
|
11
11
|
const getNodeDetailToolSchema = getNodeDetailQueryParams.extend(formattingOptionsSchema.shape);
|
|
12
|
+
const getNodeErrorsToolSchema = getNodeErrorsQueryParams.extend(formattingOptionsSchema.shape);
|
|
12
13
|
const createNodeToolSchema = createNodeBody.extend(detailOnlyFormattingSchema.shape);
|
|
13
14
|
const updateNodeToolSchema = updateNodeBody.extend(detailOnlyFormattingSchema.shape);
|
|
14
15
|
const deleteNodeToolSchema = deleteNodeQueryParams.extend(detailOnlyFormattingSchema.shape);
|
|
15
16
|
const classListToolSchema = formattingOptionsSchema;
|
|
16
17
|
const classDetailToolSchema = getTdPythonClassDetailsParams.extend(formattingOptionsSchema.shape);
|
|
18
|
+
const moduleHelpToolSchema = getModuleHelpQueryParams.extend(detailOnlyFormattingSchema.shape);
|
|
17
19
|
const execNodeMethodToolSchema = execNodeMethodBody.extend(detailOnlyFormattingSchema.shape);
|
|
18
20
|
const describeToolsSchema = detailOnlyFormattingSchema.extend({
|
|
19
21
|
filter: z
|
|
@@ -38,22 +40,22 @@ export function registerTdTools(server, logger, tdClient) {
|
|
|
38
40
|
return {
|
|
39
41
|
content: [
|
|
40
42
|
{
|
|
41
|
-
type: "text",
|
|
42
43
|
text: message,
|
|
44
|
+
type: "text",
|
|
43
45
|
},
|
|
44
46
|
],
|
|
45
47
|
};
|
|
46
48
|
}
|
|
47
49
|
const formattedText = formatToolMetadata(filteredEntries, {
|
|
48
50
|
detailLevel: detailLevel ?? (filter ? "summary" : "minimal"),
|
|
49
|
-
responseFormat,
|
|
50
51
|
filter: normalizedFilter,
|
|
52
|
+
responseFormat,
|
|
51
53
|
});
|
|
52
54
|
return {
|
|
53
55
|
content: [
|
|
54
56
|
{
|
|
55
|
-
type: "text",
|
|
56
57
|
text: formattedText,
|
|
58
|
+
type: "text",
|
|
57
59
|
},
|
|
58
60
|
],
|
|
59
61
|
};
|
|
@@ -76,8 +78,8 @@ export function registerTdTools(server, logger, tdClient) {
|
|
|
76
78
|
return {
|
|
77
79
|
content: [
|
|
78
80
|
{
|
|
79
|
-
type: "text",
|
|
80
81
|
text: formattedText,
|
|
82
|
+
type: "text",
|
|
81
83
|
},
|
|
82
84
|
],
|
|
83
85
|
};
|
|
@@ -89,7 +91,10 @@ export function registerTdTools(server, logger, tdClient) {
|
|
|
89
91
|
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) => {
|
|
90
92
|
try {
|
|
91
93
|
const { detailLevel, responseFormat, ...scriptParams } = params;
|
|
92
|
-
logger.
|
|
94
|
+
logger.sendLog({
|
|
95
|
+
data: `Executing script: ${scriptParams.script}`,
|
|
96
|
+
level: "debug",
|
|
97
|
+
});
|
|
93
98
|
const result = await tdClient.execPythonScript(scriptParams);
|
|
94
99
|
if (!result.success) {
|
|
95
100
|
throw result.error;
|
|
@@ -102,8 +107,8 @@ export function registerTdTools(server, logger, tdClient) {
|
|
|
102
107
|
return {
|
|
103
108
|
content: [
|
|
104
109
|
{
|
|
105
|
-
type: "text",
|
|
106
110
|
text: formattedText,
|
|
111
|
+
type: "text",
|
|
107
112
|
},
|
|
108
113
|
],
|
|
109
114
|
};
|
|
@@ -126,8 +131,8 @@ export function registerTdTools(server, logger, tdClient) {
|
|
|
126
131
|
return {
|
|
127
132
|
content: [
|
|
128
133
|
{
|
|
129
|
-
type: "text",
|
|
130
134
|
text: formattedText,
|
|
135
|
+
type: "text",
|
|
131
136
|
},
|
|
132
137
|
],
|
|
133
138
|
};
|
|
@@ -150,8 +155,8 @@ export function registerTdTools(server, logger, tdClient) {
|
|
|
150
155
|
return {
|
|
151
156
|
content: [
|
|
152
157
|
{
|
|
153
|
-
type: "text",
|
|
154
158
|
text: formattedText,
|
|
159
|
+
type: "text",
|
|
155
160
|
},
|
|
156
161
|
],
|
|
157
162
|
};
|
|
@@ -179,8 +184,8 @@ export function registerTdTools(server, logger, tdClient) {
|
|
|
179
184
|
return {
|
|
180
185
|
content: [
|
|
181
186
|
{
|
|
182
|
-
type: "text",
|
|
183
187
|
text: formattedText,
|
|
188
|
+
type: "text",
|
|
184
189
|
},
|
|
185
190
|
],
|
|
186
191
|
};
|
|
@@ -205,8 +210,8 @@ export function registerTdTools(server, logger, tdClient) {
|
|
|
205
210
|
return {
|
|
206
211
|
content: [
|
|
207
212
|
{
|
|
208
|
-
type: "text",
|
|
209
213
|
text: formattedText,
|
|
214
|
+
type: "text",
|
|
210
215
|
},
|
|
211
216
|
],
|
|
212
217
|
};
|
|
@@ -215,6 +220,31 @@ export function registerTdTools(server, logger, tdClient) {
|
|
|
215
220
|
return handleToolError(error, logger, TOOL_NAMES.GET_TD_NODE_PARAMETERS, REFERENCE_COMMENT);
|
|
216
221
|
}
|
|
217
222
|
});
|
|
223
|
+
server.tool(TOOL_NAMES.GET_TD_NODE_ERRORS, "Check node and descendant errors reported by TouchDesigner", getNodeErrorsToolSchema.strict().shape, async (params) => {
|
|
224
|
+
try {
|
|
225
|
+
const { detailLevel, limit, responseFormat, ...queryParams } = params;
|
|
226
|
+
const result = await tdClient.getNodeErrors(queryParams);
|
|
227
|
+
if (!result.success) {
|
|
228
|
+
throw result.error;
|
|
229
|
+
}
|
|
230
|
+
const formattedText = formatNodeErrors(result.data, {
|
|
231
|
+
detailLevel: detailLevel ?? "summary",
|
|
232
|
+
limit,
|
|
233
|
+
responseFormat,
|
|
234
|
+
});
|
|
235
|
+
return {
|
|
236
|
+
content: [
|
|
237
|
+
{
|
|
238
|
+
text: formattedText,
|
|
239
|
+
type: "text",
|
|
240
|
+
},
|
|
241
|
+
],
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
return handleToolError(error, logger, TOOL_NAMES.GET_TD_NODE_ERRORS, REFERENCE_COMMENT);
|
|
246
|
+
}
|
|
247
|
+
});
|
|
218
248
|
server.tool(TOOL_NAMES.UPDATE_TD_NODE_PARAMETERS, "Update parameters of a specific node in TouchDesigner", updateNodeToolSchema.strict().shape, async (params) => {
|
|
219
249
|
try {
|
|
220
250
|
const { detailLevel, responseFormat, ...updateParams } = params;
|
|
@@ -229,8 +259,8 @@ export function registerTdTools(server, logger, tdClient) {
|
|
|
229
259
|
return {
|
|
230
260
|
content: [
|
|
231
261
|
{
|
|
232
|
-
type: "text",
|
|
233
262
|
text: formattedText,
|
|
263
|
+
type: "text",
|
|
234
264
|
},
|
|
235
265
|
],
|
|
236
266
|
};
|
|
@@ -247,18 +277,21 @@ export function registerTdTools(server, logger, tdClient) {
|
|
|
247
277
|
if (!result.success) {
|
|
248
278
|
throw result.error;
|
|
249
279
|
}
|
|
250
|
-
const formattedText = formatExecNodeMethodResult(result.data, {
|
|
280
|
+
const formattedText = formatExecNodeMethodResult(result.data, { args, kwargs, method, nodePath }, { detailLevel: detailLevel ?? "summary", responseFormat });
|
|
251
281
|
return {
|
|
252
282
|
content: [
|
|
253
283
|
{
|
|
254
|
-
type: "text",
|
|
255
284
|
text: formattedText,
|
|
285
|
+
type: "text",
|
|
256
286
|
},
|
|
257
287
|
],
|
|
258
288
|
};
|
|
259
289
|
}
|
|
260
290
|
catch (error) {
|
|
261
|
-
logger.
|
|
291
|
+
logger.sendLog({
|
|
292
|
+
data: error,
|
|
293
|
+
level: "error",
|
|
294
|
+
});
|
|
262
295
|
return handleToolError(error, logger, TOOL_NAMES.EXECUTE_NODE_METHOD, REFERENCE_COMMENT);
|
|
263
296
|
}
|
|
264
297
|
});
|
|
@@ -277,8 +310,8 @@ export function registerTdTools(server, logger, tdClient) {
|
|
|
277
310
|
return {
|
|
278
311
|
content: [
|
|
279
312
|
{
|
|
280
|
-
type: "text",
|
|
281
313
|
text: formattedText,
|
|
314
|
+
type: "text",
|
|
282
315
|
},
|
|
283
316
|
],
|
|
284
317
|
};
|
|
@@ -303,8 +336,8 @@ export function registerTdTools(server, logger, tdClient) {
|
|
|
303
336
|
return {
|
|
304
337
|
content: [
|
|
305
338
|
{
|
|
306
|
-
type: "text",
|
|
307
339
|
text: formattedText,
|
|
340
|
+
type: "text",
|
|
308
341
|
},
|
|
309
342
|
],
|
|
310
343
|
};
|
|
@@ -313,6 +346,30 @@ export function registerTdTools(server, logger, tdClient) {
|
|
|
313
346
|
return handleToolError(error, logger, TOOL_NAMES.GET_TD_CLASS_DETAILS, REFERENCE_COMMENT);
|
|
314
347
|
}
|
|
315
348
|
});
|
|
349
|
+
server.tool(TOOL_NAMES.GET_TD_MODULE_HELP, "Retrieve Python help() text for a TouchDesigner module or class", moduleHelpToolSchema.strict().shape, async (params) => {
|
|
350
|
+
try {
|
|
351
|
+
const { detailLevel, moduleName, responseFormat } = params;
|
|
352
|
+
const result = await tdClient.getModuleHelp({ moduleName });
|
|
353
|
+
if (!result.success) {
|
|
354
|
+
throw result.error;
|
|
355
|
+
}
|
|
356
|
+
const formattedText = formatModuleHelp(result.data, {
|
|
357
|
+
detailLevel: detailLevel ?? "summary",
|
|
358
|
+
responseFormat,
|
|
359
|
+
});
|
|
360
|
+
return {
|
|
361
|
+
content: [
|
|
362
|
+
{
|
|
363
|
+
text: formattedText,
|
|
364
|
+
type: "text",
|
|
365
|
+
},
|
|
366
|
+
],
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
catch (error) {
|
|
370
|
+
return handleToolError(error, logger, TOOL_NAMES.GET_TD_MODULE_HELP);
|
|
371
|
+
}
|
|
372
|
+
});
|
|
316
373
|
}
|
|
317
374
|
function matchesMetadataFilter(entry, keyword) {
|
|
318
375
|
const normalizedKeyword = keyword.toLowerCase();
|