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.
@@ -0,0 +1,236 @@
1
+ import semver from "semver";
2
+ import { MIN_COMPATIBLE_API_VERSION } from "./version.js";
3
+ export const COMPATIBILITY_POLICY_TYPES = {
4
+ BELOW_MIN_VERSION: "belowMinVersion",
5
+ COMPATIBLE: "compatible",
6
+ MAJOR_MISMATCH: "majorMismatch",
7
+ NEWER_MINOR: "newerMinor",
8
+ NO_VERSION: "noVersion",
9
+ OLDER_MINOR: "olderMinor",
10
+ PATCH_DIFF: "patchDiff",
11
+ };
12
+ export const COMPATIBILITY_POLICY_ERROR_LEVELS = {
13
+ ALLOW: "info",
14
+ ERROR: "error",
15
+ WARNING: "warning",
16
+ };
17
+ /**
18
+ * Compatibility policy configuration
19
+ */
20
+ const COMPATIBILITY_POLICY = {
21
+ /**
22
+ * Behavior when no version information is available
23
+ * - 'error': Stop processing with error
24
+ */
25
+ [COMPATIBILITY_POLICY_TYPES.NO_VERSION]: {
26
+ compatible: false,
27
+ level: COMPATIBILITY_POLICY_ERROR_LEVELS.ERROR,
28
+ message: generateNoVersionMessage,
29
+ },
30
+ /**
31
+ * Behavior when API server version is below minimum required version
32
+ * - 'error': Stop processing with error
33
+ */
34
+ [COMPATIBILITY_POLICY_TYPES.BELOW_MIN_VERSION]: {
35
+ compatible: false,
36
+ level: COMPATIBILITY_POLICY_ERROR_LEVELS.ERROR,
37
+ message: generateMinVersionMessage,
38
+ },
39
+ /**
40
+ * Behavior when MAJOR versions differ
41
+ * - 'error': Stop processing with error
42
+ */
43
+ [COMPATIBILITY_POLICY_TYPES.MAJOR_MISMATCH]: {
44
+ compatible: false,
45
+ level: COMPATIBILITY_POLICY_ERROR_LEVELS.ERROR,
46
+ message: generateMajorMismatchMessage,
47
+ },
48
+ /**
49
+ * Behavior when MCP server has newer MINOR version than API server
50
+ * - 'warning': Continue with warning only
51
+ */
52
+ [COMPATIBILITY_POLICY_TYPES.NEWER_MINOR]: {
53
+ compatible: true,
54
+ level: COMPATIBILITY_POLICY_ERROR_LEVELS.WARNING,
55
+ message: generateNewerMinorMessage,
56
+ },
57
+ /**
58
+ * Behavior when API server has newer MINOR version than MCP server
59
+ * - 'warning': Continue with warning
60
+ */
61
+ [COMPATIBILITY_POLICY_TYPES.OLDER_MINOR]: {
62
+ compatible: true,
63
+ level: COMPATIBILITY_POLICY_ERROR_LEVELS.WARNING,
64
+ message: generateOlderMinorMessage,
65
+ },
66
+ /**
67
+ * Behavior when PATCH versions differ
68
+ * - 'warning': Continue with warning
69
+ */
70
+ [COMPATIBILITY_POLICY_TYPES.PATCH_DIFF]: {
71
+ compatible: true,
72
+ level: COMPATIBILITY_POLICY_ERROR_LEVELS.WARNING,
73
+ message: generatePatchDiffMessage,
74
+ },
75
+ /**
76
+ * Behavior when versions are fully compatible
77
+ * - 'allow': Allow without logging
78
+ */
79
+ [COMPATIBILITY_POLICY_TYPES.COMPATIBLE]: {
80
+ compatible: true,
81
+ level: COMPATIBILITY_POLICY_ERROR_LEVELS.ALLOW,
82
+ message: generateFullyCompatibleMessage,
83
+ },
84
+ };
85
+ export const getCompatibilityPolicyType = (params) => {
86
+ const mcpSemVer = semver.coerce(params.mcpVersion);
87
+ const apiSemVer = semver.coerce(params.apiVersion);
88
+ if (!mcpSemVer || !apiSemVer) {
89
+ return COMPATIBILITY_POLICY_TYPES.NO_VERSION;
90
+ }
91
+ if (semver.lt(apiSemVer, MIN_COMPATIBLE_API_VERSION)) {
92
+ return COMPATIBILITY_POLICY_TYPES.BELOW_MIN_VERSION;
93
+ }
94
+ if (mcpSemVer.major !== apiSemVer.major) {
95
+ return COMPATIBILITY_POLICY_TYPES.MAJOR_MISMATCH;
96
+ }
97
+ if (mcpSemVer.minor > apiSemVer.minor) {
98
+ return COMPATIBILITY_POLICY_TYPES.NEWER_MINOR;
99
+ }
100
+ if (mcpSemVer.minor < apiSemVer.minor) {
101
+ return COMPATIBILITY_POLICY_TYPES.OLDER_MINOR;
102
+ }
103
+ if (mcpSemVer.patch !== apiSemVer.patch) {
104
+ return COMPATIBILITY_POLICY_TYPES.PATCH_DIFF;
105
+ }
106
+ return COMPATIBILITY_POLICY_TYPES.COMPATIBLE;
107
+ };
108
+ export const getCompatibilityPolicy = (type) => {
109
+ return COMPATIBILITY_POLICY[type];
110
+ };
111
+ /**
112
+ * Update guide template
113
+ */
114
+ const updateGuide = `
115
+ Update Guide:
116
+ 1. Download the latest release: https://github.com/8beeeaaat/touchdesigner-mcp/releases/latest
117
+ 2. Replace TouchDesigner components:
118
+ - Delete the existing touchdesigner-mcp-td folder
119
+ - Extract and import the new mcp_webserver_base.tox
120
+ 3. Restart TouchDesigner and the MCP client (e.g., Claude Desktop)
121
+
122
+ For more details, see: https://github.com/8beeeaaat/touchdesigner-mcp#troubleshooting-version-compatibility
123
+ `.trim();
124
+ /**
125
+ * Generate error message for unknown version information
126
+ */
127
+ export function generateNoVersionMessage(args) {
128
+ return `
129
+ 🚨 Version Information Missing
130
+
131
+ MCP Server: ${args.mcpVersion || "Unknown"}
132
+ API Server: ${args.apiVersion || "Unknown"}
133
+
134
+ Version information is required to ensure compatibility between the MCP server and TouchDesigner components.
135
+ Please ensure both components are updated to compatible versions.
136
+
137
+ ${updateGuide}
138
+ `.trim();
139
+ }
140
+ /**
141
+ * Generate error message for MAJOR version mismatch
142
+ */
143
+ export function generateMajorMismatchMessage(args) {
144
+ return `
145
+ 🚨 Version Incompatibility Detected
146
+
147
+ MCP Server: ${args.mcpVersion}
148
+ API Server: ${args.apiVersion}
149
+
150
+ MAJOR version mismatch indicates breaking changes.
151
+ Both components must be updated to compatible versions.
152
+
153
+ ${updateGuide}
154
+ `.trim();
155
+ }
156
+ /**
157
+ * Generate error message when API version is below minimum compatible version
158
+ */
159
+ export function generateMinVersionMessage(args) {
160
+ return `
161
+ ⚠️ TouchDesigner API Server Update Required
162
+
163
+ Current: ${args.apiVersion}
164
+ Required: ${args.minRequired}+
165
+
166
+ Your TouchDesigner components are outdated and may not support all features.
167
+
168
+ ${updateGuide}
169
+ `.trim();
170
+ }
171
+ /**
172
+ * Generate warning message when MCP server has newer MINOR version
173
+ */
174
+ export function generateNewerMinorMessage(args) {
175
+ return `
176
+ 💡 Update Recommended
177
+
178
+ MCP Server: ${args.mcpVersion}
179
+ API Server: ${args.apiVersion}
180
+
181
+ The MCP server has newer features that may not work with your TouchDesigner components.
182
+ Consider updating for the best experience.
183
+
184
+ ${updateGuide}
185
+ `.trim();
186
+ }
187
+ /**
188
+ * Generate warning message when API server has newer MINOR version
189
+ */
190
+ export function generateOlderMinorMessage(args) {
191
+ return `
192
+ 💡 Update Recommended
193
+
194
+ MCP Server: ${args.mcpVersion}
195
+ API Server: ${args.apiVersion}
196
+
197
+ Your TouchDesigner components have features that may not be supported by the MCP server.
198
+ Consider updating the MCP server for the best experience.
199
+
200
+ ${updateGuide}
201
+ `.trim();
202
+ }
203
+ /**
204
+ * Generate warning message when PATCH versions differ
205
+ */
206
+ export function generatePatchDiffMessage(args) {
207
+ return `
208
+ 💡 Patch Version Mismatch
209
+
210
+ MCP Server: ${args.mcpVersion}
211
+ API Server: ${args.apiVersion}
212
+
213
+ The MCP server and TouchDesigner components have different PATCH versions.
214
+ While generally compatible, updating both to the latest versions is recommended.
215
+
216
+ ${updateGuide}
217
+ `.trim();
218
+ }
219
+ /**
220
+ * Generate info message when versions are fully compatible
221
+ *
222
+ * @param mcpVersion MCP server version
223
+ * @param apiVersion TouchDesigner API server version
224
+ * @returns Info message
225
+ */
226
+ export function generateFullyCompatibleMessage(args) {
227
+ return `
228
+ ✅ Versions Fully Compatible
229
+
230
+ MCP Server: ${args.mcpVersion}
231
+ API Server: ${args.apiVersion}
232
+
233
+ Your MCP server and TouchDesigner components are fully compatible.
234
+ No action is needed.
235
+ `.trim();
236
+ }
@@ -14,10 +14,30 @@ export class McpLogger {
14
14
  });
