touchdesigner-mcp-server 0.2.1
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/LICENSE +21 -0
- package/README.ja.md +211 -0
- package/README.md +204 -0
- package/dist/api/customInstance.js +18 -0
- package/dist/cli.js +36 -0
- package/dist/core/constants.js +26 -0
- package/dist/core/errorHandling.js +19 -0
- package/dist/core/logger.js +45 -0
- package/dist/core/result.js +12 -0
- package/dist/features/prompts/handlers/td_prompts.js +131 -0
- package/dist/features/prompts/index.js +1 -0
- package/dist/features/prompts/register.js +7 -0
- package/dist/features/tools/handlers/tdTools.js +214 -0
- package/dist/features/tools/index.js +1 -0
- package/dist/features/tools/register.js +7 -0
- package/dist/features/tools/types.js +1 -0
- package/dist/features/tools/utils/toolUtils.js +1 -0
- package/dist/gen/endpoints/TouchDesignerAPI.js +250 -0
- package/dist/gen/mcp/touchDesignerAPI.zod.js +203 -0
- package/dist/index.js +3 -0
- package/dist/mock/index.js +5 -0
- package/dist/mock/node.js +3 -0
- package/dist/server/connectionManager.js +83 -0
- package/dist/server/touchDesignerServer.js +60 -0
- package/dist/tdClient/index.js +6 -0
- package/dist/tdClient/touchDesignerClient.js +150 -0
- package/package.json +76 -0
- package/src/index.ts +6 -0
- package/td/genHandlers.js +47 -0
- package/td/import_modules.py +52 -0
- package/td/mcp_webserver_base.tox +0 -0
- package/td/modules/mcp/controllers/__init__.py +9 -0
- package/td/modules/mcp/controllers/api_controller.py +623 -0
- package/td/modules/mcp/controllers/generated_handlers.py +365 -0
- package/td/modules/mcp/controllers/openapi_router.py +265 -0
- package/td/modules/mcp/services/__init__.py +8 -0
- package/td/modules/mcp/services/api_service.py +535 -0
- package/td/modules/mcp_webserver_script.py +134 -0
- package/td/modules/td_server/.dockerignore +72 -0
- package/td/modules/td_server/.openapi-generator/FILES +55 -0
- package/td/modules/td_server/.openapi-generator/VERSION +1 -0
- package/td/modules/td_server/.openapi-generator-ignore +23 -0
- package/td/modules/td_server/.travis.yml +14 -0
- package/td/modules/td_server/Dockerfile +16 -0
- package/td/modules/td_server/README.md +49 -0
- package/td/modules/td_server/git_push.sh +57 -0
- package/td/modules/td_server/openapi_server/__init__.py +0 -0
- package/td/modules/td_server/openapi_server/__main__.py +19 -0
- package/td/modules/td_server/openapi_server/controllers/__init__.py +0 -0
- package/td/modules/td_server/openapi_server/controllers/default_controller.py +160 -0
- package/td/modules/td_server/openapi_server/controllers/security_controller.py +2 -0
- package/td/modules/td_server/openapi_server/encoder.py +19 -0
- package/td/modules/td_server/openapi_server/models/__init__.py +33 -0
- package/td/modules/td_server/openapi_server/models/base_model.py +68 -0
- package/td/modules/td_server/openapi_server/models/create_node200_response.py +125 -0
- package/td/modules/td_server/openapi_server/models/create_node200_response_data.py +63 -0
- package/td/modules/td_server/openapi_server/models/create_node_request.py +123 -0
- package/td/modules/td_server/openapi_server/models/delete_node200_response.py +125 -0
- package/td/modules/td_server/openapi_server/models/delete_node200_response_data.py +91 -0
- package/td/modules/td_server/openapi_server/models/exec_node_method200_response.py +125 -0
- package/td/modules/td_server/openapi_server/models/exec_node_method200_response_data.py +65 -0
- package/td/modules/td_server/openapi_server/models/exec_node_method_request.py +153 -0
- package/td/modules/td_server/openapi_server/models/exec_node_method_request_args_inner.py +34 -0
- package/td/modules/td_server/openapi_server/models/exec_python_script200_response.py +125 -0
- package/td/modules/td_server/openapi_server/models/exec_python_script200_response_data.py +65 -0
- package/td/modules/td_server/openapi_server/models/exec_python_script200_response_data_result.py +63 -0
- package/td/modules/td_server/openapi_server/models/exec_python_script_request.py +65 -0
- package/td/modules/td_server/openapi_server/models/get_node_detail200_response.py +125 -0
- package/td/modules/td_server/openapi_server/models/get_nodes200_response.py +125 -0
- package/td/modules/td_server/openapi_server/models/get_nodes200_response_data.py +65 -0
- package/td/modules/td_server/openapi_server/models/get_td_info200_response.py +125 -0
- package/td/modules/td_server/openapi_server/models/get_td_info200_response_data.py +155 -0
- package/td/modules/td_server/openapi_server/models/get_td_python_class_details200_response.py +125 -0
- package/td/modules/td_server/openapi_server/models/get_td_python_classes200_response.py +125 -0
- package/td/modules/td_server/openapi_server/models/get_td_python_classes200_response_data.py +63 -0
- package/td/modules/td_server/openapi_server/models/td_node.py +175 -0
- package/td/modules/td_server/openapi_server/models/td_node_family_type.py +44 -0
- package/td/modules/td_server/openapi_server/models/td_python_class_details.py +191 -0
- package/td/modules/td_server/openapi_server/models/td_python_class_info.py +127 -0
- package/td/modules/td_server/openapi_server/models/td_python_method_info.py +121 -0
- package/td/modules/td_server/openapi_server/models/td_python_property_info.py +123 -0
- package/td/modules/td_server/openapi_server/models/update_node200_response.py +125 -0
- package/td/modules/td_server/openapi_server/models/update_node200_response_data.py +149 -0
- package/td/modules/td_server/openapi_server/models/update_node200_response_data_failed_inner.py +91 -0
- package/td/modules/td_server/openapi_server/models/update_node_request.py +93 -0
- package/td/modules/td_server/openapi_server/openapi/openapi.yaml +966 -0
- package/td/modules/td_server/openapi_server/test/__init__.py +16 -0
- package/td/modules/td_server/openapi_server/test/test_default_controller.py +200 -0
- package/td/modules/td_server/openapi_server/typing_utils.py +30 -0
- package/td/modules/td_server/openapi_server/util.py +147 -0
- package/td/modules/td_server/requirements.txt +13 -0
- package/td/modules/td_server/setup.py +37 -0
- package/td/modules/td_server/test-requirements.txt +4 -0
- package/td/modules/td_server/tox.ini +11 -0
- package/td/modules/utils/config.py +7 -0
- package/td/modules/utils/error_handling.py +104 -0
- package/td/modules/utils/logging.py +23 -0
- package/td/modules/utils/result.py +40 -0
- package/td/modules/utils/serialization.py +57 -0
- package/td/modules/utils/types.py +33 -0
- package/td/modules/utils/utils_logging.py +60 -0
- package/td/templates/mcp/api_controller_handlers.mustache +63 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { GetPromptRequestSchema, ListPromptsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
import { PROMPT_NAMES, TOOL_NAMES } from "../../../core/constants.js";
|
|
3
|
+
const PROMPTS = [
|
|
4
|
+
{
|
|
5
|
+
name: PROMPT_NAMES.SEARCH_NODE,
|
|
6
|
+
description: "Fuzzy search for node",
|
|
7
|
+
arguments: [
|
|
8
|
+
{
|
|
9
|
+
name: "nodeName",
|
|
10
|
+
description: "Name of the node to check",
|
|
11
|
+
required: true,
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
name: "nodeFamily",
|
|
15
|
+
description: "Family of the node to check",
|
|
16
|
+
required: false,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: "nodeType",
|
|
20
|
+
description: "Type of the node to check",
|
|
21
|
+
required: false,
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: PROMPT_NAMES.CHECK_NODE_ERRORS,
|
|
27
|
+
description: "Fuzzy search for node and return errors in TouchDesigner.",
|
|
28
|
+
arguments: [
|
|
29
|
+
{
|
|
30
|
+
name: "nodePath",
|
|
31
|
+
description: "Path to the node to check",
|
|
32
|
+
required: true,
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: PROMPT_NAMES.NODE_CONNECTION,
|
|
38
|
+
description: "Connect nodes between each other in TouchDesigner.",
|
|
39
|
+
},
|
|
40
|
+
];
|
|
41
|
+
/**
|
|
42
|
+
* Register prompt handlers with MCP server
|
|
43
|
+
*/
|
|
44
|
+
export function registerTdPrompts(server, logger) {
|
|
45
|
+
server.server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
46
|
+
return {
|
|
47
|
+
prompts: PROMPTS,
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
server.server.setRequestHandler(GetPromptRequestSchema, (request) => {
|
|
51
|
+
try {
|
|
52
|
+
logger.debug(`Handling GetPromptRequest: ${request.params.name}`);
|
|
53
|
+
const prompt = getPrompt(request.params.name);
|
|
54
|
+
if (!prompt) {
|
|
55
|
+
throw new Error("Prompt name is required");
|
|
56
|
+
}
|
|
57
|
+
if (prompt.name === PROMPT_NAMES.SEARCH_NODE) {
|
|
58
|
+
if (!request.params.arguments?.nodeName) {
|
|
59
|
+
throw new Error("Missing required argument: nodeName");
|
|
60
|
+
}
|
|
61
|
+
const { nodeName, nodeFamily, nodeType } = request.params.arguments;
|
|
62
|
+
const messages = handleSearchNodePrompt({
|
|
63
|
+
nodeName,
|
|
64
|
+
nodeFamily,
|
|
65
|
+
nodeType,
|
|
66
|
+
});
|
|
67
|
+
return { messages };
|
|
68
|
+
}
|
|
69
|
+
if (prompt.name === PROMPT_NAMES.CHECK_NODE_ERRORS) {
|
|
70
|
+
if (!request.params.arguments?.nodePath) {
|
|
71
|
+
throw new Error("Missing required argument: nodePath");
|
|
72
|
+
}
|
|
73
|
+
const { nodePath } = request.params.arguments;
|
|
74
|
+
const messages = handleCheckNodeErrorsPrompt({
|
|
75
|
+
nodePath,
|
|
76
|
+
});
|
|
77
|
+
return { messages };
|
|
78
|
+
}
|
|
79
|
+
if (prompt.name === PROMPT_NAMES.NODE_CONNECTION) {
|
|
80
|
+
const messages = handleNodeConnectionPrompt();
|
|
81
|
+
return { messages };
|
|
82
|
+
}
|
|
83
|
+
throw new Error(`Prompt implementation for ${request.params.name} not found`);
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
87
|
+
logger.error(`Error handling prompt request: ${errorMessage}`);
|
|
88
|
+
throw new Error(errorMessage);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
function handleSearchNodePrompt(params) {
|
|
93
|
+
return [
|
|
94
|
+
{
|
|
95
|
+
role: "user",
|
|
96
|
+
content: {
|
|
97
|
+
type: "text",
|
|
98
|
+
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}.` : ""}`,
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
];
|
|
102
|
+
}
|
|
103
|
+
function handleCheckNodeErrorsPrompt(params) {
|
|
104
|
+
return [
|
|
105
|
+
{
|
|
106
|
+
role: "user",
|
|
107
|
+
content: {
|
|
108
|
+
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
|
+
},
|
|
111
|
+
},
|
|
112
|
+
];
|
|
113
|
+
}
|
|
114
|
+
function handleNodeConnectionPrompt() {
|
|
115
|
+
return [
|
|
116
|
+
{
|
|
117
|
+
role: "user",
|
|
118
|
+
content: {
|
|
119
|
+
type: "text",
|
|
120
|
+
text: `Use the "${TOOL_NAMES.EXECUTE_PYTHON_SCRIPT}" tool e.g. op('/project1/text_over_image').outputConnectors[0].connect(op('/project1/out1'))`,
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
];
|
|
124
|
+
}
|
|
125
|
+
function getPrompt(name) {
|
|
126
|
+
const prompt = PROMPTS.find((p) => p.name === name);
|
|
127
|
+
if (!prompt) {
|
|
128
|
+
throw new Error(`Prompt ${name} not found`);
|
|
129
|
+
}
|
|
130
|
+
return prompt;
|
|
131
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./register.js";
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { REFERENCE_COMMENT, TOOL_NAMES } from "../../../core/constants.js";
|
|
2
|
+
import { handleToolError } from "../../../core/errorHandling.js";
|
|
3
|
+
import { createNodeBody, deleteNodeQueryParams, execNodeMethodBody, execPythonScriptBody, getNodeDetailQueryParams, getNodesQueryParams, getTdPythonClassDetailsParams, updateNodeBody, } from "../../../gen/mcp/touchDesignerAPI.zod.js";
|
|
4
|
+
export function registerTdTools(server, logger, tdClient) {
|
|
5
|
+
server.tool(TOOL_NAMES.GET_TD_INFO, "Get server information from TouchDesigner", async () => {
|
|
6
|
+
try {
|
|
7
|
+
const result = await tdClient.getTdInfo();
|
|
8
|
+
if (!result.success) {
|
|
9
|
+
throw result.error;
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
content: [
|
|
13
|
+
{
|
|
14
|
+
type: "text",
|
|
15
|
+
text: `Server information: ${JSON.stringify(result, null, 2)}`,
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
return handleToolError(error, logger, TOOL_NAMES.GET_TD_INFO);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
server.tool(TOOL_NAMES.EXECUTE_PYTHON_SCRIPT, "Execute Python script directly in TouchDesigner", execPythonScriptBody.strict().shape, async (params) => {
|
|
25
|
+
try {
|
|
26
|
+
const { script } = params;
|
|
27
|
+
logger.debug(`Executing script: ${script}`);
|
|
28
|
+
const result = await tdClient.execPythonScript(params);
|
|
29
|
+
if (!result.success) {
|
|
30
|
+
throw result.error;
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
content: [
|
|
34
|
+
{
|
|
35
|
+
type: "text",
|
|
36
|
+
text: `Script executed successfully. Result: ${JSON.stringify(result, null, 2)}`,
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
return handleToolError(error, logger, TOOL_NAMES.EXECUTE_PYTHON_SCRIPT);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
server.tool(TOOL_NAMES.CREATE_TD_NODE, "Create a new node in TouchDesigner", createNodeBody.strict().shape, async (params) => {
|
|
46
|
+
try {
|
|
47
|
+
const { parentPath, nodeType, nodeName } = params;
|
|
48
|
+
const result = await tdClient.createNode({
|
|
49
|
+
parentPath,
|
|
50
|
+
nodeType,
|
|
51
|
+
nodeName,
|
|
52
|
+
});
|
|
53
|
+
if (!result.success) {
|
|
54
|
+
throw result.error;
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
content: [
|
|
58
|
+
{
|
|
59
|
+
type: "text",
|
|
60
|
+
text: `Node created successfully: ${JSON.stringify(result, null, 2)}`,
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
return handleToolError(error, logger, TOOL_NAMES.CREATE_TD_NODE, REFERENCE_COMMENT);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
server.tool(TOOL_NAMES.DELETE_TD_NODE, "Delete an existing node in TouchDesigner", deleteNodeQueryParams.strict().shape, async (params) => {
|
|
70
|
+
try {
|
|
71
|
+
const result = await tdClient.deleteNode(params);
|
|
72
|
+
if (!result.success) {
|
|
73
|
+
throw result.error;
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
content: [
|
|
77
|
+
{
|
|
78
|
+
type: "text",
|
|
79
|
+
text: `Node deleted successfully: ${JSON.stringify(result, null, 2)}`,
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
return handleToolError(error, logger, TOOL_NAMES.DELETE_TD_NODE, REFERENCE_COMMENT);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
server.tool(TOOL_NAMES.GET_TD_NODES, "Get all nodes in the parent path", getNodesQueryParams.strict().shape, async (params) => {
|
|
89
|
+
try {
|
|
90
|
+
const result = await tdClient.getNodes(params);
|
|
91
|
+
if (!result.success) {
|
|
92
|
+
throw result.error;
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
content: [
|
|
96
|
+
{
|
|
97
|
+
type: "text",
|
|
98
|
+
text: `Project nodes retrieved: ${JSON.stringify(result, null, 2)}`,
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
return handleToolError(error, logger, TOOL_NAMES.GET_TD_NODES, REFERENCE_COMMENT);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
server.tool(TOOL_NAMES.GET_TD_NODE_PARAMETERS, "Get parameters of a specific node in TouchDesigner", getNodeDetailQueryParams.strict().shape, async (params) => {
|
|
108
|
+
try {
|
|
109
|
+
const { nodePath } = params;
|
|
110
|
+
const result = await tdClient.getNodeDetail(params);
|
|
111
|
+
if (!result.success) {
|
|
112
|
+
throw result.error;
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
content: [
|
|
116
|
+
{
|
|
117
|
+
type: "text",
|
|
118
|
+
text: `Node parameters for node at path ${nodePath}: ${JSON.stringify(result, null, 2)}`,
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
return handleToolError(error, logger, TOOL_NAMES.GET_TD_NODE_PARAMETERS, REFERENCE_COMMENT);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
server.tool(TOOL_NAMES.UPDATE_TD_NODE_PARAMETERS, "Update parameters of a specific node in TouchDesigner", updateNodeBody.strict().shape, async (params) => {
|
|
128
|
+
try {
|
|
129
|
+
const result = await tdClient.updateNode(params);
|
|
130
|
+
if (!result.success) {
|
|
131
|
+
throw result.error;
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
content: [
|
|
135
|
+
{
|
|
136
|
+
type: "text",
|
|
137
|
+
text: `Node parameters updated successfully: ${JSON.stringify(result, null, 2)}`,
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
return handleToolError(error, logger, TOOL_NAMES.UPDATE_TD_NODE_PARAMETERS, REFERENCE_COMMENT);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
server.tool(TOOL_NAMES.EXECUTE_NODE_METHOD, "Execute a method on a specific node in TouchDesigner", execNodeMethodBody.strict().shape, async (params) => {
|
|
147
|
+
try {
|
|
148
|
+
const { nodePath, method, args } = params;
|
|
149
|
+
const result = await tdClient.execNodeMethod(params);
|
|
150
|
+
if (!result.success) {
|
|
151
|
+
throw result.error;
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
content: [
|
|
155
|
+
{
|
|
156
|
+
type: "text",
|
|
157
|
+
text: `op('${nodePath}').${method}(${args?.map((v) => `"${v}"`).join(",")}${params.kwargs
|
|
158
|
+
? Object.entries(params.kwargs)
|
|
159
|
+
.map(([key, value]) => `${key}=${JSON.stringify(value)}`)
|
|
160
|
+
.join(", ")
|
|
161
|
+
: ""})`,
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
type: "text",
|
|
165
|
+
text: JSON.stringify(result, null, 2),
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
logger.error(error);
|
|
172
|
+
return handleToolError(error, logger, TOOL_NAMES.EXECUTE_NODE_METHOD, REFERENCE_COMMENT);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
server.tool(TOOL_NAMES.GET_TD_CLASSES, "Get list of classes and modules in TouchDesigner", async () => {
|
|
176
|
+
try {
|
|
177
|
+
const result = await tdClient.getClasses();
|
|
178
|
+
if (!result.success) {
|
|
179
|
+
throw result.error;
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
content: [
|
|
183
|
+
{
|
|
184
|
+
type: "text",
|
|
185
|
+
text: `TouchDesigner classes list: ${JSON.stringify(result, null, 2)}`,
|
|
186
|
+
},
|
|
187
|
+
],
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
return handleToolError(error, logger, TOOL_NAMES.GET_TD_CLASSES, REFERENCE_COMMENT);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
server.tool(TOOL_NAMES.GET_TD_CLASS_DETAILS, "Get detailed information about a specific TouchDesigner class or module", getTdPythonClassDetailsParams.strict().shape, async (params) => {
|
|
195
|
+
try {
|
|
196
|
+
const { className } = params;
|
|
197
|
+
const result = await tdClient.getClassDetails(className);
|
|
198
|
+
if (!result.success) {
|
|
199
|
+
throw result.error;
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
content: [
|
|
203
|
+
{
|
|
204
|
+
type: "text",
|
|
205
|
+
text: `Details for ${className}: ${JSON.stringify(result, null, 2)}`,
|
|
206
|
+
},
|
|
207
|
+
],
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
return handleToolError(error, logger, TOOL_NAMES.GET_TD_CLASS_DETAILS, REFERENCE_COMMENT);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./register.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generated by orval v7.9.0 🍺
|
|
3
|
+
* Do not edit manually.
|
|
4
|
+
* TouchDesigner API
|
|
5
|
+
* OpenAPI schema for generating TouchDesigner API client code
|
|
6
|
+
* OpenAPI spec version: 0.2.1
|
|
7
|
+
*/
|
|
8
|
+
import { faker } from '@faker-js/faker';
|
|
9
|
+
import { HttpResponse, delay, http } from 'msw';
|
|
10
|
+
import { customInstance } from '../../api/customInstance.js';
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
|
12
|
+
export const TdNodeFamilyType = {
|
|
13
|
+
COMP: 'COMP',
|
|
14
|
+
CHOP: 'CHOP',
|
|
15
|
+
TOP: 'TOP',
|
|
16
|
+
SOP: 'SOP',
|
|
17
|
+
DAT: 'DAT',
|
|
18
|
+
MAT: 'MAT',
|
|
19
|
+
CUSTOM: 'CUSTOM',
|
|
20
|
+
};
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
|
22
|
+
export const TdPythonClassInfoType = {
|
|
23
|
+
class: 'class',
|
|
24
|
+
module: 'module',
|
|
25
|
+
function: 'function',
|
|
26
|
+
object: 'object',
|
|
27
|
+
};
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
|
29
|
+
export const TdPythonClassDetailsType = {
|
|
30
|
+
class: 'class',
|
|
31
|
+
module: 'module',
|
|
32
|
+
function: 'function',
|
|
33
|
+
object: 'object',
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* @summary Delete an existing node
|
|
37
|
+
*/
|
|
38
|
+
export const deleteNode = (params, options) => {
|
|
39
|
+
return customInstance({ url: `http://localhost:9981/api/nodes`, method: 'DELETE',
|
|
40
|
+
params
|
|
41
|
+
}, options);
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* @summary Get nodes in the path
|
|
45
|
+
*/
|
|
46
|
+
export const getNodes = (params, options) => {
|
|
47
|
+
return customInstance({ url: `http://localhost:9981/api/nodes`, method: 'GET',
|
|
48
|
+
params
|
|
49
|
+
}, options);
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* @summary Create a new node
|
|
53
|
+
*/
|
|
54
|
+
export const createNode = (createNodeRequest, options) => {
|
|
55
|
+
return customInstance({ url: `http://localhost:9981/api/nodes`, method: 'POST',
|
|
56
|
+
headers: { 'Content-Type': 'application/json', },
|
|
57
|
+
data: createNodeRequest
|
|
58
|
+
}, options);
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Retrieves detailed information about a specific node including its properties, parameters and connections
|
|
62
|
+
* @summary Get node detail
|
|
63
|
+
*/
|
|
64
|
+
export const getNodeDetail = (params, options) => {
|
|
65
|
+
return customInstance({ url: `http://localhost:9981/api/nodes/detail`, method: 'GET',
|
|
66
|
+
params
|
|
67
|
+
}, options);
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* @summary Update node properties
|
|
71
|
+
*/
|
|
72
|
+
export const updateNode = (updateNodeRequest, options) => {
|
|
73
|
+
return customInstance({ url: `http://localhost:9981/api/nodes/detail`, method: 'PATCH',
|
|
74
|
+
headers: { 'Content-Type': 'application/json', },
|
|
75
|
+
data: updateNodeRequest
|
|
76
|
+
}, options);
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Returns a list of Python classes, modules, and functions available in TouchDesigner
|
|
80
|
+
* @summary Get a list of Python classes and modules
|
|
81
|
+
*/
|
|
82
|
+
export const getTdPythonClasses = (options) => {
|
|
83
|
+
return customInstance({ url: `http://localhost:9981/api/td/classes`, method: 'GET'
|
|
84
|
+
}, options);
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Returns detailed information about a specific Python class, module, or function including methods, properties, and documentation
|
|
88
|
+
* @summary Get details of a specific Python class or module
|
|
89
|
+
*/
|
|
90
|
+
export const getTdPythonClassDetails = (className, options) => {
|
|
91
|
+
return customInstance({ url: `http://localhost:9981/api/td/classes/${className}`, method: 'GET'
|
|
92
|
+
}, options);
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Call a method on the node at the specified path (e.g., /project1).
|
|
96
|
+
This allows operations equivalent to TouchDesigner's Python API such as
|
|
97
|
+
`parent_comp = op('/project1')` and `parent_comp.create('textTOP', 'myText')`.
|
|
98
|
+
|
|
99
|
+
* @summary Call a method of the specified node
|
|
100
|
+
*/
|
|
101
|
+
export const execNodeMethod = (execNodeMethodRequest, options) => {
|
|
102
|
+
return customInstance({ url: `http://localhost:9981/api/td/nodes/exec`, method: 'POST',
|
|
103
|
+
headers: { 'Content-Type': 'application/json', },
|
|
104
|
+
data: execNodeMethodRequest
|
|
105
|
+
}, options);
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Execute a Python script directly in TouchDesigner.
|
|
109
|
+
Multiline scripts and scripts containing comments are supported.
|
|
110
|
+
The script can optionally set a `result` variable to explicitly return a value.
|
|
111
|
+
This endpoint allows you to interact with TouchDesigner nodes programmatically.
|
|
112
|
+
|
|
113
|
+
* @summary Execute python code on the server
|
|
114
|
+
*/
|
|
115
|
+
export const execPythonScript = (execPythonScriptRequest, options) => {
|
|
116
|
+
return customInstance({ url: `http://localhost:9981/api/td/server/exec`, method: 'POST',
|
|
117
|
+
headers: { 'Content-Type': 'application/json', },
|
|
118
|
+
data: execPythonScriptRequest
|
|
119
|
+
}, options);
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* Returns information about the TouchDesigner
|
|
123
|
+
* @summary Get TouchDesigner information
|
|
124
|
+
*/
|
|
125
|
+
export const getTdInfo = (options) => {
|
|
126
|
+
return customInstance({ url: `http://localhost:9981/api/td/server/td`, method: 'GET'
|
|
127
|
+
}, options);
|
|
128
|
+
};
|
|
129
|
+
export const getDeleteNodeResponseMock = (overrideResponse = {}) => ({ success: faker.datatype.boolean(), data: { deleted: faker.helpers.arrayElement([faker.datatype.boolean(), undefined]), node: faker.helpers.arrayElement([{ id: faker.number.int({ min: undefined, max: undefined }), opType: faker.string.alpha(20), name: faker.string.alpha(20), path: faker.string.alpha(20), properties: {} }, undefined]) }, error: faker.helpers.arrayElement([faker.string.alpha(20), null]), ...overrideResponse });
|
|
130
|
+
export const getGetNodesResponseMock = (overrideResponse = {}) => ({ success: faker.datatype.boolean(), data: { nodes: faker.helpers.arrayElement([Array.from({ length: faker.number.int({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => ({ id: faker.number.int({ min: undefined, max: undefined }), opType: faker.string.alpha(20), name: faker.string.alpha(20), path: faker.string.alpha(20), properties: {} })), undefined]) }, error: faker.helpers.arrayElement([faker.string.alpha(20), null]), ...overrideResponse });
|
|
131
|
+
export const getCreateNodeResponseMock = (overrideResponse = {}) => ({ success: faker.datatype.boolean(), data: { result: faker.helpers.arrayElement([{ id: faker.number.int({ min: undefined, max: undefined }), opType: faker.string.alpha(20), name: faker.string.alpha(20), path: faker.string.alpha(20), properties: {} }, undefined]) }, error: faker.helpers.arrayElement([faker.string.alpha(20), null]), ...overrideResponse });
|
|
132
|
+
export const getGetNodeDetailResponseMock = (overrideResponse = {}) => ({ success: faker.datatype.boolean(), data: { id: faker.number.int({ min: undefined, max: undefined }), opType: faker.string.alpha(20), name: faker.string.alpha(20), path: faker.string.alpha(20), properties: {} }, error: faker.helpers.arrayElement([faker.string.alpha(20), null]), ...overrideResponse });
|
|
133
|
+
export const getUpdateNodeResponseMock = (overrideResponse = {}) => ({ success: faker.datatype.boolean(), data: { path: faker.helpers.arrayElement([faker.string.alpha(20), undefined]), updated: faker.helpers.arrayElement([Array.from({ length: faker.number.int({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => (faker.string.alpha(20))), undefined]), failed: faker.helpers.arrayElement([Array.from({ length: faker.number.int({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => ({ name: faker.helpers.arrayElement([faker.string.alpha(20), undefined]), reason: faker.helpers.arrayElement([faker.string.alpha(20), undefined]) })), undefined]), message: faker.helpers.arrayElement([faker.string.alpha(20), undefined]) }, error: faker.helpers.arrayElement([faker.string.alpha(20), null]), ...overrideResponse });
|
|
134
|
+
export const getGetTdPythonClassesResponseMock = (overrideResponse = {}) => ({ success: faker.datatype.boolean(), data: { classes: faker.helpers.arrayElement([Array.from({ length: faker.number.int({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => ({ name: faker.string.alpha(20), type: faker.helpers.arrayElement(['class', 'module', 'function', 'object']), description: faker.helpers.arrayElement([faker.string.alpha(20), undefined]) })), undefined]) }, error: faker.helpers.arrayElement([faker.string.alpha(20), null]), ...overrideResponse });
|
|
135
|
+
export const getGetTdPythonClassDetailsResponseMock = (overrideResponse = {}) => ({ success: faker.datatype.boolean(), data: { name: faker.string.alpha(20), type: faker.helpers.arrayElement(['class', 'module', 'function', 'object']), description: faker.helpers.arrayElement([faker.string.alpha(20), undefined]), methods: Array.from({ length: faker.number.int({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => ({ name: faker.string.alpha(20), signature: faker.helpers.arrayElement([faker.string.alpha(20), undefined]), description: faker.helpers.arrayElement([faker.string.alpha(20), undefined]) })), properties: Array.from({ length: faker.number.int({ min: 1, max: 10 }) }, (_, i) => i + 1).map(() => ({ name: faker.string.alpha(20), type: faker.string.alpha(20), value: faker.helpers.arrayElement([{}, undefined]) })) }, error: faker.helpers.arrayElement([faker.string.alpha(20), null]), ...overrideResponse });
|
|
136
|
+
export const getExecNodeMethodResponseMock = (overrideResponse = {}) => ({ success: faker.datatype.boolean(), data: { result: {} }, error: faker.helpers.arrayElement([faker.string.alpha(20), null]), ...overrideResponse });
|
|
137
|
+
export const getExecPythonScriptResponseMock = (overrideResponse = {}) => ({ success: faker.datatype.boolean(), data: { result: { value: faker.helpers.arrayElement([{}, undefined]) } }, error: faker.helpers.arrayElement([faker.string.alpha(20), null]), ...overrideResponse });
|
|
138
|
+
export const getGetTdInfoResponseMock = (overrideResponse = {}) => ({ success: faker.datatype.boolean(), data: { server: faker.string.alpha(20), version: faker.string.alpha(20), osName: faker.string.alpha(20), osVersion: faker.string.alpha(20) }, error: faker.helpers.arrayElement([faker.string.alpha(20), null]), ...overrideResponse });
|
|
139
|
+
export const getDeleteNodeMockHandler = (overrideResponse) => {
|
|
140
|
+
return http.delete('*/api/nodes', async (info) => {
|
|
141
|
+
await delay(1000);
|
|
142
|
+
return new HttpResponse(JSON.stringify(overrideResponse !== undefined
|
|
143
|
+
? (typeof overrideResponse === "function" ? await overrideResponse(info) : overrideResponse)
|
|
144
|
+
: getDeleteNodeResponseMock()), { status: 200,
|
|
145
|
+
headers: { 'Content-Type': 'application/json' }
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
export const getGetNodesMockHandler = (overrideResponse) => {
|
|
150
|
+
return http.get('*/api/nodes', async (info) => {
|
|
151
|
+
await delay(1000);
|
|
152
|
+
return new HttpResponse(JSON.stringify(overrideResponse !== undefined
|
|
153
|
+
? (typeof overrideResponse === "function" ? await overrideResponse(info) : overrideResponse)
|
|
154
|
+
: getGetNodesResponseMock()), { status: 200,
|
|
155
|
+
headers: { 'Content-Type': 'application/json' }
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
};
|
|
159
|
+
export const getCreateNodeMockHandler = (overrideResponse) => {
|
|
160
|
+
return http.post('*/api/nodes', async (info) => {
|
|
161
|
+
await delay(1000);
|
|
162
|
+
return new HttpResponse(JSON.stringify(overrideResponse !== undefined
|
|
163
|
+
? (typeof overrideResponse === "function" ? await overrideResponse(info) : overrideResponse)
|
|
164
|
+
: getCreateNodeResponseMock()), { status: 200,
|
|
165
|
+
headers: { 'Content-Type': 'application/json' }
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
};
|
|
169
|
+
export const getGetNodeDetailMockHandler = (overrideResponse) => {
|
|
170
|
+
return http.get('*/api/nodes/detail', async (info) => {
|
|
171
|
+
await delay(1000);
|
|
172
|
+
return new HttpResponse(JSON.stringify(overrideResponse !== undefined
|
|
173
|
+
? (typeof overrideResponse === "function" ? await overrideResponse(info) : overrideResponse)
|
|
174
|
+
: getGetNodeDetailResponseMock()), { status: 200,
|
|
175
|
+
headers: { 'Content-Type': 'application/json' }
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
};
|
|
179
|
+
export const getUpdateNodeMockHandler = (overrideResponse) => {
|
|
180
|
+
return http.patch('*/api/nodes/detail', async (info) => {
|
|
181
|
+
await delay(1000);
|
|
182
|
+
return new HttpResponse(JSON.stringify(overrideResponse !== undefined
|
|
183
|
+
? (typeof overrideResponse === "function" ? await overrideResponse(info) : overrideResponse)
|
|
184
|
+
: getUpdateNodeResponseMock()), { status: 200,
|
|
185
|
+
headers: { 'Content-Type': 'application/json' }
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
};
|
|
189
|
+
export const getGetTdPythonClassesMockHandler = (overrideResponse) => {
|
|
190
|
+
return http.get('*/api/td/classes', async (info) => {
|
|
191
|
+
await delay(1000);
|
|
192
|
+
return new HttpResponse(JSON.stringify(overrideResponse !== undefined
|
|
193
|
+
? (typeof overrideResponse === "function" ? await overrideResponse(info) : overrideResponse)
|
|
194
|
+
: getGetTdPythonClassesResponseMock()), { status: 200,
|
|
195
|
+
headers: { 'Content-Type': 'application/json' }
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
};
|
|
199
|
+
export const getGetTdPythonClassDetailsMockHandler = (overrideResponse) => {
|
|
200
|
+
return http.get('*/api/td/classes/:className', async (info) => {
|
|
201
|
+
await delay(1000);
|
|
202
|
+
return new HttpResponse(JSON.stringify(overrideResponse !== undefined
|
|
203
|
+
? (typeof overrideResponse === "function" ? await overrideResponse(info) : overrideResponse)
|
|
204
|
+
: getGetTdPythonClassDetailsResponseMock()), { status: 200,
|
|
205
|
+
headers: { 'Content-Type': 'application/json' }
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
};
|
|
209
|
+
export const getExecNodeMethodMockHandler = (overrideResponse) => {
|
|
210
|
+
return http.post('*/api/td/nodes/exec', async (info) => {
|
|
211
|
+
await delay(1000);
|
|
212
|
+
return new HttpResponse(JSON.stringify(overrideResponse !== undefined
|
|
213
|
+
? (typeof overrideResponse === "function" ? await overrideResponse(info) : overrideResponse)
|
|
214
|
+
: getExecNodeMethodResponseMock()), { status: 200,
|
|
215
|
+
headers: { 'Content-Type': 'application/json' }
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
};
|
|
219
|
+
export const getExecPythonScriptMockHandler = (overrideResponse) => {
|
|
220
|
+
return http.post('*/api/td/server/exec', async (info) => {
|
|
221
|
+
await delay(1000);
|
|
222
|
+
return new HttpResponse(JSON.stringify(overrideResponse !== undefined
|
|
223
|
+
? (typeof overrideResponse === "function" ? await overrideResponse(info) : overrideResponse)
|
|
224
|
+
: getExecPythonScriptResponseMock()), { status: 200,
|
|
225
|
+
headers: { 'Content-Type': 'application/json' }
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
};
|
|
229
|
+
export const getGetTdInfoMockHandler = (overrideResponse) => {
|
|
230
|
+
return http.get('*/api/td/server/td', async (info) => {
|
|
231
|
+
await delay(1000);
|
|
232
|
+
return new HttpResponse(JSON.stringify(overrideResponse !== undefined
|
|
233
|
+
? (typeof overrideResponse === "function" ? await overrideResponse(info) : overrideResponse)
|
|
234
|
+
: getGetTdInfoResponseMock()), { status: 200,
|
|
235
|
+
headers: { 'Content-Type': 'application/json' }
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
};
|
|
239
|
+
export const getTouchDesignerAPIMock = () => [
|
|
240
|
+
getDeleteNodeMockHandler(),
|
|
241
|
+
getGetNodesMockHandler(),
|
|
242
|
+
getCreateNodeMockHandler(),
|
|
243
|
+
getGetNodeDetailMockHandler(),
|
|
244
|
+
getUpdateNodeMockHandler(),
|
|
245
|
+
getGetTdPythonClassesMockHandler(),
|
|
246
|
+
getGetTdPythonClassDetailsMockHandler(),
|
|
247
|
+
getExecNodeMethodMockHandler(),
|
|
248
|
+
getExecPythonScriptMockHandler(),
|
|
249
|
+
getGetTdInfoMockHandler()
|
|
250
|
+
];
|