hanzo-mcp 0.1.36__tar.gz → 0.3.1__tar.gz

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.

Files changed (64) hide show
  1. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/PKG-INFO +1 -1
  2. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/__init__.py +1 -1
  3. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/cli.py +136 -31
  4. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/server.py +34 -13
  5. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/__init__.py +4 -2
  6. hanzo_mcp-0.3.1/hanzo_mcp/tools/agent/base_provider.py +73 -0
  7. hanzo_mcp-0.3.1/hanzo_mcp/tools/agent/litellm_provider.py +45 -0
  8. hanzo_mcp-0.3.1/hanzo_mcp/tools/agent/lmstudio_agent.py +385 -0
  9. hanzo_mcp-0.3.1/hanzo_mcp/tools/agent/lmstudio_provider.py +219 -0
  10. hanzo_mcp-0.3.1/hanzo_mcp/tools/agent/provider_registry.py +120 -0
  11. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/common/__init__.py +0 -1
  12. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/common/context.py +6 -8
  13. hanzo_mcp-0.3.1/hanzo_mcp/tools/common/error_handling.py +86 -0
  14. hanzo_mcp-0.3.1/hanzo_mcp/tools/common/logging_config.py +84 -0
  15. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/common/permissions.py +6 -8
  16. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/filesystem/__init__.py +6 -1
  17. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/filesystem/directory_tree.py +7 -46
  18. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/jupyter/__init__.py +6 -1
  19. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/shell/command_executor.py +7 -6
  20. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp.egg-info/PKG-INFO +1 -1
  21. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp.egg-info/SOURCES.txt +8 -1
  22. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/pyproject.toml +1 -1
  23. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/tests/test_cli.py +94 -3
  24. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/tests/test_server.py +20 -0
  25. hanzo_mcp-0.3.1/tests/test_tools_registration.py +180 -0
  26. hanzo_mcp-0.1.36/hanzo_mcp/tools/common/path_utils.py +0 -51
  27. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/LICENSE +0 -0
  28. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/README.md +0 -0
  29. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/agent/__init__.py +0 -0
  30. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/agent/agent_tool.py +0 -0
  31. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/agent/prompt.py +0 -0
  32. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/agent/tool_adapter.py +0 -0
  33. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/common/base.py +0 -0
  34. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/common/session.py +0 -0
  35. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/common/think_tool.py +0 -0
  36. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/common/validation.py +0 -0
  37. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/common/version_tool.py +0 -0
  38. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/filesystem/base.py +0 -0
  39. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/filesystem/content_replace.py +0 -0
  40. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/filesystem/edit_file.py +0 -0
  41. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/filesystem/get_file_info.py +0 -0
  42. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/filesystem/read_files.py +0 -0
  43. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/filesystem/search_content.py +0 -0
  44. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/filesystem/write_file.py +0 -0
  45. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/jupyter/base.py +0 -0
  46. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/jupyter/edit_notebook.py +0 -0
  47. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/jupyter/notebook_operations.py +0 -0
  48. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/jupyter/read_notebook.py +0 -0
  49. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/project/__init__.py +0 -0
  50. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/project/analysis.py +0 -0
  51. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/project/base.py +0 -0
  52. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/project/project_analyze.py +0 -0
  53. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/shell/__init__.py +0 -0
  54. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/shell/base.py +0 -0
  55. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/shell/run_command.py +0 -0
  56. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/shell/run_script.py +0 -0
  57. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp/tools/shell/script_tool.py +0 -0
  58. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp.egg-info/dependency_links.txt +0 -0
  59. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp.egg-info/entry_points.txt +0 -0
  60. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp.egg-info/requires.txt +0 -0
  61. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/hanzo_mcp.egg-info/top_level.txt +0 -0
  62. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/setup.cfg +0 -0
  63. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/setup.py +0 -0
  64. {hanzo_mcp-0.1.36 → hanzo_mcp-0.3.1}/tests/test_validation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hanzo-mcp
3
- Version: 0.1.36
3
+ Version: 0.3.1
4
4
  Summary: MCP implementation of Hanzo capabilities
5
5
  Author-email: Hanzo Industries Inc <dev@hanzo.ai>
6
6
  License: MIT
@@ -1,3 +1,3 @@
1
1
  """Hanzo MCP - Implementation of Hanzo capabilities using MCP."""
2
2
 
3
- __version__ = "0.1.36"
3
+ __version__ = "0.3.1"
@@ -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, normalize it and add to allowed paths
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
- project_dir = PathUtils.normalize_path(project_dir)
124
- if project_dir not in allowed_paths:
125
- allowed_paths.append(project_dir)
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
- if not project_dir and allowed_paths:
200
+ elif allowed_paths:
129
201
  project_dir = allowed_paths[0]
130
202
 
131
203
  # Run the server
132
- server = HanzoServer(
133
- name=name,
134
- allowed_paths=allowed_paths,
135
- project_dir=project_dir, # Pass project_dir for initial working directory
136
- agent_model=agent_model,
137
- agent_max_tokens=agent_max_tokens,
138
- agent_api_key=agent_api_key,
139
- agent_max_iterations=agent_max_iterations,
140
- agent_max_tool_uses=agent_max_tool_uses,
141
- enable_agent_tool=enable_agent_tool
142
- )
143
- # Transport will be automatically cast to Literal['stdio', 'sse'] by the server
144
- server.run(transport=transport)
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}}
@@ -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
- # Path should already be normalized from CLI, but normalize here for safety
75
- normalized_path = PathUtils.normalize_path(path)
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
- # Normalize path before adding
111
- normalized_path = PathUtils.normalize_path(path)
112
- self.permission_manager.add_allowed_path(normalized_path)
113
- self.document_context.add_allowed_path(normalized_path)
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)
@@ -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