hanzo-mcp 0.9.0__py3-none-any.whl → 0.9.1__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.
- hanzo_mcp/__init__.py +1 -1
- hanzo_mcp/analytics/posthog_analytics.py +14 -1
- hanzo_mcp/cli.py +108 -4
- hanzo_mcp/server.py +11 -0
- hanzo_mcp/tools/__init__.py +3 -16
- hanzo_mcp/tools/agent/__init__.py +5 -0
- hanzo_mcp/tools/agent/agent.py +5 -0
- hanzo_mcp/tools/agent/agent_tool.py +3 -17
- hanzo_mcp/tools/agent/agent_tool_v1_deprecated.py +623 -0
- hanzo_mcp/tools/agent/clarification_tool.py +7 -1
- hanzo_mcp/tools/agent/claude_desktop_auth.py +16 -6
- hanzo_mcp/tools/agent/cli_agent_base.py +5 -0
- hanzo_mcp/tools/agent/cli_tools.py +26 -0
- hanzo_mcp/tools/agent/code_auth_tool.py +5 -0
- hanzo_mcp/tools/agent/critic_tool.py +7 -1
- hanzo_mcp/tools/agent/iching_tool.py +5 -0
- hanzo_mcp/tools/agent/network_tool.py +5 -0
- hanzo_mcp/tools/agent/review_tool.py +7 -1
- hanzo_mcp/tools/agent/swarm_alias.py +5 -0
- hanzo_mcp/tools/agent/swarm_tool.py +701 -0
- hanzo_mcp/tools/agent/swarm_tool_v1_deprecated.py +554 -0
- hanzo_mcp/tools/agent/unified_cli_tools.py +5 -0
- hanzo_mcp/tools/common/auto_timeout.py +234 -0
- hanzo_mcp/tools/common/base.py +4 -0
- hanzo_mcp/tools/common/batch_tool.py +5 -0
- hanzo_mcp/tools/common/config_tool.py +5 -0
- hanzo_mcp/tools/common/critic_tool.py +5 -0
- hanzo_mcp/tools/common/paginated_base.py +4 -0
- hanzo_mcp/tools/common/permissions.py +38 -12
- hanzo_mcp/tools/common/personality.py +673 -980
- hanzo_mcp/tools/common/stats.py +5 -0
- hanzo_mcp/tools/common/thinking_tool.py +5 -0
- hanzo_mcp/tools/common/timeout_parser.py +103 -0
- hanzo_mcp/tools/common/tool_disable.py +5 -0
- hanzo_mcp/tools/common/tool_enable.py +5 -0
- hanzo_mcp/tools/common/tool_list.py +5 -0
- hanzo_mcp/tools/config/config_tool.py +5 -0
- hanzo_mcp/tools/config/mode_tool.py +5 -0
- hanzo_mcp/tools/database/graph.py +5 -0
- hanzo_mcp/tools/database/graph_add.py +5 -0
- hanzo_mcp/tools/database/graph_query.py +5 -0
- hanzo_mcp/tools/database/graph_remove.py +5 -0
- hanzo_mcp/tools/database/graph_search.py +5 -0
- hanzo_mcp/tools/database/graph_stats.py +5 -0
- hanzo_mcp/tools/database/sql.py +5 -0
- hanzo_mcp/tools/database/sql_query.py +2 -0
- hanzo_mcp/tools/database/sql_search.py +5 -0
- hanzo_mcp/tools/database/sql_stats.py +5 -0
- hanzo_mcp/tools/editor/neovim_command.py +5 -0
- hanzo_mcp/tools/editor/neovim_edit.py +7 -2
- hanzo_mcp/tools/editor/neovim_session.py +5 -0
- hanzo_mcp/tools/filesystem/__init__.py +23 -26
- hanzo_mcp/tools/filesystem/ast_tool.py +2 -3
- hanzo_mcp/tools/filesystem/base.py +0 -16
- hanzo_mcp/tools/filesystem/batch_search.py +825 -0
- hanzo_mcp/tools/filesystem/content_replace.py +5 -3
- hanzo_mcp/tools/filesystem/diff.py +5 -0
- hanzo_mcp/tools/filesystem/directory_tree.py +34 -281
- hanzo_mcp/tools/filesystem/directory_tree_paginated.py +345 -0
- hanzo_mcp/tools/filesystem/edit.py +5 -4
- hanzo_mcp/tools/filesystem/find.py +177 -311
- hanzo_mcp/tools/filesystem/find_files.py +370 -0
- hanzo_mcp/tools/filesystem/git_search.py +5 -3
- hanzo_mcp/tools/filesystem/grep.py +454 -0
- hanzo_mcp/tools/filesystem/multi_edit.py +5 -4
- hanzo_mcp/tools/filesystem/read.py +11 -8
- hanzo_mcp/tools/filesystem/rules_tool.py +5 -3
- hanzo_mcp/tools/filesystem/search_tool.py +728 -0
- hanzo_mcp/tools/filesystem/symbols_tool.py +510 -0
- hanzo_mcp/tools/filesystem/tree.py +273 -0
- hanzo_mcp/tools/filesystem/watch.py +6 -1
- hanzo_mcp/tools/filesystem/write.py +12 -6
- hanzo_mcp/tools/jupyter/jupyter.py +30 -2
- hanzo_mcp/tools/jupyter/notebook_edit.py +298 -0
- hanzo_mcp/tools/jupyter/notebook_read.py +148 -0
- hanzo_mcp/tools/llm/consensus_tool.py +8 -6
- hanzo_mcp/tools/llm/llm_manage.py +5 -0
- hanzo_mcp/tools/llm/llm_tool.py +2 -0
- hanzo_mcp/tools/llm/llm_unified.py +5 -0
- hanzo_mcp/tools/llm/provider_tools.py +5 -0
- hanzo_mcp/tools/lsp/lsp_tool.py +475 -622
- hanzo_mcp/tools/mcp/mcp_add.py +7 -2
- hanzo_mcp/tools/mcp/mcp_remove.py +15 -2
- hanzo_mcp/tools/mcp/mcp_stats.py +5 -0
- hanzo_mcp/tools/mcp/mcp_tool.py +5 -0
- hanzo_mcp/tools/memory/knowledge_tools.py +14 -0
- hanzo_mcp/tools/memory/memory_tools.py +17 -0
- hanzo_mcp/tools/search/find_tool.py +5 -3
- hanzo_mcp/tools/search/unified_search.py +3 -1
- hanzo_mcp/tools/shell/__init__.py +2 -14
- hanzo_mcp/tools/shell/base_process.py +4 -2
- hanzo_mcp/tools/shell/bash_tool.py +2 -0
- hanzo_mcp/tools/shell/command_executor.py +7 -7
- hanzo_mcp/tools/shell/logs.py +5 -0
- hanzo_mcp/tools/shell/npx.py +5 -0
- hanzo_mcp/tools/shell/npx_background.py +5 -0
- hanzo_mcp/tools/shell/npx_tool.py +5 -0
- hanzo_mcp/tools/shell/open.py +5 -0
- hanzo_mcp/tools/shell/pkill.py +5 -0
- hanzo_mcp/tools/shell/process_tool.py +5 -0
- hanzo_mcp/tools/shell/processes.py +5 -0
- hanzo_mcp/tools/shell/run_background.py +5 -0
- hanzo_mcp/tools/shell/run_command.py +2 -0
- hanzo_mcp/tools/shell/run_command_windows.py +5 -0
- hanzo_mcp/tools/shell/streaming_command.py +5 -0
- hanzo_mcp/tools/shell/uvx.py +5 -0
- hanzo_mcp/tools/shell/uvx_background.py +5 -0
- hanzo_mcp/tools/shell/uvx_tool.py +5 -0
- hanzo_mcp/tools/shell/zsh_tool.py +3 -0
- hanzo_mcp/tools/todo/todo.py +5 -0
- hanzo_mcp/tools/todo/todo_read.py +142 -0
- hanzo_mcp/tools/todo/todo_write.py +367 -0
- hanzo_mcp/tools/vector/__init__.py +42 -95
- hanzo_mcp/tools/vector/index_tool.py +5 -0
- hanzo_mcp/tools/vector/vector.py +5 -0
- hanzo_mcp/tools/vector/vector_index.py +5 -0
- hanzo_mcp/tools/vector/vector_search.py +5 -0
- {hanzo_mcp-0.9.0.dist-info → hanzo_mcp-0.9.1.dist-info}/METADATA +1 -1
- hanzo_mcp-0.9.1.dist-info/RECORD +195 -0
- hanzo_mcp/tools/common/path_utils.py +0 -34
- hanzo_mcp/tools/compiler/__init__.py +0 -8
- hanzo_mcp/tools/compiler/sandboxed_compiler.py +0 -681
- hanzo_mcp/tools/environment/__init__.py +0 -8
- hanzo_mcp/tools/environment/environment_detector.py +0 -594
- hanzo_mcp/tools/filesystem/search.py +0 -1160
- hanzo_mcp/tools/framework/__init__.py +0 -8
- hanzo_mcp/tools/framework/framework_modes.py +0 -714
- hanzo_mcp/tools/memory/conversation_memory.py +0 -636
- hanzo_mcp/tools/shell/run_tool.py +0 -56
- hanzo_mcp/tools/vector/node_tool.py +0 -538
- hanzo_mcp/tools/vector/unified_vector.py +0 -384
- hanzo_mcp-0.9.0.dist-info/RECORD +0 -191
- {hanzo_mcp-0.9.0.dist-info → hanzo_mcp-0.9.1.dist-info}/WHEEL +0 -0
- {hanzo_mcp-0.9.0.dist-info → hanzo_mcp-0.9.1.dist-info}/entry_points.txt +0 -0
- {hanzo_mcp-0.9.0.dist-info → hanzo_mcp-0.9.1.dist-info}/top_level.txt +0 -0
hanzo_mcp/tools/mcp/mcp_add.py
CHANGED
|
@@ -8,6 +8,8 @@ from pathlib import Path
|
|
|
8
8
|
from pydantic import Field
|
|
9
9
|
from mcp.server.fastmcp import Context as MCPContext
|
|
10
10
|
|
|
11
|
+
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
|
12
|
+
|
|
11
13
|
from hanzo_mcp.tools.common.base import BaseTool
|
|
12
14
|
from hanzo_mcp.tools.common.context import create_tool_context
|
|
13
15
|
|
|
@@ -131,6 +133,9 @@ Use 'mcp_stats' to see all added servers and their status.
|
|
|
131
133
|
"""
|
|
132
134
|
|
|
133
135
|
@override
|
|
136
|
+
@auto_timeout("mcp_add")
|
|
137
|
+
|
|
138
|
+
|
|
134
139
|
async def call(
|
|
135
140
|
self,
|
|
136
141
|
ctx: MCPContext,
|
|
@@ -219,8 +224,8 @@ Use 'mcp_stats' to see all added servers and their status.
|
|
|
219
224
|
if not shutil.which("uvx"):
|
|
220
225
|
return "Error: uvx not found. Install uv first."
|
|
221
226
|
|
|
222
|
-
#
|
|
223
|
-
#
|
|
227
|
+
# Server is validated and ready to be used
|
|
228
|
+
# The actual connection happens when tools are invoked
|
|
224
229
|
server_config["status"] = "ready"
|
|
225
230
|
|
|
226
231
|
except Exception as e:
|
|
@@ -5,6 +5,8 @@ from typing import Unpack, Annotated, TypedDict, final, override
|
|
|
5
5
|
from pydantic import Field
|
|
6
6
|
from mcp.server.fastmcp import Context as MCPContext
|
|
7
7
|
|
|
8
|
+
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
|
9
|
+
|
|
8
10
|
from hanzo_mcp.tools.common.base import BaseTool
|
|
9
11
|
from hanzo_mcp.tools.mcp.mcp_add import McpAddTool
|
|
10
12
|
from hanzo_mcp.tools.common.context import create_tool_context
|
|
@@ -64,6 +66,9 @@ Use 'mcp_stats' to see all servers before removing.
|
|
|
64
66
|
"""
|
|
65
67
|
|
|
66
68
|
@override
|
|
69
|
+
@auto_timeout("mcp_remove")
|
|
70
|
+
|
|
71
|
+
|
|
67
72
|
async def call(
|
|
68
73
|
self,
|
|
69
74
|
ctx: MCPContext,
|
|
@@ -103,8 +108,16 @@ Use 'mcp_stats' to see all servers before removing.
|
|
|
103
108
|
if not force:
|
|
104
109
|
return f"Error: Server '{name}' is currently running. Use --force to remove anyway."
|
|
105
110
|
else:
|
|
106
|
-
#
|
|
107
|
-
|
|
111
|
+
# Stop the server process if it's running
|
|
112
|
+
process_id = server.get("process_id")
|
|
113
|
+
if process_id:
|
|
114
|
+
try:
|
|
115
|
+
import signal
|
|
116
|
+
import os
|
|
117
|
+
os.kill(process_id, signal.SIGTERM)
|
|
118
|
+
await tool_ctx.info(f"Stopped running server '{name}' (PID: {process_id})")
|
|
119
|
+
except ProcessLookupError:
|
|
120
|
+
await tool_ctx.info(f"Server '{name}' process not found (already stopped)")
|
|
108
121
|
|
|
109
122
|
# Remove from registry
|
|
110
123
|
del McpAddTool._mcp_servers[name]
|
hanzo_mcp/tools/mcp/mcp_stats.py
CHANGED
|
@@ -4,6 +4,8 @@ from typing import Unpack, TypedDict, final, override
|
|
|
4
4
|
|
|
5
5
|
from mcp.server.fastmcp import Context as MCPContext
|
|
6
6
|
|
|
7
|
+
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
|
8
|
+
|
|
7
9
|
from hanzo_mcp.tools.common.base import BaseTool
|
|
8
10
|
from hanzo_mcp.tools.mcp.mcp_add import McpAddTool
|
|
9
11
|
from hanzo_mcp.tools.common.context import create_tool_context
|
|
@@ -47,6 +49,9 @@ Example:
|
|
|
47
49
|
"""
|
|
48
50
|
|
|
49
51
|
@override
|
|
52
|
+
@auto_timeout("mcp_stats")
|
|
53
|
+
|
|
54
|
+
|
|
50
55
|
async def call(
|
|
51
56
|
self,
|
|
52
57
|
ctx: MCPContext,
|
hanzo_mcp/tools/mcp/mcp_tool.py
CHANGED
|
@@ -20,6 +20,8 @@ from pathlib import Path
|
|
|
20
20
|
from pydantic import Field
|
|
21
21
|
from mcp.server.fastmcp import Context as MCPContext
|
|
22
22
|
|
|
23
|
+
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
|
24
|
+
|
|
23
25
|
from hanzo_mcp.tools.common.base import BaseTool
|
|
24
26
|
from hanzo_mcp.tools.common.context import create_tool_context
|
|
25
27
|
|
|
@@ -270,6 +272,9 @@ mcp --action enable --name github
|
|
|
270
272
|
Status: {enabled} enabled, {running} running"""
|
|
271
273
|
|
|
272
274
|
@override
|
|
275
|
+
@auto_timeout("mcp")
|
|
276
|
+
|
|
277
|
+
|
|
273
278
|
async def call(
|
|
274
279
|
self,
|
|
275
280
|
ctx: MCPContext,
|
|
@@ -7,6 +7,8 @@ supporting hierarchical organization (session, project, global).
|
|
|
7
7
|
from typing import Any, Dict, List, Optional, final, override
|
|
8
8
|
|
|
9
9
|
from mcp.server import FastMCP
|
|
10
|
+
|
|
11
|
+
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
|
10
12
|
from mcp.server.fastmcp import Context as MCPContext
|
|
11
13
|
|
|
12
14
|
from hanzo_mcp.tools.common.base import BaseTool
|
|
@@ -69,6 +71,9 @@ recall_facts(queries=["company policies"], scope="global", limit=5)
|
|
|
69
71
|
"""
|
|
70
72
|
|
|
71
73
|
@override
|
|
74
|
+
@auto_timeout("knowledge_tools")
|
|
75
|
+
|
|
76
|
+
|
|
72
77
|
async def call(
|
|
73
78
|
self,
|
|
74
79
|
ctx: MCPContext,
|
|
@@ -190,6 +195,9 @@ store_facts(facts=["Company founded in 2020"], scope="global", kb_name="company_
|
|
|
190
195
|
"""
|
|
191
196
|
|
|
192
197
|
@override
|
|
198
|
+
@auto_timeout("knowledge_tools")
|
|
199
|
+
|
|
200
|
+
|
|
193
201
|
async def call(
|
|
194
202
|
self,
|
|
195
203
|
ctx: MCPContext,
|
|
@@ -286,6 +294,9 @@ summarize_to_memory(content="Company guidelines...", topic="Guidelines", scope="
|
|
|
286
294
|
"""
|
|
287
295
|
|
|
288
296
|
@override
|
|
297
|
+
@auto_timeout("knowledge_tools")
|
|
298
|
+
|
|
299
|
+
|
|
289
300
|
async def call(
|
|
290
301
|
self,
|
|
291
302
|
ctx: MCPContext,
|
|
@@ -390,6 +401,9 @@ manage_knowledge_bases(action="delete", kb_name="old_docs")
|
|
|
390
401
|
"""
|
|
391
402
|
|
|
392
403
|
@override
|
|
404
|
+
@auto_timeout("knowledge_tools")
|
|
405
|
+
|
|
406
|
+
|
|
393
407
|
async def call(
|
|
394
408
|
self,
|
|
395
409
|
ctx: MCPContext,
|
|
@@ -7,6 +7,8 @@ The hanzo-memory package provides embedded database and vector search capabiliti
|
|
|
7
7
|
from typing import Dict, List, Optional, final, override
|
|
8
8
|
|
|
9
9
|
from mcp.server import FastMCP
|
|
10
|
+
|
|
11
|
+
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
|
10
12
|
from mcp.server.fastmcp import Context as MCPContext
|
|
11
13
|
|
|
12
14
|
from hanzo_mcp.tools.common.base import BaseTool
|
|
@@ -69,6 +71,9 @@ recall_memories(queries=["coding standards"], scope="global")
|
|
|
69
71
|
"""
|
|
70
72
|
|
|
71
73
|
@override
|
|
74
|
+
@auto_timeout("memory_tools")
|
|
75
|
+
|
|
76
|
+
|
|
72
77
|
async def call(
|
|
73
78
|
self,
|
|
74
79
|
ctx: MCPContext,
|
|
@@ -155,6 +160,9 @@ create_memories(statements=["User prefers dark mode", "User works in Python"])
|
|
|
155
160
|
"""
|
|
156
161
|
|
|
157
162
|
@override
|
|
163
|
+
@auto_timeout("memory_tools")
|
|
164
|
+
|
|
165
|
+
|
|
158
166
|
async def call(self, ctx: MCPContext, statements: List[str]) -> str:
|
|
159
167
|
"""Create new memories.
|
|
160
168
|
|
|
@@ -219,6 +227,9 @@ update_memories(updates=[
|
|
|
219
227
|
"""
|
|
220
228
|
|
|
221
229
|
@override
|
|
230
|
+
@auto_timeout("memory_tools")
|
|
231
|
+
|
|
232
|
+
|
|
222
233
|
async def call(self, ctx: MCPContext, updates: List[Dict[str, str]]) -> str:
|
|
223
234
|
"""Update memories.
|
|
224
235
|
|
|
@@ -283,6 +294,9 @@ delete_memories(ids=["mem_1", "mem_2"])
|
|
|
283
294
|
"""
|
|
284
295
|
|
|
285
296
|
@override
|
|
297
|
+
@auto_timeout("memory_tools")
|
|
298
|
+
|
|
299
|
+
|
|
286
300
|
async def call(self, ctx: MCPContext, ids: List[str]) -> str:
|
|
287
301
|
"""Delete memories.
|
|
288
302
|
|
|
@@ -345,6 +359,9 @@ manage_memories(
|
|
|
345
359
|
"""
|
|
346
360
|
|
|
347
361
|
@override
|
|
362
|
+
@auto_timeout("memory_tools")
|
|
363
|
+
|
|
364
|
+
|
|
348
365
|
async def call(
|
|
349
366
|
self,
|
|
350
367
|
ctx: MCPContext,
|
|
@@ -11,6 +11,7 @@ from dataclasses import dataclass
|
|
|
11
11
|
|
|
12
12
|
from hanzo_mcp.types import MCPResourceDocument
|
|
13
13
|
from hanzo_mcp.tools.common.base import BaseTool
|
|
14
|
+
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
|
14
15
|
|
|
15
16
|
# Check if ffind command is available
|
|
16
17
|
try:
|
|
@@ -75,8 +76,8 @@ class FindTool(BaseTool):
|
|
|
75
76
|
reading or searching within files.
|
|
76
77
|
"""
|
|
77
78
|
|
|
78
|
-
def __init__(self
|
|
79
|
-
super().__init__(
|
|
79
|
+
def __init__(self):
|
|
80
|
+
super().__init__()
|
|
80
81
|
self._cache = {}
|
|
81
82
|
self._gitignore_cache = {}
|
|
82
83
|
|
|
@@ -375,7 +376,8 @@ class FindTool(BaseTool):
|
|
|
375
376
|
}
|
|
376
377
|
)
|
|
377
378
|
|
|
378
|
-
|
|
379
|
+
@auto_timeout("find")
|
|
380
|
+
async def call(self, ctx=None, **kwargs) -> str:
|
|
379
381
|
"""Tool interface for MCP - converts result to JSON string."""
|
|
380
382
|
result = await self.run(**kwargs)
|
|
381
383
|
return result.to_json_string()
|
|
@@ -14,6 +14,7 @@ from dataclasses import dataclass
|
|
|
14
14
|
|
|
15
15
|
from hanzo_mcp.types import MCPResourceDocument
|
|
16
16
|
from hanzo_mcp.tools.common.base import BaseTool
|
|
17
|
+
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
|
17
18
|
|
|
18
19
|
# Import memory tools if available
|
|
19
20
|
try:
|
|
@@ -349,7 +350,8 @@ class UnifiedSearch(BaseTool):
|
|
|
349
350
|
|
|
350
351
|
return MCPResourceDocument(data=response_data)
|
|
351
352
|
|
|
352
|
-
|
|
353
|
+
@auto_timeout("search")
|
|
354
|
+
async def call(self, ctx=None, **kwargs) -> str:
|
|
353
355
|
"""Tool interface for MCP - converts result to JSON string."""
|
|
354
356
|
result = await self.run(**kwargs)
|
|
355
357
|
return result.to_json_string()
|
|
@@ -13,7 +13,6 @@ from hanzo_mcp.tools.shell.zsh_tool import zsh_tool, shell_tool
|
|
|
13
13
|
|
|
14
14
|
# Import tools
|
|
15
15
|
from hanzo_mcp.tools.shell.bash_tool import bash_tool
|
|
16
|
-
from hanzo_mcp.tools.shell.run_tool import run_tool
|
|
17
16
|
from hanzo_mcp.tools.common.permissions import PermissionManager
|
|
18
17
|
from hanzo_mcp.tools.shell.process_tool import process_tool
|
|
19
18
|
|
|
@@ -41,16 +40,14 @@ def get_shell_tools(
|
|
|
41
40
|
bash_tool.permission_manager = permission_manager
|
|
42
41
|
zsh_tool.permission_manager = permission_manager
|
|
43
42
|
shell_tool.permission_manager = permission_manager
|
|
44
|
-
run_tool.permission_manager = permission_manager
|
|
45
43
|
npx_tool.permission_manager = permission_manager
|
|
46
44
|
uvx_tool.permission_manager = permission_manager
|
|
47
45
|
|
|
48
46
|
# Note: StreamingCommandTool is abstract and shouldn't be instantiated directly
|
|
49
47
|
# It's used as a base class for other streaming tools
|
|
50
48
|
|
|
51
|
-
# Return
|
|
49
|
+
# Return shell_tool first (smart default), then specific shells
|
|
52
50
|
return [
|
|
53
|
-
run_tool, # Simplified run command with auto-backgrounding
|
|
54
51
|
shell_tool, # Smart shell (prefers zsh if available)
|
|
55
52
|
zsh_tool, # Explicit zsh
|
|
56
53
|
bash_tool, # Explicit bash
|
|
@@ -65,25 +62,16 @@ def get_shell_tools(
|
|
|
65
62
|
def register_shell_tools(
|
|
66
63
|
mcp_server: FastMCP,
|
|
67
64
|
permission_manager: PermissionManager,
|
|
68
|
-
enabled_tools: dict[str, bool] | None = None,
|
|
69
65
|
) -> list[BaseTool]:
|
|
70
66
|
"""Register all shell tools with the MCP server.
|
|
71
67
|
|
|
72
68
|
Args:
|
|
73
69
|
mcp_server: The FastMCP server instance
|
|
74
70
|
permission_manager: Permission manager for access control
|
|
75
|
-
enabled_tools: Optional dict of tool names to enable/disable
|
|
76
71
|
|
|
77
72
|
Returns:
|
|
78
73
|
List of registered tools
|
|
79
74
|
"""
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
# Filter tools based on enabled_tools if provided
|
|
83
|
-
if enabled_tools is not None:
|
|
84
|
-
tools = [tool for tool in all_tools if enabled_tools.get(tool.name, True)]
|
|
85
|
-
else:
|
|
86
|
-
tools = all_tools
|
|
87
|
-
|
|
75
|
+
tools = get_shell_tools(permission_manager)
|
|
88
76
|
ToolRegistry.register_tools(mcp_server, tools)
|
|
89
77
|
return tools
|
|
@@ -183,11 +183,13 @@ class BaseProcessTool(BaseTool):
|
|
|
183
183
|
else:
|
|
184
184
|
if output.startswith("Command failed"):
|
|
185
185
|
raise RuntimeError(output)
|
|
186
|
+
# Get configurable max tokens from environment, default to 25000
|
|
187
|
+
max_tokens = int(os.environ.get("HANZO_MCP_MAX_RESPONSE_TOKENS", "25000"))
|
|
186
188
|
# Truncate output to prevent token limit issues
|
|
187
189
|
return truncate_response(
|
|
188
190
|
output,
|
|
189
|
-
max_tokens=
|
|
190
|
-
truncation_message="\n\n[Command output truncated due to token limit. Output may be available in logs or files.]",
|
|
191
|
+
max_tokens=max_tokens,
|
|
192
|
+
truncation_message=f"\n\n[Command output truncated due to {max_tokens} token limit. Output may be available in logs or files. Set HANZO_MCP_MAX_RESPONSE_TOKENS env var to adjust limit.]",
|
|
191
193
|
)
|
|
192
194
|
|
|
193
195
|
async def execute_background(
|
|
@@ -8,6 +8,7 @@ from pathlib import Path
|
|
|
8
8
|
from mcp.server import FastMCP
|
|
9
9
|
from mcp.server.fastmcp import Context as MCPContext
|
|
10
10
|
|
|
11
|
+
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
|
11
12
|
from hanzo_mcp.tools.shell.base_process import BaseScriptTool
|
|
12
13
|
|
|
13
14
|
|
|
@@ -30,6 +31,7 @@ class BashTool(BaseScriptTool):
|
|
|
30
31
|
) -> str:
|
|
31
32
|
return await tool_self.run(ctx, command=command, cwd=cwd, env=env, timeout=timeout)
|
|
32
33
|
|
|
34
|
+
@auto_timeout("bash")
|
|
33
35
|
async def call(self, ctx: MCPContext, **params) -> str:
|
|
34
36
|
"""Call the tool with arguments."""
|
|
35
37
|
return await self.run(
|
|
@@ -109,13 +109,12 @@ class CommandExecutor:
|
|
|
109
109
|
if shell_basename in ["wsl", "wsl.exe"]:
|
|
110
110
|
# For WSL, handle commands with shell operators differently
|
|
111
111
|
if any(char in command for char in ";&|<>(){}[]$\"'`"):
|
|
112
|
-
#
|
|
113
|
-
|
|
114
|
-
escaped_command = command.replace('"', '\\"')
|
|
112
|
+
# Use shlex.quote for proper escaping to prevent command injection
|
|
113
|
+
escaped_command = shlex.quote(command)
|
|
115
114
|
if use_login_shell:
|
|
116
|
-
formatted_command = f'{user_shell} bash -l -c
|
|
115
|
+
formatted_command = f'{user_shell} bash -l -c {escaped_command}'
|
|
117
116
|
else:
|
|
118
|
-
formatted_command = f'{user_shell} bash -c
|
|
117
|
+
formatted_command = f'{user_shell} bash -c {escaped_command}'
|
|
119
118
|
else:
|
|
120
119
|
# # For simple commands without special characters
|
|
121
120
|
# # Still respect login shell preference
|
|
@@ -125,8 +124,9 @@ class CommandExecutor:
|
|
|
125
124
|
formatted_command = f"{user_shell} {command}"
|
|
126
125
|
|
|
127
126
|
elif shell_basename in ["powershell", "powershell.exe", "pwsh", "pwsh.exe"]:
|
|
128
|
-
#
|
|
129
|
-
|
|
127
|
+
# Use proper escaping for PowerShell to prevent injection
|
|
128
|
+
# PowerShell requires different escaping than POSIX shells
|
|
129
|
+
escaped_command = command.replace('"', '`"').replace("'", "``'").replace('$', '`$')
|
|
130
130
|
formatted_command = f'"{user_shell}" -Command "{escaped_command}"'
|
|
131
131
|
|
|
132
132
|
else:
|
hanzo_mcp/tools/shell/logs.py
CHANGED
|
@@ -6,6 +6,8 @@ from pathlib import Path
|
|
|
6
6
|
from pydantic import Field
|
|
7
7
|
from mcp.server.fastmcp import Context as MCPContext
|
|
8
8
|
|
|
9
|
+
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
|
10
|
+
|
|
9
11
|
from hanzo_mcp.tools.common.base import BaseTool
|
|
10
12
|
from hanzo_mcp.tools.common.context import create_tool_context
|
|
11
13
|
from hanzo_mcp.tools.common.permissions import PermissionManager
|
|
@@ -105,6 +107,9 @@ Use run_command with 'tail -f' for continuous monitoring.
|
|
|
105
107
|
"""
|
|
106
108
|
|
|
107
109
|
@override
|
|
110
|
+
@auto_timeout("logs")
|
|
111
|
+
|
|
112
|
+
|
|
108
113
|
async def call(
|
|
109
114
|
self,
|
|
110
115
|
ctx: MCPContext,
|
hanzo_mcp/tools/shell/npx.py
CHANGED
|
@@ -7,6 +7,8 @@ from typing import Unpack, Optional, Annotated, TypedDict, final, override
|
|
|
7
7
|
from pydantic import Field
|
|
8
8
|
from mcp.server.fastmcp import Context as MCPContext
|
|
9
9
|
|
|
10
|
+
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
|
11
|
+
|
|
10
12
|
from hanzo_mcp.tools.common.base import BaseTool
|
|
11
13
|
from hanzo_mcp.tools.common.context import create_tool_context
|
|
12
14
|
from hanzo_mcp.tools.common.permissions import PermissionManager
|
|
@@ -103,6 +105,9 @@ For long-running servers, use npx_background instead.
|
|
|
103
105
|
"""
|
|
104
106
|
|
|
105
107
|
@override
|
|
108
|
+
@auto_timeout("npx")
|
|
109
|
+
|
|
110
|
+
|
|
106
111
|
async def call(
|
|
107
112
|
self,
|
|
108
113
|
ctx: MCPContext,
|
|
@@ -8,6 +8,8 @@ from typing import Unpack, Optional, Annotated, TypedDict, final, override
|
|
|
8
8
|
from pydantic import Field
|
|
9
9
|
from mcp.server.fastmcp import Context as MCPContext
|
|
10
10
|
|
|
11
|
+
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
|
12
|
+
|
|
11
13
|
from hanzo_mcp.tools.common.base import BaseTool
|
|
12
14
|
from hanzo_mcp.tools.common.context import create_tool_context
|
|
13
15
|
from hanzo_mcp.tools.common.permissions import PermissionManager
|
|
@@ -120,6 +122,9 @@ Use 'processes' to list running processes and 'pkill' to stop them.
|
|
|
120
122
|
"""
|
|
121
123
|
|
|
122
124
|
@override
|
|
125
|
+
@auto_timeout("npx")
|
|
126
|
+
|
|
127
|
+
|
|
123
128
|
async def call(
|
|
124
129
|
self,
|
|
125
130
|
ctx: MCPContext,
|
|
@@ -4,6 +4,8 @@ from typing import Optional, override
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
6
|
from mcp.server import FastMCP
|
|
7
|
+
|
|
8
|
+
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
|
7
9
|
from mcp.server.fastmcp import Context as MCPContext
|
|
8
10
|
|
|
9
11
|
from hanzo_mcp.tools.shell.base_process import BaseBinaryTool
|
|
@@ -88,6 +90,9 @@ npx json-server db.json # Auto-backgrounds if needed"""
|
|
|
88
90
|
) -> str:
|
|
89
91
|
return await tool_self.run(ctx, package=package, args=args, cwd=cwd, yes=yes)
|
|
90
92
|
|
|
93
|
+
@auto_timeout("npx")
|
|
94
|
+
|
|
95
|
+
|
|
91
96
|
async def call(self, ctx: MCPContext, **params) -> str:
|
|
92
97
|
"""Call the tool with arguments."""
|
|
93
98
|
return await self.run(
|
hanzo_mcp/tools/shell/open.py
CHANGED
|
@@ -8,6 +8,8 @@ from pathlib import Path
|
|
|
8
8
|
from urllib.parse import urlparse
|
|
9
9
|
|
|
10
10
|
from mcp.server import FastMCP
|
|
11
|
+
|
|
12
|
+
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
|
11
13
|
from mcp.server.fastmcp import Context as MCPContext
|
|
12
14
|
|
|
13
15
|
from hanzo_mcp.tools.common.base import BaseTool
|
|
@@ -26,6 +28,9 @@ class OpenTool(BaseTool):
|
|
|
26
28
|
async def open(path: str, ctx: MCPContext) -> str:
|
|
27
29
|
return await tool_self.run(ctx, path)
|
|
28
30
|
|
|
31
|
+
@auto_timeout("open")
|
|
32
|
+
|
|
33
|
+
|
|
29
34
|
async def call(self, ctx: MCPContext, **params) -> str:
|
|
30
35
|
"""Call the tool with arguments."""
|
|
31
36
|
return await self.run(ctx, params["path"])
|
hanzo_mcp/tools/shell/pkill.py
CHANGED
|
@@ -7,6 +7,8 @@ import psutil
|
|
|
7
7
|
from pydantic import Field
|
|
8
8
|
from mcp.server.fastmcp import Context as MCPContext
|
|
9
9
|
|
|
10
|
+
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
|
11
|
+
|
|
10
12
|
from hanzo_mcp.tools.common.base import BaseTool
|
|
11
13
|
from hanzo_mcp.tools.common.context import create_tool_context
|
|
12
14
|
from hanzo_mcp.tools.common.permissions import PermissionManager
|
|
@@ -106,6 +108,9 @@ Examples:
|
|
|
106
108
|
"""
|
|
107
109
|
|
|
108
110
|
@override
|
|
111
|
+
@auto_timeout("pkill")
|
|
112
|
+
|
|
113
|
+
|
|
109
114
|
async def call(
|
|
110
115
|
self,
|
|
111
116
|
ctx: MCPContext,
|
|
@@ -4,6 +4,8 @@ import signal
|
|
|
4
4
|
from typing import Optional, override
|
|
5
5
|
|
|
6
6
|
from mcp.server import FastMCP
|
|
7
|
+
|
|
8
|
+
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
|
7
9
|
from mcp.server.fastmcp import Context as MCPContext
|
|
8
10
|
|
|
9
11
|
from hanzo_mcp.tools.common.base import BaseTool
|
|
@@ -132,6 +134,9 @@ process --action logs --id bash_ghi789 --lines 50"""
|
|
|
132
134
|
) -> str:
|
|
133
135
|
return await tool_self.run(ctx, action=action, id=id, signal_type=signal_type, lines=lines)
|
|
134
136
|
|
|
137
|
+
@auto_timeout("process")
|
|
138
|
+
|
|
139
|
+
|
|
135
140
|
async def call(self, ctx: MCPContext, **params) -> str:
|
|
136
141
|
"""Call the tool with arguments."""
|
|
137
142
|
return await self.run(
|
|
@@ -7,6 +7,8 @@ import psutil
|
|
|
7
7
|
from pydantic import Field
|
|
8
8
|
from mcp.server.fastmcp import Context as MCPContext
|
|
9
9
|
|
|
10
|
+
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
|
11
|
+
|
|
10
12
|
from hanzo_mcp.tools.common.base import BaseTool
|
|
11
13
|
from hanzo_mcp.tools.common.context import create_tool_context
|
|
12
14
|
from hanzo_mcp.tools.common.permissions import PermissionManager
|
|
@@ -90,6 +92,9 @@ Examples:
|
|
|
90
92
|
"""
|
|
91
93
|
|
|
92
94
|
@override
|
|
95
|
+
@auto_timeout("processes")
|
|
96
|
+
|
|
97
|
+
|
|
93
98
|
async def call(
|
|
94
99
|
self,
|
|
95
100
|
ctx: MCPContext,
|
|
@@ -10,6 +10,8 @@ from datetime import datetime
|
|
|
10
10
|
from pydantic import Field
|
|
11
11
|
from mcp.server.fastmcp import Context as MCPContext
|
|
12
12
|
|
|
13
|
+
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
|
14
|
+
|
|
13
15
|
from hanzo_mcp.tools.common.base import BaseTool
|
|
14
16
|
from hanzo_mcp.tools.common.context import create_tool_context
|
|
15
17
|
from hanzo_mcp.tools.common.permissions import PermissionManager
|
|
@@ -181,6 +183,9 @@ Examples:
|
|
|
181
183
|
"""
|
|
182
184
|
|
|
183
185
|
@override
|
|
186
|
+
@auto_timeout("run")
|
|
187
|
+
|
|
188
|
+
|
|
184
189
|
async def call(
|
|
185
190
|
self,
|
|
186
191
|
ctx: MCPContext,
|
|
@@ -9,6 +9,7 @@ from pydantic import Field
|
|
|
9
9
|
from mcp.server import FastMCP
|
|
10
10
|
from mcp.server.fastmcp import Context as MCPContext
|
|
11
11
|
|
|
12
|
+
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
|
12
13
|
from hanzo_mcp.tools.shell.base import ShellBaseTool
|
|
13
14
|
from hanzo_mcp.tools.common.base import handle_connection_errors
|
|
14
15
|
from hanzo_mcp.tools.common.context import create_tool_context
|
|
@@ -269,6 +270,7 @@ Important:
|
|
|
269
270
|
return tool_ctx
|
|
270
271
|
|
|
271
272
|
@override
|
|
273
|
+
@auto_timeout("run_command")
|
|
272
274
|
async def call(
|
|
273
275
|
self,
|
|
274
276
|
ctx: MCPContext,
|
|
@@ -8,6 +8,8 @@ from typing import Any, Annotated, final, override
|
|
|
8
8
|
|
|
9
9
|
from pydantic import Field
|
|
10
10
|
from mcp.server import FastMCP
|
|
11
|
+
|
|
12
|
+
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
|
11
13
|
from mcp.server.fastmcp import Context as MCPContext
|
|
12
14
|
|
|
13
15
|
from hanzo_mcp.tools.shell.base import ShellBaseTool
|
|
@@ -194,6 +196,9 @@ Important:
|
|
|
194
196
|
return tool_ctx
|
|
195
197
|
|
|
196
198
|
@override
|
|
199
|
+
@auto_timeout("run_command_windows")
|
|
200
|
+
|
|
201
|
+
|
|
197
202
|
async def call(self, ctx: MCPContext, **params: Any) -> str:
|
|
198
203
|
"""Execute the tool with the given parameters.
|
|
199
204
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
|
|
2
|
+
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
|
1
3
|
"""Streaming command execution with disk-based logging and session management."""
|
|
2
4
|
|
|
3
5
|
import os
|
|
@@ -173,6 +175,9 @@ class StreamingCommandTool(BaseProcessTool):
|
|
|
173
175
|
|
|
174
176
|
return None
|
|
175
177
|
|
|
178
|
+
@auto_timeout("streaming_command")
|
|
179
|
+
|
|
180
|
+
|
|
176
181
|
async def call(self, ctx: Any, **kwargs) -> Dict[str, Any]:
|
|
177
182
|
"""MCP tool entry point.
|
|
178
183
|
|
hanzo_mcp/tools/shell/uvx.py
CHANGED
|
@@ -7,6 +7,8 @@ from typing import Unpack, Optional, Annotated, TypedDict, final, override
|
|
|
7
7
|
from pydantic import Field
|
|
8
8
|
from mcp.server.fastmcp import Context as MCPContext
|
|
9
9
|
|
|
10
|
+
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
|
11
|
+
|
|
10
12
|
from hanzo_mcp.tools.common.base import BaseTool
|
|
11
13
|
from hanzo_mcp.tools.common.context import create_tool_context
|
|
12
14
|
from hanzo_mcp.tools.common.permissions import PermissionManager
|
|
@@ -100,6 +102,9 @@ For long-running servers, use uvx_background instead.
|
|
|
100
102
|
"""
|
|
101
103
|
|
|
102
104
|
@override
|
|
105
|
+
@auto_timeout("uvx")
|
|
106
|
+
|
|
107
|
+
|
|
103
108
|
async def call(
|
|
104
109
|
self,
|
|
105
110
|
ctx: MCPContext,
|
|
@@ -8,6 +8,8 @@ from typing import Unpack, Optional, Annotated, TypedDict, final, override
|
|
|
8
8
|
from pydantic import Field
|
|
9
9
|
from mcp.server.fastmcp import Context as MCPContext
|
|
10
10
|
|
|
11
|
+
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
|
12
|
+
|
|
11
13
|
from hanzo_mcp.tools.common.base import BaseTool
|
|
12
14
|
from hanzo_mcp.tools.common.context import create_tool_context
|
|
13
15
|
from hanzo_mcp.tools.common.permissions import PermissionManager
|
|
@@ -119,6 +121,9 @@ Use 'processes' to list running processes and 'pkill' to stop them.
|
|
|
119
121
|
"""
|
|
120
122
|
|
|
121
123
|
@override
|
|
124
|
+
@auto_timeout("uvx")
|
|
125
|
+
|
|
126
|
+
|
|
122
127
|
async def call(
|
|
123
128
|
self,
|
|
124
129
|
ctx: MCPContext,
|
|
@@ -4,6 +4,8 @@ from typing import Optional, override
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
6
|
from mcp.server import FastMCP
|
|
7
|
+
|
|
8
|
+
from hanzo_mcp.tools.common.auto_timeout import auto_timeout
|
|
7
9
|
from mcp.server.fastmcp import Context as MCPContext
|
|
8
10
|
|
|
9
11
|
from hanzo_mcp.tools.shell.base_process import BaseBinaryTool
|
|
@@ -88,6 +90,9 @@ uvx jupyter lab --port 8888 # Auto-backgrounds if needed"""
|
|
|
88
90
|
) -> str:
|
|
89
91
|
return await tool_self.run(ctx, package=package, args=args, cwd=cwd, python=python)
|
|
90
92
|
|
|
93
|
+
@auto_timeout("uvx")
|
|
94
|
+
|
|
95
|
+
|
|
91
96
|
async def call(self, ctx: MCPContext, **params) -> str:
|
|
92
97
|
"""Call the tool with arguments."""
|
|
93
98
|
return await self.run(
|