hanzo-mcp 0.3.4__py3-none-any.whl → 0.5.0__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.

Files changed (87) hide show
  1. hanzo_mcp/__init__.py +1 -1
  2. hanzo_mcp/cli.py +123 -160
  3. hanzo_mcp/cli_enhanced.py +438 -0
  4. hanzo_mcp/config/__init__.py +19 -0
  5. hanzo_mcp/config/settings.py +388 -0
  6. hanzo_mcp/config/tool_config.py +197 -0
  7. hanzo_mcp/prompts/__init__.py +117 -0
  8. hanzo_mcp/prompts/compact_conversation.py +77 -0
  9. hanzo_mcp/prompts/create_release.py +38 -0
  10. hanzo_mcp/prompts/project_system.py +120 -0
  11. hanzo_mcp/prompts/project_todo_reminder.py +111 -0
  12. hanzo_mcp/prompts/utils.py +286 -0
  13. hanzo_mcp/server.py +120 -98
  14. hanzo_mcp/tools/__init__.py +107 -31
  15. hanzo_mcp/tools/agent/__init__.py +8 -11
  16. hanzo_mcp/tools/agent/agent_tool.py +290 -224
  17. hanzo_mcp/tools/agent/prompt.py +16 -13
  18. hanzo_mcp/tools/agent/tool_adapter.py +9 -9
  19. hanzo_mcp/tools/common/__init__.py +17 -16
  20. hanzo_mcp/tools/common/base.py +79 -110
  21. hanzo_mcp/tools/common/batch_tool.py +330 -0
  22. hanzo_mcp/tools/common/context.py +26 -292
  23. hanzo_mcp/tools/common/permissions.py +12 -12
  24. hanzo_mcp/tools/common/thinking_tool.py +153 -0
  25. hanzo_mcp/tools/common/validation.py +1 -63
  26. hanzo_mcp/tools/filesystem/__init__.py +88 -41
  27. hanzo_mcp/tools/filesystem/base.py +32 -24
  28. hanzo_mcp/tools/filesystem/content_replace.py +114 -107
  29. hanzo_mcp/tools/filesystem/directory_tree.py +129 -105
  30. hanzo_mcp/tools/filesystem/edit.py +279 -0
  31. hanzo_mcp/tools/filesystem/grep.py +458 -0
  32. hanzo_mcp/tools/filesystem/grep_ast_tool.py +250 -0
  33. hanzo_mcp/tools/filesystem/multi_edit.py +362 -0
  34. hanzo_mcp/tools/filesystem/read.py +255 -0
  35. hanzo_mcp/tools/filesystem/write.py +156 -0
  36. hanzo_mcp/tools/jupyter/__init__.py +41 -29
  37. hanzo_mcp/tools/jupyter/base.py +66 -57
  38. hanzo_mcp/tools/jupyter/{edit_notebook.py → notebook_edit.py} +162 -139
  39. hanzo_mcp/tools/jupyter/notebook_read.py +152 -0
  40. hanzo_mcp/tools/shell/__init__.py +29 -20
  41. hanzo_mcp/tools/shell/base.py +87 -45
  42. hanzo_mcp/tools/shell/bash_session.py +731 -0
  43. hanzo_mcp/tools/shell/bash_session_executor.py +295 -0
  44. hanzo_mcp/tools/shell/command_executor.py +435 -384
  45. hanzo_mcp/tools/shell/run_command.py +284 -131
  46. hanzo_mcp/tools/shell/run_command_windows.py +328 -0
  47. hanzo_mcp/tools/shell/session_manager.py +196 -0
  48. hanzo_mcp/tools/shell/session_storage.py +325 -0
  49. hanzo_mcp/tools/todo/__init__.py +66 -0
  50. hanzo_mcp/tools/todo/base.py +319 -0
  51. hanzo_mcp/tools/todo/todo_read.py +148 -0
  52. hanzo_mcp/tools/todo/todo_write.py +378 -0
  53. hanzo_mcp/tools/vector/__init__.py +95 -0
  54. hanzo_mcp/tools/vector/infinity_store.py +365 -0
  55. hanzo_mcp/tools/vector/project_manager.py +361 -0
  56. hanzo_mcp/tools/vector/vector_index.py +115 -0
  57. hanzo_mcp/tools/vector/vector_search.py +215 -0
  58. {hanzo_mcp-0.3.4.dist-info → hanzo_mcp-0.5.0.dist-info}/METADATA +35 -3
  59. hanzo_mcp-0.5.0.dist-info/RECORD +63 -0
  60. {hanzo_mcp-0.3.4.dist-info → hanzo_mcp-0.5.0.dist-info}/WHEEL +1 -1
  61. hanzo_mcp/tools/agent/base_provider.py +0 -73
  62. hanzo_mcp/tools/agent/litellm_provider.py +0 -45
  63. hanzo_mcp/tools/agent/lmstudio_agent.py +0 -385
  64. hanzo_mcp/tools/agent/lmstudio_provider.py +0 -219
  65. hanzo_mcp/tools/agent/provider_registry.py +0 -120
  66. hanzo_mcp/tools/common/error_handling.py +0 -86
  67. hanzo_mcp/tools/common/logging_config.py +0 -115
  68. hanzo_mcp/tools/common/session.py +0 -91
  69. hanzo_mcp/tools/common/think_tool.py +0 -123
  70. hanzo_mcp/tools/common/version_tool.py +0 -120
  71. hanzo_mcp/tools/filesystem/edit_file.py +0 -287
  72. hanzo_mcp/tools/filesystem/get_file_info.py +0 -170
  73. hanzo_mcp/tools/filesystem/read_files.py +0 -198
  74. hanzo_mcp/tools/filesystem/search_content.py +0 -275
  75. hanzo_mcp/tools/filesystem/write_file.py +0 -162
  76. hanzo_mcp/tools/jupyter/notebook_operations.py +0 -514
  77. hanzo_mcp/tools/jupyter/read_notebook.py +0 -165
  78. hanzo_mcp/tools/project/__init__.py +0 -64
  79. hanzo_mcp/tools/project/analysis.py +0 -882
  80. hanzo_mcp/tools/project/base.py +0 -66
  81. hanzo_mcp/tools/project/project_analyze.py +0 -173
  82. hanzo_mcp/tools/shell/run_script.py +0 -215
  83. hanzo_mcp/tools/shell/script_tool.py +0 -244
  84. hanzo_mcp-0.3.4.dist-info/RECORD +0 -53
  85. {hanzo_mcp-0.3.4.dist-info → hanzo_mcp-0.5.0.dist-info}/entry_points.txt +0 -0
  86. {hanzo_mcp-0.3.4.dist-info → hanzo_mcp-0.5.0.dist-info}/licenses/LICENSE +0 -0
  87. {hanzo_mcp-0.3.4.dist-info → hanzo_mcp-0.5.0.dist-info}/top_level.txt +0 -0
