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
|
@@ -14,9 +14,9 @@ export function formatTdInfo(data, options) {
|
|
|
14
14
|
? `Server: ${data.server}, v${data.version}`
|
|
15
15
|
: `${summary}${osLine}`;
|
|
16
16
|
return finalizeFormattedText(text.trim(), opts, {
|
|
17
|
-
template: opts.detailLevel === "detailed" ? "detailedPayload" : "default",
|
|
18
17
|
context: { title: "TouchDesigner Info", ...data },
|
|
19
18
|
structured: data,
|
|
19
|
+
template: opts.detailLevel === "detailed" ? "detailedPayload" : "default",
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
22
|
export function formatCreateNodeResult(data, options) {
|
|
@@ -37,9 +37,9 @@ export function formatCreateNodeResult(data, options) {
|
|
|
37
37
|
? base
|
|
38
38
|
: `${base}\nProperties detected: ${propCount}`;
|
|
39
39
|
return finalizeFormattedText(text, opts, {
|
|
40
|
-
|
|
41
|
-
context: { title: "Create Node", path, opType },
|
|
40
|
+
context: { opType, path, title: "Create Node" },
|
|
42
41
|
structured: data,
|
|
42
|
+
template: opts.detailLevel === "detailed" ? "detailedPayload" : "default",
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
45
|
export function formatUpdateNodeResult(data, options) {
|
|
@@ -51,15 +51,15 @@ export function formatUpdateNodeResult(data, options) {
|
|
|
51
51
|
? base
|
|
52
52
|
: `${base}${failedCount ? `, ${failedCount} failed` : ""}`;
|
|
53
53
|
const context = {
|
|
54
|
-
title: "Update Node",
|
|
55
|
-
updated: data?.updated,
|
|
56
54
|
failed: data?.failed,
|
|
57
55
|
message: data?.message,
|
|
56
|
+
title: "Update Node",
|
|
57
|
+
updated: data?.updated,
|
|
58
58
|
};
|
|
59
59
|
return finalizeFormattedText(text, opts, {
|
|
60
|
-
template: opts.detailLevel === "detailed" ? "detailedPayload" : "default",
|
|
61
60
|
context,
|
|
62
61
|
structured: data,
|
|
62
|
+
template: opts.detailLevel === "detailed" ? "detailedPayload" : "default",
|
|
63
63
|
});
|
|
64
64
|
}
|
|
65
65
|
export function formatDeleteNodeResult(data, options) {
|
|
@@ -71,9 +71,9 @@ export function formatDeleteNodeResult(data, options) {
|
|
|
71
71
|
? `🗑️ Deleted '${name}' at ${path}`
|
|
72
72
|
: `Deletion status unknown for '${name}' at ${path}`;
|
|
73
73
|
return finalizeFormattedText(text, opts, {
|
|
74
|
-
|
|
75
|
-
context: { title: "Delete Node", path, deleted },
|
|
74
|
+
context: { deleted, path, title: "Delete Node" },
|
|
76
75
|
structured: data,
|
|
76
|
+
template: opts.detailLevel === "detailed" ? "detailedPayload" : "default",
|
|
77
77
|
});
|
|
78
78
|
}
|
|
79
79
|
export function formatExecNodeMethodResult(data, context, options) {
|
|
@@ -82,9 +82,9 @@ export function formatExecNodeMethodResult(data, context, options) {
|
|
|
82
82
|
const resultPreview = summarizeValue(data?.result);
|
|
83
83
|
const text = `${callSignature}\nResult: ${resultPreview}`;
|
|
84
84
|
return finalizeFormattedText(text, opts, {
|
|
85
|
-
|
|
86
|
-
context: { title: "Execute Node Method", callSignature },
|
|
85
|
+
context: { callSignature, title: "Execute Node Method" },
|
|
87
86
|
structured: { ...context, result: data?.result },
|
|
87
|
+
template: opts.detailLevel === "detailed" ? "detailedPayload" : "default",
|
|
88
88
|
});
|
|
89
89
|
}
|
|
90
90
|
function buildCallSignature(params) {
|
|
@@ -13,8 +13,8 @@ import { presentStructuredData } from "./presenter.js";
|
|
|
13
13
|
*/
|
|
14
14
|
export const DEFAULT_FORMATTER_OPTIONS = {
|
|
15
15
|
detailLevel: "summary",
|
|
16
|
-
limit: undefined,
|
|
17
16
|
includeHints: true,
|
|
17
|
+
limit: undefined,
|
|
18
18
|
responseFormat: undefined,
|
|
19
19
|
};
|
|
20
20
|
/**
|
|
@@ -27,8 +27,8 @@ export function mergeFormatterOptions(options) {
|
|
|
27
27
|
DEFAULT_FORMATTER_OPTIONS.detailLevel;
|
|
28
28
|
return {
|
|
29
29
|
detailLevel,
|
|
30
|
-
limit: merged.limit,
|
|
31
30
|
includeHints: merged.includeHints ?? true,
|
|
31
|
+
limit: merged.limit,
|
|
32
32
|
responseFormat: merged.responseFormat,
|
|
33
33
|
};
|
|
34
34
|
}
|
|
@@ -36,11 +36,11 @@ export function finalizeFormattedText(text, opts, metadata) {
|
|
|
36
36
|
const chosenFormat = opts.responseFormat ??
|
|
37
37
|
(opts.detailLevel === "detailed" ? "yaml" : "markdown");
|
|
38
38
|
return presentStructuredData({
|
|
39
|
-
|
|
39
|
+
context: metadata?.context,
|
|
40
40
|
detailLevel: opts.detailLevel,
|
|
41
41
|
structured: metadata?.structured,
|
|
42
|
-
context: metadata?.context,
|
|
43
42
|
template: metadata?.template,
|
|
43
|
+
text,
|
|
44
44
|
}, chosenFormat);
|
|
45
45
|
}
|
|
46
46
|
/**
|
|
@@ -41,9 +41,9 @@ export function formatScriptResult(data, scriptSnippet, options) {
|
|
|
41
41
|
}
|
|
42
42
|
const ctx = context;
|
|
43
43
|
return finalizeFormattedText(formattedText, opts, {
|
|
44
|
-
template: "scriptSummary",
|
|
45
44
|
context: ctx,
|
|
46
45
|
structured: ctx,
|
|
46
|
+
template: "scriptSummary",
|
|
47
47
|
});
|
|
48
48
|
}
|
|
49
49
|
/**
|
|
@@ -98,25 +98,25 @@ function formatSummary(result, output, scriptSnippet) {
|
|
|
98
98
|
formatted += `\nOutput:\n${outputPreview}`;
|
|
99
99
|
}
|
|
100
100
|
return {
|
|
101
|
-
text: formatted,
|
|
102
101
|
context: {
|
|
103
|
-
snippet,
|
|
104
|
-
resultType: getValueType(result),
|
|
105
|
-
resultPreview,
|
|
106
102
|
hasOutput: Boolean(outputPreview),
|
|
107
|
-
outputType: getValueType(output),
|
|
108
103
|
outputPreview,
|
|
104
|
+
outputType: getValueType(output),
|
|
105
|
+
resultPreview,
|
|
106
|
+
resultType: getValueType(result),
|
|
107
|
+
snippet,
|
|
109
108
|
},
|
|
109
|
+
text: formatted,
|
|
110
110
|
};
|
|
111
111
|
}
|
|
112
112
|
function buildScriptContext(scriptSnippet, result, output) {
|
|
113
113
|
return {
|
|
114
|
-
snippet: scriptSnippet ? truncateScript(scriptSnippet) : "",
|
|
115
|
-
resultType: getValueType(result),
|
|
116
|
-
resultPreview: formatResultValue(result ?? "", 200),
|
|
117
114
|
hasOutput: Boolean(output?.trim()),
|
|
118
|
-
outputType: getValueType(output),
|
|
119
115
|
outputPreview: output,
|
|
116
|
+
outputType: getValueType(output),
|
|
117
|
+
resultPreview: formatResultValue(result ?? "", 200),
|
|
118
|
+
resultType: getValueType(result),
|
|
119
|
+
snippet: scriptSnippet ? truncateScript(scriptSnippet) : "",
|
|
120
120
|
};
|
|
121
121
|
}
|
|
122
122
|
/**
|
|
@@ -126,14 +126,14 @@ function formatDetailed(data, format) {
|
|
|
126
126
|
const title = "Script Result";
|
|
127
127
|
const payloadFormat = format ?? DEFAULT_PRESENTER_FORMAT;
|
|
128
128
|
return presentStructuredData({
|
|
129
|
-
text: title,
|
|
130
|
-
detailLevel: "detailed",
|
|
131
|
-
structured: data,
|
|
132
129
|
context: {
|
|
133
|
-
title,
|
|
134
130
|
payloadFormat,
|
|
131
|
+
title,
|
|
135
132
|
},
|
|
133
|
+
detailLevel: "detailed",
|
|
134
|
+
structured: data,
|
|
136
135
|
template: "detailedPayload",
|
|
136
|
+
text: title,
|
|
137
137
|
}, payloadFormat);
|
|
138
138
|
}
|
|
139
139
|
function getValueType(value) {
|
|
@@ -3,27 +3,27 @@ export function formatToolMetadata(entries, options) {
|
|
|
3
3
|
const { detailLevel, responseFormat } = mergeFormatterOptions(options);
|
|
4
4
|
if (entries.length === 0) {
|
|
5
5
|
return finalizeFormattedText("No tools matched the requested criteria.", { detailLevel, responseFormat }, {
|
|
6
|
-
context: {
|
|
6
|
+
context: { filter: options?.filter, totalTools: 0 },
|
|
7
7
|
});
|
|
8
8
|
}
|
|
9
9
|
const sortedEntries = [...entries].sort((a, b) => a.modulePath.localeCompare(b.modulePath));
|
|
10
10
|
const structured = sortedEntries.map((entry) => ({
|
|
11
|
-
tool: entry.tool,
|
|
12
|
-
modulePath: entry.modulePath,
|
|
13
|
-
functionName: entry.functionName,
|
|
14
|
-
description: entry.description,
|
|
15
11
|
category: entry.category,
|
|
12
|
+
description: entry.description,
|
|
13
|
+
functionName: entry.functionName,
|
|
14
|
+
modulePath: entry.modulePath,
|
|
15
|
+
notes: entry.notes,
|
|
16
16
|
parameters: entry.parameters,
|
|
17
17
|
returns: entry.returns,
|
|
18
|
-
|
|
18
|
+
tool: entry.tool,
|
|
19
19
|
}));
|
|
20
20
|
const text = buildText(sortedEntries, detailLevel);
|
|
21
21
|
return finalizeFormattedText(text, { detailLevel, responseFormat }, {
|
|
22
|
-
structured,
|
|
23
22
|
context: {
|
|
24
|
-
totalTools: sortedEntries.length,
|
|
25
23
|
filter: options?.filter,
|
|
24
|
+
totalTools: sortedEntries.length,
|
|
26
25
|
},
|
|
26
|
+
structured,
|
|
27
27
|
template: detailLevel === "detailed" ? "detailedPayload" : undefined,
|
|
28
28
|
});
|
|
29
29
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Generated by orval v7.
|
|
2
|
+
* Generated by orval v7.17.0 🍺
|
|
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.
|
|
6
|
+
* OpenAPI spec version: 1.3.0
|
|
7
7
|
*/
|
|
8
8
|
import { customInstance } from '../../api/customInstance.js';
|
|
9
9
|
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
|
@@ -17,14 +17,14 @@ export const TdNodeFamilyType = {
|
|
|
17
17
|
CUSTOM: 'CUSTOM',
|
|
18
18
|
};
|
|
19
19
|
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
|
20
|
-
export const
|
|
20
|
+
export const TdPythonClassDetailsType = {
|
|
21
21
|
class: 'class',
|
|
22
22
|
module: 'module',
|
|
23
23
|
function: 'function',
|
|
24
24
|
object: 'object',
|
|
25
25
|
};
|
|
26
26
|
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
|
27
|
-
export const
|
|
27
|
+
export const TdPythonClassInfoType = {
|
|
28
28
|
class: 'class',
|
|
29
29
|
module: 'module',
|
|
30
30
|
function: 'function',
|
|
@@ -73,6 +73,15 @@ export const updateNode = (updateNodeRequest, options) => {
|
|
|
73
73
|
data: updateNodeRequest
|
|
74
74
|
}, options);
|
|
75
75
|
};
|
|
76
|
+
/**
|
|
77
|
+
* Collects TouchDesigner error messages for a node and its children
|
|
78
|
+
* @summary Get node errors
|
|
79
|
+
*/
|
|
80
|
+
export const getNodeErrors = (params, options) => {
|
|
81
|
+
return customInstance({ url: `${process.env.TD_WEB_SERVER_HOST}:${process.env.TD_WEB_SERVER_PORT}/api/nodes/errors`, method: 'GET',
|
|
82
|
+
params
|
|
83
|
+
}, options);
|
|
84
|
+
};
|
|
76
85
|
/**
|
|
77
86
|
* Returns a list of Python classes, modules, and functions available in TouchDesigner
|
|
78
87
|
* @summary Get a list of Python classes and modules
|
|
@@ -89,6 +98,15 @@ export const getTdPythonClassDetails = (className, options) => {
|
|
|
89
98
|
return customInstance({ url: `${process.env.TD_WEB_SERVER_HOST}:${process.env.TD_WEB_SERVER_PORT}/api/td/classes/${className}`, method: 'GET'
|
|
90
99
|
}, options);
|
|
91
100
|
};
|
|
101
|
+
/**
|
|
102
|
+
* Retrieve Python help() documentation for TouchDesigner modules, classes, or utilities like tdu.
|
|
103
|
+
* @summary Get module/class Python help documentation
|
|
104
|
+
*/
|
|
105
|
+
export const getModuleHelp = (params, options) => {
|
|
106
|
+
return customInstance({ url: `${process.env.TD_WEB_SERVER_HOST}:${process.env.TD_WEB_SERVER_PORT}/api/td/modules/help`, method: 'GET',
|
|
107
|
+
params
|
|
108
|
+
}, options);
|
|
109
|
+
};
|
|
92
110
|
/**
|
|
93
111
|
* Call a method on the node at the specified path (e.g., /project1).
|
|
94
112
|
This allows operations equivalent to TouchDesigner's Python API such as
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Generated by orval v7.
|
|
2
|
+
* Generated by orval v7.17.0 🍺
|
|
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.
|
|
6
|
+
* OpenAPI spec version: 1.3.0
|
|
7
7
|
*/
|
|
8
|
-
import
|
|
8
|
+
import * as zod from 'zod';
|
|
9
9
|
/**
|
|
10
10
|
* @summary Delete an existing node
|
|
11
11
|
*/
|
|
@@ -21,7 +21,7 @@ export const deleteNodeResponse = zod.object({
|
|
|
21
21
|
"opType": zod.string(),
|
|
22
22
|
"name": zod.string(),
|
|
23
23
|
"path": zod.string(),
|
|
24
|
-
"properties": zod.record(zod.string(), zod.
|
|
24
|
+
"properties": zod.record(zod.string(), zod.unknown())
|
|
25
25
|
}).optional().describe('Information about a TouchDesigner node')
|
|
26
26
|
}).nullable(),
|
|
27
27
|
"error": zod.string().nullable().describe('Error message if the operation was not successful')
|
|
@@ -44,7 +44,7 @@ export const getNodesResponse = zod.object({
|
|
|
44
44
|
"opType": zod.string(),
|
|
45
45
|
"name": zod.string(),
|
|
46
46
|
"path": zod.string(),
|
|
47
|
-
"properties": zod.record(zod.string(), zod.
|
|
47
|
+
"properties": zod.record(zod.string(), zod.unknown())
|
|
48
48
|
}).describe('Information about a TouchDesigner node')).optional().describe('Result of the execution')
|
|
49
49
|
}).nullable(),
|
|
50
50
|
"error": zod.string().nullable().describe('Error message if the operation was not successful')
|
|
@@ -65,7 +65,7 @@ export const createNodeResponse = zod.object({
|
|
|
65
65
|
"opType": zod.string(),
|
|
66
66
|
"name": zod.string(),
|
|
67
67
|
"path": zod.string(),
|
|
68
|
-
"properties": zod.record(zod.string(), zod.
|
|
68
|
+
"properties": zod.record(zod.string(), zod.unknown())
|
|
69
69
|
}).optional().describe('Information about a TouchDesigner node')
|
|
70
70
|
}).nullable(),
|
|
71
71
|
"error": zod.string().nullable().describe('Error message if the operation was not successful')
|
|
@@ -84,7 +84,7 @@ export const getNodeDetailResponse = zod.object({
|
|
|
84
84
|
"opType": zod.string(),
|
|
85
85
|
"name": zod.string(),
|
|
86
86
|
"path": zod.string(),
|
|
87
|
-
"properties": zod.record(zod.string(), zod.
|
|
87
|
+
"properties": zod.record(zod.string(), zod.unknown())
|
|
88
88
|
}).describe('Information about a TouchDesigner node'),
|
|
89
89
|
"error": zod.string().nullable().describe('Error message if the operation was not successful')
|
|
90
90
|
});
|
|
@@ -93,7 +93,7 @@ export const getNodeDetailResponse = zod.object({
|
|
|
93
93
|
*/
|
|
94
94
|
export const updateNodeBody = zod.object({
|
|
95
95
|
"nodePath": zod.string().describe('Path to the node (e.g., /project1/null1)'),
|
|
96
|
-
"properties": zod.record(zod.string(), zod.
|
|
96
|
+
"properties": zod.record(zod.string(), zod.unknown())
|
|
97
97
|
});
|
|
98
98
|
export const updateNodeResponse = zod.object({
|
|
99
99
|
"success": zod.boolean().describe('Whether the update operation was successful'),
|
|
@@ -108,6 +108,30 @@ export const updateNodeResponse = zod.object({
|
|
|
108
108
|
}).nullable(),
|
|
109
109
|
"error": zod.string().nullable().describe('Error message if the operation was not successful')
|
|
110
110
|
});
|
|
111
|
+
/**
|
|
112
|
+
* Collects TouchDesigner error messages for a node and its children
|
|
113
|
+
* @summary Get node errors
|
|
114
|
+
*/
|
|
115
|
+
export const getNodeErrorsQueryParams = zod.object({
|
|
116
|
+
"nodePath": zod.string().describe('Absolute path to the node to inspect. e.g., \"/project1/text1\"')
|
|
117
|
+
});
|
|
118
|
+
export const getNodeErrorsResponse = zod.object({
|
|
119
|
+
"success": zod.boolean().describe('Whether the operation was successful'),
|
|
120
|
+
"data": zod.object({
|
|
121
|
+
"nodePath": zod.string().describe('Path that was inspected for errors'),
|
|
122
|
+
"nodeName": zod.string().describe('Name of the inspected node'),
|
|
123
|
+
"opType": zod.string().describe('Operator type of the inspected node'),
|
|
124
|
+
"errorCount": zod.number().describe('Number of errors that were discovered'),
|
|
125
|
+
"hasErrors": zod.boolean().describe('Convenience flag indicating if any errors were found'),
|
|
126
|
+
"errors": zod.array(zod.object({
|
|
127
|
+
"nodePath": zod.string().describe('Absolute operator path that reported the error'),
|
|
128
|
+
"nodeName": zod.string().describe('Name of the operator'),
|
|
129
|
+
"opType": zod.string().describe('TouchDesigner operator type'),
|
|
130
|
+
"message": zod.string().describe('Error message reported by TouchDesigner')
|
|
131
|
+
}).describe('Single node error entry')).describe('Collection of raw node errors')
|
|
132
|
+
}).describe('Aggregated node error report'),
|
|
133
|
+
"error": zod.string().nullable().describe('Error message if the operation was not successful')
|
|
134
|
+
});
|
|
111
135
|
/**
|
|
112
136
|
* Returns a list of Python classes, modules, and functions available in TouchDesigner
|
|
113
137
|
* @summary Get a list of Python classes and modules
|
|
@@ -149,6 +173,21 @@ export const getTdPythonClassDetailsResponse = zod.object({
|
|
|
149
173
|
}).describe('Detailed information about a Python class or module'),
|
|
150
174
|
"error": zod.string().nullable().describe('Error message if the operation was not successful')
|
|
151
175
|
});
|
|
176
|
+
/**
|
|
177
|
+
* Retrieve Python help() documentation for TouchDesigner modules, classes, or utilities like tdu.
|
|
178
|
+
* @summary Get module/class Python help documentation
|
|
179
|
+
*/
|
|
180
|
+
export const getModuleHelpQueryParams = zod.object({
|
|
181
|
+
"moduleName": zod.string().describe('Module or class name (e.g., \"noiseCHOP\", \"td.noiseCHOP\", \"tdu\").')
|
|
182
|
+
});
|
|
183
|
+
export const getModuleHelpResponse = zod.object({
|
|
184
|
+
"success": zod.boolean().describe('Whether the operation was successful'),
|
|
185
|
+
"data": zod.object({
|
|
186
|
+
"moduleName": zod.string().describe('Normalized module/class name the help text was generated for'),
|
|
187
|
+
"helpText": zod.string().describe('Captured output from Python\'s help() function')
|
|
188
|
+
}).describe('Raw Python help() output for a TouchDesigner module or class'),
|
|
189
|
+
"error": zod.string().nullable().describe('Error message if the operation was not successful')
|
|
190
|
+
});
|
|
152
191
|
/**
|
|
153
192
|
* Call a method on the node at the specified path (e.g., /project1).
|
|
154
193
|
This allows operations equivalent to TouchDesigner's Python API such as
|
|
@@ -160,7 +199,7 @@ export const execNodeMethodBody = zod.object({
|
|
|
160
199
|
"nodePath": zod.string().describe('Path to the node (e.g., /project1/null1)'),
|
|
161
200
|
"method": zod.string().describe('Name of the method to call'),
|
|
162
201
|
"args": zod.array(zod.union([zod.string(), zod.number(), zod.boolean()])).optional().describe('List of arguments for the method call'),
|
|
163
|
-
"kwargs": zod.record(zod.string(), zod.
|
|
202
|
+
"kwargs": zod.record(zod.string(), zod.unknown()).optional().describe('Keyword arguments for the method call')
|
|
164
203
|
});
|
|
165
204
|
export const execNodeMethodResponse = zod.object({
|
|
166
205
|
"success": zod.boolean().describe('Whether the operation was successful'),
|
|
@@ -196,10 +235,11 @@ export const execPythonScriptResponse = zod.object({
|
|
|
196
235
|
export const getTdInfoResponse = zod.object({
|
|
197
236
|
"success": zod.boolean().describe('Whether the operation was successful'),
|
|
198
237
|
"data": zod.object({
|
|
199
|
-
"
|
|
200
|
-
"version": zod.string().describe('TouchDesigner version number'),
|
|
238
|
+
"mcpApiVersion": zod.string().describe('Version of the TouchDesigner MCP API server'),
|
|
201
239
|
"osName": zod.string().describe('Operating system name'),
|
|
202
|
-
"osVersion": zod.string().describe('Operating system version')
|
|
240
|
+
"osVersion": zod.string().describe('Operating system version'),
|
|
241
|
+
"server": zod.string().describe('Server name (typically \"TouchDesigner\")'),
|
|
242
|
+
"version": zod.string().describe('TouchDesigner version number')
|
|
203
243
|
}).nullable(),
|
|
204
244
|
"error": zod.string().nullable().describe('Error message if the operation was not successful')
|
|
205
245
|
});
|
|
@@ -5,31 +5,31 @@ import { createErrorResult, createSuccessResult } from "../core/result.js";
|
|
|
5
5
|
export class ConnectionManager {
|
|
6
6
|
server;
|
|
7
7
|
logger;
|
|
8
|
-
tdClient;
|
|
9
8
|
transport = null;
|
|
10
|
-
constructor(server, logger
|
|
9
|
+
constructor(server, logger) {
|
|
11
10
|
this.server = server;
|
|
12
11
|
this.logger = logger;
|
|
13
|
-
this.tdClient = tdClient;
|
|
14
12
|
}
|
|
15
13
|
/**
|
|
16
14
|
* Connect to MCP transport
|
|
17
15
|
*/
|
|
18
16
|
async connect(transport) {
|
|
19
17
|
if (this.isConnected()) {
|
|
20
|
-
this.logger.
|
|
18
|
+
this.logger.sendLog({
|
|
19
|
+
data: "MCP server already connected",
|
|
20
|
+
level: "info",
|
|
21
|
+
logger: "ConnectionManager",
|
|
22
|
+
});
|
|
21
23
|
return createSuccessResult(undefined);
|
|
22
24
|
}
|
|
23
25
|
this.transport = transport;
|
|
24
26
|
try {
|
|
25
27
|
await this.server.connect(transport);
|
|
26
|
-
this.logger.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
this.logger.log("TouchDesigner connection verified");
|
|
28
|
+
this.logger.sendLog({
|
|
29
|
+
data: `Server connected and ready to process requests: ${process.env.TD_WEB_SERVER_HOST}:${process.env.TD_WEB_SERVER_PORT}`,
|
|
30
|
+
level: "info",
|
|
31
|
+
logger: "ConnectionManager",
|
|
32
|
+
});
|
|
33
33
|
return createSuccessResult(undefined);
|
|
34
34
|
}
|
|
35
35
|
catch (error) {
|
|
@@ -65,21 +65,4 @@ export class ConnectionManager {
|
|
|
65
65
|
isConnected() {
|
|
66
66
|
return this.transport !== null;
|
|
67
67
|
}
|
|
68
|
-
/**
|
|
69
|
-
* Check connection to TouchDesigner
|
|
70
|
-
*/
|
|
71
|
-
async checkTDConnection() {
|
|
72
|
-
this.logger.log("Testing connection to TouchDesigner server...");
|
|
73
|
-
try {
|
|
74
|
-
const result = await this.tdClient.getTdInfo();
|
|
75
|
-
if (!result.success) {
|
|
76
|
-
throw result.error;
|
|
77
|
-
}
|
|
78
|
-
return createSuccessResult(result.data);
|
|
79
|
-
}
|
|
80
|
-
catch (error) {
|
|
81
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
82
|
-
return createErrorResult(err);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
68
|
}
|
|
@@ -1,5 +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
4
|
import { registerPrompts } from "../features/prompts/index.js";
|
|
4
5
|
import { registerTools } from "../features/tools/index.js";
|
|
5
6
|
import { createTouchDesignerClient } from "../tdClient/index.js";
|
|
@@ -18,17 +19,17 @@ export class TouchDesignerServer {
|
|
|
18
19
|
constructor() {
|
|
19
20
|
this.server = new McpServer({
|
|
20
21
|
name: "TouchDesigner",
|
|
21
|
-
version:
|
|
22
|
+
version: PACKAGE_VERSION,
|
|
22
23
|
}, {
|
|
23
24
|
capabilities: {
|
|
24
|
-
prompts: {},
|
|
25
25
|
logging: {},
|
|
26
|
+
prompts: {},
|
|
26
27
|
tools: {},
|
|
27
28
|
},
|
|
28
29
|
});
|
|
29
30
|
this.logger = new McpLogger(this.server);
|
|
30
31
|
this.tdClient = createTouchDesignerClient({ logger: this.logger });
|
|
31
|
-
this.connectionManager = new ConnectionManager(this.server, this.logger
|
|
32
|
+
this.connectionManager = new ConnectionManager(this.server, this.logger);
|
|
32
33
|
this.registerAllFeatures();
|
|
33
34
|
}
|
|
34
35
|
/**
|