hanzo-mcp 0.1.36__py3-none-any.whl → 0.3.1__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.
Potentially problematic release.
This version of hanzo-mcp might be problematic. Click here for more details.
- hanzo_mcp/__init__.py +1 -1
- hanzo_mcp/cli.py +136 -31
- hanzo_mcp/server.py +34 -13
- hanzo_mcp/tools/__init__.py +4 -2
- hanzo_mcp/tools/agent/base_provider.py +73 -0
- hanzo_mcp/tools/agent/litellm_provider.py +45 -0
- hanzo_mcp/tools/agent/lmstudio_agent.py +385 -0
- hanzo_mcp/tools/agent/lmstudio_provider.py +219 -0
- hanzo_mcp/tools/agent/provider_registry.py +120 -0
- hanzo_mcp/tools/common/__init__.py +0 -1
- hanzo_mcp/tools/common/context.py +6 -8
- hanzo_mcp/tools/common/error_handling.py +86 -0
- hanzo_mcp/tools/common/logging_config.py +84 -0
- hanzo_mcp/tools/common/permissions.py +6 -8
- hanzo_mcp/tools/filesystem/__init__.py +6 -1
- hanzo_mcp/tools/filesystem/directory_tree.py +7 -46
- hanzo_mcp/tools/jupyter/__init__.py +6 -1
- hanzo_mcp/tools/shell/command_executor.py +7 -6
- {hanzo_mcp-0.1.36.dist-info → hanzo_mcp-0.3.1.dist-info}/METADATA +1 -1
- {hanzo_mcp-0.1.36.dist-info → hanzo_mcp-0.3.1.dist-info}/RECORD +24 -18
- hanzo_mcp/tools/common/path_utils.py +0 -51
- {hanzo_mcp-0.1.36.dist-info → hanzo_mcp-0.3.1.dist-info}/WHEEL +0 -0
- {hanzo_mcp-0.1.36.dist-info → hanzo_mcp-0.3.1.dist-info}/entry_points.txt +0 -0
- {hanzo_mcp-0.1.36.dist-info → hanzo_mcp-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {hanzo_mcp-0.1.36.dist-info → hanzo_mcp-0.3.1.dist-info}/top_level.txt +0 -0
hanzo_mcp/__init__.py
CHANGED
hanzo_mcp/cli.py
CHANGED
|
@@ -1,21 +1,42 @@
|
|
|
1
|
-
"""Command-line interface for the Hanzo MCP server.
|
|
1
|
+
"""Command-line interface for the Hanzo MCP server.
|
|
2
|
+
|
|
3
|
+
Includes logging configuration and enhanced error handling.
|
|
4
|
+
"""
|
|
2
5
|
|
|
3
6
|
import argparse
|
|
4
7
|
import json
|
|
8
|
+
import logging
|
|
5
9
|
import os
|
|
6
10
|
import sys
|
|
7
11
|
from pathlib import Path
|
|
8
12
|
from typing import Any, cast
|
|
9
13
|
|
|
14
|
+
from hanzo_mcp import __version__
|
|
15
|
+
from hanzo_mcp.tools.common.logging_config import setup_logging
|
|
16
|
+
|
|
10
17
|
from hanzo_mcp.server import HanzoServer
|
|
11
|
-
from hanzo_mcp.tools.common.path_utils import PathUtils
|
|
12
18
|
|
|
13
19
|
|
|
14
20
|
def main() -> None:
|
|
15
21
|
"""Run the CLI for the Hanzo MCP server."""
|
|
22
|
+
# Initialize logger
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
# Check if 'version' is the first argument
|
|
26
|
+
if len(sys.argv) > 1 and sys.argv[1] == 'version':
|
|
27
|
+
print(f"hanzo-mcp {__version__}")
|
|
28
|
+
return
|
|
29
|
+
|
|
16
30
|
parser = argparse.ArgumentParser(
|
|
17
31
|
description="MCP server implementing Hanzo capabilities"
|
|
18
32
|
)
|
|
33
|
+
|
|
34
|
+
parser.add_argument(
|
|
35
|
+
"--version",
|
|
36
|
+
action="version",
|
|
37
|
+
version=f"%(prog)s {__version__}",
|
|
38
|
+
help="Show the current version and exit"
|
|
39
|
+
)
|
|
19
40
|
|
|
20
41
|
_ = parser.add_argument(
|
|
21
42
|
"--transport",
|
|
@@ -24,6 +45,19 @@ def main() -> None:
|
|
|
24
45
|
help="Transport protocol to use (default: stdio)",
|
|
25
46
|
)
|
|
26
47
|
|
|
48
|
+
_ = parser.add_argument(
|
|
49
|
+
"--port",
|
|
50
|
+
type=int,
|
|
51
|
+
default=3001,
|
|
52
|
+
help="Port to use for SSE transport (default: 3001)",
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
_ = parser.add_argument(
|
|
56
|
+
"--host",
|
|
57
|
+
default="0.0.0.0",
|
|
58
|
+
help="Host to bind to for SSE transport (default: 0.0.0.0)",
|
|
59
|
+
)
|
|
60
|
+
|
|
27
61
|
_ = parser.add_argument(
|
|
28
62
|
"--name",
|
|
29
63
|
default="claude-code",
|
|
@@ -40,26 +74,26 @@ def main() -> None:
|
|
|
40
74
|
_ = parser.add_argument(
|
|
41
75
|
"--project-dir", dest="project_dir", help="Set the project directory to analyze"
|
|
42
76
|
)
|
|
43
|
-
|
|
77
|
+
|
|
44
78
|
_ = parser.add_argument(
|
|
45
79
|
"--agent-model",
|
|
46
80
|
dest="agent_model",
|
|
47
81
|
help="Specify the model name in LiteLLM format (e.g., 'openai/gpt-4o', 'anthropic/claude-3-sonnet')"
|
|
48
82
|
)
|
|
49
|
-
|
|
83
|
+
|
|
50
84
|
_ = parser.add_argument(
|
|
51
85
|
"--agent-max-tokens",
|
|
52
86
|
dest="agent_max_tokens",
|
|
53
87
|
type=int,
|
|
54
88
|
help="Specify the maximum tokens for agent responses"
|
|
55
89
|
)
|
|
56
|
-
|
|
90
|
+
|
|
57
91
|
_ = parser.add_argument(
|
|
58
92
|
"--agent-api-key",
|
|
59
93
|
dest="agent_api_key",
|
|
60
94
|
help="Specify the API key for the LLM provider (for development/testing only)"
|
|
61
95
|
)
|
|
62
|
-
|
|
96
|
+
|
|
63
97
|
_ = parser.add_argument(
|
|
64
98
|
"--agent-max-iterations",
|
|
65
99
|
dest="agent_max_iterations",
|
|
@@ -67,7 +101,7 @@ def main() -> None:
|
|
|
67
101
|
default=10,
|
|
68
102
|
help="Maximum number of iterations for agent (default: 10)"
|
|
69
103
|
)
|
|
70
|
-
|
|
104
|
+
|
|
71
105
|
_ = parser.add_argument(
|
|
72
106
|
"--agent-max-tool-uses",
|
|
73
107
|
dest="agent_max_tool_uses",
|
|
@@ -75,7 +109,7 @@ def main() -> None:
|
|
|
75
109
|
default=30,
|
|
76
110
|
help="Maximum number of total tool uses for agent (default: 30)"
|
|
77
111
|
)
|
|
78
|
-
|
|
112
|
+
|
|
79
113
|
_ = parser.add_argument(
|
|
80
114
|
"--enable-agent-tool",
|
|
81
115
|
dest="enable_agent_tool",
|
|
@@ -84,6 +118,30 @@ def main() -> None:
|
|
|
84
118
|
help="Enable the agent tool (disabled by default)"
|
|
85
119
|
)
|
|
86
120
|
|
|
121
|
+
_ = parser.add_argument(
|
|
122
|
+
"--log-level",
|
|
123
|
+
dest="log_level",
|
|
124
|
+
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
|
|
125
|
+
default="INFO",
|
|
126
|
+
help="Set the logging level (default: INFO)"
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
_ = parser.add_argument(
|
|
130
|
+
"--disable-file-logging",
|
|
131
|
+
dest="disable_file_logging",
|
|
132
|
+
action="store_true",
|
|
133
|
+
default=False,
|
|
134
|
+
help="Disable logging to file (logs to console only)"
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
_ = parser.add_argument(
|
|
138
|
+
"--disable-write-tools",
|
|
139
|
+
dest="disable_write_tools",
|
|
140
|
+
action="store_true",
|
|
141
|
+
default=False,
|
|
142
|
+
help="Disable write/edit tools (file writing, editing, notebook editing) to use IDE tools instead. Note: Shell commands can still modify files."
|
|
143
|
+
)
|
|
144
|
+
|
|
87
145
|
_ = parser.add_argument(
|
|
88
146
|
"--install",
|
|
89
147
|
action="store_true",
|
|
@@ -96,6 +154,8 @@ def main() -> None:
|
|
|
96
154
|
name: str = cast(str, args.name)
|
|
97
155
|
install: bool = cast(bool, args.install)
|
|
98
156
|
transport: str = cast(str, args.transport)
|
|
157
|
+
port: int = cast(int, args.port)
|
|
158
|
+
host: str = cast(str, args.host)
|
|
99
159
|
project_dir: str | None = cast(str | None, args.project_dir)
|
|
100
160
|
agent_model: str | None = cast(str | None, args.agent_model)
|
|
101
161
|
agent_max_tokens: int | None = cast(int | None, args.agent_max_tokens)
|
|
@@ -103,55 +163,88 @@ def main() -> None:
|
|
|
103
163
|
agent_max_iterations: int = cast(int, args.agent_max_iterations)
|
|
104
164
|
agent_max_tool_uses: int = cast(int, args.agent_max_tool_uses)
|
|
105
165
|
enable_agent_tool: bool = cast(bool, args.enable_agent_tool)
|
|
166
|
+
disable_write_tools: bool = cast(bool, args.disable_write_tools)
|
|
167
|
+
log_level: str = cast(str, args.log_level)
|
|
168
|
+
disable_file_logging: bool = cast(bool, args.disable_file_logging)
|
|
106
169
|
allowed_paths: list[str] = (
|
|
107
170
|
cast(list[str], args.allowed_paths) if args.allowed_paths else []
|
|
108
171
|
)
|
|
109
172
|
|
|
173
|
+
# Setup logging
|
|
174
|
+
setup_logging(log_level=log_level, log_to_file=not disable_file_logging, testing="pytest" in sys.modules)
|
|
175
|
+
logger.debug(f"Hanzo MCP CLI started with arguments: {args}")
|
|
176
|
+
|
|
177
|
+
|
|
110
178
|
if install:
|
|
111
|
-
install_claude_desktop_config(name, allowed_paths)
|
|
179
|
+
install_claude_desktop_config(name, allowed_paths, host, port)
|
|
112
180
|
return
|
|
113
181
|
|
|
114
182
|
# If no allowed paths are specified, use the user's home directory
|
|
115
183
|
if not allowed_paths:
|
|
116
184
|
allowed_paths = [str(Path.home())]
|
|
117
|
-
|
|
118
|
-
# Normalize all allowed paths
|
|
119
|
-
allowed_paths = [PathUtils.normalize_path(path) for path in allowed_paths]
|
|
185
|
+
logger.info(f"No allowed paths specified, using home directory: {allowed_paths[0]}")
|
|
120
186
|
|
|
121
|
-
# If project directory is specified,
|
|
187
|
+
# If project directory is specified, add it to allowed paths
|
|
188
|
+
if project_dir and project_dir not in allowed_paths:
|
|
189
|
+
allowed_paths.append(project_dir)
|
|
190
|
+
|
|
191
|
+
# Set project directory as initial working directory if provided
|
|
122
192
|
if project_dir:
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
193
|
+
# Expand user paths
|
|
194
|
+
project_dir = os.path.expanduser(project_dir)
|
|
195
|
+
# Make absolute
|
|
196
|
+
if not os.path.isabs(project_dir):
|
|
197
|
+
project_dir = os.path.abspath(project_dir)
|
|
126
198
|
|
|
127
199
|
# If no specific project directory, use the first allowed path
|
|
128
|
-
|
|
200
|
+
elif allowed_paths:
|
|
129
201
|
project_dir = allowed_paths[0]
|
|
130
202
|
|
|
131
203
|
# Run the server
|
|
132
|
-
server
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
204
|
+
logger.info(f"Starting Hanzo MCP server with name: {name}")
|
|
205
|
+
logger.debug(f"Allowed paths: {allowed_paths}")
|
|
206
|
+
logger.debug(f"Project directory: {project_dir}")
|
|
207
|
+
|
|
208
|
+
try:
|
|
209
|
+
server = HanzoServer(
|
|
210
|
+
name=name,
|
|
211
|
+
allowed_paths=allowed_paths,
|
|
212
|
+
project_dir=project_dir, # Pass project_dir for initial working directory
|
|
213
|
+
agent_model=agent_model,
|
|
214
|
+
agent_max_tokens=agent_max_tokens,
|
|
215
|
+
agent_api_key=agent_api_key,
|
|
216
|
+
agent_max_iterations=agent_max_iterations,
|
|
217
|
+
agent_max_tool_uses=agent_max_tool_uses,
|
|
218
|
+
enable_agent_tool=enable_agent_tool,
|
|
219
|
+
disable_write_tools=disable_write_tools,
|
|
220
|
+
host=host,
|
|
221
|
+
port=port
|
|
222
|
+
)
|
|
223
|
+
logger.info(f"Server initialized successfully, running with transport: {transport}")
|
|
224
|
+
# Transport will be automatically cast to Literal['stdio', 'sse'] by the server
|
|
225
|
+
server.run(transport=transport)
|
|
226
|
+
except Exception as e:
|
|
227
|
+
logger.error(f"Error starting server: {str(e)}")
|
|
228
|
+
logger.exception("Server startup failed with exception:")
|
|
229
|
+
# Re-raise the exception for proper error handling
|
|
230
|
+
raise
|
|
145
231
|
|
|
146
232
|
|
|
147
233
|
def install_claude_desktop_config(
|
|
148
|
-
name: str = "claude-code", allowed_paths: list[str] | None = None
|
|
234
|
+
name: str = "claude-code", allowed_paths: list[str] | None = None,
|
|
235
|
+
disable_write_tools: bool = False,
|
|
236
|
+
host: str = "0.0.0.0", port: int = 3001
|
|
149
237
|
) -> None:
|
|
150
238
|
"""Install the server configuration in Claude Desktop.
|
|
151
239
|
|
|
152
240
|
Args:
|
|
153
241
|
name: The name to use for the server in the config
|
|
154
242
|
allowed_paths: Optional list of paths to allow
|
|
243
|
+
disable_write_tools: Whether to disable write/edit tools (file writing, editing, notebook editing)
|
|
244
|
+
to use IDE tools instead. Note: Shell commands can still modify files.
|
|
245
|
+
(default: False)
|
|
246
|
+
host: Host to bind to for SSE transport (default: '0.0.0.0')
|
|
247
|
+
port: Port to use for SSE transport (default: 3001)
|
|
155
248
|
"""
|
|
156
249
|
# Find the Claude Desktop config directory
|
|
157
250
|
home: Path = Path.home()
|
|
@@ -182,6 +275,18 @@ def install_claude_desktop_config(
|
|
|
182
275
|
# Allow home directory by default
|
|
183
276
|
args.extend(["--allow-path", str(home)])
|
|
184
277
|
|
|
278
|
+
# Add host and port
|
|
279
|
+
args.extend(["--host", host])
|
|
280
|
+
args.extend(["--port", str(port)])
|
|
281
|
+
|
|
282
|
+
# Add disable_write_tools flag if specified
|
|
283
|
+
if disable_write_tools:
|
|
284
|
+
args.append("--disable-write-tools")
|
|
285
|
+
|
|
286
|
+
# Add host and port
|
|
287
|
+
args.extend(["--host", host])
|
|
288
|
+
args.extend(["--port", str(port)])
|
|
289
|
+
|
|
185
290
|
# Create config object
|
|
186
291
|
config: dict[str, Any] = {
|
|
187
292
|
"mcpServers": {name: {"command": str(script_path), "args": args}}
|
hanzo_mcp/server.py
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
"""MCP server implementing Hanzo capabilities.
|
|
1
|
+
"""MCP server implementing Hanzo capabilities.
|
|
2
|
+
|
|
3
|
+
Includes improved error handling and debugging for tool execution.
|
|
4
|
+
"""
|
|
2
5
|
|
|
3
6
|
from typing import Literal, cast, final
|
|
4
7
|
|
|
@@ -6,7 +9,6 @@ from mcp.server.fastmcp import FastMCP
|
|
|
6
9
|
|
|
7
10
|
from hanzo_mcp.tools import register_all_tools
|
|
8
11
|
from hanzo_mcp.tools.common.context import DocumentContext
|
|
9
|
-
from hanzo_mcp.tools.common.path_utils import PathUtils
|
|
10
12
|
from hanzo_mcp.tools.common.permissions import PermissionManager
|
|
11
13
|
from hanzo_mcp.tools.project.analysis import ProjectAnalyzer, ProjectManager
|
|
12
14
|
from hanzo_mcp.tools.shell.command_executor import CommandExecutor
|
|
@@ -14,7 +16,10 @@ from hanzo_mcp.tools.shell.command_executor import CommandExecutor
|
|
|
14
16
|
|
|
15
17
|
@final
|
|
16
18
|
class HanzoServer:
|
|
17
|
-
"""MCP server implementing Hanzo capabilities.
|
|
19
|
+
"""MCP server implementing Hanzo capabilities.
|
|
20
|
+
|
|
21
|
+
Includes improved error handling and debugging for tool execution.
|
|
22
|
+
"""
|
|
18
23
|
|
|
19
24
|
def __init__(
|
|
20
25
|
self,
|
|
@@ -28,6 +33,9 @@ class HanzoServer:
|
|
|
28
33
|
agent_max_iterations: int = 10,
|
|
29
34
|
agent_max_tool_uses: int = 30,
|
|
30
35
|
enable_agent_tool: bool = False,
|
|
36
|
+
disable_write_tools: bool = False,
|
|
37
|
+
host: str = "0.0.0.0",
|
|
38
|
+
port: int = 3001,
|
|
31
39
|
):
|
|
32
40
|
"""Initialize the Hanzo server.
|
|
33
41
|
|
|
@@ -42,6 +50,9 @@ class HanzoServer:
|
|
|
42
50
|
agent_max_iterations: Maximum number of iterations for agent (default: 10)
|
|
43
51
|
agent_max_tool_uses: Maximum number of total tool uses for agent (default: 30)
|
|
44
52
|
enable_agent_tool: Whether to enable the agent tool (default: False)
|
|
53
|
+
disable_write_tools: Whether to disable write/edit tools (default: False)
|
|
54
|
+
host: Host to bind to for SSE transport (default: '0.0.0.0')
|
|
55
|
+
port: Port to use for SSE transport (default: 3001)
|
|
45
56
|
"""
|
|
46
57
|
self.mcp = mcp_instance if mcp_instance is not None else FastMCP(name)
|
|
47
58
|
|
|
@@ -54,7 +65,7 @@ class HanzoServer:
|
|
|
54
65
|
permission_manager=self.permission_manager,
|
|
55
66
|
verbose=False, # Set to True for debugging
|
|
56
67
|
)
|
|
57
|
-
|
|
68
|
+
|
|
58
69
|
# If project_dir is specified, set it as initial working directory for all sessions
|
|
59
70
|
if project_dir:
|
|
60
71
|
initial_session_id = name # Use server name as default session ID
|
|
@@ -71,10 +82,8 @@ class HanzoServer:
|
|
|
71
82
|
# Add allowed paths
|
|
72
83
|
if allowed_paths:
|
|
73
84
|
for path in allowed_paths:
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
self.permission_manager.add_allowed_path(normalized_path)
|
|
77
|
-
self.document_context.add_allowed_path(normalized_path)
|
|
85
|
+
self.permission_manager.add_allowed_path(path)
|
|
86
|
+
self.document_context.add_allowed_path(path)
|
|
78
87
|
|
|
79
88
|
# Store agent options
|
|
80
89
|
self.agent_model = agent_model
|
|
@@ -83,7 +92,12 @@ class HanzoServer:
|
|
|
83
92
|
self.agent_max_iterations = agent_max_iterations
|
|
84
93
|
self.agent_max_tool_uses = agent_max_tool_uses
|
|
85
94
|
self.enable_agent_tool = enable_agent_tool
|
|
86
|
-
|
|
95
|
+
self.disable_write_tools = disable_write_tools
|
|
96
|
+
|
|
97
|
+
# Store network options
|
|
98
|
+
self.host = host
|
|
99
|
+
self.port = port
|
|
100
|
+
|
|
87
101
|
# Register all tools
|
|
88
102
|
register_all_tools(
|
|
89
103
|
mcp_server=self.mcp,
|
|
@@ -95,6 +109,7 @@ class HanzoServer:
|
|
|
95
109
|
agent_max_iterations=self.agent_max_iterations,
|
|
96
110
|
agent_max_tool_uses=self.agent_max_tool_uses,
|
|
97
111
|
enable_agent_tool=self.enable_agent_tool,
|
|
112
|
+
disable_write_tools=self.disable_write_tools,
|
|
98
113
|
)
|
|
99
114
|
|
|
100
115
|
def run(self, transport: str = "stdio", allowed_paths: list[str] | None = None):
|
|
@@ -107,10 +122,16 @@ class HanzoServer:
|
|
|
107
122
|
# Add allowed paths if provided
|
|
108
123
|
allowed_paths_list = allowed_paths or []
|
|
109
124
|
for path in allowed_paths_list:
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
125
|
+
self.permission_manager.add_allowed_path(path)
|
|
126
|
+
self.document_context.add_allowed_path(path)
|
|
127
|
+
|
|
128
|
+
# If using SSE, set the port and host in the environment variables
|
|
129
|
+
if transport == "sse":
|
|
130
|
+
import os
|
|
131
|
+
# Set environment variables for FastMCP settings
|
|
132
|
+
os.environ["FASTMCP_PORT"] = str(self.port)
|
|
133
|
+
os.environ["FASTMCP_HOST"] = self.host
|
|
134
|
+
print(f"Starting SSE server on {self.host}:{self.port}")
|
|
114
135
|
|
|
115
136
|
# Run the server
|
|
116
137
|
transport_type = cast(Literal["stdio", "sse"], transport)
|
hanzo_mcp/tools/__init__.py
CHANGED
|
@@ -32,6 +32,7 @@ def register_all_tools(
|
|
|
32
32
|
agent_max_iterations: int = 10,
|
|
33
33
|
agent_max_tool_uses: int = 30,
|
|
34
34
|
enable_agent_tool: bool = False,
|
|
35
|
+
disable_write_tools: bool = False,
|
|
35
36
|
) -> None:
|
|
36
37
|
"""Register all Hanzo tools with the MCP server.
|
|
37
38
|
|
|
@@ -45,12 +46,13 @@ def register_all_tools(
|
|
|
45
46
|
agent_max_iterations: Maximum number of iterations for agent (default: 10)
|
|
46
47
|
agent_max_tool_uses: Maximum number of total tool uses for agent (default: 30)
|
|
47
48
|
enable_agent_tool: Whether to enable the agent tool (default: False)
|
|
49
|
+
disable_write_tools: Whether to disable write/edit tools (default: False)
|
|
48
50
|
"""
|
|
49
51
|
# Register all filesystem tools
|
|
50
|
-
register_filesystem_tools(mcp_server, document_context, permission_manager)
|
|
52
|
+
register_filesystem_tools(mcp_server, document_context, permission_manager, disable_write_tools)
|
|
51
53
|
|
|
52
54
|
# Register all jupyter tools
|
|
53
|
-
register_jupyter_tools(mcp_server, document_context, permission_manager)
|
|
55
|
+
register_jupyter_tools(mcp_server, document_context, permission_manager, disable_write_tools)
|
|
54
56
|
|
|
55
57
|
# Register shell tools
|
|
56
58
|
register_shell_tools(mcp_server, permission_manager)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Base model provider for agent delegation.
|
|
2
|
+
|
|
3
|
+
Defines the interface for model providers.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class BaseModelProvider(ABC):
|
|
14
|
+
"""Base class for model providers."""
|
|
15
|
+
|
|
16
|
+
@abstractmethod
|
|
17
|
+
async def initialize(self) -> None:
|
|
18
|
+
"""Initialize the provider."""
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
@abstractmethod
|
|
22
|
+
async def load_model(self, model_name: str, identifier: Optional[str] = None) -> str:
|
|
23
|
+
"""Load a model.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
model_name: The name of the model to load
|
|
27
|
+
identifier: Optional identifier for the model instance
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
The identifier for the loaded model
|
|
31
|
+
"""
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
@abstractmethod
|
|
35
|
+
async def generate(
|
|
36
|
+
self,
|
|
37
|
+
model_id: str,
|
|
38
|
+
prompt: str,
|
|
39
|
+
system_prompt: Optional[str] = None,
|
|
40
|
+
max_tokens: int = 4096,
|
|
41
|
+
temperature: float = 0.7,
|
|
42
|
+
top_p: float = 0.95,
|
|
43
|
+
stop_sequences: Optional[List[str]] = None,
|
|
44
|
+
) -> Tuple[str, Dict[str, Any]]:
|
|
45
|
+
"""Generate a response from the model.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
model_id: The identifier of the model to use
|
|
49
|
+
prompt: The prompt to send to the model
|
|
50
|
+
system_prompt: Optional system prompt to send to the model
|
|
51
|
+
max_tokens: Maximum number of tokens to generate
|
|
52
|
+
temperature: Sampling temperature
|
|
53
|
+
top_p: Top-p sampling parameter
|
|
54
|
+
stop_sequences: Optional list of strings that will stop generation
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
A tuple of (generated text, metadata)
|
|
58
|
+
"""
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
@abstractmethod
|
|
62
|
+
async def unload_model(self, model_id: str) -> None:
|
|
63
|
+
"""Unload a model.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
model_id: The identifier of the model to unload
|
|
67
|
+
"""
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
@abstractmethod
|
|
71
|
+
async def shutdown(self) -> None:
|
|
72
|
+
"""Shutdown the provider."""
|
|
73
|
+
pass
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""LiteLLM provider for agent delegation.
|
|
2
|
+
|
|
3
|
+
Enables the use of various cloud LLM providers via LiteLLM.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import asyncio
|
|
7
|
+
import logging
|
|
8
|
+
import json
|
|
9
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
10
|
+
|
|
11
|
+
from hanzo_mcp.tools.agent.base_provider import BaseModelProvider
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
# Define model capabilities
|
|
16
|
+
DEFAULT_MAX_TOKENS = 4096
|
|
17
|
+
DEFAULT_CONTEXT_WINDOW = 8192
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class LiteLLMProvider(BaseModelProvider):
|
|
21
|
+
"""Provider for cloud models via LiteLLM."""
|
|
22
|
+
|
|
23
|
+
def __init__(self):
|
|
24
|
+
"""Initialize the LiteLLM provider."""
|
|
25
|
+
self.models = {}
|
|
26
|
+
self.initialized = False
|
|
27
|
+
|
|
28
|
+
async def initialize(self) -> None:
|
|
29
|
+
"""Initialize the LiteLLM provider."""
|
|
30
|
+
if self.initialized:
|
|
31
|
+
return
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
# Import LiteLLM
|
|
35
|
+
import litellm
|
|
36
|
+
self.litellm = litellm
|
|
37
|
+
self.initialized = True
|
|
38
|
+
logger.info("LiteLLM provider initialized successfully")
|
|
39
|
+
except ImportError:
|
|
40
|
+
logger.error("Failed to import LiteLLM")
|
|
41
|
+
logger.error("Install LiteLLM with 'pip install litellm'")
|
|
42
|
+
except Exception as e:
|
|
43
|
+
logger.error(f"Failed to initialize LiteLLM provider: {str(e)}")
|
|
44
|
+
|
|
45
|
+
async def load_model(self, model_name: str, identifier: Optional[str] = None
|