touchdesigner-mcp-server 0.4.0-alpha.0 → 0.4.0-alpha.2
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 +4 -4
- package/README.md +4 -3
- package/dist/cli.js +0 -0
- package/dist/gen/endpoints/TouchDesignerAPI.js +1 -1
- package/dist/gen/mcp/touchDesignerAPI.zod.js +1 -1
- package/dist/index.js +0 -0
- package/dist/server/touchDesignerServer.js +1 -1
- package/package.json +14 -12
- package/td/genHandlers.js +0 -47
- package/td/import_modules.py +0 -52
- package/td/mcp_webserver_base.tox +0 -0
- package/td/modules/mcp/controllers/__init__.py +0 -9
- package/td/modules/mcp/controllers/api_controller.py +0 -637
- package/td/modules/mcp/controllers/generated_handlers.py +0 -365
- package/td/modules/mcp/controllers/openapi_router.py +0 -265
- package/td/modules/mcp/services/__init__.py +0 -8
- package/td/modules/mcp/services/api_service.py +0 -555
- package/td/modules/mcp_webserver_script.py +0 -134
- package/td/modules/td_server/.dockerignore +0 -72
- package/td/modules/td_server/.openapi-generator/FILES +0 -55
- package/td/modules/td_server/.openapi-generator/VERSION +0 -1
- package/td/modules/td_server/.openapi-generator-ignore +0 -23
- package/td/modules/td_server/.travis.yml +0 -14
- package/td/modules/td_server/Dockerfile +0 -16
- package/td/modules/td_server/README.md +0 -49
- package/td/modules/td_server/git_push.sh +0 -57
- package/td/modules/td_server/openapi_server/__init__.py +0 -0
- package/td/modules/td_server/openapi_server/__main__.py +0 -19
- package/td/modules/td_server/openapi_server/controllers/__init__.py +0 -0
- package/td/modules/td_server/openapi_server/controllers/default_controller.py +0 -162
- package/td/modules/td_server/openapi_server/controllers/security_controller.py +0 -2
- package/td/modules/td_server/openapi_server/encoder.py +0 -19
- package/td/modules/td_server/openapi_server/models/__init__.py +0 -33
- package/td/modules/td_server/openapi_server/models/base_model.py +0 -68
- package/td/modules/td_server/openapi_server/models/create_node200_response.py +0 -125
- package/td/modules/td_server/openapi_server/models/create_node200_response_data.py +0 -63
- package/td/modules/td_server/openapi_server/models/create_node_request.py +0 -123
- package/td/modules/td_server/openapi_server/models/delete_node200_response.py +0 -125
- package/td/modules/td_server/openapi_server/models/delete_node200_response_data.py +0 -91
- package/td/modules/td_server/openapi_server/models/exec_node_method200_response.py +0 -125
- package/td/modules/td_server/openapi_server/models/exec_node_method200_response_data.py +0 -65
- package/td/modules/td_server/openapi_server/models/exec_node_method_request.py +0 -153
- package/td/modules/td_server/openapi_server/models/exec_node_method_request_args_inner.py +0 -34
- package/td/modules/td_server/openapi_server/models/exec_python_script200_response.py +0 -125
- package/td/modules/td_server/openapi_server/models/exec_python_script200_response_data.py +0 -65
- package/td/modules/td_server/openapi_server/models/exec_python_script200_response_data_result.py +0 -63
- package/td/modules/td_server/openapi_server/models/exec_python_script_request.py +0 -65
- package/td/modules/td_server/openapi_server/models/get_node_detail200_response.py +0 -125
- package/td/modules/td_server/openapi_server/models/get_nodes200_response.py +0 -125
- package/td/modules/td_server/openapi_server/models/get_nodes200_response_data.py +0 -65
- package/td/modules/td_server/openapi_server/models/get_td_info200_response.py +0 -125
- package/td/modules/td_server/openapi_server/models/get_td_info200_response_data.py +0 -155
- package/td/modules/td_server/openapi_server/models/get_td_python_class_details200_response.py +0 -125
- package/td/modules/td_server/openapi_server/models/get_td_python_classes200_response.py +0 -125
- package/td/modules/td_server/openapi_server/models/get_td_python_classes200_response_data.py +0 -63
- package/td/modules/td_server/openapi_server/models/td_node.py +0 -175
- package/td/modules/td_server/openapi_server/models/td_node_family_type.py +0 -44
- package/td/modules/td_server/openapi_server/models/td_python_class_details.py +0 -191
- package/td/modules/td_server/openapi_server/models/td_python_class_info.py +0 -127
- package/td/modules/td_server/openapi_server/models/td_python_method_info.py +0 -121
- package/td/modules/td_server/openapi_server/models/td_python_property_info.py +0 -123
- package/td/modules/td_server/openapi_server/models/update_node200_response.py +0 -125
- package/td/modules/td_server/openapi_server/models/update_node200_response_data.py +0 -149
- package/td/modules/td_server/openapi_server/models/update_node200_response_data_failed_inner.py +0 -91
- package/td/modules/td_server/openapi_server/models/update_node_request.py +0 -93
- package/td/modules/td_server/openapi_server/openapi/openapi.yaml +0 -975
- package/td/modules/td_server/openapi_server/test/__init__.py +0 -16
- package/td/modules/td_server/openapi_server/test/test_default_controller.py +0 -201
- package/td/modules/td_server/openapi_server/typing_utils.py +0 -30
- package/td/modules/td_server/openapi_server/util.py +0 -147
- package/td/modules/td_server/requirements.txt +0 -13
- package/td/modules/td_server/setup.py +0 -37
- package/td/modules/td_server/test-requirements.txt +0 -4
- package/td/modules/td_server/tox.ini +0 -11
- package/td/modules/utils/config.py +0 -7
- package/td/modules/utils/error_handling.py +0 -104
- package/td/modules/utils/logging.py +0 -23
- package/td/modules/utils/result.py +0 -40
- package/td/modules/utils/serialization.py +0 -57
- package/td/modules/utils/types.py +0 -33
- package/td/modules/utils/utils_logging.py +0 -60
- package/td/templates/mcp/api_controller_handlers.mustache +0 -63
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
|
|
3
|
-
import connexion
|
|
4
|
-
from flask_testing import TestCase
|
|
5
|
-
|
|
6
|
-
from openapi_server.encoder import JSONEncoder
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class BaseTestCase(TestCase):
|
|
10
|
-
|
|
11
|
-
def create_app(self):
|
|
12
|
-
logging.getLogger('connexion.operation').setLevel('ERROR')
|
|
13
|
-
app = connexion.App(__name__, specification_dir='../openapi/')
|
|
14
|
-
app.app.json_encoder = JSONEncoder
|
|
15
|
-
app.add_api('openapi.yaml', pythonic_params=True)
|
|
16
|
-
return app.app
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
import unittest
|
|
2
|
-
|
|
3
|
-
from flask import json
|
|
4
|
-
|
|
5
|
-
from openapi_server.models.create_node200_response import CreateNode200Response # noqa: E501
|
|
6
|
-
from openapi_server.models.create_node_request import CreateNodeRequest # noqa: E501
|
|
7
|
-
from openapi_server.models.delete_node200_response import DeleteNode200Response # noqa: E501
|
|
8
|
-
from openapi_server.models.exec_node_method200_response import ExecNodeMethod200Response # noqa: E501
|
|
9
|
-
from openapi_server.models.exec_node_method_request import ExecNodeMethodRequest # noqa: E501
|
|
10
|
-
from openapi_server.models.exec_python_script200_response import ExecPythonScript200Response # noqa: E501
|
|
11
|
-
from openapi_server.models.exec_python_script_request import ExecPythonScriptRequest # noqa: E501
|
|
12
|
-
from openapi_server.models.get_node_detail200_response import GetNodeDetail200Response # noqa: E501
|
|
13
|
-
from openapi_server.models.get_nodes200_response import GetNodes200Response # noqa: E501
|
|
14
|
-
from openapi_server.models.get_td_info200_response import GetTdInfo200Response # noqa: E501
|
|
15
|
-
from openapi_server.models.get_td_python_class_details200_response import GetTdPythonClassDetails200Response # noqa: E501
|
|
16
|
-
from openapi_server.models.get_td_python_classes200_response import GetTdPythonClasses200Response # noqa: E501
|
|
17
|
-
from openapi_server.models.update_node200_response import UpdateNode200Response # noqa: E501
|
|
18
|
-
from openapi_server.models.update_node_request import UpdateNodeRequest # noqa: E501
|
|
19
|
-
from openapi_server.test import BaseTestCase
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class TestDefaultController(BaseTestCase):
|
|
23
|
-
"""DefaultController integration test stubs"""
|
|
24
|
-
|
|
25
|
-
def test_create_node(self):
|
|
26
|
-
"""Test case for create_node
|
|
27
|
-
|
|
28
|
-
Create a new node
|
|
29
|
-
"""
|
|
30
|
-
create_node_request = openapi_server.CreateNodeRequest()
|
|
31
|
-
headers = {
|
|
32
|
-
'Accept': 'application/json',
|
|
33
|
-
'Content-Type': 'application/json',
|
|
34
|
-
}
|
|
35
|
-
response = self.client.open(
|
|
36
|
-
'/api/nodes',
|
|
37
|
-
method='POST',
|
|
38
|
-
headers=headers,
|
|
39
|
-
data=json.dumps(create_node_request),
|
|
40
|
-
content_type='application/json')
|
|
41
|
-
self.assert200(response,
|
|
42
|
-
'Response body is : ' + response.data.decode('utf-8'))
|
|
43
|
-
|
|
44
|
-
def test_delete_node(self):
|
|
45
|
-
"""Test case for delete_node
|
|
46
|
-
|
|
47
|
-
Delete an existing node
|
|
48
|
-
"""
|
|
49
|
-
query_string = [('nodePath', 'node_path_example')]
|
|
50
|
-
headers = {
|
|
51
|
-
'Accept': 'application/json',
|
|
52
|
-
}
|
|
53
|
-
response = self.client.open(
|
|
54
|
-
'/api/nodes',
|
|
55
|
-
method='DELETE',
|
|
56
|
-
headers=headers,
|
|
57
|
-
query_string=query_string)
|
|
58
|
-
self.assert200(response,
|
|
59
|
-
'Response body is : ' + response.data.decode('utf-8'))
|
|
60
|
-
|
|
61
|
-
def test_exec_node_method(self):
|
|
62
|
-
"""Test case for exec_node_method
|
|
63
|
-
|
|
64
|
-
Call a method of the specified node
|
|
65
|
-
"""
|
|
66
|
-
exec_node_method_request = openapi_server.ExecNodeMethodRequest()
|
|
67
|
-
headers = {
|
|
68
|
-
'Accept': 'application/json',
|
|
69
|
-
'Content-Type': 'application/json',
|
|
70
|
-
}
|
|
71
|
-
response = self.client.open(
|
|
72
|
-
'/api/td/nodes/exec',
|
|
73
|
-
method='POST',
|
|
74
|
-
headers=headers,
|
|
75
|
-
data=json.dumps(exec_node_method_request),
|
|
76
|
-
content_type='application/json')
|
|
77
|
-
self.assert200(response,
|
|
78
|
-
'Response body is : ' + response.data.decode('utf-8'))
|
|
79
|
-
|
|
80
|
-
def test_exec_python_script(self):
|
|
81
|
-
"""Test case for exec_python_script
|
|
82
|
-
|
|
83
|
-
Execute python code on the server
|
|
84
|
-
"""
|
|
85
|
-
exec_python_script_request = openapi_server.ExecPythonScriptRequest()
|
|
86
|
-
headers = {
|
|
87
|
-
'Accept': 'application/json',
|
|
88
|
-
'Content-Type': 'application/json',
|
|
89
|
-
}
|
|
90
|
-
response = self.client.open(
|
|
91
|
-
'/api/td/server/exec',
|
|
92
|
-
method='POST',
|
|
93
|
-
headers=headers,
|
|
94
|
-
data=json.dumps(exec_python_script_request),
|
|
95
|
-
content_type='application/json')
|
|
96
|
-
self.assert200(response,
|
|
97
|
-
'Response body is : ' + response.data.decode('utf-8'))
|
|
98
|
-
|
|
99
|
-
def test_get_node_detail(self):
|
|
100
|
-
"""Test case for get_node_detail
|
|
101
|
-
|
|
102
|
-
Get node detail
|
|
103
|
-
"""
|
|
104
|
-
query_string = [('nodePath', 'node_path_example')]
|
|
105
|
-
headers = {
|
|
106
|
-
'Accept': 'application/json',
|
|
107
|
-
}
|
|
108
|
-
response = self.client.open(
|
|
109
|
-
'/api/nodes/detail',
|
|
110
|
-
method='GET',
|
|
111
|
-
headers=headers,
|
|
112
|
-
query_string=query_string)
|
|
113
|
-
self.assert200(response,
|
|
114
|
-
'Response body is : ' + response.data.decode('utf-8'))
|
|
115
|
-
|
|
116
|
-
def test_get_nodes(self):
|
|
117
|
-
"""Test case for get_nodes
|
|
118
|
-
|
|
119
|
-
Get nodes in the path
|
|
120
|
-
"""
|
|
121
|
-
query_string = [('parentPath', 'parent_path_example'),
|
|
122
|
-
('pattern', '*'),
|
|
123
|
-
('includeProperties', False)]
|
|
124
|
-
headers = {
|
|
125
|
-
'Accept': 'application/json',
|
|
126
|
-
}
|
|
127
|
-
response = self.client.open(
|
|
128
|
-
'/api/nodes',
|
|
129
|
-
method='GET',
|
|
130
|
-
headers=headers,
|
|
131
|
-
query_string=query_string)
|
|
132
|
-
self.assert200(response,
|
|
133
|
-
'Response body is : ' + response.data.decode('utf-8'))
|
|
134
|
-
|
|
135
|
-
def test_get_td_info(self):
|
|
136
|
-
"""Test case for get_td_info
|
|
137
|
-
|
|
138
|
-
Get TouchDesigner information
|
|
139
|
-
"""
|
|
140
|
-
headers = {
|
|
141
|
-
'Accept': 'application/json',
|
|
142
|
-
}
|
|
143
|
-
response = self.client.open(
|
|
144
|
-
'/api/td/server/td',
|
|
145
|
-
method='GET',
|
|
146
|
-
headers=headers)
|
|
147
|
-
self.assert200(response,
|
|
148
|
-
'Response body is : ' + response.data.decode('utf-8'))
|
|
149
|
-
|
|
150
|
-
def test_get_td_python_class_details(self):
|
|
151
|
-
"""Test case for get_td_python_class_details
|
|
152
|
-
|
|
153
|
-
Get details of a specific Python class or module
|
|
154
|
-
"""
|
|
155
|
-
headers = {
|
|
156
|
-
'Accept': 'application/json',
|
|
157
|
-
}
|
|
158
|
-
response = self.client.open(
|
|
159
|
-
'/api/td/classes/{class_name}'.format(class_name='class_name_example'),
|
|
160
|
-
method='GET',
|
|
161
|
-
headers=headers)
|
|
162
|
-
self.assert200(response,
|
|
163
|
-
'Response body is : ' + response.data.decode('utf-8'))
|
|
164
|
-
|
|
165
|
-
def test_get_td_python_classes(self):
|
|
166
|
-
"""Test case for get_td_python_classes
|
|
167
|
-
|
|
168
|
-
Get a list of Python classes and modules
|
|
169
|
-
"""
|
|
170
|
-
headers = {
|
|
171
|
-
'Accept': 'application/json',
|
|
172
|
-
}
|
|
173
|
-
response = self.client.open(
|
|
174
|
-
'/api/td/classes',
|
|
175
|
-
method='GET',
|
|
176
|
-
headers=headers)
|
|
177
|
-
self.assert200(response,
|
|
178
|
-
'Response body is : ' + response.data.decode('utf-8'))
|
|
179
|
-
|
|
180
|
-
def test_update_node(self):
|
|
181
|
-
"""Test case for update_node
|
|
182
|
-
|
|
183
|
-
Update node properties
|
|
184
|
-
"""
|
|
185
|
-
update_node_request = openapi_server.UpdateNodeRequest()
|
|
186
|
-
headers = {
|
|
187
|
-
'Accept': 'application/json',
|
|
188
|
-
'Content-Type': 'application/json',
|
|
189
|
-
}
|
|
190
|
-
response = self.client.open(
|
|
191
|
-
'/api/nodes/detail',
|
|
192
|
-
method='PATCH',
|
|
193
|
-
headers=headers,
|
|
194
|
-
data=json.dumps(update_node_request),
|
|
195
|
-
content_type='application/json')
|
|
196
|
-
self.assert200(response,
|
|
197
|
-
'Response body is : ' + response.data.decode('utf-8'))
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
if __name__ == '__main__':
|
|
201
|
-
unittest.main()
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import sys
|
|
2
|
-
|
|
3
|
-
if sys.version_info < (3, 7):
|
|
4
|
-
import typing
|
|
5
|
-
|
|
6
|
-
def is_generic(klass):
|
|
7
|
-
""" Determine whether klass is a generic class """
|
|
8
|
-
return type(klass) == typing.GenericMeta
|
|
9
|
-
|
|
10
|
-
def is_dict(klass):
|
|
11
|
-
""" Determine whether klass is a Dict """
|
|
12
|
-
return klass.__extra__ == dict
|
|
13
|
-
|
|
14
|
-
def is_list(klass):
|
|
15
|
-
""" Determine whether klass is a List """
|
|
16
|
-
return klass.__extra__ == list
|
|
17
|
-
|
|
18
|
-
else:
|
|
19
|
-
|
|
20
|
-
def is_generic(klass):
|
|
21
|
-
""" Determine whether klass is a generic class """
|
|
22
|
-
return hasattr(klass, '__origin__')
|
|
23
|
-
|
|
24
|
-
def is_dict(klass):
|
|
25
|
-
""" Determine whether klass is a Dict """
|
|
26
|
-
return klass.__origin__ == dict
|
|
27
|
-
|
|
28
|
-
def is_list(klass):
|
|
29
|
-
""" Determine whether klass is a List """
|
|
30
|
-
return klass.__origin__ == list
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import datetime
|
|
2
|
-
|
|
3
|
-
import typing
|
|
4
|
-
from openapi_server import typing_utils
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def _deserialize(data, klass):
|
|
8
|
-
"""Deserializes dict, list, str into an object.
|
|
9
|
-
|
|
10
|
-
:param data: dict, list or str.
|
|
11
|
-
:param klass: class literal, or string of class name.
|
|
12
|
-
|
|
13
|
-
:return: object.
|
|
14
|
-
"""
|
|
15
|
-
if data is None:
|
|
16
|
-
return None
|
|
17
|
-
|
|
18
|
-
if klass in (int, float, str, bool, bytearray):
|
|
19
|
-
return _deserialize_primitive(data, klass)
|
|
20
|
-
elif klass == object:
|
|
21
|
-
return _deserialize_object(data)
|
|
22
|
-
elif klass == datetime.date:
|
|
23
|
-
return deserialize_date(data)
|
|
24
|
-
elif klass == datetime.datetime:
|
|
25
|
-
return deserialize_datetime(data)
|
|
26
|
-
elif typing_utils.is_generic(klass):
|
|
27
|
-
if typing_utils.is_list(klass):
|
|
28
|
-
return _deserialize_list(data, klass.__args__[0])
|
|
29
|
-
if typing_utils.is_dict(klass):
|
|
30
|
-
return _deserialize_dict(data, klass.__args__[1])
|
|
31
|
-
else:
|
|
32
|
-
return deserialize_model(data, klass)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def _deserialize_primitive(data, klass):
|
|
36
|
-
"""Deserializes to primitive type.
|
|
37
|
-
|
|
38
|
-
:param data: data to deserialize.
|
|
39
|
-
:param klass: class literal.
|
|
40
|
-
|
|
41
|
-
:return: int, long, float, str, bool.
|
|
42
|
-
:rtype: int | long | float | str | bool
|
|
43
|
-
"""
|
|
44
|
-
try:
|
|
45
|
-
value = klass(data)
|
|
46
|
-
except UnicodeEncodeError:
|
|
47
|
-
value = data
|
|
48
|
-
except TypeError:
|
|
49
|
-
value = data
|
|
50
|
-
return value
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def _deserialize_object(value):
|
|
54
|
-
"""Return an original value.
|
|
55
|
-
|
|
56
|
-
:return: object.
|
|
57
|
-
"""
|
|
58
|
-
return value
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def deserialize_date(string):
|
|
62
|
-
"""Deserializes string to date.
|
|
63
|
-
|
|
64
|
-
:param string: str.
|
|
65
|
-
:type string: str
|
|
66
|
-
:return: date.
|
|
67
|
-
:rtype: date
|
|
68
|
-
"""
|
|
69
|
-
if string is None:
|
|
70
|
-
return None
|
|
71
|
-
|
|
72
|
-
try:
|
|
73
|
-
from dateutil.parser import parse
|
|
74
|
-
return parse(string).date()
|
|
75
|
-
except ImportError:
|
|
76
|
-
return string
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def deserialize_datetime(string):
|
|
80
|
-
"""Deserializes string to datetime.
|
|
81
|
-
|
|
82
|
-
The string should be in iso8601 datetime format.
|
|
83
|
-
|
|
84
|
-
:param string: str.
|
|
85
|
-
:type string: str
|
|
86
|
-
:return: datetime.
|
|
87
|
-
:rtype: datetime
|
|
88
|
-
"""
|
|
89
|
-
if string is None:
|
|
90
|
-
return None
|
|
91
|
-
|
|
92
|
-
try:
|
|
93
|
-
from dateutil.parser import parse
|
|
94
|
-
return parse(string)
|
|
95
|
-
except ImportError:
|
|
96
|
-
return string
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
def deserialize_model(data, klass):
|
|
100
|
-
"""Deserializes list or dict to model.
|
|
101
|
-
|
|
102
|
-
:param data: dict, list.
|
|
103
|
-
:type data: dict | list
|
|
104
|
-
:param klass: class literal.
|
|
105
|
-
:return: model object.
|
|
106
|
-
"""
|
|
107
|
-
instance = klass()
|
|
108
|
-
|
|
109
|
-
if not instance.openapi_types:
|
|
110
|
-
return data
|
|
111
|
-
|
|
112
|
-
for attr, attr_type in instance.openapi_types.items():
|
|
113
|
-
if data is not None \
|
|
114
|
-
and instance.attribute_map[attr] in data \
|
|
115
|
-
and isinstance(data, (list, dict)):
|
|
116
|
-
value = data[instance.attribute_map[attr]]
|
|
117
|
-
setattr(instance, attr, _deserialize(value, attr_type))
|
|
118
|
-
|
|
119
|
-
return instance
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
def _deserialize_list(data, boxed_type):
|
|
123
|
-
"""Deserializes a list and its elements.
|
|
124
|
-
|
|
125
|
-
:param data: list to deserialize.
|
|
126
|
-
:type data: list
|
|
127
|
-
:param boxed_type: class literal.
|
|
128
|
-
|
|
129
|
-
:return: deserialized list.
|
|
130
|
-
:rtype: list
|
|
131
|
-
"""
|
|
132
|
-
return [_deserialize(sub_data, boxed_type)
|
|
133
|
-
for sub_data in data]
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
def _deserialize_dict(data, boxed_type):
|
|
137
|
-
"""Deserializes a dict and its elements.
|
|
138
|
-
|
|
139
|
-
:param data: dict to deserialize.
|
|
140
|
-
:type data: dict
|
|
141
|
-
:param boxed_type: class literal.
|
|
142
|
-
|
|
143
|
-
:return: deserialized dict.
|
|
144
|
-
:rtype: dict
|
|
145
|
-
"""
|
|
146
|
-
return {k: _deserialize(v, boxed_type)
|
|
147
|
-
for k, v in data.items() }
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
connexion[swagger-ui] >= 2.6.0; python_version>="3.6"
|
|
2
|
-
# 2.3 is the last version that supports python 3.4-3.5
|
|
3
|
-
connexion[swagger-ui] <= 2.3.0; python_version=="3.5" or python_version=="3.4"
|
|
4
|
-
# prevent breaking dependencies from advent of connexion>=3.0
|
|
5
|
-
connexion[swagger-ui] <= 2.14.2; python_version>"3.4"
|
|
6
|
-
# connexion requires werkzeug but connexion < 2.4.0 does not install werkzeug
|
|
7
|
-
# we must peg werkzeug versions below to fix connexion
|
|
8
|
-
# https://github.com/zalando/connexion/pull/1044
|
|
9
|
-
werkzeug == 0.16.1; python_version=="3.5" or python_version=="3.4"
|
|
10
|
-
swagger-ui-bundle >= 0.0.2
|
|
11
|
-
python_dateutil >= 2.6.0
|
|
12
|
-
setuptools >= 21.0.0
|
|
13
|
-
Flask == 2.1.1
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import sys
|
|
2
|
-
from setuptools import setup, find_packages
|
|
3
|
-
|
|
4
|
-
NAME = "openapi_server"
|
|
5
|
-
VERSION = "1.0.0"
|
|
6
|
-
|
|
7
|
-
# To install the library, run the following
|
|
8
|
-
#
|
|
9
|
-
# python setup.py install
|
|
10
|
-
#
|
|
11
|
-
# prerequisite: setuptools
|
|
12
|
-
# http://pypi.python.org/pypi/setuptools
|
|
13
|
-
|
|
14
|
-
REQUIRES = [
|
|
15
|
-
"connexion>=2.0.2",
|
|
16
|
-
"swagger-ui-bundle>=0.0.2",
|
|
17
|
-
"python_dateutil>=2.6.0"
|
|
18
|
-
]
|
|
19
|
-
|
|
20
|
-
setup(
|
|
21
|
-
name=NAME,
|
|
22
|
-
version=VERSION,
|
|
23
|
-
description="TouchDesigner API",
|
|
24
|
-
author_email="",
|
|
25
|
-
url="",
|
|
26
|
-
keywords=["OpenAPI", "TouchDesigner API"],
|
|
27
|
-
install_requires=REQUIRES,
|
|
28
|
-
packages=find_packages(),
|
|
29
|
-
package_data={'': ['openapi/openapi.yaml']},
|
|
30
|
-
include_package_data=True,
|
|
31
|
-
entry_points={
|
|
32
|
-
'console_scripts': ['openapi_server=openapi_server.__main__:main']},
|
|
33
|
-
long_description="""\
|
|
34
|
-
OpenAPI schema for generating TouchDesigner API client code
|
|
35
|
-
"""
|
|
36
|
-
)
|
|
37
|
-
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Error handling utilities for TouchDesigner MCP Web server
|
|
3
|
-
Provides standardized error categorization, formatting, and handling
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
import functools
|
|
7
|
-
import traceback
|
|
8
|
-
from enum import Enum
|
|
9
|
-
from typing import Callable, Optional, TypeVar
|
|
10
|
-
|
|
11
|
-
from utils.logging import log_message
|
|
12
|
-
from utils.result import Result, error_result
|
|
13
|
-
from utils.types import LogLevel
|
|
14
|
-
|
|
15
|
-
T = TypeVar("T")
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class ErrorCategory(Enum):
|
|
19
|
-
"""
|
|
20
|
-
Error categories for better classification and handling
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
INTERNAL = "INTERNAL"
|
|
24
|
-
VALIDATION = "VALIDATION"
|
|
25
|
-
NOT_FOUND = "NOT_FOUND"
|
|
26
|
-
PERMISSION = "PERMISSION"
|
|
27
|
-
NETWORK = "NETWORK"
|
|
28
|
-
EXTERNAL = "EXTERNAL"
|
|
29
|
-
|
|
30
|
-
def __str__(self) -> str:
|
|
31
|
-
return self.value
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def categorize_error(exception: Exception) -> ErrorCategory:
|
|
35
|
-
"""
|
|
36
|
-
Categorize an exception based on its type and message
|
|
37
|
-
|
|
38
|
-
Args:
|
|
39
|
-
exception: The exception to categorize
|
|
40
|
-
|
|
41
|
-
Returns:
|
|
42
|
-
Appropriate ErrorCategory
|
|
43
|
-
"""
|
|
44
|
-
error_message = str(exception).lower()
|
|
45
|
-
|
|
46
|
-
if isinstance(exception, ValueError):
|
|
47
|
-
return ErrorCategory.VALIDATION
|
|
48
|
-
elif isinstance(exception, FileNotFoundError):
|
|
49
|
-
return ErrorCategory.NOT_FOUND
|
|
50
|
-
elif "not found" in error_message or "doesn't exist" in error_message:
|
|
51
|
-
return ErrorCategory.NOT_FOUND
|
|
52
|
-
elif "permission" in error_message or "access denied" in error_message:
|
|
53
|
-
return ErrorCategory.PERMISSION
|
|
54
|
-
elif "network" in error_message or "connection" in error_message:
|
|
55
|
-
return ErrorCategory.NETWORK
|
|
56
|
-
elif "external" in error_message or "service unavailable" in error_message:
|
|
57
|
-
return ErrorCategory.EXTERNAL
|
|
58
|
-
|
|
59
|
-
return ErrorCategory.INTERNAL
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def format_error(message: str, category: Optional[ErrorCategory] = None) -> str:
|
|
63
|
-
"""
|
|
64
|
-
Format an error message with its category
|
|
65
|
-
|
|
66
|
-
Args:
|
|
67
|
-
message: The error message
|
|
68
|
-
category: Error category (defaults to INTERNAL)
|
|
69
|
-
|
|
70
|
-
Returns:
|
|
71
|
-
Formatted error message
|
|
72
|
-
"""
|
|
73
|
-
if category is None:
|
|
74
|
-
category = ErrorCategory.INTERNAL
|
|
75
|
-
|
|
76
|
-
return f"{category}: {message}"
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def handle_service_errors(func: Callable[..., Result]) -> Callable[..., Result]:
|
|
80
|
-
"""
|
|
81
|
-
Decorator to handle errors in service methods and convert them to Result type
|
|
82
|
-
|
|
83
|
-
Args:
|
|
84
|
-
func: The service method to decorate
|
|
85
|
-
|
|
86
|
-
Returns:
|
|
87
|
-
Wrapped function that catches exceptions and returns Result
|
|
88
|
-
"""
|
|
89
|
-
|
|
90
|
-
@functools.wraps(func)
|
|
91
|
-
def wrapper(*args, **kwargs) -> Result:
|
|
92
|
-
try:
|
|
93
|
-
return func(*args, **kwargs)
|
|
94
|
-
except Exception as e:
|
|
95
|
-
category = categorize_error(e)
|
|
96
|
-
|
|
97
|
-
func_name = func.__name__
|
|
98
|
-
log_message(f"Error in {func_name}: {str(e)}", LogLevel.ERROR)
|
|
99
|
-
log_message(traceback.format_exc(), LogLevel.DEBUG)
|
|
100
|
-
|
|
101
|
-
error_message = format_error(str(e), category)
|
|
102
|
-
return error_result(error_message, {"errorCategory": category})
|
|
103
|
-
|
|
104
|
-
return wrapper
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Logging module for TouchDesigner MCP Web server
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from datetime import datetime
|
|
6
|
-
|
|
7
|
-
from utils.types import LogLevel
|
|
8
|
-
|
|
9
|
-
from .config import DEBUG
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def log_message(message: str, level: LogLevel = LogLevel.INFO) -> None:
|
|
13
|
-
"""Log a message using the appropriate logging mechanism"""
|
|
14
|
-
|
|
15
|
-
if not DEBUG and level == LogLevel.DEBUG:
|
|
16
|
-
return
|
|
17
|
-
|
|
18
|
-
time_stamp = datetime.now().strftime("%Y%m%d_%H:%M:%S.%f")[
|
|
19
|
-
:-3
|
|
20
|
-
] + datetime.now().strftime("%z")
|
|
21
|
-
prefix = f"{time_stamp} [{level}]"
|
|
22
|
-
full_message = f"{prefix}\t{message}"
|
|
23
|
-
print(full_message)
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Result pattern utilities for TouchDesigner MCP Web server
|
|
3
|
-
Provides utility functions for handling success and failure results
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
from typing import Any, Dict, Optional
|
|
7
|
-
|
|
8
|
-
from .types import Result
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def success_result(data: Any) -> Result:
|
|
12
|
-
"""
|
|
13
|
-
Create a success result with data
|
|
14
|
-
|
|
15
|
-
Args:
|
|
16
|
-
data: The success result data
|
|
17
|
-
|
|
18
|
-
Returns:
|
|
19
|
-
Result dictionary with success flag and data
|
|
20
|
-
"""
|
|
21
|
-
return {"success": True, "data": data, "error": None}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def error_result(message: str, metadata: Optional[Dict[str, Any]] = None) -> Result:
|
|
25
|
-
"""
|
|
26
|
-
Create an error result with message and optional metadata
|
|
27
|
-
|
|
28
|
-
Args:
|
|
29
|
-
message: The error message
|
|
30
|
-
metadata: Optional additional error metadata
|
|
31
|
-
|
|
32
|
-
Returns:
|
|
33
|
-
Result dictionary with error information
|
|
34
|
-
"""
|
|
35
|
-
result = {"success": False,"data": None, "error": message}
|
|
36
|
-
|
|
37
|
-
if metadata:
|
|
38
|
-
result.update(metadata)
|
|
39
|
-
|
|
40
|
-
return result
|