15
15
  }
16
16
  catch (error) {
17
+ // Only swallow the expected "Not connected" error during startup/shutdown
17
18
  if (error instanceof Error && error.message === "Not connected") {
18
19
  return;
19
20
  }
20
- console.error(`Failed to send log to MCP server: ${error instanceof Error ? error.message : String(error)}`);
21
+ // For all other errors, log detailed information to help diagnose logging system failures
22
+ console.error("CRITICAL: Failed to send log to MCP server. Logging system may be compromised.", {
23
+ error: error instanceof Error ? error.message : String(error),
24
+ originalLogger: args.logger,
25
+ originalLogLevel: args.level,
26
+ stack: error instanceof Error ? error.stack : undefined,
27
+ });
21
28
  }
22
29
  }
23
30
  }
31
+ /**
32
+ * Console Logger implementation for standalone use (e.g., HTTP mode setup)
33
+ * Outputs to stderr to avoid interfering with stdio transport
34
+ */
35
+ export class ConsoleLogger {
36
+ sendLog(args) {
37
+ const timestamp = new Date().toISOString();
38
+ const level = args.level?.toUpperCase() || "INFO";
39
+ const logger = args.logger || "unknown";
40
+ const data = args.data;
41
+ console.error(`[${timestamp}] [${level}] [${logger}] ${data}`);
42
+ }
43
+ }
@@ -1,4 +1,24 @@
1
1
  import { createRequire } from "node:module";