hanzo_mcp/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """Hanzo MCP - Implementation of Hanzo capabilities using MCP."""
2
2
 
3
- __version__ = "0.3.4"
3
+ __version__ = "0.5.0"
hanzo_mcp/cli.py CHANGED
@@ -1,41 +1,19 @@
1
- """Command-line interface for the Hanzo MCP server.
2
-
3
- Includes logging configuration and enhanced error handling.
4
- """
1
+ """Command-line interface for the Hanzo MCP server."""
5
2
 
6
3
  import argparse
7
4
  import json
8
- import logging
9
5
  import os
10
6
  import sys
11
7
  from pathlib import Path
12
8
  from typing import Any, cast
13
9
 
14
- from hanzo_mcp import __version__
15
- from hanzo_mcp.tools.common.logging_config import setup_logging
16
-
17
- from hanzo_mcp.server import HanzoServer
10
+ from hanzo_mcp.server import HanzoMCPServer
18
11
 
19
12
 
20
13
  def main() -> None:
21
14
  """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
-
30
15
  parser = argparse.ArgumentParser(
31
- description="MCP server implementing Hanzo capabilities"
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"
16
+ description="MCP server implementing Hanzo AI capabilities"
39
17
  )
40
18
 
41
19
  _ = parser.add_argument(
@@ -45,53 +23,49 @@ def main() -> None:
45
23
  help="Transport protocol to use (default: stdio)",
46
24
  )
47
25
 
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
-
61
26
  _ = parser.add_argument(
62
27
  "--name",
63
- default="claude-code",
64
- help="Name of the MCP server (default: claude-code)",
28
+ default="hanzo-mcp",
29
+ help="Name of the MCP server (default: hanzo-mcp)",
65
30
  )
66
31
 
67
32
  _ = parser.add_argument(
68
33
  "--allow-path",
69
34
  action="append",
70
35
  dest="allowed_paths",
71
- help="Add an allowed path (can be specified multiple times, defaults to user's home directory)",
36
+ help="Add an allowed path (can be specified multiple times)",
72
37
  )
73
38
 
74
39
  _ = parser.add_argument(
75
- "--project-dir", dest="project_dir", help="Set the project directory to analyze"
40
+ "--project",
41
+ action="append",
42
+ dest="project_paths",
43
+ help="Add a project path for prompt generation (can be specified multiple times)",
76
44
  )
77
45
 
78
46
  _ = parser.add_argument(
79
47
  "--agent-model",
80
48
  dest="agent_model",
81
- help="Specify the model name in LiteLLM format (e.g., 'openai/gpt-4o', 'anthropic/claude-3-sonnet')"
49
+ help="Specify the model name in LiteLLM format (e.g., 'openai/gpt-4o', 'anthropic/claude-4-sonnet')",
82
50
  )
83
51
 
84
52
  _ = parser.add_argument(
85
53
  "--agent-max-tokens",
86
54
  dest="agent_max_tokens",
87
55
  type=int,
88
- help="Specify the maximum tokens for agent responses"
56
+ help="Specify the maximum tokens for agent responses",
89
57
  )
90
58
 
91
59
  _ = parser.add_argument(
92
60
  "--agent-api-key",
93
61
  dest="agent_api_key",
94
- help="Specify the API key for the LLM provider (for development/testing only)"
62
+ help="Specify the API key for the LLM provider (for development/testing only)",
63
+ )
64
+
65
+ _ = parser.add_argument(
66
+ "--agent-base-url",
67
+ dest="agent_base_url",
68
+ help="Specify the base URL for the LLM provider API endpoint (e.g., 'http://localhost:1234/v1')",
95
69
  )
96
70
 
97
71
  _ = parser.add_argument(
@@ -99,7 +73,7 @@ def main() -> None:
99
73
  dest="agent_max_iterations",
100
74
  type=int,
101
75
  default=10,
102
- help="Maximum number of iterations for agent (default: 10)"
76
+ help="Maximum number of iterations for agent (default: 10)",
103
77
  )
104
78
 
105
79
  _ = parser.add_argument(
@@ -107,7 +81,7 @@ def main() -> None:
107
81
  dest="agent_max_tool_uses",
108
82
  type=int,
109
83
  default=30,
110
- help="Maximum number of total tool uses for agent (default: 30)"
84
+ help="Maximum number of total tool uses for agent (default: 30)",
111
85
  )
112
86
 
113
87
  _ = parser.add_argument(
@@ -115,39 +89,60 @@ def main() -> None:
115
89
  dest="enable_agent_tool",
116
90
  action="store_true",
117
91
  default=False,
118
- help="Enable the agent tool (disabled by default)"
92
+ help="Enable the agent tool (disabled by default)",
119
93
  )
120
94
 
121
95
  _ = 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)"
96
+ "--command-timeout",
97
+ dest="command_timeout",
98
+ type=float,
99
+ default=120.0,
100
+ help="Default timeout for command execution in seconds (default: 120.0)",
127
101
  )
128
102
 
129
103
  _ = parser.add_argument(
130
- "--disable-file-logging",
131
- dest="disable_file_logging",
104
+ "--disable-write-tools",
105
+ dest="disable_write_tools",
132
106
  action="store_true",
133
107
  default=False,
134
- help="Disable logging to file (logs to console only)"
108
+ help="Disable write tools (edit, write, etc.)",
135
109
  )
136
-
110
+
137
111
  _ = parser.add_argument(
138
- "--enable-console-logging",
139
- dest="enable_console_logging",
112
+ "--disable-search-tools",
113
+ dest="disable_search_tools",
140
114
  action="store_true",
141
115
  default=False,
142
- help="Enable logging to console (stdout/stderr). By default, logs to file only."
116
+ help="Disable search tools (grep, search_content, etc.)",
143
117
  )
144
118
 
145
119
  _ = parser.add_argument(
146
- "--disable-write-tools",
147
- dest="disable_write_tools",
148
- action="store_true",
149
- default=False,
150
- help="Disable write/edit tools (file writing, editing, notebook editing) to use IDE tools instead. Note: Shell commands can still modify files."
120
+ "--host",
121
+ dest="host",
122
+ default="127.0.0.1",
123
+ help="Host for SSE server (default: 127.0.0.1)",
124
+ )
125
+
126
+ _ = parser.add_argument(
127
+ "--port",
128
+ dest="port",
129
+ type=int,
130
+ default=3000,
131
+ help="Port for SSE server (default: 3000)",
132
+ )
133
+
134
+ _ = parser.add_argument(
135
+ "--log-level",
136
+ dest="log_level",
137
+ default="INFO",
138
+ choices=["DEBUG", "INFO", "WARNING", "ERROR"],
139
+ help="Logging level (default: INFO)",
140
+ )
141
+
142
+ _ = parser.add_argument(
143
+ "--project-dir",
144
+ dest="project_dir",
145
+ help="Single project directory (alias for --project)",
151
146
  )
152
147
 
153
148
  _ = parser.add_argument(
@@ -162,117 +157,87 @@ def main() -> None:
162
157
  name: str = cast(str, args.name)
163
158
  install: bool = cast(bool, args.install)
164
159
  transport: str = cast(str, args.transport)
165
- port: int = cast(int, args.port)
166
- host: str = cast(str, args.host)
167
- project_dir: str | None = cast(str | None, args.project_dir)
168
160
  agent_model: str | None = cast(str | None, args.agent_model)
169
161
  agent_max_tokens: int | None = cast(int | None, args.agent_max_tokens)
170
162
  agent_api_key: str | None = cast(str | None, args.agent_api_key)
163
+ agent_base_url: str | None = cast(str | None, args.agent_base_url)
171
164
  agent_max_iterations: int = cast(int, args.agent_max_iterations)
172
165
  agent_max_tool_uses: int = cast(int, args.agent_max_tool_uses)
173
166
  enable_agent_tool: bool = cast(bool, args.enable_agent_tool)
167
+ command_timeout: float = cast(float, args.command_timeout)
174
168
  disable_write_tools: bool = cast(bool, args.disable_write_tools)
169
+ disable_search_tools: bool = cast(bool, args.disable_search_tools)
170
+ host: str = cast(str, args.host)
171
+ port: int = cast(int, args.port)
175
172
  log_level: str = cast(str, args.log_level)
176
- disable_file_logging: bool = cast(bool, args.disable_file_logging)
177
- enable_console_logging: bool = cast(bool, args.enable_console_logging)
173
+ project_dir: str | None = cast(str | None, args.project_dir)
178
174
  allowed_paths: list[str] = (
179
175
  cast(list[str], args.allowed_paths) if args.allowed_paths else []
180
176
  )
177
+ project_paths: list[str] = (
178
+ cast(list[str], args.project_paths) if args.project_paths else []
179
+ )
181
180
 
182
- # Setup logging
183
- # Ensure absolutely NO logging when using stdio transport to avoid protocol corruption
184
- # For sse transport, use file logging by default and console logging only if explicitly requested
185
- # Only set up logging if not using stdio transport or explicitly requested
186
- if transport != "stdio" or (enable_console_logging or not disable_file_logging):
187
- setup_logging(
188
- log_level=log_level,
189
- log_to_file=not disable_file_logging and transport != "stdio", # Disable file logging for stdio transport
190
- log_to_console=enable_console_logging and transport != "stdio", # Only enable console logging if requested AND not using stdio
191
- transport=transport, # Pass the transport to ensure it's properly handled
192
- testing="pytest" in sys.modules
193
- )
194
- logger.debug(f"Hanzo MCP CLI started with arguments: {args}")
195
- # No logging setup at all for stdio transport unless explicitly requested
196
-
181
+ # Handle project_dir parameter (add to both allowed_paths and project_paths)
182
+ if project_dir:
183
+ if project_dir not in allowed_paths:
184
+ allowed_paths.append(project_dir)
185
+ if project_dir not in project_paths:
186
+ project_paths.append(project_dir)
197
187
 
198
188
  if install:
199
- install_claude_desktop_config(name, allowed_paths, host, port)
189
+ install_claude_desktop_config(
190
+ name,
191
+ allowed_paths,
192
+ disable_write_tools,
193
+ disable_search_tools,
194
+ host,
195
+ port
196
+ )
200
197
  return
201
198
 
202
- # If no allowed paths are specified, use the user's home directory
199
+ # If no allowed paths are specified, use the current directory
203
200
  if not allowed_paths:
204
- allowed_paths = [str(Path.home())]
205
- logger.info(f"No allowed paths specified, using home directory: {allowed_paths[0]}")
206
-
207
- # If project directory is specified, add it to allowed paths
208
- if project_dir and project_dir not in allowed_paths:
209
- allowed_paths.append(project_dir)
210
-
211
- # Set project directory as initial working directory if provided
212
- if project_dir:
213
- # Expand user paths
214
- project_dir = os.path.expanduser(project_dir)
215
- # Make absolute
216
- if not os.path.isabs(project_dir):
217
- project_dir = os.path.abspath(project_dir)
218
-
219
- # If no specific project directory, use the first allowed path
220
- elif allowed_paths:
221
- project_dir = allowed_paths[0]
222
-
223
- # Run the server - only log if not using stdio transport or logging is explicitly enabled
224
- if transport != "stdio" or (enable_console_logging or not disable_file_logging):
225
- logger.info(f"Starting Hanzo MCP server with name: {name}")
226
- logger.debug(f"Allowed paths: {allowed_paths}")
227
- logger.debug(f"Project directory: {project_dir}")
228
-
229
- try:
230
- server = HanzoServer(
231
- name=name,
232
- allowed_paths=allowed_paths,
233
- project_dir=project_dir, # Pass project_dir for initial working directory
234
- agent_model=agent_model,
235
- agent_max_tokens=agent_max_tokens,
236
- agent_api_key=agent_api_key,
237
- agent_max_iterations=agent_max_iterations,
238
- agent_max_tool_uses=agent_max_tool_uses,
239
- enable_agent_tool=enable_agent_tool,
240
- disable_write_tools=disable_write_tools,
241
- host=host,
242
- port=port
243
- )
244
-
245
- # Only log if not using stdio transport or logging is explicitly enabled
246
- if transport != "stdio" or (enable_console_logging or not disable_file_logging):
247
- logger.info(f"Server initialized successfully, running with transport: {transport}")
248
-
249
- # Transport will be automatically cast to Literal['stdio', 'sse'] by the server
250
- server.run(transport=transport)
251
- except Exception as e:
252
- # Only log if not using stdio transport or logging is explicitly enabled
253
- if transport != "stdio" or (enable_console_logging or not disable_file_logging):
254
- logger.error(f"Error starting server: {str(e)}")
255
- logger.exception("Server startup failed with exception:")
256
- # For stdio transport, we want a clean exception without any logging
257
- # Re-raise the exception for proper error handling
258
- raise
201
+ allowed_paths = [os.getcwd()]
202
+
203
+ # Run the server
204
+ server = HanzoMCPServer(
205
+ name=name,
206
+ allowed_paths=allowed_paths,
207
+ project_dir=project_dir,
208
+ agent_model=agent_model,
209
+ agent_max_tokens=agent_max_tokens,
210
+ agent_api_key=agent_api_key,
211
+ agent_base_url=agent_base_url,
212
+ agent_max_iterations=agent_max_iterations,
213
+ agent_max_tool_uses=agent_max_tool_uses,
214
+ enable_agent_tool=enable_agent_tool,
215
+ disable_write_tools=disable_write_tools,
216
+ disable_search_tools=disable_search_tools,
217
+ host=host,
218
+ port=port,
219
+ )
220
+ # Transport will be automatically cast to Literal['stdio', 'sse'] by the server
221
+ server.run(transport=transport)
259
222
 
260
223
 
261
224
  def install_claude_desktop_config(
262
- name: str = "claude-code", allowed_paths: list[str] | None = None,
225
+ name: str = "hanzo-mcp",
226
+ allowed_paths: list[str] | None = None,
263
227
  disable_write_tools: bool = False,
264
- host: str = "0.0.0.0", port: int = 3001
228
+ disable_search_tools: bool = False,
229
+ host: str = "127.0.0.1",
230
+ port: int = 3000,
265
231
  ) -> None:
266
232
  """Install the server configuration in Claude Desktop.
