touchdesigner-mcp-server 1.3.0 → 1.4.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 +33 -310
- package/README.md +40 -313
- package/dist/cli.js +132 -16
- package/dist/core/compatibility.js +236 -0
- package/dist/core/logger.js +21 -1
- package/dist/core/version.js +21 -1
- package/dist/features/tools/presenter/operationFormatter.js +17 -10
- package/dist/server/touchDesignerServer.js +21 -2
- package/dist/tdClient/touchDesignerClient.js +203 -83
- package/dist/transport/config.js +75 -0
- package/dist/transport/expressHttpManager.js +235 -0
- package/dist/transport/factory.js +198 -0
- package/dist/transport/index.js +12 -0
- package/dist/transport/sessionManager.js +276 -0
- package/dist/transport/transportRegistry.js +272 -0
- package/dist/transport/validator.js +78 -0
- package/package.json +17 -7
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
This is an implementation of an MCP (Model Context Protocol) server for TouchDesigner. Its goal is to enable AI agents to control and operate TouchDesigner projects.
|
|
4
4
|
|
|
5
|
-
[English](
|
|
5
|
+
[English](README.md) / [日本語](README.ja.md)
|
|
6
6
|
|
|
7
7
|
## Overview
|
|
8
8
|
|
|
@@ -14,227 +14,12 @@ TouchDesigner MCP acts as a bridge between AI models and the TouchDesigner WebSe
|
|
|
14
14
|
- Query node properties and project structure
|
|
15
15
|
- Programmatically control TouchDesigner via Python scripts
|
|
16
16
|
|
|
17
|
-
##
|
|
17
|
+
## Installation
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
flowchart LR
|
|
21
|
-
A["🤖<br/>MCP client<br/>(Claude / Codex / ...)"]
|
|
19
|
+
Read the **[Installation Guide](docs/installation.md)**.
|
|
22
20
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
B2["🖌️<br/>Presenters & formatters<br/>(markdown output)"]
|
|
26
|
-
B3["🌐<br/>OpenAPI HTTP client<br/>(src/tdClient)"]
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
subgraph T [TouchDesigner project]
|
|
30
|
-
C1["🧩<br/>WebServer DAT<br/>(mcp_webserver_base.tox)"]
|
|
31
|
-
C2["🐍<br/>Python controllers / services<br/>(td/modules/mcp)"]
|
|
32
|
-
C3["🎛️<br/>Project nodes & parameters<br/>(/project1/...)"]
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
A --> B1
|
|
36
|
-
B1 --> B2
|
|
37
|
-
B1 --> B3
|
|
38
|
-
B2 --> A
|
|
39
|
-
B3 <--> C1
|
|
40
|
-
C1 <--> C2
|
|
41
|
-
C2 <--> C3
|
|
42
|
-
|
|
43
|
-
%% Higher-contrast colors for readability
|
|
44
|
-
classDef client fill:#d8e8ff,stroke:#1f6feb,stroke-width:2px,color:#111,font-weight:bold
|
|
45
|
-
classDef server fill:#efe1ff,stroke:#8250df,stroke-width:2px,color:#111,font-weight:bold
|
|
46
|
-
classDef td fill:#d7f5e3,stroke:#2f9e44,stroke-width:2px,color:#111,font-weight:bold
|
|
47
|
-
class A client;
|
|
48
|
-
class B1,B2,B3 server;
|
|
49
|
-
class C1,C2,C3 td;
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
## Usage
|
|
53
|
-
|
|
54
|
-
<details>
|
|
55
|
-
<summary>Method 1: Using Claude Desktop and MCP Bundle (Recommended)</summary>
|
|
56
|
-
|
|
57
|
-
### 1. Download Files
|
|
58
|
-
|
|
59
|
-
Download the following from the [releases page](https://github.com/8beeeaaat/touchdesigner-mcp/releases/latest):
|
|
60
|
-
|
|
61
|
-
- **TouchDesigner Components**: `touchdesigner-mcp-td.zip`
|
|
62
|
-
- **[MCP Bundle](https://github.com/modelcontextprotocol/mcpb) (.mcpb)**: `touchdesigner-mcp.mcpb`
|
|
63
|
-
|
|
64
|
-
### 2. Set up TouchDesigner Components
|
|
65
|
-
|
|
66
|
-
1. Extract the TouchDesigner components from `touchdesigner-mcp-td.zip`.
|
|
67
|
-
2. Import `mcp_webserver_base.tox` into your TouchDesigner project.
|
|
68
|
-
3. Place it at `/project1/mcp_webserver_base`.
|
|
69
|
-
|
|
70
|
-
<https://github.com/user-attachments/assets/215fb343-6ed8-421c-b948-2f45fb819ff4>
|
|
71
|
-
|
|
72
|
-
You can check the startup logs by opening the Textport from the TouchDesigner menu.
|
|
73
|
-
|
|
74
|
-

|
|
75
|
-
|
|
76
|
-
### 3. Install the MCP Bundle
|
|
77
|
-
|
|
78
|
-
Double-click the `touchdesigner-mcp.mcpb` file to install the bundle in Claude Desktop.
|
|
79
|
-
|
|
80
|
-
<https://github.com/user-attachments/assets/0786d244-8b82-4387-bbe4-9da048212854>
|
|
81
|
-
|
|
82
|
-
### 4. Connect to the Server
|
|
83
|
-
|
|
84
|
-
The MCP bundle will automatically handle the connection to the TouchDesigner server.
|
|
85
|
-
|
|
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
|
-
|
|
88
|
-
</details>
|
|
89
|
-
|
|
90
|
-
<details>
|
|
91
|
-
<summary>Method 2: Using npx</summary>
|
|
92
|
-
|
|
93
|
-
*Requires Node.js to be installed.*
|
|
94
|
-
|
|
95
|
-
### 1. Set up TouchDesigner Components
|
|
96
|
-
|
|
97
|
-
1. Download and extract the TouchDesigner components from `touchdesigner-mcp-td.zip` ([releases page](https://github.com/8beeeaaat/touchdesigner-mcp/releases/latest)).
|
|
98
|
-
2. Import `mcp_webserver_base.tox` into your TouchDesigner project.
|
|
99
|
-
3. Place it at `/project1/mcp_webserver_base`.
|
|
100
|
-
|
|
101
|
-
<https://github.com/user-attachments/assets/215fb343-6ed8-421c-b948-2f45fb819ff4>
|
|
102
|
-
|
|
103
|
-
You can check the startup logs by opening the Textport from the TouchDesigner menu.
|
|
104
|
-
|
|
105
|
-

|
|
106
|
-
|
|
107
|
-
### 2. Set up the MCP Server Configuration
|
|
108
|
-
|
|
109
|
-
*Example for Claude Desktop:*
|
|
110
|
-
|
|
111
|
-
```json
|
|
112
|
-
{
|
|
113
|
-
"mcpServers": {
|
|
114
|
-
"touchdesigner": {
|
|
115
|
-
"command": "npx",
|
|
116
|
-
"args": ["-y", "touchdesigner-mcp-server@latest", "--stdio"]
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
**Customization:** You can customize the TouchDesigner server connection by adding `--host` and `--port` arguments:
|
|
123
|
-
|
|
124
|
-
```json
|
|
125
|
-
"args": [
|
|
126
|
-
"-y",
|
|
127
|
-
"touchdesigner-mcp-server@latest",
|
|
128
|
-
"--stdio",
|
|
129
|
-
"--host=http://custom_host",
|
|
130
|
-
"--port=9982"
|
|
131
|
-
]
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
</details>
|
|
135
|
-
|
|
136
|
-
<details>
|
|
137
|
-
<summary>Method 3: Using a Docker Image</summary>
|
|
138
|
-
|
|
139
|
-
[](https://www.youtube.com/watch?v=BRWoIEVb0TU)
|
|
140
|
-
|
|
141
|
-
### 1. Clone the repository
|
|
142
|
-
|
|
143
|
-
```bash
|
|
144
|
-
git clone https://github.com/8beeeaaat/touchdesigner-mcp.git
|
|
145
|
-
cd touchdesigner-mcp
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
### 2. Build the Docker image
|
|
149
|
-
|
|
150
|
-
```bash
|
|
151
|
-
make build
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
### 3. Install the API Server in Your TouchDesigner Project
|
|
155
|
-
|
|
156
|
-
Start TouchDesigner and import the `td/mcp_webserver_base.tox` component into the project you want to control.
|
|
157
|
-
Example: Place it at `/project1/mcp_webserver_base`.
|
|
158
|
-
|
|
159
|
-
Importing the `.tox` file will trigger the `td/import_modules.py` script, which loads the necessary modules for the API server.
|
|
160
|
-
|
|
161
|
-
<https://github.com/user-attachments/assets/215fb343-6ed8-421c-b948-2f45fb819ff4>
|
|
162
|
-
|
|
163
|
-
You can check the startup logs by opening the Textport from the TouchDesigner menu.
|
|
164
|
-
|
|
165
|
-

|
|
166
|
-
|
|
167
|
-
### 4. Start the MCP server container
|
|
168
|
-
|
|
169
|
-
```bash
|
|
170
|
-
docker-compose up -d
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
### 5. Configure your AI agent to use the Docker container
|
|
174
|
-
|
|
175
|
-
*Example for Claude Desktop:*
|
|
176
|
-
|
|
177
|
-
```json
|
|
178
|
-
{
|
|
179
|
-
"mcpServers": {
|
|
180
|
-
"touchdesigner": {
|
|
181
|
-
"command": "docker",
|
|
182
|
-
"args": [
|
|
183
|
-
"compose",
|
|
184
|
-
"-f",
|
|
185
|
-
"/path/to/your/touchdesigner-mcp/docker-compose.yml",
|
|
186
|
-
"exec",
|
|
187
|
-
"-i",
|
|
188
|
-
"touchdesigner-mcp-server",
|
|
189
|
-
"node",
|
|
190
|
-
"dist/cli.js",
|
|
191
|
-
"--stdio",
|
|
192
|
-
"--host=http://host.docker.internal"
|
|
193
|
-
]
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
*On Windows systems, include the drive letter, e.g., `C:\path\to\your\touchdesigner-mcp\docker-compose.yml`.*
|
|
200
|
-
|
|
201
|
-
**Note:** You can customize the TouchDesigner server connection by adding `--host` and `--port` arguments:
|
|
202
|
-
|
|
203
|
-
```json
|
|
204
|
-
"args": [
|
|
205
|
-
...,
|
|
206
|
-
"--stdio",
|
|
207
|
-
"--host=http://host.docker.internal",
|
|
208
|
-
"--port=9982"
|
|
209
|
-
]
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
</details>
|
|
213
|
-
|
|
214
|
-
## Verify Connection
|
|
215
|
-
|
|
216
|
-
If the MCP server is recognized, the setup is complete.
|
|
217
|
-
If it's not recognized, try restarting your AI agent.
|
|
218
|
-
If you see an error at startup, try launching the agent again after starting TouchDesigner.
|
|
219
|
-
When the API server is running properly in TouchDesigner, the agent can use the provided tools to operate it.
|
|
220
|
-
|
|
221
|
-
### Directory Structure Requirements
|
|
222
|
-
|
|
223
|
-
**Critical:** When using any method, you must maintain the original directory structure:
|
|
224
|
-
|
|
225
|
-
```
|
|
226
|
-
td/
|
|
227
|
-
├── import_modules.py # Module loader script
|
|
228
|
-
├── mcp_webserver_base.tox # Main TouchDesigner component
|
|
229
|
-
└── modules/ # Python modules directory
|
|
230
|
-
├── mcp/ # MCP core logic
|
|
231
|
-
├── utils/ # Shared utilities
|
|
232
|
-
└── td_server/ # Generated API server code
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
The `mcp_webserver_base.tox` component uses relative paths to locate Python modules. Moving or reorganizing these files will cause import errors in TouchDesigner.
|
|
236
|
-
|
|
237
|
-

|
|
21
|
+
The guide includes the required TouchDesigner preparation, per-agent setup, verification steps, and
|
|
22
|
+
troubleshooting tips.
|
|
238
23
|
|
|
239
24
|
## MCP Server Features
|
|
240
25
|
|
|
@@ -273,106 +58,48 @@ Prompts provide instructions for AI agents to perform specific actions in TouchD
|
|
|
273
58
|
|
|
274
59
|
Not implemented.
|
|
275
60
|
|
|
276
|
-
##
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
1. **Set up your environment:**
|
|
281
|
-
|
|
282
|
-
```bash
|
|
283
|
-
# Clone and install dependencies
|
|
284
|
-
git clone https://github.com/8beeeaaat/touchdesigner-mcp.git
|
|
285
|
-
cd touchdesigner-mcp
|
|
286
|
-
npm install
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
2. **Build the project:**
|
|
290
|
-
|
|
291
|
-
```bash
|
|
292
|
-
make build # Docker-based build (recommended)
|
|
293
|
-
# OR
|
|
294
|
-
npm run build # Node.js-based build
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
3. **Available commands:**
|
|
298
|
-
|
|
299
|
-
```bash
|
|
300
|
-
npm run test # Run unit and integration tests
|
|
301
|
-
npm run dev # Launch the MCP inspector for debugging
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
**Note:** When you update the code, you must restart both the MCP server and TouchDesigner to apply the changes.
|
|
305
|
-
|
|
306
|
-
### Project Structure Overview
|
|
307
|
-
|
|
308
|
-
```
|
|
309
|
-
├── src/ # MCP server source code
|
|
310
|
-
│ ├── api/ # OpenAPI spec for the TouchDesigner WebServer
|
|
311
|
-
│ ├── core/ # Core utilities (logger, error handling)
|
|
312
|
-
│ ├── features/ # MCP feature implementations
|
|
313
|
-
│ │ ├── prompts/ # Prompt handlers
|
|
314
|
-
│ │ ├── resources/ # Resource handlers
|
|
315
|
-
│ │ └── tools/ # Tool handlers (e.g., tdTools.ts)
|
|
316
|
-
│ ├── gen/ # Code generated from the OpenAPI schema for the MCP server
|
|
317
|
-
│ ├── server/ # MCP server logic (connections, main server class)
|
|
318
|
-
│ ├── tdClient/ # TouchDesigner connection API client
|
|
319
|
-
│ ├── index.ts # Main entry point for the Node.js server
|
|
320
|
-
│ └── ...
|
|
321
|
-
├── td/ # TouchDesigner-related files
|
|
322
|
-
│ ├── modules/ # Python modules for TouchDesigner
|
|
323
|
-
│ │ ├── mcp/ # Core logic for handling MCP requests in TouchDesigner
|
|
324
|
-
│ │ │ ├── controllers/ # API request controllers (api_controller.py, generated_handlers.py)
|
|
325
|
-
│ │ │ └── services/ # Business logic (api_service.py)
|
|
326
|
-
│ │ ├── td_server/ # Python model code generated from the OpenAPI schema
|
|
327
|
-
│ │ └── utils/ # Shared Python utilities
|
|
328
|
-
│ ├── templates/ # Mustache templates for Python code generation
|
|
329
|
-
│ ├── genHandlers.js # Node.js script for generating generated_handlers.py
|
|
330
|
-
│ ├── import_modules.py # Helper script to import API server modules into TouchDesigner
|
|
331
|
-
│ └── mcp_webserver_base.tox # Main TouchDesigner component
|
|
332
|
-
├── tests/ # Test code
|
|
333
|
-
│ ├── integration/
|
|
334
|
-
│ └── unit/
|
|
335
|
-
└── orval.config.ts # Orval config (TypeScript client generation)
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
### API Code Generation Workflow
|
|
339
|
-
|
|
340
|
-
This project uses OpenAPI-based code generation tools (Orval and openapi-generator-cli).
|
|
341
|
-
|
|
342
|
-
**API Definition:** The API contract between the Node.js MCP server and the Python server running inside TouchDesigner is defined in `src/api/index.yml`.
|
|
343
|
-
|
|
344
|
-
1. **Python server generation (`npm run gen:webserver`):**
|
|
345
|
-
- Uses `openapi-generator-cli` via Docker.
|
|
346
|
-
- Reads `src/api/index.yml`.
|
|
347
|
-
- Generates a Python server skeleton (`td/modules/td_server/`) based on the API definition. This code runs inside TouchDesigner's WebServer DAT.
|
|
348
|
-
- **Requires Docker to be installed and running.**
|
|
349
|
-
2. **Python handler generation (`npm run gen:handlers`):**
|
|
350
|
-
- Uses a custom Node.js script (`td/genHandlers.js`) and Mustache templates (`td/templates/`).
|
|
351
|
-
- Reads the generated Python server code or OpenAPI spec.
|
|
352
|
-
- Generates handler implementations (`td/modules/mcp/controllers/generated_handlers.py`) that connect to the business logic in `td/modules/mcp/services/api_service.py`.
|
|
353
|
-
3. **TypeScript client generation (`npm run gen:mcp`):**
|
|
354
|
-
- Uses `Orval` to generate an API client and Zod schemas for tool validation from the schema YAML, which is bundled by `openapi-generator-cli`.
|
|
355
|
-
- Generates a typed TypeScript client (`src/tdClient/`) used by the Node.js server to make requests to the WebServer DAT.
|
|
356
|
-
|
|
357
|
-
The build process (`npm run build`) runs all necessary generation steps (`npm run gen`), followed by TypeScript compilation (`tsc`).
|
|
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.
|
|
61
|
+
## Developer Guide
|
|
62
|
+
|
|
63
|
+
Looking for local setup, client configuration, project structure, or release workflow notes?
|
|
64
|
+
See the **[Developer Guide](docs/development.md)** for all developer-facing documentation.
|
|
364
65
|
|
|
365
66
|
## Troubleshooting
|
|
366
67
|
|
|
367
68
|
### Troubleshooting version compatibility
|
|
368
69
|
|
|
369
|
-
|
|
370
|
-
|
|
70
|
+
The MCP server uses **semantic versioning** for flexible compatibility checks
|
|
71
|
+
|
|
72
|
+
| MCP Server | API Server | Minimum compatible API version | Behavior | Status | Notes |
|
|
73
|
+
|------------|------------|----------------|----------|--------|-------|
|
|
74
|
+
| 1.3.x | 1.3.0 | 1.3.0 | ✅ Works normally | Compatible | Recommended baseline configuration |
|
|
75
|
+
| 1.3.x | 1.4.0 | 1.3.0 | ⚠️ Warning shown, continues | Warning | Older MCP MINOR with newer API may lack new features |
|
|
76
|
+
| 1.4.0 | 1.3.x | 1.3.0 | ⚠️ Warning shown, continues | Warning | Newer MCP MINOR may have additional features |
|
|
77
|
+
| 1.3.2 | 1.3.1 | 1.3.2 | ❌ Execution stops | Error | API below minimum compatible version |
|
|
78
|
+
| 2.0.0 | 1.x.x | N/A | ❌ Execution stops | Error | Different MAJOR = breaking changes |
|
|
79
|
+
|
|
80
|
+
**Compatibility Rules**:
|
|
81
|
+
|
|
82
|
+
- ✅ **Compatible**: Same MAJOR version AND API version ≥ 1.3.0 (minimum compatible version)
|
|
83
|
+
- ⚠️ **Warning**: Different MINOR or PATCH versions within the same MAJOR version (shows warning but continues execution)
|
|
84
|
+
- ❌ **Error**: Different MAJOR versions OR API server < 1.3.0 (execution stops immediately, update required)
|
|
85
|
+
|
|
86
|
+
- **To resolve compatibility errors:**
|
|
371
87
|
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
|
|
373
|
-
3. Remove the old
|
|
88
|
+
2. Delete the existing `touchdesigner-mcp-td` folder and replace it with the newly extracted contents.
|
|
89
|
+
3. Remove the old `mcp_webserver_base` component from your TouchDesigner project and import the `.tox` from the new folder.
|
|
374
90
|
4. Restart TouchDesigner and the AI agent running the MCP server (e.g., Claude Desktop).
|
|
375
|
-
|
|
91
|
+
|
|
92
|
+
- **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
|
+
|
|
94
|
+
### Troubleshooting connection errors
|
|
95
|
+
|
|
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
|
+
- When the MCP server cannot reach TouchDesigner, you now get guided error messages with concrete fixes:
|
|
98
|
+
- `ECONNREFUSED` / "connect refused": start TouchDesigner, ensure the WebServer DAT from `mcp_webserver_base.tox` is running, and confirm the configured port (default `9981`).
|
|
99
|
+
- `ETIMEDOUT` / "timeout": TouchDesigner is responding slowly or the network is blocked. Restart TouchDesigner/WebServer DAT or check your network connection.
|
|
100
|
+
- `ENOTFOUND` / `getaddrinfo`: the host name is invalid. Use `127.0.0.1` unless you explicitly changed it.
|
|
101
|
+
- The structured error text is also logged through `ILogger`, so you can check the MCP logs to understand why a request stopped before hitting TouchDesigner.
|
|
102
|
+
- Once the underlying issue is fixed, simply run the tool again—the client clears the cached error and re-verifies the connection automatically.
|
|
376
103
|
|
|
377
104
|
## Contributing
|
|
378
105
|
|
package/dist/cli.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { ConsoleLogger } from "./core/logger.js";
|
|
3
3
|
import { TouchDesignerServer } from "./server/touchDesignerServer.js";
|
|
4
|
+
import { isStreamableHttpTransportConfig } from "./transport/config.js";
|
|
5
|
+
import { ExpressHttpManager } from "./transport/expressHttpManager.js";
|
|
6
|
+
import { TransportFactory } from "./transport/factory.js";
|
|
7
|
+
import { SessionManager } from "./transport/sessionManager.js";
|
|
4
8
|
// Note: Environment variables should be set by the MCP Bundle runtime or CLI arguments
|
|
5
9
|
const DEFAULT_HOST = "http://127.0.0.1";
|
|
6
10
|
const DEFAULT_PORT = 9981;
|
|
11
|
+
const DEFAULT_MCP_ENDPOINT = "/mcp";
|
|
7
12
|
/**
|
|
8
|
-
* Parse command line arguments
|
|
13
|
+
* Parse command line arguments for TouchDesigner connection
|
|
9
14
|
*/
|
|
10
15
|
export function parseArgs(args) {
|
|
11
16
|
const argsToProcess = args || process.argv.slice(2);
|
|
@@ -25,38 +30,149 @@ export function parseArgs(args) {
|
|
|
25
30
|
return parsed;
|
|
26
31
|
}
|
|
27
32
|
/**
|
|
28
|
-
*
|
|
33
|
+
* Parse transport configuration from command line arguments
|
|
34
|
+
*
|
|
35
|
+
* Detects if HTTP mode is requested via --mcp-http-port flag.
|
|
36
|
+
* If not specified, defaults to stdio mode.
|
|
37
|
+
*
|
|
38
|
+
* @param args - Command line arguments (defaults to process.argv.slice(2))
|
|
39
|
+
* @returns Transport configuration (stdio or streamable-http)
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```bash
|
|
43
|
+
* # Stdio mode (default)
|
|
44
|
+
* touchdesigner-mcp-server --host=http://localhost --port=9981
|
|
45
|
+
*
|
|
46
|
+
* # HTTP mode
|
|
47
|
+
* touchdesigner-mcp-server --mcp-http-port=6280 --mcp-http-host=127.0.0.1
|
|
48
|
+
* ```
|
|
29
49
|
*/
|
|
30
|
-
export function
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
50
|
+
export function parseTransportConfig(args) {
|
|
51
|
+
const argsToProcess = args || process.argv.slice(2);
|
|
52
|
+
// Check for HTTP mode
|
|
53
|
+
const httpPortArg = argsToProcess.find((arg) => arg.startsWith("--mcp-http-port="));
|
|
54
|
+
if (httpPortArg) {
|
|
55
|
+
const portStr = httpPortArg.split("=")[1];
|
|
56
|
+
const port = Number.parseInt(portStr, 10);
|
|
57
|
+
if (Number.isNaN(port) || port < 1 || port > 65535) {
|
|
58
|
+
console.error(`Invalid value for --mcp-http-port: "${portStr}". Please specify a valid port number (1-65535).`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
const hostArg = argsToProcess.find((arg) => arg.startsWith("--mcp-http-host="));
|
|
62
|
+
const host = hostArg ? hostArg.split("=")[1] : "127.0.0.1";
|
|
63
|
+
const config = {
|
|
64
|
+
endpoint: DEFAULT_MCP_ENDPOINT,
|
|
65
|
+
host,
|
|
66
|
+
port,
|
|
67
|
+
sessionConfig: { enabled: true },
|
|
68
|
+
type: "streamable-http",
|
|
69
|
+
};
|
|
70
|
+
return config;
|
|
71
|
+
}
|
|
72
|
+
// Default to stdio mode
|
|
73
|
+
return { type: "stdio" };
|
|
34
74
|
}
|
|
35
75
|
/**
|
|
36
76
|
* Start TouchDesigner MCP server
|
|
77
|
+
*
|
|
78
|
+
* Supports both stdio and HTTP transport modes based on command line arguments.
|
|
79
|
+
*
|
|
80
|
+
* @param params - Server startup parameters
|
|
81
|
+
* @param params.argv - Command line arguments
|
|
82
|
+
* @param params.nodeEnv - Node environment
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```bash
|
|
86
|
+
* # Stdio mode (default)
|
|
87
|
+
* touchdesigner-mcp-server --host=http://localhost --port=9981
|
|
88
|
+
*
|
|
89
|
+
* # HTTP mode
|
|
90
|
+
* touchdesigner-mcp-server --mcp-http-port=6280 --host=http://localhost --port=9981
|
|
91
|
+
* ```
|
|
37
92
|
*/
|
|
38
93
|
export async function startServer(params) {
|
|
39
94
|
try {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
// Parse command line arguments and set environment variables
|
|
95
|
+
// Parse transport configuration
|
|
96
|
+
const transportConfig = parseTransportConfig(params?.argv);
|
|
97
|
+
// Parse TouchDesigner connection arguments
|
|
45
98
|
const args = parseArgs(params?.argv);
|
|
46
99
|
process.env.TD_WEB_SERVER_HOST = args.host;
|
|
47
100
|
process.env.TD_WEB_SERVER_PORT = args.port.toString();
|
|
101
|
+
// Create MCP server
|
|
48
102
|
const server = new TouchDesignerServer();
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
103
|
+
// Handle stdio mode
|
|
104
|
+
if (transportConfig.type === "stdio") {
|
|
105
|
+
const transportResult = TransportFactory.create(transportConfig);
|
|
106
|
+
if (!transportResult.success) {
|
|
107
|
+
throw transportResult.error;
|
|
108
|
+
}
|
|
109
|
+
const result = await server.connect(transportResult.data);
|
|
110
|
+
if (!result.success) {
|
|
111
|
+
throw new Error(`Failed to connect: ${result.error.message}`);
|
|
112
|
+
}
|
|
113
|
+
console.error("MCP server started in stdio mode");
|
|
114
|
+
return;
|
|
53
115
|
}
|
|
116
|
+
// Handle HTTP mode
|
|
117
|
+
if (isStreamableHttpTransportConfig(transportConfig)) {
|
|
118
|
+
// Use ConsoleLogger for HTTP manager and session manager
|
|
119
|
+
// This avoids "Not connected" errors since HTTP mode doesn't have a global MCP connection
|
|
120
|
+
const logger = new ConsoleLogger();
|
|
121
|
+
// Create session manager if enabled
|
|
122
|
+
const sessionManager = transportConfig.sessionConfig?.enabled
|
|
123
|
+
? new SessionManager(transportConfig.sessionConfig, logger)
|
|
124
|
+
: null;
|
|
125
|
+
// Server factory for creating per-session instances
|
|
126
|
+
// Each session gets its own TouchDesignerServer with independent MCP protocol state
|
|
127
|
+
const serverFactory = () => TouchDesignerServer.create();
|
|
128
|
+
// Create Express HTTP manager with server factory
|
|
129
|
+
const httpManager = new ExpressHttpManager(transportConfig, serverFactory, sessionManager, logger);
|
|
130
|
+
// Start HTTP server
|
|
131
|
+
const startResult = await httpManager.start();
|
|
132
|
+
if (!startResult.success) {
|
|
133
|
+
throw startResult.error;
|
|
134
|
+
}
|
|
135
|
+
console.error(`MCP server started in HTTP mode on ${transportConfig.host}:${transportConfig.port}${transportConfig.endpoint}`);
|
|
136
|
+
// Start session cleanup if enabled
|
|
137
|
+
if (sessionManager) {
|
|
138
|
+
sessionManager.startTTLCleanup();
|
|
139
|
+
}
|
|
140
|
+
// Set up graceful shutdown
|
|
141
|
+
const shutdown = async () => {
|
|
142
|
+
console.error("\nShutting down server...");
|
|
143
|
+
// Stop session cleanup
|
|
144
|
+
if (sessionManager) {
|
|
145
|
+
sessionManager.stopTTLCleanup();
|
|
146
|
+
}
|
|
147
|
+
// Stop HTTP server
|
|
148
|
+
const stopResult = await httpManager.stop();
|
|
149
|
+
if (!stopResult.success) {
|
|
150
|
+
console.error(`Error during shutdown: ${stopResult.error.message}`);
|
|
151
|
+
}
|
|
152
|
+
console.error("Server shutdown complete");
|
|
153
|
+
process.exit(0);
|
|
154
|
+
};
|
|
155
|
+
process.on("SIGINT", shutdown);
|
|
156
|
+
process.on("SIGTERM", shutdown);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
// Type-safe exhaustive check using never type
|
|
160
|
+
// This ensures all cases of the TransportConfig discriminated union are handled
|
|
161
|
+
// If a new transport type is added, TypeScript will error at compile time
|
|
162
|
+
assertNever(transportConfig);
|
|
54
163
|
}
|
|
55
164
|
catch (error) {
|
|
56
165
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
57
166
|
throw new Error(`Failed to initialize server: ${errorMessage}`);
|
|
58
167
|
}
|
|
59
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* Helper function for exhaustive type checking
|
|
171
|
+
* TypeScript will error if called with a non-never type, ensuring all cases are handled
|
|
172
|
+
*/
|
|
173
|
+
function assertNever(value) {
|
|
174
|
+
throw new Error(`Unsupported transport type: ${value.type}`);
|
|
175
|
+
}
|
|
60
176
|
// Start server if this file is executed directly
|
|
61
177
|
startServer({
|
|
62
178
|
argv: process.argv,
|