onetool-mcp 1.0.0b1__py3-none-any.whl → 1.0.0rc2__py3-none-any.whl

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 (81) hide show
  1. onetool/cli.py +63 -4
  2. onetool_mcp-1.0.0rc2.dist-info/METADATA +266 -0
  3. onetool_mcp-1.0.0rc2.dist-info/RECORD +129 -0
  4. {onetool_mcp-1.0.0b1.dist-info → onetool_mcp-1.0.0rc2.dist-info}/licenses/LICENSE.txt +1 -1
  5. {onetool_mcp-1.0.0b1.dist-info → onetool_mcp-1.0.0rc2.dist-info}/licenses/NOTICE.txt +54 -64
  6. ot/__main__.py +6 -6
  7. ot/config/__init__.py +48 -46
  8. ot/config/global_templates/__init__.py +2 -2
  9. ot/config/{defaults → global_templates}/diagram-templates/api-flow.mmd +33 -33
  10. ot/config/{defaults → global_templates}/diagram-templates/c4-context.puml +30 -30
  11. ot/config/{defaults → global_templates}/diagram-templates/class-diagram.mmd +87 -87
  12. ot/config/{defaults → global_templates}/diagram-templates/feature-mindmap.mmd +70 -70
  13. ot/config/{defaults → global_templates}/diagram-templates/microservices.d2 +81 -81
  14. ot/config/{defaults → global_templates}/diagram-templates/project-gantt.mmd +37 -37
  15. ot/config/{defaults → global_templates}/diagram-templates/state-machine.mmd +42 -42
  16. ot/config/global_templates/diagram.yaml +167 -0
  17. ot/config/global_templates/onetool.yaml +3 -1
  18. ot/config/{defaults → global_templates}/prompts.yaml +102 -97
  19. ot/config/global_templates/security.yaml +31 -0
  20. ot/config/global_templates/servers.yaml +93 -12
  21. ot/config/global_templates/snippets.yaml +5 -26
  22. ot/config/{defaults → global_templates}/tool_templates/__init__.py +7 -7
  23. ot/config/loader.py +221 -105
  24. ot/config/mcp.py +5 -1
  25. ot/config/secrets.py +192 -190
  26. ot/decorators.py +116 -116
  27. ot/executor/__init__.py +35 -35
  28. ot/executor/base.py +16 -16
  29. ot/executor/fence_processor.py +83 -83
  30. ot/executor/linter.py +142 -142
  31. ot/executor/pep723.py +288 -288
  32. ot/executor/runner.py +20 -6
  33. ot/executor/simple.py +163 -163
  34. ot/executor/validator.py +603 -164
  35. ot/http_client.py +145 -145
  36. ot/logging/__init__.py +37 -37
  37. ot/logging/entry.py +213 -213
  38. ot/logging/format.py +191 -188
  39. ot/logging/span.py +349 -349
  40. ot/meta.py +236 -14
  41. ot/paths.py +32 -49
  42. ot/prompts.py +218 -218
  43. ot/proxy/manager.py +14 -2
  44. ot/registry/__init__.py +189 -189
  45. ot/registry/parser.py +269 -269
  46. ot/server.py +330 -315
  47. ot/shortcuts/__init__.py +15 -15
  48. ot/shortcuts/aliases.py +87 -87
  49. ot/shortcuts/snippets.py +258 -258
  50. ot/stats/__init__.py +35 -35
  51. ot/stats/html.py +2 -2
  52. ot/stats/reader.py +354 -354
  53. ot/stats/timing.py +57 -57
  54. ot/support.py +63 -63
  55. ot/tools.py +1 -1
  56. ot/utils/batch.py +161 -161
  57. ot/utils/cache.py +120 -120
  58. ot/utils/exceptions.py +23 -23
  59. ot/utils/factory.py +178 -179
  60. ot/utils/format.py +65 -65
  61. ot/utils/http.py +202 -202
  62. ot/utils/platform.py +45 -45
  63. ot/utils/truncate.py +69 -69
  64. ot_tools/__init__.py +4 -4
  65. ot_tools/_convert/__init__.py +12 -12
  66. ot_tools/_convert/pdf.py +254 -254
  67. ot_tools/diagram.yaml +167 -167
  68. ot_tools/scaffold.py +2 -2
  69. ot_tools/transform.py +124 -19
  70. ot_tools/web_fetch.py +94 -43
  71. onetool_mcp-1.0.0b1.dist-info/METADATA +0 -163
  72. onetool_mcp-1.0.0b1.dist-info/RECORD +0 -132
  73. ot/config/defaults/bench.yaml +0 -4
  74. ot/config/defaults/onetool.yaml +0 -25
  75. ot/config/defaults/servers.yaml +0 -7
  76. ot/config/defaults/snippets.yaml +0 -4
  77. ot_tools/firecrawl.py +0 -732
  78. {onetool_mcp-1.0.0b1.dist-info → onetool_mcp-1.0.0rc2.dist-info}/WHEEL +0 -0
  79. {onetool_mcp-1.0.0b1.dist-info → onetool_mcp-1.0.0rc2.dist-info}/entry_points.txt +0 -0
  80. /ot/config/{defaults → global_templates}/tool_templates/extension.py +0 -0
  81. /ot/config/{defaults → global_templates}/tool_templates/isolated.py +0 -0