267
233
 
268
234
  Args:
269
235
  name: The name to use for the server in the config
270
236
  allowed_paths: Optional list of paths to allow
271
- disable_write_tools: Whether to disable write/edit tools (file writing, editing, notebook editing)
272
- to use IDE tools instead. Note: Shell commands can still modify files.
273
- (default: False)
274
- host: Host to bind to for SSE transport (default: '0.0.0.0')
275
- port: Port to use for SSE transport (default: 3001)
237
+ disable_write_tools: Whether to disable write tools
238
+ disable_search_tools: Whether to disable search tools
239
+ host: Host for SSE server
240
+ port: Port for SSE server
276
241
  """
277
242
  # Find the Claude Desktop config directory
278
243
  home: Path = Path.home()
@@ -303,21 +268,16 @@ def install_claude_desktop_config(
303
268
  # Allow home directory by default
304
269
  args.extend(["--allow-path", str(home)])
305
270
 
306
- # Add host and port
307
- args.extend(["--host", host])
308
- args.extend(["--port", str(port)])
309
-
310
- # Add disable_write_tools flag if specified
271
+ # Add tool disable flags if specified
311
272
  if disable_write_tools:
312
273
  args.append("--disable-write-tools")
313
-
314
- # Add host and port
315
- args.extend(["--host", host])
316
- args.extend(["--port", str(port)])
274
+
275
+ if disable_search_tools:
276
+ args.append("--disable-search-tools")
317
277
 
318
278
  # Create config object
319
279
  config: dict[str, Any] = {
320
- "mcpServers": {name: {"command": str(script_path), "args": args}}
280
+ "mcpServers": {name: {"command": script_path.as_posix(), "args": args}}
321
281
  }
322
282
 
323
283
  # Check if the file already exists
@@ -349,7 +309,10 @@ def install_claude_desktop_config(
349
309
  print(f"- {path}")
350
310
  else:
351
311
  print(f"\nDefault allowed path: {home}")
352
- print("\nYou can modify allowed paths in the config file directly.")
312
+
313
+ print(
314
+ "\nYou can modify allowed paths in the config file directly."
315
+ )
353
316
  print("Restart Claude Desktop for changes to take effect.")
354
317
 
355
318