2
2
  const requirePackage = createRequire(import.meta.url);
3
3
  const packageJson = requirePackage("../../package.json");
4
- export const PACKAGE_VERSION = packageJson.version ?? "0.0.0";
4
+ /**
5
+ * Current MCP server version
6
+ */
7
+ export const getMcpServerVersion = () => packageJson.version ?? "0.0.0";
8
+ export const MCP_SERVER_VERSION = getMcpServerVersion();
9
+ /**
10
+ * Minimum compatible TouchDesigner API Server version required by the MCP server
11
+ *
12
+ * Loaded from package.json's mcpCompatibility.minApiVersion field.
13
+ * Falls back to the current package version if undefined.
14
+ *
15
+ * API Server must be at or above this version.
16
+ * - MAJOR mismatch: Error
17
+ * - MINOR/PATCH differences: Warning or allow
18
+ *
19
+ * Update when:
20
+ * - Introducing breaking API changes
21
+ * - Making incompatible changes to OpenAPI schema
22
+ */
23
+ export const getMinCompatibleApiVersion = () => packageJson.mcpCompatibility?.minApiVersion ?? MCP_SERVER_VERSION;
24
+ export const MIN_COMPATIBLE_API_VERSION = getMinCompatibleApiVersion();
@@ -1,3 +1,4 @@
1
+ import { MCP_SERVER_VERSION } from "../../../core/version.js";
1
2
  import { finalizeFormattedText, mergeFormatterOptions, } from "./responseFormatter.js";
