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.
Files changed (102) hide show
  1. package/LICENSE +21 -0
  2. package/README.ja.md +211 -0
  3. package/README.md +204 -0
  4. package/dist/api/customInstance.js +18 -0
  5. package/dist/cli.js +36 -0
  6. package/dist/core/constants.js +26 -0
  7. package/dist/core/errorHandling.js +19 -0
  8. package/dist/core/logger.js +45 -0
  9. package/dist/core/result.js +12 -0
  10. package/dist/features/prompts/handlers/td_prompts.js +131 -0
  11. package/dist/features/prompts/index.js +1 -0
  12. package/dist/features/prompts/register.js +7 -0
  13. package/dist/features/tools/handlers/tdTools.js +214 -0
  14. package/dist/features/tools/index.js +1 -0
  15. package/dist/features/tools/register.js +7 -0
  16. package/dist/features/tools/types.js +1 -0
  17. package/dist/features/tools/utils/toolUtils.js +1 -0
  18. package/dist/gen/endpoints/TouchDesignerAPI.js +250 -0
  19. package/dist/gen/mcp/touchDesignerAPI.zod.js +203 -0
  20. package/dist/index.js +3 -0
  21. package/dist/mock/index.js +5 -0
  22. package/dist/mock/node.js +3 -0
  23. package/dist/server/connectionManager.js +83 -0
  24. package/dist/server/touchDesignerServer.js +60 -0
  25. package/dist/tdClient/index.js +6 -0
  26. package/dist/tdClient/touchDesignerClient.js +150 -0
  27. package/package.json +76 -0
  28. package/src/index.ts +6 -0
  29. package/td/genHandlers.js +47 -0
  30. package/td/import_modules.py +52 -0
  31. package/td/mcp_webserver_base.tox +0 -0
  32. package/td/modules/mcp/controllers/__init__.py +9 -0
  33. package/td/modules/mcp/controllers/api_controller.py +623 -0
  34. package/td/modules/mcp/controllers/generated_handlers.py +365 -0
  35. package/td/modules/mcp/controllers/openapi_router.py +265 -0
  36. package/td/modules/mcp/services/__init__.py +8 -0
  37. package/td/modules/mcp/services/api_service.py +535 -0
  38. package/td/modules/mcp_webserver_script.py +134 -0
  39. package/td/modules/td_server/.dockerignore +72 -0
  40. package/td/modules/td_server/.openapi-generator/FILES +55 -0
  41. package/td/modules/td_server/.openapi-generator/VERSION +1 -0
  42. package/td/modules/td_server/.openapi-generator-ignore +23 -0
  43. package/td/modules/td_server/.travis.yml +14 -0
  44. package/td/modules/td_server/Dockerfile +16 -0
  45. package/td/modules/td_server/README.md +49 -0
  46. package/td/modules/td_server/git_push.sh +57 -0
  47. package/td/modules/td_server/openapi_server/__init__.py +0 -0
  48. package/td/modules/td_server/openapi_server/__main__.py +19 -0
  49. package/td/modules/td_server/openapi_server/controllers/__init__.py +0 -0
  50. package/td/modules/td_server/openapi_server/controllers/default_controller.py +160 -0
  51. package/td/modules/td_server/openapi_server/controllers/security_controller.py +2 -0
  52. package/td/modules/td_server/openapi_server/encoder.py +19 -0
  53. package/td/modules/td_server/openapi_server/models/__init__.py +33 -0
  54. package/td/modules/td_server/openapi_server/models/base_model.py +68 -0
  55. package/td/modules/td_server/openapi_server/models/create_node200_response.py +125 -0
  56. package/td/modules/td_server/openapi_server/models/create_node200_response_data.py +63 -0
  57. package/td/modules/td_server/openapi_server/models/create_node_request.py +123 -0
  58. package/td/modules/td_server/openapi_server/models/delete_node200_response.py +125 -0
  59. package/td/modules/td_server/openapi_server/models/delete_node200_response_data.py +91 -0
  60. package/td/modules/td_server/openapi_server/models/exec_node_method200_response.py +125 -0
  61. package/td/modules/td_server/openapi_server/models/exec_node_method200_response_data.py +65 -0
  62. package/td/modules/td_server/openapi_server/models/exec_node_method_request.py +153 -0
  63. package/td/modules/td_server/openapi_server/models/exec_node_method_request_args_inner.py +34 -0
  64. package/td/modules/td_server/openapi_server/models/exec_python_script200_response.py +125 -0
  65. package/td/modules/td_server/openapi_server/models/exec_python_script200_response_data.py +65 -0
  66. package/td/modules/td_server/openapi_server/models/exec_python_script200_response_data_result.py +63 -0
  67. package/td/modules/td_server/openapi_server/models/exec_python_script_request.py +65 -0
  68. package/td/modules/td_server/openapi_server/models/get_node_detail200_response.py +125 -0
  69. package/td/modules/td_server/openapi_server/models/get_nodes200_response.py +125 -0
  70. package/td/modules/td_server/openapi_server/models/get_nodes200_response_data.py +65 -0
  71. package/td/modules/td_server/openapi_server/models/get_td_info200_response.py +125 -0
  72. package/td/modules/td_server/openapi_server/models/get_td_info200_response_data.py +155 -0
  73. package/td/modules/td_server/openapi_server/models/get_td_python_class_details200_response.py +125 -0
  74. package/td/modules/td_server/openapi_server/models/get_td_python_classes200_response.py +125 -0
  75. package/td/modules/td_server/openapi_server/models/get_td_python_classes200_response_data.py +63 -0
  76. package/td/modules/td_server/openapi_server/models/td_node.py +175 -0
  77. package/td/modules/td_server/openapi_server/models/td_node_family_type.py +44 -0
  78. package/td/modules/td_server/openapi_server/models/td_python_class_details.py +191 -0
  79. package/td/modules/td_server/openapi_server/models/td_python_class_info.py +127 -0
  80. package/td/modules/td_server/openapi_server/models/td_python_method_info.py +121 -0
  81. package/td/modules/td_server/openapi_server/models/td_python_property_info.py +123 -0
  82. package/td/modules/td_server/openapi_server/models/update_node200_response.py +125 -0
  83. package/td/modules/td_server/openapi_server/models/update_node200_response_data.py +149 -0
  84. package/td/modules/td_server/openapi_server/models/update_node200_response_data_failed_inner.py +91 -0
  85. package/td/modules/td_server/openapi_server/models/update_node_request.py +93 -0
  86. package/td/modules/td_server/openapi_server/openapi/openapi.yaml +966 -0
  87. package/td/modules/td_server/openapi_server/test/__init__.py +16 -0
  88. package/td/modules/td_server/openapi_server/test/test_default_controller.py +200 -0
  89. package/td/modules/td_server/openapi_server/typing_utils.py +30 -0
  90. package/td/modules/td_server/openapi_server/util.py +147 -0
  91. package/td/modules/td_server/requirements.txt +13 -0
  92. package/td/modules/td_server/setup.py +37 -0
  93. package/td/modules/td_server/test-requirements.txt +4 -0
  94. package/td/modules/td_server/tox.ini +11 -0
  95. package/td/modules/utils/config.py +7 -0
  96. package/td/modules/utils/error_handling.py +104 -0
  97. package/td/modules/utils/logging.py +23 -0
  98. package/td/modules/utils/result.py +40 -0
  99. package/td/modules/utils/serialization.py +57 -0
  100. package/td/modules/utils/types.py +33 -0
  101. package/td/modules/utils/utils_logging.py +60 -0
  102. 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,7 @@
1
+ import { registerTdPrompts } from "./handlers/td_prompts.js";
2
+ /**
3
+ * Register prompt handlers with MCP server
4
+ */
5
+ export function registerPrompts(server, logger) {
6
+ registerTdPrompts(server, logger);
7
+ }
@@ -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,7 @@
1
+ import { registerTdTools } from "./handlers/tdTools.js";
2
+ /**
3
+ * Register resource handlers with MCP server
4
+ */
5
+ export function registerTools(server, logger, tdClient) {
6
+ registerTdTools(server, logger, tdClient);
7
+ }
@@ -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
+ ];