ot/registry/parser.py CHANGED
@@ -1,269 +1,269 @@
1
- """AST parsing utilities for extracting function information."""
2
-
3
- from __future__ import annotations
4
-
5
- import ast
6
- from typing import Any
7
-
8
- from docstring_parser import parse as parse_docstring_lib
9
-
10
- from .models import ArgInfo, ToolInfo
11
-
12
-
13
- def parse_function(
14
- node: ast.FunctionDef, module: str, pack: str | None = None
15
- ) -> ToolInfo:
16
- """Extract information from a function AST node.
17
-
18
- Args:
19
- node: AST FunctionDef node.
20
- module: Module path for the function.
21
- pack: Optional pack name for namespace-qualified tool names.
22
-
23
- Returns:
24
- ToolInfo with extracted signature and docstring info.
25
- """
26
- # Extract signature (with pack-qualified name if pack is provided)
27
- signature = extract_signature(node, pack=pack)
28
-
29
- # Parse docstring
30
- docstring = ast.get_docstring(node) or ""
31
- doc_info = parse_docstring(docstring)
32
-
33
- # Extract args
34
- args = extract_args(node, doc_info.get("args", {}))
35
-
36
- # Extract @tool decorator metadata if present
37
- decorator_info = extract_tool_decorator(node)
38
-
39
- # Decorator description overrides docstring if provided
40
- description = decorator_info.get("description") or doc_info.get("description", "")
41
-
42
- # Use pack-qualified name if pack is provided
43
- qualified_name = f"{pack}.{node.name}" if pack else node.name
44
-
45
- return ToolInfo(
46
- name=qualified_name,
47
- pack=pack,
48
- module=module,
49
- signature=signature,
50
- description=description,
51
- args=args,
52
- returns=doc_info.get("returns", ""),
53
- examples=decorator_info.get("examples", []),
54
- tags=decorator_info.get("tags", []),
55
- enabled=decorator_info.get("enabled", True),
56
- deprecated=decorator_info.get("deprecated", False),
57
- deprecated_message=decorator_info.get("deprecated_message"),
58
- )
59
-
60
-
61
- def extract_tool_decorator(node: ast.FunctionDef) -> dict[str, Any]:
62
- """Extract metadata from @tool decorator if present.
63
-
64
- Args:
65
- node: AST FunctionDef node.
66
-
67
- Returns:
68
- Dict with decorator metadata (description, examples, tags, enabled, deprecated).
69
- """
70
- result: dict[str, Any] = {}
71
-
72
- for decorator in node.decorator_list:
73
- # Look for @tool(...) decorator
74
- if isinstance(decorator, ast.Call):
75
- func = decorator.func
76
- if isinstance(func, ast.Name) and func.id == "tool":
77
- # Extract keyword arguments
78
- for keyword in decorator.keywords:
79
- if keyword.arg == "description":
80
- if isinstance(keyword.value, ast.Constant):
81
- result["description"] = keyword.value.value
82
- elif keyword.arg == "examples":
83
- if isinstance(keyword.value, ast.List):
84
- result["examples"] = [
85
- elt.value
86
- for elt in keyword.value.elts
87
- if isinstance(elt, ast.Constant)
88
- ]
89
- elif keyword.arg == "tags":
90
- if isinstance(keyword.value, ast.List):
91
- result["tags"] = [
92
- elt.value
93
- for elt in keyword.value.elts
94
- if isinstance(elt, ast.Constant)
95
- ]
96
- elif keyword.arg == "enabled":
97
- if isinstance(keyword.value, ast.Constant):
98
- result["enabled"] = keyword.value.value
99
- elif keyword.arg == "deprecated" and isinstance(
100
- keyword.value, ast.Constant
101
- ):
102
- result["deprecated"] = keyword.value.value
103
- elif keyword.arg == "deprecated_message" and isinstance(
104
- keyword.value, ast.Constant
105
- ):
106
- result["deprecated_message"] = keyword.value.value
107
- break
108
-
109
- return result
110
-
111
-
112
- def extract_signature(node: ast.FunctionDef, pack: str | None = None) -> str:
113
- """Extract function signature string.
114
-
115
- Args:
116
- node: AST FunctionDef node.
117
- pack: Optional pack name for namespace-qualified tool names.
118
-
119
- Returns:
120
- Signature string like 'pack.func_name(arg: type = default) -> return_type'
121
- """
122
- parts: list[str] = []
123
-
124
- # Process regular args
125
- args = node.args
126
- defaults = args.defaults
127
- num_defaults = len(defaults)
128
- num_args = len(args.args)
129
-
130
- for i, arg in enumerate(args.args):
131
- arg_str = arg.arg
132
- # Add type annotation
133
- if arg.annotation:
134
- arg_str += f": {annotation_to_str(arg.annotation)}"
135
-
136
- # Add default value if present
137
- default_idx = i - (num_args - num_defaults)
138
- if default_idx >= 0:
139
- default = defaults[default_idx]
140
- arg_str += f" = {value_to_str(default)}"
141
-
142
- parts.append(arg_str)
143
-
144
- # Process keyword-only args
145
- kw_defaults = args.kw_defaults
146
- for i, arg in enumerate(args.kwonlyargs):
147
- arg_str = arg.arg
148
- if arg.annotation:
149
- arg_str += f": {annotation_to_str(arg.annotation)}"
150
- if kw_defaults[i] is not None:
151
- arg_str += f" = {value_to_str(kw_defaults[i])}"
152
- parts.append(arg_str)
153
-
154
- # Build signature with pack-qualified name if pack is provided
155
- func_name = f"{pack}.{node.name}" if pack else node.name
156
- sig = f"{func_name}({', '.join(parts)})"
157
-
158
- # Add return type
159
- if node.returns:
160
- sig += f" -> {annotation_to_str(node.returns)}"
161
-
162
- return sig
163
-
164
-
165
- def annotation_to_str(node: ast.expr) -> str:
166
- """Convert AST annotation node to string.
167
-
168
- Args:
169
- node: AST expression node representing a type annotation.
170
-
171
- Returns:
172
- String representation of the type.
173
- """
174
- return ast.unparse(node)
175
-
176
-
177
- def value_to_str(node: ast.expr | None) -> str:
178
- """Convert AST value node to string representation.
179
-
180
- Args:
181
- node: AST expression node representing a default value.
182
-
183
- Returns:
184
- String representation of the value.
185
- """
186
- if node is None:
187
- return "None"
188
- return ast.unparse(node)
189
-
190
-
191
- def extract_args(
192
- node: ast.FunctionDef, docstring_args: dict[str, str]
193
- ) -> list[ArgInfo]:
194
- """Extract argument information from function node.
195
-
196
- Args:
197
- node: AST FunctionDef node.
198
- docstring_args: Dict of arg_name -> description from docstring.
199
-
200
- Returns:
201
- List of ArgInfo objects.
202
- """
203
- result: list[ArgInfo] = []
204
- args = node.args
205
- defaults = args.defaults
206
- num_defaults = len(defaults)
207
- num_args = len(args.args)
208
-
209
- for i, arg in enumerate(args.args):
210
- type_str = "Any"
211
- if arg.annotation:
212
- type_str = annotation_to_str(arg.annotation)
213
-
214
- default_str: str | None = None
215
- default_idx = i - (num_args - num_defaults)
216
- if default_idx >= 0:
217
- default_str = value_to_str(defaults[default_idx])
218
-
219
- result.append(
220
- ArgInfo(
221
- name=arg.arg,
222
- type=type_str,
223
- default=default_str,
224
- description=docstring_args.get(arg.arg, ""),
225
- )
226
- )
227
-
228
- # Keyword-only args
229
- kw_defaults = args.kw_defaults
230
- for i, arg in enumerate(args.kwonlyargs):
231
- type_str = "Any"
232
- if arg.annotation:
233
- type_str = annotation_to_str(arg.annotation)
234
-
235
- default_str = None
236
- if kw_defaults[i] is not None:
237
- default_str = value_to_str(kw_defaults[i])
238
-
239
- result.append(
240
- ArgInfo(
241
- name=arg.arg,
242
- type=type_str,
243
- default=default_str,
244
- description=docstring_args.get(arg.arg, ""),
245
- )
246
- )
247
-
248
- return result
249
-
250
-
251
- def parse_docstring(docstring: str) -> dict[str, Any]:
252
- """Parse Google-style docstring.
253
-
254
- Args:
255
- docstring: The docstring to parse.
256
-
257
- Returns:
258
- Dict with 'description', 'args', and 'returns' keys.
259
- """
260
- if not docstring:
261
- return {"description": "", "args": {}, "returns": ""}
262
-
263
- parsed = parse_docstring_lib(docstring)
264
-
265
- return {
266
- "description": parsed.short_description or "",
267
- "args": {p.arg_name: p.description or "" for p in parsed.params},
268
- "returns": parsed.returns.description if parsed.returns else "",
269
- }
1
+ """AST parsing utilities for extracting function information."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import ast
6
+ from typing import Any
7
+
8
+ from docstring_parser import parse as parse_docstring_lib
9
+
10
+ from .models import ArgInfo, ToolInfo
11
+
12
+
13
+ def parse_function(
14
+ node: ast.FunctionDef, module: str, pack: str | None = None
15
+ ) -> ToolInfo:
16
+ """Extract information from a function AST node.
17
+
18
+ Args:
19
+ node: AST FunctionDef node.
20
+ module: Module path for the function.
21
+ pack: Optional pack name for namespace-qualified tool names.
22
+
23
+ Returns:
24
+ ToolInfo with extracted signature and docstring info.
25
+ """
26
+ # Extract signature (with pack-qualified name if pack is provided)
27
+ signature = extract_signature(node, pack=pack)
28
+
29
+ # Parse docstring
30
+ docstring = ast.get_docstring(node) or ""
31
+ doc_info = parse_docstring(docstring)
32
+
33
+ # Extract args
34
+ args = extract_args(node, doc_info.get("args", {}))
35
+
36
+ # Extract @tool decorator metadata if present
37
+ decorator_info = extract_tool_decorator(node)
38
+
39
+ # Decorator description overrides docstring if provided
40
+ description = decorator_info.get("description") or doc_info.get("description", "")
41
+
42
+ # Use pack-qualified name if pack is provided
43
+ qualified_name = f"{pack}.{node.name}" if pack else node.name
44
+
45
+ return ToolInfo(
46
+ name=qualified_name,
47
+ pack=pack,
48
+ module=module,
49
+ signature=signature,
50
+ description=description,
51
+ args=args,
52
+ returns=doc_info.get("returns", ""),
53
+ examples=decorator_info.get("examples", []),
54
+ tags=decorator_info.get("tags", []),
55
+ enabled=decorator_info.get("enabled", True),
56
+ deprecated=decorator_info.get("deprecated", False),
57
+ deprecated_message=decorator_info.get("deprecated_message"),
58
+ )
59
+
60
+
61
+ def extract_tool_decorator(node: ast.FunctionDef) -> dict[str, Any]:
62
+ """Extract metadata from @tool decorator if present.
63
+
64
+ Args:
65
+ node: AST FunctionDef node.
66
+
67
+ Returns:
68
+ Dict with decorator metadata (description, examples, tags, enabled, deprecated).
69
+ """
70
+ result: dict[str, Any] = {}
71
+
72
+ for decorator in node.decorator_list:
73
+ # Look for @tool(...) decorator
74
+ if isinstance(decorator, ast.Call):
75
+ func = decorator.func
76
+ if isinstance(func, ast.Name) and func.id == "tool":
77
+ # Extract keyword arguments
78
+ for keyword in decorator.keywords:
79
+ if keyword.arg == "description":
80
+ if isinstance(keyword.value, ast.Constant):
81
+ result["description"] = keyword.value.value
82
+ elif keyword.arg == "examples":
83
+ if isinstance(keyword.value, ast.List):
84
+ result["examples"] = [
85
+ elt.value
86
+ for elt in keyword.value.elts
87
+ if isinstance(elt, ast.Constant)
88
+ ]
89
+ elif keyword.arg == "tags":
90
+ if isinstance(keyword.value, ast.List):
91
+ result["tags"] = [
92
+ elt.value
93
+ for elt in keyword.value.elts
94
+ if isinstance(elt, ast.Constant)
95
+ ]
96
+ elif keyword.arg == "enabled":
97
+ if isinstance(keyword.value, ast.Constant):
98
+ result["enabled"] = keyword.value.value
99
+ elif keyword.arg == "deprecated" and isinstance(
100
+ keyword.value, ast.Constant
101
+ ):
102
+ result["deprecated"] = keyword.value.value
103
+ elif keyword.arg == "deprecated_message" and isinstance(
104
+ keyword.value, ast.Constant
105
+ ):
106
+ result["deprecated_message"] = keyword.value.value
107
+ break
108
+
109
+ return result
110
+
111
+
112
+ def extract_signature(node: ast.FunctionDef, pack: str | None = None) -> str:
113
+ """Extract function signature string.
114
+
115
+ Args:
116
+ node: AST FunctionDef node.
117
+ pack: Optional pack name for namespace-qualified tool names.
118
+
119
+ Returns:
120
+ Signature string like 'pack.func_name(arg: type = default) -> return_type'
121
+ """
122
+ parts: list[str] = []
123
+
124
+ # Process regular args
125
+ args = node.args
126
+ defaults = args.defaults
127
+ num_defaults = len(defaults)
128
+ num_args = len(args.args)
129
+
130
+ for i, arg in enumerate(args.args):
131
+ arg_str = arg.arg
132
+ # Add type annotation
133
+ if arg.annotation:
134
+ arg_str += f": {annotation_to_str(arg.annotation)}"
135
+
136
+ # Add default value if present
137
+ default_idx = i - (num_args - num_defaults)
138
+ if default_idx >= 0:
139
+ default = defaults[default_idx]
140
+ arg_str += f" = {value_to_str(default)}"
141
+
142
+ parts.append(arg_str)
143
+
144
+ # Process keyword-only args
145
+ kw_defaults = args.kw_defaults
146
+ for i, arg in enumerate(args.kwonlyargs):
147
+ arg_str = arg.arg
148
+ if arg.annotation:
149
+ arg_str += f": {annotation_to_str(arg.annotation)}"
150
+ if kw_defaults[i] is not None:
151
+ arg_str += f" = {value_to_str(kw_defaults[i])}"
152
+ parts.append(arg_str)
153
+
154
+ # Build signature with pack-qualified name if pack is provided
155
+ func_name = f"{pack}.{node.name}" if pack else node.name
156
+ sig = f"{func_name}({', '.join(parts)})"
157
+
158
+ # Add return type
159
+ if node.returns:
160
+ sig += f" -> {annotation_to_str(node.returns)}"
161
+
162
+ return sig
163
+
164
+
165
+ def annotation_to_str(node: ast.expr) -> str:
166
+ """Convert AST annotation node to string.
167
+
168
+ Args:
169
+ node: AST expression node representing a type annotation.
170
+
171
+ Returns:
172
+ String representation of the type.
173
+ """
174
+ return ast.unparse(node)
175
+
176
+
177
+ def value_to_str(node: ast.expr | None) -> str:
178
+ """Convert AST value node to string representation.
179
+
180
+ Args:
181
+ node: AST expression node representing a default value.
182
+
183
+ Returns:
184
+ String representation of the value.
185
+ """
186
+ if node is None:
187
+ return "None"
188
+ return ast.unparse(node)
189
+
190
+
191
+ def extract_args(
192
+ node: ast.FunctionDef, docstring_args: dict[str, str]
193
+ ) -> list[ArgInfo]:
194
+ """Extract argument information from function node.
195
+
196
+ Args:
197
+ node: AST FunctionDef node.
198
+ docstring_args: Dict of arg_name -> description from docstring.
199
+
200
+ Returns:
201
+ List of ArgInfo objects.
202
+ """
203
+ result: list[ArgInfo] = []
204
+ args = node.args
205
+ defaults = args.defaults
206
+ num_defaults = len(defaults)
207
+ num_args = len(args.args)
208
+
209
+ for i, arg in enumerate(args.args):
210
+ type_str = "Any"
211
+ if arg.annotation:
212
+ type_str = annotation_to_str(arg.annotation)
213
+
214
+ default_str: str | None = None
215
+ default_idx = i - (num_args - num_defaults)
216
+ if default_idx >= 0:
217
+ default_str = value_to_str(defaults[default_idx])
218
+
219
+ result.append(
220
+ ArgInfo(
221
+ name=arg.arg,
222
+ type=type_str,
223
+ default=default_str,
224
+ description=docstring_args.get(arg.arg, ""),
225
+ )
226
+ )
227
+
228
+ # Keyword-only args
229
+ kw_defaults = args.kw_defaults
230
+ for i, arg in enumerate(args.kwonlyargs):
231
+ type_str = "Any"
232
+ if arg.annotation:
233
+ type_str = annotation_to_str(arg.annotation)
234
+
235
+ default_str = None
236
+ if kw_defaults[i] is not None:
237
+ default_str = value_to_str(kw_defaults[i])
238
+
239
+ result.append(
240
+ ArgInfo(
241
+ name=arg.arg,
242
+ type=type_str,
243
+ default=default_str,
244
+ description=docstring_args.get(arg.arg, ""),
245
+ )
246
+ )
247
+
248
+ return result
249
+
250
+
251
+ def parse_docstring(docstring: str) -> dict[str, Any]:
252
+ """Parse Google-style docstring.
253
+
254
+ Args:
255
+ docstring: The docstring to parse.
256
+
257
+ Returns:
258
+ Dict with 'description', 'args', and 'returns' keys.
259
+ """
260
+ if not docstring:
261
+ return {"description": "", "args": {}, "returns": ""}
262
+
263
+ parsed = parse_docstring_lib(docstring)
264
+
265
+ return {
266
+ "description": parsed.short_description or "",
267
+ "args": {p.arg_name: p.description or "" for p in parsed.params},
268
+ "returns": parsed.returns.description if parsed.returns else "",
269
+ }