hanzo-mcp 0.5.1__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 (54) hide show
  1. hanzo_mcp/__init__.py +1 -1
  2. hanzo_mcp/tools/__init__.py +135 -4
  3. hanzo_mcp/tools/common/base.py +7 -2
  4. hanzo_mcp/tools/common/stats.py +261 -0
  5. hanzo_mcp/tools/common/tool_disable.py +144 -0
  6. hanzo_mcp/tools/common/tool_enable.py +182 -0
  7. hanzo_mcp/tools/common/tool_list.py +263 -0
  8. hanzo_mcp/tools/database/__init__.py +71 -0
  9. hanzo_mcp/tools/database/database_manager.py +246 -0
  10. hanzo_mcp/tools/database/graph_add.py +257 -0
  11. hanzo_mcp/tools/database/graph_query.py +536 -0
  12. hanzo_mcp/tools/database/graph_remove.py +267 -0
  13. hanzo_mcp/tools/database/graph_search.py +348 -0
  14. hanzo_mcp/tools/database/graph_stats.py +345 -0
  15. hanzo_mcp/tools/database/sql_query.py +229 -0
  16. hanzo_mcp/tools/database/sql_search.py +296 -0
  17. hanzo_mcp/tools/database/sql_stats.py +254 -0
  18. hanzo_mcp/tools/editor/__init__.py +11 -0
  19. hanzo_mcp/tools/editor/neovim_command.py +272 -0
  20. hanzo_mcp/tools/editor/neovim_edit.py +290 -0
  21. hanzo_mcp/tools/editor/neovim_session.py +356 -0
  22. hanzo_mcp/tools/filesystem/__init__.py +15 -5
  23. hanzo_mcp/tools/filesystem/{unified_search.py → batch_search.py} +254 -131
  24. hanzo_mcp/tools/filesystem/find_files.py +348 -0
  25. hanzo_mcp/tools/filesystem/git_search.py +505 -0
  26. hanzo_mcp/tools/llm/__init__.py +27 -0
  27. hanzo_mcp/tools/llm/consensus_tool.py +351 -0
  28. hanzo_mcp/tools/llm/llm_manage.py +413 -0
  29. hanzo_mcp/tools/llm/llm_tool.py +346 -0
  30. hanzo_mcp/tools/llm/provider_tools.py +412 -0
  31. hanzo_mcp/tools/mcp/__init__.py +11 -0
  32. hanzo_mcp/tools/mcp/mcp_add.py +263 -0
  33. hanzo_mcp/tools/mcp/mcp_remove.py +127 -0
  34. hanzo_mcp/tools/mcp/mcp_stats.py +165 -0
  35. hanzo_mcp/tools/shell/__init__.py +27 -7
  36. hanzo_mcp/tools/shell/logs.py +265 -0
  37. hanzo_mcp/tools/shell/npx.py +194 -0
  38. hanzo_mcp/tools/shell/npx_background.py +254 -0
  39. hanzo_mcp/tools/shell/pkill.py +262 -0
  40. hanzo_mcp/tools/shell/processes.py +279 -0
  41. hanzo_mcp/tools/shell/run_background.py +326 -0
  42. hanzo_mcp/tools/shell/uvx.py +187 -0
  43. hanzo_mcp/tools/shell/uvx_background.py +249 -0
  44. hanzo_mcp/tools/vector/__init__.py +5 -0
  45. hanzo_mcp/tools/vector/git_ingester.py +3 -0
  46. hanzo_mcp/tools/vector/index_tool.py +358 -0
  47. hanzo_mcp/tools/vector/infinity_store.py +98 -0
  48. hanzo_mcp/tools/vector/vector_search.py +11 -6
  49. {hanzo_mcp-0.5.1.dist-info → hanzo_mcp-0.5.2.dist-info}/METADATA +1 -1
  50. {hanzo_mcp-0.5.1.dist-info → hanzo_mcp-0.5.2.dist-info}/RECORD +54 -16
  51. {hanzo_mcp-0.5.1.dist-info → hanzo_mcp-0.5.2.dist-info}/WHEEL +0 -0
  52. {hanzo_mcp-0.5.1.dist-info → hanzo_mcp-0.5.2.dist-info}/entry_points.txt +0 -0
  53. {hanzo_mcp-0.5.1.dist-info → hanzo_mcp-0.5.2.dist-info}/licenses/LICENSE +0 -0
  54. {hanzo_mcp-0.5.1.dist-info → hanzo_mcp-0.5.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,182 @@
1
+ """Enable tools dynamically."""
2
+
3
+ import json
4
+ from typing import Annotated, TypedDict, Unpack, final, override
5
+ from pathlib import Path
6
+
7
+ from fastmcp import Context as MCPContext
8
+ from pydantic import Field
9
+
10
+ from hanzo_mcp.tools.common.base import BaseTool
11
+ from hanzo_mcp.tools.common.context import create_tool_context
12
+
13
+
14
+ ToolName = Annotated[
15
+ str,
16
+ Field(
17
+ description="Name of the tool to enable (e.g., 'grep', 'vector_search')",
18
+ min_length=1,
19
+ ),
20
+ ]
21
+
22
+ Persist = Annotated[
23
+ bool,
24
+ Field(
25
+ description="Persist the change to config file",
26
+ default=True,
27
+ ),
28
+ ]
29
+
30
+
31
+ class ToolEnableParams(TypedDict, total=False):
32
+ """Parameters for tool enable."""
33
+
34
+ tool: str
35
+ persist: bool
36
+
37
+
38
+ @final
39
+ class ToolEnableTool(BaseTool):
40
+ """Tool for enabling other tools dynamically."""
41
+
42
+ # Class variable to track enabled/disabled tools
43
+ _tool_states = {}
44
+ _config_file = Path.home() / ".hanzo" / "mcp" / "tool_states.json"
45
+ _initialized = False
46
+
47
+ def __init__(self):
48
+ """Initialize the tool enable tool."""
49
+ if not ToolEnableTool._initialized:
50
+ self._load_states()
51
+ ToolEnableTool._initialized = True
52
+
53
+ @classmethod
54
+ def _load_states(cls):
55
+ """Load tool states from config file."""
56
+ if cls._config_file.exists():
57
+ try:
58
+ with open(cls._config_file, 'r') as f:
59
+ cls._tool_states = json.load(f)
60
+ except Exception:
61
+ cls._tool_states = {}
62
+ else:
63
+ # Default all tools to enabled
64
+ cls._tool_states = {}
65
+
66
+ @classmethod
67
+ def _save_states(cls):
68
+ """Save tool states to config file."""
69
+ cls._config_file.parent.mkdir(parents=True, exist_ok=True)
70
+ with open(cls._config_file, 'w') as f:
71
+ json.dump(cls._tool_states, f, indent=2)
72
+
73
+ @classmethod
74
+ def is_tool_enabled(cls, tool_name: str) -> bool:
75
+ """Check if a tool is enabled.
76
+
77
+ Args:
78
+ tool_name: Name of the tool
79
+
80
+ Returns:
81
+ True if enabled (default), False if explicitly disabled
82
+ """
83
+ # Load states if not initialized
84
+ if not cls._initialized:
85
+ cls._load_states()
86
+ cls._initialized = True
87
+
88
+ # Default to enabled if not in states
89
+ return cls._tool_states.get(tool_name, True)
90
+
91
+ @classmethod
92
+ def get_all_states(cls) -> dict:
93
+ """Get all tool states."""
94
+ if not cls._initialized:
95
+ cls._load_states()
96
+ cls._initialized = True
97
+ return cls._tool_states.copy()
98
+
99
+ @property
100
+ @override
101
+ def name(self) -> str:
102
+ """Get the tool name."""
103
+ return "tool_enable"
104
+
105
+ @property
106
+ @override
107
+ def description(self) -> str:
108
+ """Get the tool description."""
109
+ return """Enable tools that have been disabled.
110
+
111
+ This allows you to re-enable tools that were previously disabled.
112
+ Changes are persisted by default.
113
+
114
+ Examples:
115
+ - tool_enable --tool grep
116
+ - tool_enable --tool vector_search
117
+ - tool_enable --tool uvx_background --no-persist
118
+
119
+ Use 'tool_list' to see all available tools and their status.
120
+ """
121
+
122
+ @override
123
+ async def call(
124
+ self,
125
+ ctx: MCPContext,
126
+ **params: Unpack[ToolEnableParams],
127
+ ) -> str:
128
+ """Enable a tool.
129
+
130
+ Args:
131
+ ctx: MCP context
132
+ **params: Tool parameters
133
+
134
+ Returns:
135
+ Result of enabling the tool
136
+ """
137
+ tool_ctx = create_tool_context(ctx)
138
+ await tool_ctx.set_tool_info(self.name)
139
+
140
+ # Extract parameters
141
+ tool_name = params.get("tool")
142
+ if not tool_name:
143
+ return "Error: tool name is required"
144
+
145
+ persist = params.get("persist", True)
146
+
147
+ # Check current state
148
+ was_enabled = self.is_tool_enabled(tool_name)
149
+
150
+ if was_enabled:
151
+ return f"Tool '{tool_name}' is already enabled."
152
+
153
+ # Enable the tool
154
+ self._tool_states[tool_name] = True
155
+
156
+ # Persist if requested
157
+ if persist:
158
+ self._save_states()
159
+ await tool_ctx.info(f"Enabled tool '{tool_name}' (persisted)")
160
+ else:
161
+ await tool_ctx.info(f"Enabled tool '{tool_name}' (temporary)")
162
+
163
+ output = [
164
+ f"Successfully enabled tool '{tool_name}'",
165
+ "",
166
+ "The tool is now available for use.",
167
+ ]
168
+
169
+ if not persist:
170
+ output.append("Note: This change is temporary and will be lost on restart.")
171
+
172
+ # Count enabled/disabled tools
173
+ disabled_count = sum(1 for enabled in self._tool_states.values() if not enabled)
174
+ if disabled_count > 0:
175
+ output.append(f"\nCurrently disabled tools: {disabled_count}")
176
+ output.append("Use 'tool_list --disabled' to see them.")
177
+
178
+ return "\n".join(output)
179
+
180
+ def register(self, mcp_server) -> None:
181
+ """Register this tool with the MCP server."""
182
+ pass
@@ -0,0 +1,263 @@
1
+ """List all available tools and their status."""
2
+
3
+ from typing import Annotated, TypedDict, Unpack, final, override, Optional
4
+
5
+ from fastmcp import Context as MCPContext
6
+ from pydantic import Field
7
+
8
+ from hanzo_mcp.tools.common.base import BaseTool
9
+ from hanzo_mcp.tools.common.context import create_tool_context
10
+ from hanzo_mcp.tools.common.tool_enable import ToolEnableTool
11
+
12
+
13
+ ShowDisabled = Annotated[
14
+ bool,
15
+ Field(
16
+ description="Show only disabled tools",
17
+ default=False,
18
+ ),
19
+ ]
20
+
21
+ ShowEnabled = Annotated[
22
+ bool,
23
+ Field(
24
+ description="Show only enabled tools",
25
+ default=False,
26
+ ),
27
+ ]
28
+
29
+ Category = Annotated[
30
+ Optional[str],
31
+ Field(
32
+ description="Filter by category (filesystem, shell, database, etc.)",
33
+ default=None,
34
+ ),
35
+ ]
36
+
37
+
38
+ class ToolListParams(TypedDict, total=False):
39
+ """Parameters for tool list."""
40
+
41
+ show_disabled: bool
42
+ show_enabled: bool
43
+ category: Optional[str]
44
+
45
+
46
+ @final
47
+ class ToolListTool(BaseTool):
48
+ """Tool for listing all available tools and their status."""
49
+
50
+ # Tool information organized by category
51
+ TOOL_INFO = {
52
+ "filesystem": [
53
+ ("read", "Read contents of files"),
54
+ ("write", "Write contents to files"),
55
+ ("edit", "Edit specific parts of files"),
56
+ ("multi_edit", "Make multiple edits to a file"),
57
+ ("directory_tree", "Display directory structure"),
58
+ ("grep", "Search file contents with patterns"),
59
+ ("grep_ast", "Search code with AST patterns"),
60
+ ("git_search", "Search git history"),
61
+ ("batch_search", "Run multiple searches in parallel"),
62
+ ("find_files", "Find files by name pattern"),
63
+ ("content_replace", "Replace content across files"),
64
+ ],
65
+ "shell": [
66
+ ("run_command", "Execute shell commands"),
67
+ ("run_background", "Run commands in background"),
68
+ ("processes", "List background processes"),
69
+ ("pkill", "Kill background processes"),
70
+ ("logs", "View process logs"),
71
+ ("uvx", "Run Python packages"),
72
+ ("uvx_background", "Run Python servers"),
73
+ ("npx", "Run Node.js packages"),
74
+ ("npx_background", "Run Node.js servers"),
75
+ ],
76
+ "database": [
77
+ ("sql_query", "Execute SQL queries"),
78
+ ("sql_search", "Search in SQLite databases"),
79
+ ("sql_stats", "SQLite database statistics"),
80
+ ("graph_add", "Add nodes/edges to graph"),
81
+ ("graph_remove", "Remove nodes/edges from graph"),
82
+ ("graph_query", "Query graph relationships"),
83
+ ("graph_search", "Search in graph database"),
84
+ ("graph_stats", "Graph database statistics"),
85
+ ],
86
+ "vector": [
87
+ ("vector_index", "Index files into vector store"),
88
+ ("vector_search", "Semantic search in vector store"),
89
+ ],
90
+ "mcp": [
91
+ ("mcp_add", "Add MCP servers"),
92
+ ("mcp_remove", "Remove MCP servers"),
93
+ ("mcp_stats", "MCP server statistics"),
94
+ ],
95
+ "system": [
96
+ ("stats", "System and resource statistics"),
97
+ ("tool_enable", "Enable tools"),
98
+ ("tool_disable", "Disable tools"),
99
+ ("tool_list", "List all tools (this tool)"),
100
+ ],
101
+ "editor": [
102
+ ("neovim_edit", "Open files in Neovim"),
103
+ ("neovim_command", "Execute Neovim commands"),
104
+ ("neovim_session", "Manage Neovim sessions"),
105
+ ],
106
+ "llm": [
107
+ ("llm", "Query any LLM via LiteLLM"),
108
+ ("consensus", "Get consensus from multiple LLMs"),
109
+ ("llm_manage", "Manage LLM providers"),
110
+ ("openai", "Query OpenAI models"),
111
+ ("anthropic", "Query Anthropic Claude models"),
112
+ ("gemini", "Query Google Gemini models"),
113
+ ("groq", "Query Groq fast models"),
114
+ ("mistral", "Query Mistral models"),
115
+ ("perplexity", "Query Perplexity with search"),
116
+ ],
117
+ "other": [
118
+ ("think", "Structured thinking space"),
119
+ ("dispatch_agent", "Delegate tasks to sub-agents"),
120
+ ("todo_read", "Read todo list"),
121
+ ("todo_write", "Write todo list"),
122
+ ("notebook_read", "Read Jupyter notebooks"),
123
+ ("notebook_edit", "Edit Jupyter notebooks"),
124
+ ("batch", "Run multiple tools in parallel"),
125
+ ],
126
+ }
127
+
128
+ def __init__(self):
129
+ """Initialize the tool list tool."""
130
+ pass
131
+
132
+ @property
133
+ @override
134
+ def name(self) -> str:
135
+ """Get the tool name."""
136
+ return "tool_list"
137
+
138
+ @property
139
+ @override
140
+ def description(self) -> str:
141
+ """Get the tool description."""
142
+ return """List all available tools and their current status.
143
+
144
+ Shows:
145
+ - Tool names and descriptions
146
+ - Whether each tool is enabled or disabled
147
+ - Tools organized by category
148
+
149
+ Examples:
150
+ - tool_list # Show all tools
151
+ - tool_list --show-disabled # Show only disabled tools
152
+ - tool_list --show-enabled # Show only enabled tools
153
+ - tool_list --category shell # Show only shell tools
154
+
155
+ Use 'tool_enable' and 'tool_disable' to change tool status.
156
+ """
157
+
158
+ @override
159
+ async def call(
160
+ self,
161
+ ctx: MCPContext,
162
+ **params: Unpack[ToolListParams],
163
+ ) -> str:
164
+ """List all tools.
165
+
166
+ Args:
167
+ ctx: MCP context
168
+ **params: Tool parameters
169
+
170
+ Returns:
171
+ List of tools and their status
172
+ """
173
+ tool_ctx = create_tool_context(ctx)
174
+ await tool_ctx.set_tool_info(self.name)
175
+
176
+ # Extract parameters
177
+ show_disabled = params.get("show_disabled", False)
178
+ show_enabled = params.get("show_enabled", False)
179
+ category_filter = params.get("category")
180
+
181
+ # Get all tool states
182
+ all_states = ToolEnableTool.get_all_states()
183
+
184
+ output = []
185
+
186
+ # Header
187
+ if show_disabled:
188
+ output.append("=== Disabled Tools ===")
189
+ elif show_enabled:
190
+ output.append("=== Enabled Tools ===")
191
+ else:
192
+ output.append("=== All Available Tools ===")
193
+
194
+ if category_filter:
195
+ output.append(f"Category: {category_filter}")
196
+
197
+ output.append("")
198
+
199
+ # Count statistics
200
+ total_tools = 0
201
+ disabled_count = 0
202
+ shown_count = 0
203
+
204
+ # Iterate through categories
205
+ categories = [category_filter] if category_filter and category_filter in self.TOOL_INFO else self.TOOL_INFO.keys()
206
+
207
+ for category in categories:
208
+ if category not in self.TOOL_INFO:
209
+ continue
210
+
211
+ category_tools = self.TOOL_INFO[category]
212
+ category_shown = []
213
+
214
+ for tool_name, description in category_tools:
215
+ total_tools += 1
216
+ is_enabled = ToolEnableTool.is_tool_enabled(tool_name)
217
+
218
+ if not is_enabled:
219
+ disabled_count += 1
220
+
221
+ # Apply filters
222
+ if show_disabled and is_enabled:
223
+ continue
224
+ if show_enabled and not is_enabled:
225
+ continue
226
+
227
+ status = "✅" if is_enabled else "❌"
228
+ category_shown.append((tool_name, description, status))
229
+ shown_count += 1
230
+
231
+ # Show category if it has tools
232
+ if category_shown:
233
+ output.append(f"=== {category.title()} Tools ===")
234
+
235
+ # Find max tool name length for alignment
236
+ max_name_len = max(len(name) for name, _, _ in category_shown)
237
+
238
+ for tool_name, description, status in category_shown:
239
+ output.append(f"{status} {tool_name.ljust(max_name_len)} - {description}")
240
+
241
+ output.append("")
242
+
243
+ # Summary
244
+ if not show_disabled and not show_enabled:
245
+ output.append("=== Summary ===")
246
+ output.append(f"Total tools: {total_tools}")
247
+ output.append(f"Enabled: {total_tools - disabled_count}")
248
+ output.append(f"Disabled: {disabled_count}")
249
+ else:
250
+ output.append(f"Showing {shown_count} tool(s)")
251
+
252
+ if disabled_count > 0 and not show_disabled:
253
+ output.append("\nUse 'tool_list --show-disabled' to see disabled tools.")
254
+ output.append("Use 'tool_enable --tool <name>' to enable a tool.")
255
+
256
+ if show_disabled:
257
+ output.append("\nUse 'tool_enable --tool <name>' to enable these tools.")
258
+
259
+ return "\n".join(output)
260
+
261
+ def register(self, mcp_server) -> None:
262
+ """Register this tool with the MCP server."""
263
+ pass
@@ -0,0 +1,71 @@
1
+ """Database tools for Hanzo MCP.
2
+
3
+ This package provides tools for working with embedded SQLite databases
4
+ and graph databases in projects.
5
+ """
6
+
7
+ from hanzo_mcp.tools.common.base import BaseTool
8
+ from hanzo_mcp.tools.common.permissions import PermissionManager
9
+ from fastmcp import FastMCP
10
+
11
+ # Import database tools
12
+ from .sql_query import SqlQueryTool
13
+ from .sql_search import SqlSearchTool
14
+ from .sql_stats import SqlStatsTool
15
+ from .graph_add import GraphAddTool
16
+ from .graph_remove import GraphRemoveTool
17
+ from .graph_query import GraphQueryTool
18
+ from .graph_search import GraphSearchTool
19
+ from .graph_stats import GraphStatsTool
20
+ from .database_manager import DatabaseManager
21
+
22
+ __all__ = [
23
+ "register_database_tools",
24
+ "DatabaseManager",
25
+ "SqlQueryTool",
26
+ "SqlSearchTool",
27
+ "SqlStatsTool",
28
+ "GraphAddTool",
29
+ "GraphRemoveTool",
30
+ "GraphQueryTool",
31
+ "GraphSearchTool",
32
+ "GraphStatsTool",
33
+ ]
34
+
35
+
36
+ def register_database_tools(
37
+ mcp_server: FastMCP,
38
+ permission_manager: PermissionManager,
39
+ db_manager: DatabaseManager | None = None,
40
+ ) -> list[BaseTool]:
41
+ """Register database tools with the MCP server.
42
+
43
+ Args:
44
+ mcp_server: The FastMCP server instance
45
+ permission_manager: Permission manager for access control
46
+ db_manager: Optional database manager instance
47
+
48
+ Returns:
49
+ List of registered tools
50
+ """
51
+ # Create database manager if not provided
52
+ if db_manager is None:
53
+ db_manager = DatabaseManager(permission_manager)
54
+
55
+ # Create tool instances
56
+ tools = [
57
+ SqlQueryTool(permission_manager, db_manager),
58
+ SqlSearchTool(permission_manager, db_manager),
59
+ SqlStatsTool(permission_manager, db_manager),
60
+ GraphAddTool(permission_manager, db_manager),
61
+ GraphRemoveTool(permission_manager, db_manager),
62
+ GraphQueryTool(permission_manager, db_manager),
63
+ GraphSearchTool(permission_manager, db_manager),
64
+ GraphStatsTool(permission_manager, db_manager),
65
+ ]
66
+
67
+ # Register with MCP server
68
+ from hanzo_mcp.tools.common.base import ToolRegistry
69
+ ToolRegistry.register_tools(mcp_server, tools)
70
+
71
+ return tools