2
3
  export function formatTdInfo(data, options) {
3
4
  const opts = mergeFormatterOptions(options);
@@ -6,17 +7,23 @@ export function formatTdInfo(data, options) {
6
7
  context: { title: "TouchDesigner Info" },
7
8
  });
8
9
  }
9
- const summary = `Server: ${data.server}\nVersion: ${data.version}`;
10
- const osLine = data.osName
11
- ? `\nOS: ${data.osName} ${data.osVersion ?? ""}`
12
- : "";
13
- const text = opts.detailLevel === "minimal"
14
- ? `Server: ${data.server}, v${data.version}`
15
- : `${summary}${osLine}`;
10
+ const structured = {
11
+ "API Server Version": data.mcpApiVersion,
12
+ "MCP Server Version": MCP_SERVER_VERSION,
13
+ "Operating System": data.osName
14
+ ? `${data.osName} ${data.osVersion ?? ""}`.trim()
15
+ : "Unknown",
16
+ "TouchDesigner Version": data.version,
17
+ };
18
+ const text = Object.entries(structured)
19
+ .map(([key, value]) => `${key}: ${value}`)
20
+ .join("\n");
16
21
  return finalizeFormattedText(text.trim(), opts, {
17
- context: { title: "TouchDesigner Info", ...data },
18
- structured: data,
19
- template: opts.detailLevel === "detailed" ? "detailedPayload" : "default",
22
+ context: {
23
+ title: "TouchDesigner Info",
24
+ },
25
+ structured,
26
+ template: "detailedPayload",
20
27
  });
21
28
  }
22
29
  export function formatCreateNodeResult(data, options) {
@@ -1,6 +1,6 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { McpLogger } from "../core/logger.js";
3
- import { PACKAGE_VERSION } from "../core/version.js";
3
+ import { MCP_SERVER_VERSION } from "../core/version.js";
4
4
  import { registerPrompts } from "../features/prompts/index.js";
5
5
  import { registerTools } from "../features/tools/index.js";
6
6
  import { createTouchDesignerClient } from "../tdClient/index.js";
@@ -19,7 +19,7 @@ export class TouchDesignerServer {
19
19
  constructor() {
20
20
  this.server = new McpServer({
21
21
  name: "TouchDesigner",
22
- version: PACKAGE_VERSION,
22
+ version: MCP_SERVER_VERSION,
23
23
  }, {
24
24
  capabilities: {
25
25
  logging: {},
@@ -32,6 +32,25 @@ export class TouchDesignerServer {
32
32
  this.connectionManager = new ConnectionManager(this.server, this.logger);
33
33
  this.registerAllFeatures();
34
34
  }
35
+ /**
36
+ * Create a new TouchDesignerServer instance
37
+ *
38
+ * Factory method for creating server instances in multi-session scenarios.
39
+ * Each session should have its own server instance to maintain independent MCP protocol state.
40
+ *
41
+ * @returns McpServer instance ready for connection to a transport
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * // In TransportRegistry
46
+ * const serverFactory = () => TouchDesignerServer.create();
47
+ * const transport = await registry.getOrCreate(sessionId, body, serverFactory);
48
+ * ```
49
+ */
50
+ static create() {
51
+ const instance = new TouchDesignerServer();
52
+ return instance.server;
53
+ }
35
54
  /**
36
55
  * Connect to MCP transport
37
56
  */