hanzo-mcp 0.5.0__py3-none-any.whl → 0.5.2__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 (60) hide show
  1. hanzo_mcp/__init__.py +1 -1
  2. hanzo_mcp/config/settings.py +61 -0
  3. hanzo_mcp/tools/__init__.py +158 -12
  4. hanzo_mcp/tools/common/base.py +7 -2
  5. hanzo_mcp/tools/common/config_tool.py +396 -0
  6. hanzo_mcp/tools/common/stats.py +261 -0
  7. hanzo_mcp/tools/common/tool_disable.py +144 -0
  8. hanzo_mcp/tools/common/tool_enable.py +182 -0
  9. hanzo_mcp/tools/common/tool_list.py +263 -0
  10. hanzo_mcp/tools/database/__init__.py +71 -0
  11. hanzo_mcp/tools/database/database_manager.py +246 -0
  12. hanzo_mcp/tools/database/graph_add.py +257 -0
  13. hanzo_mcp/tools/database/graph_query.py +536 -0
  14. hanzo_mcp/tools/database/graph_remove.py +267 -0
  15. hanzo_mcp/tools/database/graph_search.py +348 -0
  16. hanzo_mcp/tools/database/graph_stats.py +345 -0
  17. hanzo_mcp/tools/database/sql_query.py +229 -0
  18. hanzo_mcp/tools/database/sql_search.py +296 -0
  19. hanzo_mcp/tools/database/sql_stats.py +254 -0
  20. hanzo_mcp/tools/editor/__init__.py +11 -0
  21. hanzo_mcp/tools/editor/neovim_command.py +272 -0
  22. hanzo_mcp/tools/editor/neovim_edit.py +290 -0
  23. hanzo_mcp/tools/editor/neovim_session.py +356 -0
  24. hanzo_mcp/tools/filesystem/__init__.py +20 -1
  25. hanzo_mcp/tools/filesystem/batch_search.py +812 -0
  26. hanzo_mcp/tools/filesystem/find_files.py +348 -0
  27. hanzo_mcp/tools/filesystem/git_search.py +505 -0
  28. hanzo_mcp/tools/llm/__init__.py +27 -0
  29. hanzo_mcp/tools/llm/consensus_tool.py +351 -0
  30. hanzo_mcp/tools/llm/llm_manage.py +413 -0
  31. hanzo_mcp/tools/llm/llm_tool.py +346 -0
  32. hanzo_mcp/tools/llm/provider_tools.py +412 -0
  33. hanzo_mcp/tools/mcp/__init__.py +11 -0
  34. hanzo_mcp/tools/mcp/mcp_add.py +263 -0
  35. hanzo_mcp/tools/mcp/mcp_remove.py +127 -0
  36. hanzo_mcp/tools/mcp/mcp_stats.py +165 -0
  37. hanzo_mcp/tools/shell/__init__.py +27 -7
  38. hanzo_mcp/tools/shell/logs.py +265 -0
  39. hanzo_mcp/tools/shell/npx.py +194 -0
  40. hanzo_mcp/tools/shell/npx_background.py +254 -0
  41. hanzo_mcp/tools/shell/pkill.py +262 -0
  42. hanzo_mcp/tools/shell/processes.py +279 -0
  43. hanzo_mcp/tools/shell/run_background.py +326 -0
  44. hanzo_mcp/tools/shell/uvx.py +187 -0
  45. hanzo_mcp/tools/shell/uvx_background.py +249 -0
  46. hanzo_mcp/tools/vector/__init__.py +21 -12
  47. hanzo_mcp/tools/vector/ast_analyzer.py +459 -0
  48. hanzo_mcp/tools/vector/git_ingester.py +485 -0
  49. hanzo_mcp/tools/vector/index_tool.py +358 -0
  50. hanzo_mcp/tools/vector/infinity_store.py +465 -1
  51. hanzo_mcp/tools/vector/mock_infinity.py +162 -0
  52. hanzo_mcp/tools/vector/vector_index.py +7 -6
  53. hanzo_mcp/tools/vector/vector_search.py +22 -7
  54. {hanzo_mcp-0.5.0.dist-info → hanzo_mcp-0.5.2.dist-info}/METADATA +68 -20
  55. hanzo_mcp-0.5.2.dist-info/RECORD +106 -0
  56. hanzo_mcp-0.5.0.dist-info/RECORD +0 -63
  57. {hanzo_mcp-0.5.0.dist-info → hanzo_mcp-0.5.2.dist-info}/WHEEL +0 -0
  58. {hanzo_mcp-0.5.0.dist-info → hanzo_mcp-0.5.2.dist-info}/entry_points.txt +0 -0
  59. {hanzo_mcp-0.5.0.dist-info → hanzo_mcp-0.5.2.dist-info}/licenses/LICENSE +0 -0
  60. {hanzo_mcp-0.5.0.dist-info → hanzo_mcp-0.5.2.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.5.0"
3
+ __version__ = "0.5.2"
@@ -280,6 +280,67 @@ def get_project_config_path(project_dir: Optional[str] = None) -> Optional[Path]
280
280
  return None
281
281
 
282
282
 
283
+ def ensure_project_hanzo_dir(project_dir: str) -> Path:
284
+ """Ensure .hanzo directory exists in project and return its path."""
285
+ project_path = Path(project_dir)
286
+ hanzo_dir = project_path / ".hanzo"
287
+ hanzo_dir.mkdir(exist_ok=True)
288
+
289
+ # Create default structure
290
+ (hanzo_dir / "db").mkdir(exist_ok=True) # Vector database
291
+
292
+ # Create default project config if it doesn't exist
293
+ config_path = hanzo_dir / "mcp-settings.json"
294
+ if not config_path.exists():
295
+ default_project_config = {
296
+ "name": project_path.name,
297
+ "root_path": str(project_path),
298
+ "rules": [
299
+ "Follow project-specific coding standards",
300
+ "Test all changes before committing",
301
+ "Update documentation for new features"
302
+ ],
303
+ "workflows": {
304
+ "development": {
305
+ "steps": ["edit", "test", "commit"],
306
+ "tools": ["read", "write", "edit", "run_command"]
307
+ },
308
+ "documentation": {
309
+ "steps": ["read", "analyze", "write"],
310
+ "tools": ["read", "write", "vector_search"]
311
+ }
312
+ },
313
+ "tasks": [],
314
+ "enabled_tools": {},
315
+ "disabled_tools": [],
316
+ "mcp_servers": []
317
+ }
318
+
319
+ with open(config_path, 'w') as f:
320
+ json.dump(default_project_config, f, indent=2)
321
+
322
+ return hanzo_dir
323
+
324
+
325
+ def detect_project_from_path(file_path: str) -> Optional[Dict[str, str]]:
326
+ """Detect project information from a file path by looking for LLM.md."""
327
+ path = Path(file_path).resolve()
328
+ current_path = path.parent if path.is_file() else path
329
+
330
+ while current_path != current_path.parent: # Stop at filesystem root
331
+ llm_md_path = current_path / "LLM.md"
332
+ if llm_md_path.exists():
333
+ return {
334
+ "name": current_path.name,
335
+ "root_path": str(current_path),
336
+ "llm_md_path": str(llm_md_path),
337
+ "hanzo_dir": str(ensure_project_hanzo_dir(str(current_path)))
338
+ }
339
+ current_path = current_path.parent
340
+
341
+ return None
342
+
343
+
283
344
  def load_settings(
284
345
  project_dir: Optional[str] = None,
285
346
  config_overrides: Optional[Dict[str, Any]] = None
@@ -14,13 +14,22 @@ from fastmcp import FastMCP
14
14
  from hanzo_mcp.tools.agent import register_agent_tools
15
15
  from hanzo_mcp.tools.common import register_batch_tool, register_thinking_tool
16
16
  from hanzo_mcp.tools.common.base import BaseTool
17
-
18
17
  from hanzo_mcp.tools.common.permissions import PermissionManager
18
+ from hanzo_mcp.tools.common.tool_enable import ToolEnableTool
19
+ from hanzo_mcp.tools.common.tool_disable import ToolDisableTool
20
+ from hanzo_mcp.tools.common.tool_list import ToolListTool
21
+ from hanzo_mcp.tools.common.stats import StatsTool
19
22
  from hanzo_mcp.tools.filesystem import register_filesystem_tools
20
23
  from hanzo_mcp.tools.jupyter import register_jupyter_tools
21
24
  from hanzo_mcp.tools.shell import register_shell_tools
22
25
  from hanzo_mcp.tools.todo import register_todo_tools
23
26
  from hanzo_mcp.tools.vector import register_vector_tools
27
+ from hanzo_mcp.tools.database import register_database_tools, DatabaseManager
28
+ from hanzo_mcp.tools.mcp.mcp_add import McpAddTool
29
+ from hanzo_mcp.tools.mcp.mcp_remove import McpRemoveTool
30
+ from hanzo_mcp.tools.mcp.mcp_stats import McpStatsTool
31
+ from hanzo_mcp.tools.editor import NeovimEditTool, NeovimCommandTool, NeovimSessionTool
32
+ from hanzo_mcp.tools.llm import LLMTool, ConsensusTool, LLMManageTool, create_provider_tools
24
33
 
25
34
 
26
35
  def register_all_tools(
@@ -76,13 +85,38 @@ def register_all_tools(
76
85
  "directory_tree": is_tool_enabled("directory_tree", True),
77
86
  "grep": is_tool_enabled("grep", not disable_search_tools),
78
87
  "grep_ast": is_tool_enabled("grep_ast", not disable_search_tools),
88
+ "git_search": is_tool_enabled("git_search", not disable_search_tools),
79
89
  "content_replace": is_tool_enabled("content_replace", not disable_write_tools),
90
+ "batch_search": is_tool_enabled("batch_search", not disable_search_tools),
91
+ "find_files": is_tool_enabled("find_files", True),
92
+ }
93
+
94
+ # Vector tools setup (needed for unified search)
95
+ project_manager = None
96
+ vector_enabled = {
97
+ "vector_index": is_tool_enabled("vector_index", False),
98
+ "vector_search": is_tool_enabled("vector_search", False),
80
99
  }
81
100
 
101
+ # Create project manager if vector tools or batch_search are enabled
102
+ if any(vector_enabled.values()) or filesystem_enabled.get("batch_search", False):
103
+ if vector_config:
104
+ from hanzo_mcp.tools.vector.project_manager import ProjectVectorManager
105
+ search_paths = [str(path) for path in permission_manager.allowed_paths]
106
+ project_manager = ProjectVectorManager(
107
+ global_db_path=vector_config.get("data_path"),
108
+ embedding_model=vector_config.get("embedding_model", "text-embedding-3-small"),
109
+ dimension=vector_config.get("dimension", 1536),
110
+ )
111
+ # Auto-detect projects from search paths
112
+ detected_projects = project_manager.detect_projects(search_paths)
113
+ print(f"Detected {len(detected_projects)} projects with LLM.md files")
114
+
82
115
  filesystem_tools = register_filesystem_tools(
83
116
  mcp_server,
84
117
  permission_manager,
85
118
  enabled_tools=filesystem_enabled,
119
+ project_manager=project_manager,
86
120
  )
87
121
  for tool in filesystem_tools:
88
122
  all_tools[tool.name] = tool
@@ -137,22 +171,14 @@ def register_all_tools(
137
171
  for tool in thinking_tool:
138
172
  all_tools[tool.name] = tool
139
173
 
140
- # Register vector tools if enabled
141
- vector_enabled = {
142
- "vector_index": is_tool_enabled("vector_index", False),
143
- "vector_search": is_tool_enabled("vector_search", False),
144
- }
145
-
146
- if any(vector_enabled.values()) and vector_config:
147
- # Get allowed paths for project detection
148
- search_paths = [str(path) for path in permission_manager.allowed_paths]
149
-
174
+ # Register vector tools if enabled (reuse project_manager if available)
175
+ if any(vector_enabled.values()) and project_manager:
150
176
  vector_tools = register_vector_tools(
151
177
  mcp_server,
152
178
  permission_manager,
153
179
  vector_config=vector_config,
154
180
  enabled_tools=vector_enabled,
155
- search_paths=search_paths,
181
+ project_manager=project_manager,
156
182
  )
157
183
  for tool in vector_tools:
158
184
  all_tools[tool.name] = tool
@@ -160,3 +186,123 @@ def register_all_tools(
160
186
  # Register batch tool if enabled (batch tool is typically always enabled)
161
187
  if is_tool_enabled("batch", True):
162
188
  register_batch_tool(mcp_server, all_tools)
189
+
190
+ # Register database tools if enabled
191
+ db_manager = None
192
+ database_enabled = {
193
+ "sql_query": is_tool_enabled("sql_query", True),
194
+ "sql_search": is_tool_enabled("sql_search", True),
195
+ "sql_stats": is_tool_enabled("sql_stats", True),
196
+ "graph_add": is_tool_enabled("graph_add", True),
197
+ "graph_remove": is_tool_enabled("graph_remove", True),
198
+ "graph_query": is_tool_enabled("graph_query", True),
199
+ "graph_search": is_tool_enabled("graph_search", True),
200
+ "graph_stats": is_tool_enabled("graph_stats", True),
201
+ }
202
+
203
+ if any(database_enabled.values()):
204
+ db_manager = DatabaseManager(permission_manager)
205
+ database_tools = register_database_tools(
206
+ mcp_server,
207
+ permission_manager,
208
+ db_manager=db_manager,
209
+ )
210
+ # Filter based on enabled state
211
+ for tool in database_tools:
212
+ if database_enabled.get(tool.name, True):
213
+ all_tools[tool.name] = tool
214
+
215
+ # Register MCP management tools if enabled
216
+ mcp_enabled = {
217
+ "mcp_add": is_tool_enabled("mcp_add", True),
218
+ "mcp_remove": is_tool_enabled("mcp_remove", True),
219
+ "mcp_stats": is_tool_enabled("mcp_stats", True),
220
+ }
221
+
222
+ if mcp_enabled.get("mcp_add", True):
223
+ tool = McpAddTool()
224
+ tool.register(mcp_server)
225
+ all_tools[tool.name] = tool
226
+
227
+ if mcp_enabled.get("mcp_remove", True):
228
+ tool = McpRemoveTool()
229
+ tool.register(mcp_server)
230
+ all_tools[tool.name] = tool
231
+
232
+ if mcp_enabled.get("mcp_stats", True):
233
+ tool = McpStatsTool()
234
+ tool.register(mcp_server)
235
+ all_tools[tool.name] = tool
236
+
237
+ # Register system tools (always enabled)
238
+ # Tool enable/disable tools
239
+ tool_enable = ToolEnableTool()
240
+ tool_enable.register(mcp_server)
241
+ all_tools[tool_enable.name] = tool_enable
242
+
243
+ tool_disable = ToolDisableTool()
244
+ tool_disable.register(mcp_server)
245
+ all_tools[tool_disable.name] = tool_disable
246
+
247
+ tool_list = ToolListTool()
248
+ tool_list.register(mcp_server)
249
+ all_tools[tool_list.name] = tool_list
250
+
251
+ # Stats tool
252
+ stats_tool = StatsTool(db_manager=db_manager)
253
+ stats_tool.register(mcp_server)
254
+ all_tools[stats_tool.name] = stats_tool
255
+
256
+ # Register editor tools if enabled
257
+ editor_enabled = {
258
+ "neovim_edit": is_tool_enabled("neovim_edit", True),
259
+ "neovim_command": is_tool_enabled("neovim_command", True),
260
+ "neovim_session": is_tool_enabled("neovim_session", True),
261
+ }
262
+
263
+ if editor_enabled.get("neovim_edit", True):
264
+ tool = NeovimEditTool(permission_manager)
265
+ tool.register(mcp_server)
266
+ all_tools[tool.name] = tool
267
+
268
+ if editor_enabled.get("neovim_command", True):
269
+ tool = NeovimCommandTool(permission_manager)
270
+ tool.register(mcp_server)
271
+ all_tools[tool.name] = tool
272
+
273
+ if editor_enabled.get("neovim_session", True):
274
+ tool = NeovimSessionTool()
275
+ tool.register(mcp_server)
276
+ all_tools[tool.name] = tool
277
+
278
+ # Register LLM tools if enabled
279
+ llm_enabled = {
280
+ "llm": is_tool_enabled("llm", True),
281
+ "consensus": is_tool_enabled("consensus", True),
282
+ "llm_manage": is_tool_enabled("llm_manage", True),
283
+ }
284
+
285
+ if llm_enabled.get("llm", True):
286
+ tool = LLMTool()
287
+ if tool.available_providers: # Only register if API keys found
288
+ tool.register(mcp_server)
289
+ all_tools[tool.name] = tool
290
+
291
+ if llm_enabled.get("consensus", True):
292
+ tool = ConsensusTool()
293
+ if tool.llm_tool.available_providers: # Only register if API keys found
294
+ tool.register(mcp_server)
295
+ all_tools[tool.name] = tool
296
+
297
+ if llm_enabled.get("llm_manage", True):
298
+ tool = LLMManageTool()
299
+ tool.register(mcp_server)
300
+ all_tools[tool.name] = tool
301
+
302
+ # Register provider-specific LLM tools
303
+ # These are only registered if their API keys are available
304
+ provider_tools = create_provider_tools()
305
+ for tool in provider_tools:
306
+ if is_tool_enabled(tool.name, True):
307
+ tool.register(mcp_server)
308
+ all_tools[tool.name] = tool
@@ -170,8 +170,13 @@ class ToolRegistry:
170
170
  mcp_server: The FastMCP server instance
171
171
  tool: The tool to register
172
172
  """
173
- # Use the tool's register method which handles all the details
174
- tool.register(mcp_server)
173
+ # Check if tool is enabled before registering
174
+ # Import here to avoid circular imports
175
+ from hanzo_mcp.tools.common.tool_enable import ToolEnableTool
176
+
177
+ if ToolEnableTool.is_tool_enabled(tool.name):
178
+ # Use the tool's register method which handles all the details
179
+ tool.register(mcp_server)
175
180
 
176
181
  @staticmethod
177
182
  def register_tools(mcp_server: FastMCP, tools: list[BaseTool]) -> None: