hanzo-mcp 0.5.1__tar.gz → 0.5.2__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.
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/PKG-INFO +1 -1
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/__init__.py +1 -1
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/__init__.py +135 -4
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/common/base.py +7 -2
- hanzo_mcp-0.5.2/hanzo_mcp/tools/common/stats.py +261 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/common/tool_disable.py +144 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/common/tool_enable.py +182 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/common/tool_list.py +263 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/database/__init__.py +71 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/database/database_manager.py +246 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/database/graph_add.py +257 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/database/graph_query.py +536 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/database/graph_remove.py +267 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/database/graph_search.py +348 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/database/graph_stats.py +345 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/database/sql_query.py +229 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/database/sql_search.py +296 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/database/sql_stats.py +254 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/editor/__init__.py +11 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/editor/neovim_command.py +272 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/editor/neovim_edit.py +290 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/editor/neovim_session.py +356 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/filesystem/__init__.py +15 -5
- hanzo_mcp-0.5.1/hanzo_mcp/tools/filesystem/unified_search.py → hanzo_mcp-0.5.2/hanzo_mcp/tools/filesystem/batch_search.py +254 -131
- hanzo_mcp-0.5.2/hanzo_mcp/tools/filesystem/find_files.py +348 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/filesystem/git_search.py +505 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/llm/__init__.py +27 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/llm/consensus_tool.py +351 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/llm/llm_manage.py +413 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/llm/llm_tool.py +346 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/llm/provider_tools.py +412 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/mcp/__init__.py +11 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/mcp/mcp_add.py +263 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/mcp/mcp_remove.py +127 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/mcp/mcp_stats.py +165 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/shell/__init__.py +27 -7
- hanzo_mcp-0.5.2/hanzo_mcp/tools/shell/logs.py +265 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/shell/npx.py +194 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/shell/npx_background.py +254 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/shell/pkill.py +262 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/shell/processes.py +279 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/shell/run_background.py +326 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/shell/uvx.py +187 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/shell/uvx_background.py +249 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/vector/__init__.py +5 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/vector/git_ingester.py +3 -0
- hanzo_mcp-0.5.2/hanzo_mcp/tools/vector/index_tool.py +358 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/vector/infinity_store.py +98 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/vector/vector_search.py +11 -6
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp.egg-info/PKG-INFO +1 -1
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp.egg-info/SOURCES.txt +40 -1
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/pyproject.toml +1 -1
- hanzo_mcp-0.5.2/tests/test_new_tools.py +439 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/LICENSE +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/README.md +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/cli.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/cli_enhanced.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/config/__init__.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/config/settings.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/config/tool_config.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/prompts/__init__.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/prompts/compact_conversation.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/prompts/create_release.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/prompts/project_system.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/prompts/project_todo_reminder.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/prompts/utils.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/server.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/agent/__init__.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/agent/agent_tool.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/agent/prompt.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/agent/tool_adapter.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/common/__init__.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/common/batch_tool.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/common/config_tool.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/common/context.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/common/permissions.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/common/thinking_tool.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/common/validation.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/filesystem/base.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/filesystem/content_replace.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/filesystem/directory_tree.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/filesystem/edit.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/filesystem/grep.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/filesystem/grep_ast_tool.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/filesystem/multi_edit.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/filesystem/read.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/filesystem/write.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/jupyter/__init__.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/jupyter/base.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/jupyter/notebook_edit.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/jupyter/notebook_read.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/shell/base.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/shell/bash_session.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/shell/bash_session_executor.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/shell/command_executor.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/shell/run_command.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/shell/run_command_windows.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/shell/session_manager.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/shell/session_storage.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/todo/__init__.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/todo/base.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/todo/todo_read.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/todo/todo_write.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/vector/ast_analyzer.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/vector/mock_infinity.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/vector/project_manager.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp/tools/vector/vector_index.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp.egg-info/dependency_links.txt +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp.egg-info/entry_points.txt +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp.egg-info/requires.txt +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/hanzo_mcp.egg-info/top_level.txt +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/setup.cfg +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/setup.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/tests/test_async_support.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/tests/test_cli.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/tests/test_git_ingestion.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/tests/test_search_quality.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/tests/test_unified_search.py +0 -0
- {hanzo_mcp-0.5.1 → hanzo_mcp-0.5.2}/tests/test_vector_store.py +0 -0
|
@@ -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,8 +85,10 @@ 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),
|
|
80
|
-
"
|
|
90
|
+
"batch_search": is_tool_enabled("batch_search", not disable_search_tools),
|
|
91
|
+
"find_files": is_tool_enabled("find_files", True),
|
|
81
92
|
}
|
|
82
93
|
|
|
83
94
|
# Vector tools setup (needed for unified search)
|
|
@@ -87,8 +98,8 @@ def register_all_tools(
|
|
|
87
98
|
"vector_search": is_tool_enabled("vector_search", False),
|
|
88
99
|
}
|
|
89
100
|
|
|
90
|
-
# Create project manager if vector tools or
|
|
91
|
-
if any(vector_enabled.values()) or filesystem_enabled.get("
|
|
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):
|
|
92
103
|
if vector_config:
|
|
93
104
|
from hanzo_mcp.tools.vector.project_manager import ProjectVectorManager
|
|
94
105
|
search_paths = [str(path) for path in permission_manager.allowed_paths]
|
|
@@ -175,3 +186,123 @@ def register_all_tools(
|
|
|
175
186
|
# Register batch tool if enabled (batch tool is typically always enabled)
|
|
176
187
|
if is_tool_enabled("batch", True):
|
|
177
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
|
-
#
|
|
174
|
-
|
|
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:
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"""Comprehensive system and MCP statistics."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import psutil
|
|
5
|
+
import shutil
|
|
6
|
+
from typing import TypedDict, Unpack, final, override
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from fastmcp import Context as MCPContext
|
|
11
|
+
|
|
12
|
+
from hanzo_mcp.tools.common.base import BaseTool
|
|
13
|
+
from hanzo_mcp.tools.common.context import create_tool_context
|
|
14
|
+
from hanzo_mcp.tools.shell.run_background import RunBackgroundTool
|
|
15
|
+
from hanzo_mcp.tools.mcp.mcp_add import McpAddTool
|
|
16
|
+
from hanzo_mcp.tools.database.database_manager import DatabaseManager
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class StatsParams(TypedDict, total=False):
|
|
20
|
+
"""Parameters for stats tool."""
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@final
|
|
25
|
+
class StatsTool(BaseTool):
|
|
26
|
+
"""Tool for showing comprehensive system and MCP statistics."""
|
|
27
|
+
|
|
28
|
+
def __init__(self, db_manager: DatabaseManager = None):
|
|
29
|
+
"""Initialize the stats tool.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
db_manager: Optional database manager for DB stats
|
|
33
|
+
"""
|
|
34
|
+
self.db_manager = db_manager
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
@override
|
|
38
|
+
def name(self) -> str:
|
|
39
|
+
"""Get the tool name."""
|
|
40
|
+
return "stats"
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
@override
|
|
44
|
+
def description(self) -> str:
|
|
45
|
+
"""Get the tool description."""
|
|
46
|
+
return """Show comprehensive system and Hanzo MCP statistics.
|
|
47
|
+
|
|
48
|
+
Displays:
|
|
49
|
+
- System resources (CPU, memory, disk)
|
|
50
|
+
- Running processes
|
|
51
|
+
- Database usage
|
|
52
|
+
- MCP server status
|
|
53
|
+
- Tool usage statistics
|
|
54
|
+
- Warnings for high resource usage
|
|
55
|
+
|
|
56
|
+
Example:
|
|
57
|
+
- stats
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
@override
|
|
61
|
+
async def call(
|
|
62
|
+
self,
|
|
63
|
+
ctx: MCPContext,
|
|
64
|
+
**params: Unpack[StatsParams],
|
|
65
|
+
) -> str:
|
|
66
|
+
"""Get comprehensive statistics.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
ctx: MCP context
|
|
70
|
+
**params: Tool parameters
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
Comprehensive statistics
|
|
74
|
+
"""
|
|
75
|
+
tool_ctx = create_tool_context(ctx)
|
|
76
|
+
await tool_ctx.set_tool_info(self.name)
|
|
77
|
+
|
|
78
|
+
output = []
|
|
79
|
+
warnings = []
|
|
80
|
+
|
|
81
|
+
# Header
|
|
82
|
+
output.append("=== Hanzo MCP System Statistics ===")
|
|
83
|
+
output.append(f"Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
|
84
|
+
output.append("")
|
|
85
|
+
|
|
86
|
+
# System Resources
|
|
87
|
+
output.append("=== System Resources ===")
|
|
88
|
+
|
|
89
|
+
# CPU
|
|
90
|
+
cpu_percent = psutil.cpu_percent(interval=1)
|
|
91
|
+
cpu_count = psutil.cpu_count()
|
|
92
|
+
output.append(f"CPU Usage: {cpu_percent}% ({cpu_count} cores)")
|
|
93
|
+
if cpu_percent > 90:
|
|
94
|
+
warnings.append(f"⚠️ HIGH CPU USAGE: {cpu_percent}%")
|
|
95
|
+
|
|
96
|
+
# Memory
|
|
97
|
+
memory = psutil.virtual_memory()
|
|
98
|
+
memory_used_gb = memory.used / (1024**3)
|
|
99
|
+
memory_total_gb = memory.total / (1024**3)
|
|
100
|
+
memory_percent = memory.percent
|
|
101
|
+
output.append(f"Memory: {memory_used_gb:.1f}/{memory_total_gb:.1f} GB ({memory_percent}%)")
|
|
102
|
+
if memory_percent > 90:
|
|
103
|
+
warnings.append(f"⚠️ HIGH MEMORY USAGE: {memory_percent}%")
|
|
104
|
+
|
|
105
|
+
# Disk
|
|
106
|
+
disk = psutil.disk_usage('/')
|
|
107
|
+
disk_used_gb = disk.used / (1024**3)
|
|
108
|
+
disk_total_gb = disk.total / (1024**3)
|
|
109
|
+
disk_percent = disk.percent
|
|
110
|
+
disk_free_gb = disk.free / (1024**3)
|
|
111
|
+
output.append(f"Disk: {disk_used_gb:.1f}/{disk_total_gb:.1f} GB ({disk_percent}%)")
|
|
112
|
+
output.append(f"Free Space: {disk_free_gb:.1f} GB")
|
|
113
|
+
if disk_percent > 90:
|
|
114
|
+
warnings.append(f"⚠️ LOW DISK SPACE: Only {disk_free_gb:.1f} GB free ({100-disk_percent:.1f}% remaining)")
|
|
115
|
+
|
|
116
|
+
output.append("")
|
|
117
|
+
|
|
118
|
+
# Background Processes
|
|
119
|
+
output.append("=== Background Processes ===")
|
|
120
|
+
processes = RunBackgroundTool.get_processes()
|
|
121
|
+
running_count = 0
|
|
122
|
+
total_memory_mb = 0
|
|
123
|
+
|
|
124
|
+
if processes:
|
|
125
|
+
for proc in processes.values():
|
|
126
|
+
if proc.is_running():
|
|
127
|
+
running_count += 1
|
|
128
|
+
try:
|
|
129
|
+
ps_proc = psutil.Process(proc.process.pid)
|
|
130
|
+
memory_mb = ps_proc.memory_info().rss / (1024**2)
|
|
131
|
+
total_memory_mb += memory_mb
|
|
132
|
+
except:
|
|
133
|
+
pass
|
|
134
|
+
|
|
135
|
+
output.append(f"Running Processes: {running_count}")
|
|
136
|
+
output.append(f"Total Memory Usage: {total_memory_mb:.1f} MB")
|
|
137
|
+
|
|
138
|
+
# List top processes by memory
|
|
139
|
+
if running_count > 0:
|
|
140
|
+
output.append("\nTop Processes:")
|
|
141
|
+
proc_list = []
|
|
142
|
+
for proc_id, proc in processes.items():
|
|
143
|
+
if proc.is_running():
|
|
144
|
+
try:
|
|
145
|
+
ps_proc = psutil.Process(proc.process.pid)
|
|
146
|
+
memory_mb = ps_proc.memory_info().rss / (1024**2)
|
|
147
|
+
cpu = ps_proc.cpu_percent(interval=0.1)
|
|
148
|
+
proc_list.append((proc.name, memory_mb, cpu, proc_id))
|
|
149
|
+
except:
|
|
150
|
+
proc_list.append((proc.name, 0, 0, proc_id))
|
|
151
|
+
|
|
152
|
+
proc_list.sort(key=lambda x: x[1], reverse=True)
|
|
153
|
+
for name, mem, cpu, pid in proc_list[:5]:
|
|
154
|
+
output.append(f" - {name} ({pid}): {mem:.1f} MB, {cpu:.1f}% CPU")
|
|
155
|
+
else:
|
|
156
|
+
output.append("No background processes running")
|
|
157
|
+
|
|
158
|
+
output.append("")
|
|
159
|
+
|
|
160
|
+
# Database Usage
|
|
161
|
+
if self.db_manager:
|
|
162
|
+
output.append("=== Database Usage ===")
|
|
163
|
+
db_dir = Path.home() / ".hanzo" / "db"
|
|
164
|
+
total_db_size = 0
|
|
165
|
+
|
|
166
|
+
if db_dir.exists():
|
|
167
|
+
for db_file in db_dir.rglob("*.db"):
|
|
168
|
+
size = db_file.stat().st_size
|
|
169
|
+
total_db_size += size
|
|
170
|
+
|
|
171
|
+
output.append(f"Total Database Size: {total_db_size / (1024**2):.1f} MB")
|
|
172
|
+
output.append(f"Active Projects: {len(self.db_manager.projects)}")
|
|
173
|
+
|
|
174
|
+
# List largest databases
|
|
175
|
+
db_sizes = []
|
|
176
|
+
for db_file in db_dir.rglob("*.db"):
|
|
177
|
+
size = db_file.stat().st_size / (1024**2)
|
|
178
|
+
if size > 0.1: # Only show DBs > 100KB
|
|
179
|
+
project = db_file.parent.parent.name
|
|
180
|
+
db_type = db_file.stem
|
|
181
|
+
db_sizes.append((project, db_type, size))
|
|
182
|
+
|
|
183
|
+
if db_sizes:
|
|
184
|
+
db_sizes.sort(key=lambda x: x[2], reverse=True)
|
|
185
|
+
output.append("\nLargest Databases:")
|
|
186
|
+
for project, db_type, size in db_sizes[:5]:
|
|
187
|
+
output.append(f" - {project}/{db_type}: {size:.1f} MB")
|
|
188
|
+
else:
|
|
189
|
+
output.append("No databases found")
|
|
190
|
+
|
|
191
|
+
output.append("")
|
|
192
|
+
|
|
193
|
+
# MCP Servers
|
|
194
|
+
output.append("=== MCP Servers ===")
|
|
195
|
+
mcp_servers = McpAddTool.get_servers()
|
|
196
|
+
if mcp_servers:
|
|
197
|
+
running_mcp = sum(1 for s in mcp_servers.values() if s.get("status") == "running")
|
|
198
|
+
total_mcp_tools = sum(len(s.get("tools", [])) for s in mcp_servers.values())
|
|
199
|
+
|
|
200
|
+
output.append(f"Total Servers: {len(mcp_servers)}")
|
|
201
|
+
output.append(f"Running: {running_mcp}")
|
|
202
|
+
output.append(f"Total Tools Available: {total_mcp_tools}")
|
|
203
|
+
else:
|
|
204
|
+
output.append("No MCP servers configured")
|
|
205
|
+
|
|
206
|
+
output.append("")
|
|
207
|
+
|
|
208
|
+
# Hanzo MCP Specifics
|
|
209
|
+
output.append("=== Hanzo MCP ===")
|
|
210
|
+
|
|
211
|
+
# Log directory size
|
|
212
|
+
log_dir = Path.home() / ".hanzo" / "logs"
|
|
213
|
+
if log_dir.exists():
|
|
214
|
+
log_size = sum(f.stat().st_size for f in log_dir.rglob("*") if f.is_file())
|
|
215
|
+
log_count = len(list(log_dir.rglob("*.log")))
|
|
216
|
+
output.append(f"Log Files: {log_count} ({log_size / (1024**2):.1f} MB)")
|
|
217
|
+
|
|
218
|
+
if log_size > 100 * 1024**2: # > 100MB
|
|
219
|
+
warnings.append(f"⚠️ Large log directory: {log_size / (1024**2):.1f} MB")
|
|
220
|
+
|
|
221
|
+
# Config directory
|
|
222
|
+
config_dir = Path.home() / ".hanzo" / "mcp"
|
|
223
|
+
if config_dir.exists():
|
|
224
|
+
config_count = len(list(config_dir.rglob("*.json")))
|
|
225
|
+
output.append(f"Config Files: {config_count}")
|
|
226
|
+
|
|
227
|
+
# Tool status (if available)
|
|
228
|
+
# TODO: Track tool usage statistics
|
|
229
|
+
output.append("\nTool Categories:")
|
|
230
|
+
output.append(" - File Operations: grep, find_files, read, write, edit")
|
|
231
|
+
output.append(" - Shell: bash, run_background, processes, pkill")
|
|
232
|
+
output.append(" - Database: sql_query, graph_query, vector_search")
|
|
233
|
+
output.append(" - Package Runners: uvx, npx, uvx_background, npx_background")
|
|
234
|
+
output.append(" - MCP Management: mcp_add, mcp_remove, mcp_stats")
|
|
235
|
+
|
|
236
|
+
# Warnings Section
|
|
237
|
+
if warnings:
|
|
238
|
+
output.append("\n=== ⚠️ WARNINGS ===")
|
|
239
|
+
for warning in warnings:
|
|
240
|
+
output.append(warning)
|
|
241
|
+
output.append("")
|
|
242
|
+
|
|
243
|
+
# Recommendations
|
|
244
|
+
output.append("=== Recommendations ===")
|
|
245
|
+
if disk_free_gb < 5:
|
|
246
|
+
output.append("- Free up disk space (< 5GB remaining)")
|
|
247
|
+
if memory_percent > 80:
|
|
248
|
+
output.append("- Close unused applications to free memory")
|
|
249
|
+
if running_count > 10:
|
|
250
|
+
output.append("- Consider stopping unused background processes")
|
|
251
|
+
if log_size > 50 * 1024**2:
|
|
252
|
+
output.append("- Clean up old log files in ~/.hanzo/logs")
|
|
253
|
+
|
|
254
|
+
if not any([disk_free_gb < 5, memory_percent > 80, running_count > 10, log_size > 50 * 1024**2]):
|
|
255
|
+
output.append("✅ System resources are healthy")
|
|
256
|
+
|
|
257
|
+
return "\n".join(output)
|
|
258
|
+
|
|
259
|
+
def register(self, mcp_server) -> None:
|
|
260
|
+
"""Register this tool with the MCP server."""
|
|
261
|
+
pass
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"""Disable tools dynamically."""
|
|
2
|
+
|
|
3
|
+
from typing import Annotated, TypedDict, Unpack, final, override
|
|
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
|
+
ToolName = Annotated[
|
|
14
|
+
str,
|
|
15
|
+
Field(
|
|
16
|
+
description="Name of the tool to disable (e.g., 'grep', 'vector_search')",
|
|
17
|
+
min_length=1,
|
|
18
|
+
),
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
Persist = Annotated[
|
|
22
|
+
bool,
|
|
23
|
+
Field(
|
|
24
|
+
description="Persist the change to config file",
|
|
25
|
+
default=True,
|
|
26
|
+
),
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ToolDisableParams(TypedDict, total=False):
|
|
31
|
+
"""Parameters for tool disable."""
|
|
32
|
+
|
|
33
|
+
tool: str
|
|
34
|
+
persist: bool
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@final
|
|
38
|
+
class ToolDisableTool(BaseTool):
|
|
39
|
+
"""Tool for disabling other tools dynamically."""
|
|
40
|
+
|
|
41
|
+
def __init__(self):
|
|
42
|
+
"""Initialize the tool disable tool."""
|
|
43
|
+
# Ensure states are loaded
|
|
44
|
+
if not ToolEnableTool._initialized:
|
|
45
|
+
ToolEnableTool._load_states()
|
|
46
|
+
ToolEnableTool._initialized = True
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
@override
|
|
50
|
+
def name(self) -> str:
|
|
51
|
+
"""Get the tool name."""
|
|
52
|
+
return "tool_disable"
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
@override
|
|
56
|
+
def description(self) -> str:
|
|
57
|
+
"""Get the tool description."""
|
|
58
|
+
return """Disable tools to prevent their use.
|
|
59
|
+
|
|
60
|
+
This allows you to temporarily or permanently disable tools.
|
|
61
|
+
Useful for testing or when a tool is misbehaving.
|
|
62
|
+
Changes are persisted by default.
|
|
63
|
+
|
|
64
|
+
Critical tools (tool_enable, tool_disable, tool_list) cannot be disabled.
|
|
65
|
+
|
|
66
|
+
Examples:
|
|
67
|
+
- tool_disable --tool vector_search
|
|
68
|
+
- tool_disable --tool uvx_background
|
|
69
|
+
- tool_disable --tool grep --no-persist
|
|
70
|
+
|
|
71
|
+
Use 'tool_list' to see all available tools and their status.
|
|
72
|
+
Use 'tool_enable' to re-enable disabled tools.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
@override
|
|
76
|
+
async def call(
|
|
77
|
+
self,
|
|
78
|
+
ctx: MCPContext,
|
|
79
|
+
**params: Unpack[ToolDisableParams],
|
|
80
|
+
) -> str:
|
|
81
|
+
"""Disable a tool.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
ctx: MCP context
|
|
85
|
+
**params: Tool parameters
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
Result of disabling the tool
|
|
89
|
+
"""
|
|
90
|
+
tool_ctx = create_tool_context(ctx)
|
|
91
|
+
await tool_ctx.set_tool_info(self.name)
|
|
92
|
+
|
|
93
|
+
# Extract parameters
|
|
94
|
+
tool_name = params.get("tool")
|
|
95
|
+
if not tool_name:
|
|
96
|
+
return "Error: tool name is required"
|
|
97
|
+
|
|
98
|
+
persist = params.get("persist", True)
|
|
99
|
+
|
|
100
|
+
# Prevent disabling critical tools
|
|
101
|
+
critical_tools = {"tool_enable", "tool_disable", "tool_list", "stats"}
|
|
102
|
+
if tool_name in critical_tools:
|
|
103
|
+
return f"Error: Cannot disable critical tool '{tool_name}'. These tools are required for system management."
|
|
104
|
+
|
|
105
|
+
# Check current state
|
|
106
|
+
was_enabled = ToolEnableTool.is_tool_enabled(tool_name)
|
|
107
|
+
|
|
108
|
+
if not was_enabled:
|
|
109
|
+
return f"Tool '{tool_name}' is already disabled."
|
|
110
|
+
|
|
111
|
+
# Disable the tool
|
|
112
|
+
ToolEnableTool._tool_states[tool_name] = False
|
|
113
|
+
|
|
114
|
+
# Persist if requested
|
|
115
|
+
if persist:
|
|
116
|
+
ToolEnableTool._save_states()
|
|
117
|
+
await tool_ctx.info(f"Disabled tool '{tool_name}' (persisted)")
|
|
118
|
+
else:
|
|
119
|
+
await tool_ctx.info(f"Disabled tool '{tool_name}' (temporary)")
|
|
120
|
+
|
|
121
|
+
output = [
|
|
122
|
+
f"Successfully disabled tool '{tool_name}'",
|
|
123
|
+
"",
|
|
124
|
+
"The tool is now unavailable for use.",
|
|
125
|
+
f"Use 'tool_enable --tool {tool_name}' to re-enable it.",
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
if not persist:
|
|
129
|
+
output.append("\nNote: This change is temporary and will be lost on restart.")
|
|
130
|
+
|
|
131
|
+
# Warn about commonly used tools
|
|
132
|
+
common_tools = {"grep", "read", "write", "bash", "edit"}
|
|
133
|
+
if tool_name in common_tools:
|
|
134
|
+
output.append(f"\n⚠️ Warning: '{tool_name}' is a commonly used tool. Disabling it may affect normal operations.")
|
|
135
|
+
|
|
136
|
+
# Count disabled tools
|
|
137
|
+
disabled_count = sum(1 for enabled in ToolEnableTool._tool_states.values() if not enabled)
|
|
138
|
+
output.append(f"\nTotal disabled tools: {disabled_count}")
|
|
139
|
+
|
|
140
|
+
return "\n".join(output)
|
|
141
|
+
|
|
142
|
+
def register(self, mcp_server) -> None:
|
|
143
|
+
"""Register this tool with the MCP server."""
|
|
144
|
+
pass
|