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.
- onetool/cli.py +63 -4
- onetool_mcp-1.0.0rc2.dist-info/METADATA +266 -0
- onetool_mcp-1.0.0rc2.dist-info/RECORD +129 -0
- {onetool_mcp-1.0.0b1.dist-info → onetool_mcp-1.0.0rc2.dist-info}/licenses/LICENSE.txt +1 -1
- {onetool_mcp-1.0.0b1.dist-info → onetool_mcp-1.0.0rc2.dist-info}/licenses/NOTICE.txt +54 -64
- ot/__main__.py +6 -6
- ot/config/__init__.py +48 -46
- ot/config/global_templates/__init__.py +2 -2
- ot/config/{defaults → global_templates}/diagram-templates/api-flow.mmd +33 -33
- ot/config/{defaults → global_templates}/diagram-templates/c4-context.puml +30 -30
- ot/config/{defaults → global_templates}/diagram-templates/class-diagram.mmd +87 -87
- ot/config/{defaults → global_templates}/diagram-templates/feature-mindmap.mmd +70 -70
- ot/config/{defaults → global_templates}/diagram-templates/microservices.d2 +81 -81
- ot/config/{defaults → global_templates}/diagram-templates/project-gantt.mmd +37 -37
- ot/config/{defaults → global_templates}/diagram-templates/state-machine.mmd +42 -42
- ot/config/global_templates/diagram.yaml +167 -0
- ot/config/global_templates/onetool.yaml +3 -1
- ot/config/{defaults → global_templates}/prompts.yaml +102 -97
- ot/config/global_templates/security.yaml +31 -0
- ot/config/global_templates/servers.yaml +93 -12
- ot/config/global_templates/snippets.yaml +5 -26
- ot/config/{defaults → global_templates}/tool_templates/__init__.py +7 -7
- ot/config/loader.py +221 -105
- ot/config/mcp.py +5 -1
- ot/config/secrets.py +192 -190
- ot/decorators.py +116 -116
- ot/executor/__init__.py +35 -35
- ot/executor/base.py +16 -16
- ot/executor/fence_processor.py +83 -83
- ot/executor/linter.py +142 -142
- ot/executor/pep723.py +288 -288
- ot/executor/runner.py +20 -6
- ot/executor/simple.py +163 -163
- ot/executor/validator.py +603 -164
- ot/http_client.py +145 -145
- ot/logging/__init__.py +37 -37
- ot/logging/entry.py +213 -213
- ot/logging/format.py +191 -188
- ot/logging/span.py +349 -349
- ot/meta.py +236 -14
- ot/paths.py +32 -49
- ot/prompts.py +218 -218
- ot/proxy/manager.py +14 -2
- ot/registry/__init__.py +189 -189
- ot/registry/parser.py +269 -269
- ot/server.py +330 -315
- ot/shortcuts/__init__.py +15 -15
- ot/shortcuts/aliases.py +87 -87
- ot/shortcuts/snippets.py +258 -258
- ot/stats/__init__.py +35 -35
- ot/stats/html.py +2 -2
- ot/stats/reader.py +354 -354
- ot/stats/timing.py +57 -57
- ot/support.py +63 -63
- ot/tools.py +1 -1
- ot/utils/batch.py +161 -161
- ot/utils/cache.py +120 -120
- ot/utils/exceptions.py +23 -23
- ot/utils/factory.py +178 -179
- ot/utils/format.py +65 -65
- ot/utils/http.py +202 -202
- ot/utils/platform.py +45 -45
- ot/utils/truncate.py +69 -69
- ot_tools/__init__.py +4 -4
- ot_tools/_convert/__init__.py +12 -12
- ot_tools/_convert/pdf.py +254 -254
- ot_tools/diagram.yaml +167 -167
- ot_tools/scaffold.py +2 -2
- ot_tools/transform.py +124 -19
- ot_tools/web_fetch.py +94 -43
- onetool_mcp-1.0.0b1.dist-info/METADATA +0 -163
- onetool_mcp-1.0.0b1.dist-info/RECORD +0 -132
- ot/config/defaults/bench.yaml +0 -4
- ot/config/defaults/onetool.yaml +0 -25
- ot/config/defaults/servers.yaml +0 -7
- ot/config/defaults/snippets.yaml +0 -4
- ot_tools/firecrawl.py +0 -732
- {onetool_mcp-1.0.0b1.dist-info → onetool_mcp-1.0.0rc2.dist-info}/WHEEL +0 -0
- {onetool_mcp-1.0.0b1.dist-info → onetool_mcp-1.0.0rc2.dist-info}/entry_points.txt +0 -0
- /ot/config/{defaults → global_templates}/tool_templates/extension.py +0 -0
- /ot/config/{defaults → global_templates}/tool_templates/isolated.py +0 -0
ot/executor/simple.py
CHANGED
|
@@ -1,163 +1,163 @@
|
|
|
1
|
-
"""Simple executor - host process execution.
|
|
2
|
-
|
|
3
|
-
Executes tool code directly in the host Python process.
|
|
4
|
-
V1 uses this executor for all execution.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
import importlib.util
|
|
10
|
-
import sys
|
|
11
|
-
import time
|
|
12
|
-
from typing import TYPE_CHECKING, Any
|
|
13
|
-
|
|
14
|
-
from loguru import logger
|
|
15
|
-
|
|
16
|
-
from ot.executor.base import ExecutionResult
|
|
17
|
-
from ot.logging import LogEntry, LogSpan
|
|
18
|
-
from ot.utils import serialize_result
|
|
19
|
-
|
|
20
|
-
if TYPE_CHECKING:
|
|
21
|
-
from ot.registry import ToolInfo
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class SimpleExecutor:
|
|
25
|
-
"""Host process executor (v1 behaviour).
|
|
26
|
-
|
|
27
|
-
Loads and executes tool modules directly in the host Python process.
|
|
28
|
-
Fast but no isolation - tools have full filesystem access.
|
|
29
|
-
"""
|
|
30
|
-
|
|
31
|
-
@property
|
|
32
|
-
def name(self) -> str:
|
|
33
|
-
"""Return the executor name."""
|
|
34
|
-
return "simple"
|
|
35
|
-
|
|
36
|
-
def _load_tool_module(self, module_name: str) -> Any:
|
|
37
|
-
"""Dynamically load a tool module.
|
|
38
|
-
|
|
39
|
-
Args:
|
|
40
|
-
module_name: Module path like 'tools.example'
|
|
41
|
-
|
|
42
|
-
Returns:
|
|
43
|
-
The loaded module object
|
|
44
|
-
|
|
45
|
-
Raises:
|
|
46
|
-
ImportError: If module cannot be loaded
|
|
47
|
-
"""
|
|
48
|
-
from ot.config.loader import get_config
|
|
49
|
-
|
|
50
|
-
# Get tool files from config
|
|
51
|
-
config = get_config()
|
|
52
|
-
tool_files = config.get_tool_files() if config else []
|
|
53
|
-
if not tool_files:
|
|
54
|
-
raise ImportError(
|
|
55
|
-
f"No tool files configured. Cannot load module: {module_name}"
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
# Convert module path to file name (tools.example -> example.py)
|
|
59
|
-
parts = module_name.split(".")
|
|
60
|
-
if len(parts) < 2 or parts[-2] != "tools":
|
|
61
|
-
raise ImportError(f"Invalid tool module: {module_name}")
|
|
62
|
-
|
|
63
|
-
target_name = f"{parts[-1]}.py"
|
|
64
|
-
|
|
65
|
-
# Find matching tool file from config
|
|
66
|
-
file_path = None
|
|
67
|
-
for tf in tool_files:
|
|
68
|
-
if tf.name == target_name:
|
|
69
|
-
file_path = tf
|
|
70
|
-
break
|
|
71
|
-
|
|
72
|
-
if file_path is None or not file_path.exists():
|
|
73
|
-
raise ImportError(f"Tool file not found: {target_name}")
|
|
74
|
-
|
|
75
|
-
# Load the module dynamically
|
|
76
|
-
spec = importlib.util.spec_from_file_location(module_name, file_path)
|
|
77
|
-
if spec is None or spec.loader is None:
|
|
78
|
-
raise ImportError(f"Cannot load module spec for: {file_path}")
|
|
79
|
-
|
|
80
|
-
module = importlib.util.module_from_spec(spec)
|
|
81
|
-
sys.modules[module_name] = module
|
|
82
|
-
spec.loader.exec_module(module)
|
|
83
|
-
|
|
84
|
-
return module
|
|
85
|
-
|
|
86
|
-
async def execute(
|
|
87
|
-
self,
|
|
88
|
-
func_name: str,
|
|
89
|
-
kwargs: dict[str, Any],
|
|
90
|
-
tool: ToolInfo,
|
|
91
|
-
) -> ExecutionResult:
|
|
92
|
-
"""Execute a tool function in the host process.
|
|
93
|
-
|
|
94
|
-
Args:
|
|
95
|
-
func_name: Name of the function to execute
|
|
96
|
-
kwargs: Keyword arguments for the function
|
|
97
|
-
tool: ToolInfo with module and signature info
|
|
98
|
-
|
|
99
|
-
Returns:
|
|
100
|
-
ExecutionResult with success status and result string
|
|
101
|
-
"""
|
|
102
|
-
start_time = time.perf_counter()
|
|
103
|
-
|
|
104
|
-
try:
|
|
105
|
-
# Load the module and get the function
|
|
106
|
-
module = self._load_tool_module(tool.module)
|
|
107
|
-
func = getattr(module, func_name, None)
|
|
108
|
-
|
|
109
|
-
if func is None:
|
|
110
|
-
raise ValueError(
|
|
111
|
-
f"Function '{func_name}' not found in module {tool.module}"
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
# Execute the function with timing via LogSpan
|
|
115
|
-
with LogSpan(span="executor.simple", tool=func_name) as span:
|
|
116
|
-
span.add("kwargs", {k: str(v) for k, v in kwargs.items()})
|
|
117
|
-
result = func(**kwargs)
|
|
118
|
-
result_str = serialize_result(result)
|
|
119
|
-
span.add("resultLength", len(result_str))
|
|
120
|
-
|
|
121
|
-
duration = time.perf_counter() - start_time
|
|
122
|
-
|
|
123
|
-
return ExecutionResult(
|
|
124
|
-
success=True,
|
|
125
|
-
result=result_str,
|
|
126
|
-
duration_seconds=duration,
|
|
127
|
-
executor="simple",
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
except Exception as e:
|
|
131
|
-
duration = time.perf_counter() - start_time
|
|
132
|
-
logger.error(
|
|
133
|
-
LogEntry(
|
|
134
|
-
span="executor.simple.error",
|
|
135
|
-
tool=func_name,
|
|
136
|
-
error=str(e),
|
|
137
|
-
errorType=type(e).__name__,
|
|
138
|
-
duration=duration,
|
|
139
|
-
)
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
return ExecutionResult(
|
|
143
|
-
success=False,
|
|
144
|
-
result=f"Error executing tool '{func_name}': {e}",
|
|
145
|
-
duration_seconds=duration,
|
|
146
|
-
executor="simple",
|
|
147
|
-
error_type=type(e).__name__,
|
|
148
|
-
)
|
|
149
|
-
|
|
150
|
-
async def start(self) -> None:
|
|
151
|
-
"""Start the executor (no-op for simple executor)."""
|
|
152
|
-
logger.debug(LogEntry(span="executor.simple.start"))
|
|
153
|
-
|
|
154
|
-
async def stop(self) -> None:
|
|
155
|
-
"""Stop the executor (no-op for simple executor)."""
|
|
156
|
-
logger.debug(LogEntry(span="executor.simple.stop"))
|
|
157
|
-
|
|
158
|
-
async def health_check(self) -> bool:
|
|
159
|
-
"""Check if the executor is healthy.
|
|
160
|
-
|
|
161
|
-
Simple executor is always healthy.
|
|
162
|
-
"""
|
|
163
|
-
return True
|
|
1
|
+
"""Simple executor - host process execution.
|
|
2
|
+
|
|
3
|
+
Executes tool code directly in the host Python process.
|
|
4
|
+
V1 uses this executor for all execution.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import importlib.util
|
|
10
|
+
import sys
|
|
11
|
+
import time
|
|
12
|
+
from typing import TYPE_CHECKING, Any
|
|
13
|
+
|
|
14
|
+
from loguru import logger
|
|
15
|
+
|
|
16
|
+
from ot.executor.base import ExecutionResult
|
|
17
|
+
from ot.logging import LogEntry, LogSpan
|
|
18
|
+
from ot.utils import serialize_result
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from ot.registry import ToolInfo
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class SimpleExecutor:
|
|
25
|
+
"""Host process executor (v1 behaviour).
|
|
26
|
+
|
|
27
|
+
Loads and executes tool modules directly in the host Python process.
|
|
28
|
+
Fast but no isolation - tools have full filesystem access.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def name(self) -> str:
|
|
33
|
+
"""Return the executor name."""
|
|
34
|
+
return "simple"
|
|
35
|
+
|
|
36
|
+
def _load_tool_module(self, module_name: str) -> Any:
|
|
37
|
+
"""Dynamically load a tool module.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
module_name: Module path like 'tools.example'
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
The loaded module object
|
|
44
|
+
|
|
45
|
+
Raises:
|
|
46
|
+
ImportError: If module cannot be loaded
|
|
47
|
+
"""
|
|
48
|
+
from ot.config.loader import get_config
|
|
49
|
+
|
|
50
|
+
# Get tool files from config
|
|
51
|
+
config = get_config()
|
|
52
|
+
tool_files = config.get_tool_files() if config else []
|
|
53
|
+
if not tool_files:
|
|
54
|
+
raise ImportError(
|
|
55
|
+
f"No tool files configured. Cannot load module: {module_name}"
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# Convert module path to file name (tools.example -> example.py)
|
|
59
|
+
parts = module_name.split(".")
|
|
60
|
+
if len(parts) < 2 or parts[-2] != "tools":
|
|
61
|
+
raise ImportError(f"Invalid tool module: {module_name}")
|
|
62
|
+
|
|
63
|
+
target_name = f"{parts[-1]}.py"
|
|
64
|
+
|
|
65
|
+
# Find matching tool file from config
|
|
66
|
+
file_path = None
|
|
67
|
+
for tf in tool_files:
|
|
68
|
+
if tf.name == target_name:
|
|
69
|
+
file_path = tf
|
|
70
|
+
break
|
|
71
|
+
|
|
72
|
+
if file_path is None or not file_path.exists():
|
|
73
|
+
raise ImportError(f"Tool file not found: {target_name}")
|
|
74
|
+
|
|
75
|
+
# Load the module dynamically
|
|
76
|
+
spec = importlib.util.spec_from_file_location(module_name, file_path)
|
|
77
|
+
if spec is None or spec.loader is None:
|
|
78
|
+
raise ImportError(f"Cannot load module spec for: {file_path}")
|
|
79
|
+
|
|
80
|
+
module = importlib.util.module_from_spec(spec)
|
|
81
|
+
sys.modules[module_name] = module
|
|
82
|
+
spec.loader.exec_module(module)
|
|
83
|
+
|
|
84
|
+
return module
|
|
85
|
+
|
|
86
|
+
async def execute(
|
|
87
|
+
self,
|
|
88
|
+
func_name: str,
|
|
89
|
+
kwargs: dict[str, Any],
|
|
90
|
+
tool: ToolInfo,
|
|
91
|
+
) -> ExecutionResult:
|
|
92
|
+
"""Execute a tool function in the host process.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
func_name: Name of the function to execute
|
|
96
|
+
kwargs: Keyword arguments for the function
|
|
97
|
+
tool: ToolInfo with module and signature info
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
ExecutionResult with success status and result string
|
|
101
|
+
"""
|
|
102
|
+
start_time = time.perf_counter()
|
|
103
|
+
|
|
104
|
+
try:
|
|
105
|
+
# Load the module and get the function
|
|
106
|
+
module = self._load_tool_module(tool.module)
|
|
107
|
+
func = getattr(module, func_name, None)
|
|
108
|
+
|
|
109
|
+
if func is None:
|
|
110
|
+
raise ValueError(
|
|
111
|
+
f"Function '{func_name}' not found in module {tool.module}"
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# Execute the function with timing via LogSpan
|
|
115
|
+
with LogSpan(span="executor.simple", tool=func_name) as span:
|
|
116
|
+
span.add("kwargs", {k: str(v) for k, v in kwargs.items()})
|
|
117
|
+
result = func(**kwargs)
|
|
118
|
+
result_str = serialize_result(result)
|
|
119
|
+
span.add("resultLength", len(result_str))
|
|
120
|
+
|
|
121
|
+
duration = time.perf_counter() - start_time
|
|
122
|
+
|
|
123
|
+
return ExecutionResult(
|
|
124
|
+
success=True,
|
|
125
|
+
result=result_str,
|
|
126
|
+
duration_seconds=duration,
|
|
127
|
+
executor="simple",
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
except Exception as e:
|
|
131
|
+
duration = time.perf_counter() - start_time
|
|
132
|
+
logger.error(
|
|
133
|
+
LogEntry(
|
|
134
|
+
span="executor.simple.error",
|
|
135
|
+
tool=func_name,
|
|
136
|
+
error=str(e),
|
|
137
|
+
errorType=type(e).__name__,
|
|
138
|
+
duration=duration,
|
|
139
|
+
)
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
return ExecutionResult(
|
|
143
|
+
success=False,
|
|
144
|
+
result=f"Error executing tool '{func_name}': {e}",
|
|
145
|
+
duration_seconds=duration,
|
|
146
|
+
executor="simple",
|
|
147
|
+
error_type=type(e).__name__,
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
async def start(self) -> None:
|
|
151
|
+
"""Start the executor (no-op for simple executor)."""
|
|
152
|
+
logger.debug(LogEntry(span="executor.simple.start"))
|
|
153
|
+
|
|
154
|
+
async def stop(self) -> None:
|
|
155
|
+
"""Stop the executor (no-op for simple executor)."""
|
|
156
|
+
logger.debug(LogEntry(span="executor.simple.stop"))
|
|
157
|
+
|
|
158
|
+
async def health_check(self) -> bool:
|
|
159
|
+
"""Check if the executor is healthy.
|
|
160
|
+
|
|
161
|
+
Simple executor is always healthy.
|
|
162
|
+
"""
|
|
163
|
+
return True
|