hanzo-mcp 0.9.0__py3-none-any.whl → 0.9.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 (135) hide show
  1. hanzo_mcp/__init__.py +1 -1
  2. hanzo_mcp/analytics/posthog_analytics.py +14 -1
  3. hanzo_mcp/cli.py +108 -4
  4. hanzo_mcp/server.py +11 -0
  5. hanzo_mcp/tools/__init__.py +3 -16
  6. hanzo_mcp/tools/agent/__init__.py +5 -0
  7. hanzo_mcp/tools/agent/agent.py +5 -0
  8. hanzo_mcp/tools/agent/agent_tool.py +3 -17
  9. hanzo_mcp/tools/agent/agent_tool_v1_deprecated.py +623 -0
  10. hanzo_mcp/tools/agent/clarification_tool.py +7 -1
  11. hanzo_mcp/tools/agent/claude_desktop_auth.py +16 -6
  12. hanzo_mcp/tools/agent/cli_agent_base.py +5 -0
  13. hanzo_mcp/tools/agent/cli_tools.py +26 -0
  14. hanzo_mcp/tools/agent/code_auth_tool.py +5 -0
  15. hanzo_mcp/tools/agent/critic_tool.py +7 -1
  16. hanzo_mcp/tools/agent/iching_tool.py +5 -0
  17. hanzo_mcp/tools/agent/network_tool.py +5 -0
  18. hanzo_mcp/tools/agent/review_tool.py +7 -1
  19. hanzo_mcp/tools/agent/swarm_alias.py +5 -0
  20. hanzo_mcp/tools/agent/swarm_tool.py +701 -0
  21. hanzo_mcp/tools/agent/swarm_tool_v1_deprecated.py +554 -0
  22. hanzo_mcp/tools/agent/unified_cli_tools.py +5 -0
  23. hanzo_mcp/tools/common/auto_timeout.py +254 -0
  24. hanzo_mcp/tools/common/base.py +4 -0
  25. hanzo_mcp/tools/common/batch_tool.py +5 -0
  26. hanzo_mcp/tools/common/config_tool.py +5 -0
  27. hanzo_mcp/tools/common/critic_tool.py +5 -0
  28. hanzo_mcp/tools/common/paginated_base.py +4 -0
  29. hanzo_mcp/tools/common/permissions.py +38 -12
  30. hanzo_mcp/tools/common/personality.py +673 -980
  31. hanzo_mcp/tools/common/stats.py +5 -0
  32. hanzo_mcp/tools/common/thinking_tool.py +5 -0
  33. hanzo_mcp/tools/common/timeout_parser.py +103 -0
  34. hanzo_mcp/tools/common/tool_disable.py +5 -0
  35. hanzo_mcp/tools/common/tool_enable.py +5 -0
  36. hanzo_mcp/tools/common/tool_list.py +5 -0
  37. hanzo_mcp/tools/config/config_tool.py +5 -0
  38. hanzo_mcp/tools/config/mode_tool.py +5 -0
  39. hanzo_mcp/tools/database/graph.py +5 -0
  40. hanzo_mcp/tools/database/graph_add.py +5 -0
  41. hanzo_mcp/tools/database/graph_query.py +5 -0
  42. hanzo_mcp/tools/database/graph_remove.py +5 -0
  43. hanzo_mcp/tools/database/graph_search.py +5 -0
  44. hanzo_mcp/tools/database/graph_stats.py +5 -0
  45. hanzo_mcp/tools/database/sql.py +5 -0
  46. hanzo_mcp/tools/database/sql_query.py +2 -0
  47. hanzo_mcp/tools/database/sql_search.py +5 -0
  48. hanzo_mcp/tools/database/sql_stats.py +5 -0
  49. hanzo_mcp/tools/editor/neovim_command.py +5 -0
  50. hanzo_mcp/tools/editor/neovim_edit.py +7 -2
  51. hanzo_mcp/tools/editor/neovim_session.py +5 -0
  52. hanzo_mcp/tools/filesystem/__init__.py +23 -26
  53. hanzo_mcp/tools/filesystem/ast_tool.py +3 -4
  54. hanzo_mcp/tools/filesystem/base.py +2 -18
  55. hanzo_mcp/tools/filesystem/batch_search.py +825 -0
  56. hanzo_mcp/tools/filesystem/content_replace.py +5 -3
  57. hanzo_mcp/tools/filesystem/diff.py +5 -0
  58. hanzo_mcp/tools/filesystem/directory_tree.py +34 -281
  59. hanzo_mcp/tools/filesystem/directory_tree_paginated.py +345 -0
  60. hanzo_mcp/tools/filesystem/edit.py +6 -5
  61. hanzo_mcp/tools/filesystem/find.py +177 -311
  62. hanzo_mcp/tools/filesystem/find_files.py +370 -0
  63. hanzo_mcp/tools/filesystem/git_search.py +5 -3
  64. hanzo_mcp/tools/filesystem/grep.py +454 -0
  65. hanzo_mcp/tools/filesystem/multi_edit.py +6 -5
  66. hanzo_mcp/tools/filesystem/read.py +10 -9
  67. hanzo_mcp/tools/filesystem/rules_tool.py +6 -4
  68. hanzo_mcp/tools/filesystem/search_tool.py +728 -0
  69. hanzo_mcp/tools/filesystem/symbols_tool.py +510 -0
  70. hanzo_mcp/tools/filesystem/tree.py +273 -0
  71. hanzo_mcp/tools/filesystem/watch.py +6 -1
  72. hanzo_mcp/tools/filesystem/write.py +13 -7
  73. hanzo_mcp/tools/jupyter/jupyter.py +30 -2
  74. hanzo_mcp/tools/jupyter/notebook_edit.py +298 -0
  75. hanzo_mcp/tools/jupyter/notebook_read.py +148 -0
  76. hanzo_mcp/tools/llm/consensus_tool.py +8 -6
  77. hanzo_mcp/tools/llm/llm_manage.py +5 -0
  78. hanzo_mcp/tools/llm/llm_tool.py +2 -0
  79. hanzo_mcp/tools/llm/llm_unified.py +5 -0
  80. hanzo_mcp/tools/llm/provider_tools.py +5 -0
  81. hanzo_mcp/tools/lsp/lsp_tool.py +475 -622
  82. hanzo_mcp/tools/mcp/mcp_add.py +7 -2
  83. hanzo_mcp/tools/mcp/mcp_remove.py +15 -2
  84. hanzo_mcp/tools/mcp/mcp_stats.py +5 -0
  85. hanzo_mcp/tools/mcp/mcp_tool.py +5 -0
  86. hanzo_mcp/tools/memory/knowledge_tools.py +14 -0
  87. hanzo_mcp/tools/memory/memory_tools.py +17 -0
  88. hanzo_mcp/tools/search/find_tool.py +5 -3
  89. hanzo_mcp/tools/search/unified_search.py +3 -1
  90. hanzo_mcp/tools/shell/__init__.py +2 -14
  91. hanzo_mcp/tools/shell/base_process.py +4 -2
  92. hanzo_mcp/tools/shell/bash_tool.py +2 -0
  93. hanzo_mcp/tools/shell/command_executor.py +7 -7
  94. hanzo_mcp/tools/shell/logs.py +5 -0
  95. hanzo_mcp/tools/shell/npx.py +5 -0
  96. hanzo_mcp/tools/shell/npx_background.py +5 -0
  97. hanzo_mcp/tools/shell/npx_tool.py +5 -0
  98. hanzo_mcp/tools/shell/open.py +5 -0
  99. hanzo_mcp/tools/shell/pkill.py +5 -0
  100. hanzo_mcp/tools/shell/process_tool.py +5 -0
  101. hanzo_mcp/tools/shell/processes.py +5 -0
  102. hanzo_mcp/tools/shell/run_background.py +5 -0
  103. hanzo_mcp/tools/shell/run_command.py +2 -0
  104. hanzo_mcp/tools/shell/run_command_windows.py +5 -0
  105. hanzo_mcp/tools/shell/streaming_command.py +5 -0
  106. hanzo_mcp/tools/shell/uvx.py +5 -0
  107. hanzo_mcp/tools/shell/uvx_background.py +5 -0
  108. hanzo_mcp/tools/shell/uvx_tool.py +5 -0
  109. hanzo_mcp/tools/shell/zsh_tool.py +3 -0
  110. hanzo_mcp/tools/todo/todo.py +5 -0
  111. hanzo_mcp/tools/todo/todo_read.py +142 -0
  112. hanzo_mcp/tools/todo/todo_write.py +367 -0
  113. hanzo_mcp/tools/vector/__init__.py +42 -95
  114. hanzo_mcp/tools/vector/index_tool.py +5 -0
  115. hanzo_mcp/tools/vector/vector.py +5 -0
  116. hanzo_mcp/tools/vector/vector_index.py +5 -0
  117. hanzo_mcp/tools/vector/vector_search.py +5 -0
  118. {hanzo_mcp-0.9.0.dist-info → hanzo_mcp-0.9.2.dist-info}/METADATA +1 -1
  119. hanzo_mcp-0.9.2.dist-info/RECORD +195 -0
  120. hanzo_mcp/tools/common/path_utils.py +0 -34
  121. hanzo_mcp/tools/compiler/__init__.py +0 -8
  122. hanzo_mcp/tools/compiler/sandboxed_compiler.py +0 -681
  123. hanzo_mcp/tools/environment/__init__.py +0 -8
  124. hanzo_mcp/tools/environment/environment_detector.py +0 -594
  125. hanzo_mcp/tools/filesystem/search.py +0 -1160
  126. hanzo_mcp/tools/framework/__init__.py +0 -8
  127. hanzo_mcp/tools/framework/framework_modes.py +0 -714
  128. hanzo_mcp/tools/memory/conversation_memory.py +0 -636
  129. hanzo_mcp/tools/shell/run_tool.py +0 -56
  130. hanzo_mcp/tools/vector/node_tool.py +0 -538
  131. hanzo_mcp/tools/vector/unified_vector.py +0 -384
  132. hanzo_mcp-0.9.0.dist-info/RECORD +0 -191
  133. {hanzo_mcp-0.9.0.dist-info → hanzo_mcp-0.9.2.dist-info}/WHEEL +0 -0
  134. {hanzo_mcp-0.9.0.dist-info → hanzo_mcp-0.9.2.dist-info}/entry_points.txt +0 -0
  135. {hanzo_mcp-0.9.0.dist-info → hanzo_mcp-0.9.2.dist-info}/top_level.txt +0 -0
@@ -7,6 +7,8 @@ from datetime import datetime
7
7
  import psutil
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.mcp.mcp_add import McpAddTool
12
14
  from hanzo_mcp.tools.common.context import create_tool_context
@@ -57,6 +59,9 @@ Example:
57
59
  """
58
60
 
59
61
  @override
62
+ @auto_timeout("stats")
63
+
64
+
60
65
  async def call(
61
66
  self,
62
67
  ctx: MCPContext,
@@ -7,6 +7,8 @@ from typing import Unpack, Annotated, TypedDict, final, override
7
7
 
8
8
  from pydantic import Field
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
@@ -93,6 +95,9 @@ Feature Implementation Planning
93
95
  pass
94
96
 
95
97
  @override
98
+ @auto_timeout("thinking")
99
+
100
+
96
101
  async def call(
97
102
  self,
98
103
  ctx: MCPContext,
@@ -0,0 +1,103 @@
1
+ """Human-readable timeout parsing utilities."""
2
+
3
+ import re
4
+ from typing import Union
5
+
6
+
7
+ def parse_timeout(timeout_str: Union[str, int, float]) -> float:
8
+ """Parse timeout from human-readable string or numeric value.
9
+
10
+ Supports formats like:
11
+ - "2min", "5m", "120s", "30sec", "1.5h", "0.5hr"
12
+ - 120 (seconds as number)
13
+ - "120" (seconds as string)
14
+
15
+ Args:
16
+ timeout_str: Timeout value as string or number
17
+
18
+ Returns:
19
+ Timeout in seconds as float
20
+
21
+ Raises:
22
+ ValueError: If format is not recognized
23
+ """
24
+ if isinstance(timeout_str, (int, float)):
25
+ return float(timeout_str)
26
+
27
+ if isinstance(timeout_str, str):
28
+ # Handle pure numeric strings
29
+ try:
30
+ return float(timeout_str)
31
+ except ValueError:
32
+ pass
33
+
34
+ # Handle human-readable formats
35
+ timeout_str = timeout_str.lower().strip()
36
+
37
+ # Regex patterns for different time units
38
+ patterns = [
39
+ # Hours: 1h, 1.5hr, 2hour, 3hours
40
+ (r'^(\d*\.?\d+)\s*h(?:r|our|ours)?$', 3600),
41
+ # Minutes: 2m, 5min, 10mins, 1.5minute
42
+ (r'^(\d*\.?\d+)\s*m(?:in|ins|inute|inutes)?$', 60),
43
+ # Seconds: 30s, 120sec, 45secs, 60second, 90seconds
44
+ (r'^(\d*\.?\d+)\s*s(?:ec|ecs|econd|econds)?$', 1),
45
+ ]
46
+
47
+ for pattern, multiplier in patterns:
48
+ match = re.match(pattern, timeout_str)
49
+ if match:
50
+ value = float(match.group(1))
51
+ return value * multiplier
52
+
53
+ # If no pattern matches, raise error
54
+ raise ValueError(
55
+ f"Invalid timeout format: '{timeout_str}'. "
56
+ f"Supported formats: 2min, 5m, 120s, 30sec, 1.5h, 0.5hr, or numeric seconds."
57
+ )
58
+
59
+ raise ValueError(f"Unsupported timeout type: {type(timeout_str)}")
60
+
61
+
62
+ def format_timeout(seconds: float) -> str:
63
+ """Format timeout seconds into human-readable string.
64
+
65
+ Args:
66
+ seconds: Timeout in seconds
67
+
68
+ Returns:
69
+ Human-readable string like "2m", "90s", "1.5h"
70
+ """
71
+ if seconds >= 3600: # >= 1 hour
72
+ hours = seconds / 3600
73
+ if hours.is_integer():
74
+ return f"{int(hours)}h"
75
+ else:
76
+ return f"{hours:.1f}h"
77
+ elif seconds >= 60: # >= 1 minute
78
+ minutes = seconds / 60
79
+ if minutes.is_integer():
80
+ return f"{int(minutes)}m"
81
+ else:
82
+ return f"{minutes:.1f}m"
83
+ else: # < 1 minute
84
+ if seconds.is_integer():
85
+ return f"{int(seconds)}s"
86
+ else:
87
+ return f"{seconds:.1f}s"
88
+
89
+
90
+ # Test the parser
91
+ if __name__ == "__main__":
92
+ test_cases = [
93
+ "2min", "5m", "120s", "30sec", "1.5h", "0.5hr",
94
+ "90", 120, 3600.0, "1hour", "2hours", "30seconds"
95
+ ]
96
+
97
+ for case in test_cases:
98
+ try:
99
+ result = parse_timeout(case)
100
+ formatted = format_timeout(result)
101
+ print(f"{case} -> {result}s ({formatted})")
102
+ except ValueError as e:
103
+ print(f"{case} -> ERROR: {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.common.context import create_tool_context
10
12
  from hanzo_mcp.tools.common.tool_enable import ToolEnableTool
@@ -72,6 +74,9 @@ Use 'tool_enable' to re-enable disabled tools.
72
74
  """
73
75
 
74
76
  @override
77
+ @auto_timeout("tool_disable")
78
+
79
+
75
80
  async def call(
76
81
  self,
77
82
  ctx: MCPContext,
@@ -7,6 +7,8 @@ from pathlib import Path
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
 
@@ -119,6 +121,9 @@ Use 'tool_list' to see all available tools and their status.
119
121
  """
120
122
 
121
123
  @override
124
+ @auto_timeout("tool_enable")
125
+
126
+
122
127
  async def call(
123
128
  self,
124
129
  ctx: MCPContext,
@@ -5,6 +5,8 @@ from typing import Unpack, Optional, 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.common.context import create_tool_context
10
12
  from hanzo_mcp.tools.common.tool_enable import ToolEnableTool
@@ -158,6 +160,9 @@ Use 'tool_enable' and 'tool_disable' to change tool status.
158
160
  """
159
161
 
160
162
  @override
163
+ @auto_timeout("tool_list")
164
+
165
+
161
166
  async def call(
162
167
  self,
163
168
  ctx: MCPContext,
@@ -9,6 +9,8 @@ from pathlib import Path
9
9
  from pydantic import Field
10
10
  from mcp.server.fastmcp import Context as MCPContext
11
11
 
12
+ from hanzo_mcp.tools.common.auto_timeout import auto_timeout
13
+
12
14
  from hanzo_mcp.config import load_settings, save_settings
13
15
  from hanzo_mcp.tools.common.base import BaseTool
14
16
  from hanzo_mcp.tools.common.permissions import PermissionManager
@@ -104,6 +106,9 @@ config --action list
104
106
  config --action toggle index.scope --path ./project"""
105
107
 
106
108
  @override
109
+ @auto_timeout("config")
110
+
111
+
107
112
  async def call(
108
113
  self,
109
114
  ctx: MCPContext,
@@ -3,6 +3,8 @@
3
3
  from typing import Optional, override
4
4
 
5
5
  from mcp.server import FastMCP
6
+
7
+ from hanzo_mcp.tools.common.auto_timeout import auto_timeout
6
8
  from mcp.server.fastmcp import Context as MCPContext
7
9
 
8
10
  from hanzo_mcp.tools.common.base import BaseTool
@@ -310,6 +312,9 @@ mode --action current"""
310
312
  """Handle mode tool calls."""
311
313
  return await tool_self.run(ctx, action=action, name=name)
312
314
 
315
+ @auto_timeout("mode")
316
+
317
+
313
318
  async def call(self, ctx: MCPContext, **params) -> str:
314
319
  """Call the tool with arguments."""
315
320
  return await self.run(ctx, action=params.get("action", "list"), name=params.get("name"))
@@ -15,6 +15,8 @@ from typing import (
15
15
  from pydantic import Field
16
16
  from mcp.server.fastmcp import Context as MCPContext
17
17
 
18
+ from hanzo_mcp.tools.common.auto_timeout import auto_timeout
19
+
18
20
  from hanzo_mcp.tools.common.base import BaseTool
19
21
  from hanzo_mcp.tools.common.permissions import PermissionManager
20
22
  from hanzo_mcp.tools.database.database_manager import DatabaseManager
@@ -146,6 +148,9 @@ graph --action search --pattern "John" --node-type User
146
148
  """
147
149
 
148
150
  @override
151
+ @auto_timeout("graph")
152
+
153
+
149
154
  async def call(
150
155
  self,
151
156
  ctx: MCPContext,
@@ -6,6 +6,8 @@ from typing import Unpack, Optional, Annotated, TypedDict, final, override
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
@@ -137,6 +139,9 @@ Examples:
137
139
  """
138
140
 
139
141
  @override
142
+ @auto_timeout("graph_add")
143
+
144
+
140
145
  async def call(
141
146
  self,
142
147
  ctx: MCPContext,
@@ -15,6 +15,8 @@ from collections import deque
15
15
  from pydantic import Field
16
16
  from mcp.server.fastmcp import Context as MCPContext
17
17
 
18
+ from hanzo_mcp.tools.common.auto_timeout import auto_timeout
19
+
18
20
  from hanzo_mcp.tools.common.base import BaseTool
19
21
  from hanzo_mcp.tools.common.context import create_tool_context
20
22
  from hanzo_mcp.tools.common.permissions import PermissionManager
@@ -147,6 +149,9 @@ Examples:
147
149
  """
148
150
 
149
151
  @override
152
+ @auto_timeout("graph_query")
153
+
154
+
150
155
  async def call(
151
156
  self,
152
157
  ctx: MCPContext,
@@ -5,6 +5,8 @@ from typing import Unpack, Optional, 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.common.context import create_tool_context
10
12
  from hanzo_mcp.tools.common.permissions import PermissionManager
@@ -116,6 +118,9 @@ Examples:
116
118
  """
117
119
 
118
120
  @override
121
+ @auto_timeout("graph_remove")
122
+
123
+
119
124
  async def call(
120
125
  self,
121
126
  ctx: MCPContext,
@@ -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
@@ -117,6 +119,9 @@ Examples:
117
119
  """
118
120
 
119
121
  @override
122
+ @auto_timeout("graph_search")
123
+
124
+
120
125
  async def call(
121
126
  self,
122
127
  ctx: MCPContext,
@@ -7,6 +7,8 @@ from collections import defaultdict
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
@@ -97,6 +99,9 @@ Examples:
97
99
  """
98
100
 
99
101
  @override
102
+ @auto_timeout("graph_stats")
103
+
104
+
100
105
  async def call(
101
106
  self,
102
107
  ctx: MCPContext,
@@ -15,6 +15,8 @@ from typing import (
15
15
  from pydantic import Field
16
16
  from mcp.server.fastmcp import Context as MCPContext
17
17
 
18
+ from hanzo_mcp.tools.common.auto_timeout import auto_timeout
19
+
18
20
  from hanzo_mcp.tools.common.base import BaseTool
19
21
  from hanzo_mcp.tools.common.permissions import PermissionManager
20
22
  from hanzo_mcp.tools.database.database_manager import DatabaseManager
@@ -100,6 +102,9 @@ sql --action stats --table users
100
102
  """
101
103
 
102
104
  @override
105
+ @auto_timeout("sql")
106
+
107
+
103
108
  async def call(
104
109
  self,
105
110
  ctx: MCPContext,
@@ -6,6 +6,7 @@ from typing import Unpack, Optional, Annotated, TypedDict, final, override
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
9
10
  from hanzo_mcp.tools.common.base import BaseTool
10
11
  from hanzo_mcp.tools.common.context import create_tool_context
11
12
  from hanzo_mcp.tools.common.permissions import PermissionManager
@@ -89,6 +90,7 @@ Examples:
89
90
  Note: Use sql_search for text search operations."""
90
91
 
91
92
  @override
93
+ @auto_timeout("sql_query")
92
94
  async def call(
93
95
  self,
94
96
  ctx: MCPContext,
@@ -6,6 +6,8 @@ from typing import Unpack, Optional, Annotated, TypedDict, final, override
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
@@ -107,6 +109,9 @@ Examples:
107
109
  Use sql_query for complex queries with joins, conditions, etc."""
108
110
 
109
111
  @override
112
+ @auto_timeout("sql_search")
113
+
114
+
110
115
  async def call(
111
116
  self,
112
117
  ctx: MCPContext,
@@ -6,6 +6,8 @@ from typing import Unpack, Optional, Annotated, TypedDict, final, override
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
@@ -75,6 +77,9 @@ Examples:
75
77
  """
76
78
 
77
79
  @override
80
+ @auto_timeout("sql_stats")
81
+
82
+
78
83
  async def call(
79
84
  self,
80
85
  ctx: MCPContext,
@@ -9,6 +9,8 @@ from typing import List, Unpack, Optional, Annotated, TypedDict, final, override
9
9
  from pydantic import Field
10
10
  from mcp.server.fastmcp import Context as MCPContext
11
11
 
12
+ from hanzo_mcp.tools.common.auto_timeout import auto_timeout
13
+
12
14
  from hanzo_mcp.tools.common.base import BaseTool
13
15
  from hanzo_mcp.tools.common.context import create_tool_context
14
16
  from hanzo_mcp.tools.common.permissions import PermissionManager
@@ -128,6 +130,9 @@ Note: Requires Neovim to be installed.
128
130
  """
129
131
 
130
132
  @override
133
+ @auto_timeout("neovim_command")
134
+
135
+
131
136
  async def call(
132
137
  self,
133
138
  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
@@ -125,6 +127,9 @@ Note: Requires Neovim to be installed and available in PATH.
125
127
  """
126
128
 
127
129
  @override
130
+ @auto_timeout("neovim_edit")
131
+
132
+
128
133
  async def call(
129
134
  self,
130
135
  ctx: MCPContext,
@@ -252,7 +257,7 @@ Or visit: https://neovim.io/"""
252
257
  end if
253
258
  end tell"""
254
259
 
255
- subprocess.run(["osascript", "-e", applescript])
260
+ subprocess.run(["osascript", "-e", applescript], timeout=10)
256
261
  return f"Opened {file_path} in Neovim (new terminal window)"
257
262
 
258
263
  elif shutil.which("gnome-terminal"):
@@ -272,7 +277,7 @@ Or visit: https://neovim.io/"""
272
277
 
273
278
  else:
274
279
  # Run and wait for completion
275
- result = subprocess.run(cmd)
280
+ result = subprocess.run(cmd, timeout=120)
276
281
 
277
282
  if result.returncode == 0:
278
283
  return f"Successfully edited {file_path} in Neovim"
@@ -11,6 +11,8 @@ from datetime import datetime
11
11
  from pydantic import Field
12
12
  from mcp.server.fastmcp import Context as MCPContext
13
13
 
14
+ from hanzo_mcp.tools.common.auto_timeout import auto_timeout
15
+
14
16
  from hanzo_mcp.tools.common.base import BaseTool
15
17
  from hanzo_mcp.tools.common.context import create_tool_context
16
18
 
@@ -114,6 +116,9 @@ Note: Requires Neovim to be installed.
114
116
  """
115
117
 
116
118
  @override
119
+ @auto_timeout("neovim_session")
120
+
121
+
117
122
  async def call(
118
123
  self,
119
124
  ctx: MCPContext,
@@ -9,30 +9,25 @@ from mcp.server import FastMCP
9
9
  from hanzo_mcp.tools.common.base import BaseTool, ToolRegistry
10
10
  from hanzo_mcp.tools.filesystem.diff import create_diff_tool
11
11
  from hanzo_mcp.tools.filesystem.edit import Edit
12
+ from hanzo_mcp.tools.filesystem.grep import Grep
12
13
  from hanzo_mcp.tools.filesystem.read import ReadTool
13
14
  from hanzo_mcp.tools.filesystem.watch import watch_tool
14
15
  from hanzo_mcp.tools.filesystem.write import Write
15
16
  from hanzo_mcp.tools.common.permissions import PermissionManager
16
17
  from hanzo_mcp.tools.filesystem.ast_tool import ASTTool
17
- from hanzo_mcp.tools.filesystem.find import FindTool
18
+ from hanzo_mcp.tools.filesystem.find_files import FindFilesTool
18
19
  from hanzo_mcp.tools.filesystem.git_search import GitSearchTool
19
20
  from hanzo_mcp.tools.filesystem.multi_edit import MultiEdit
20
21
  from hanzo_mcp.tools.filesystem.rules_tool import RulesTool
22
+ from hanzo_mcp.tools.filesystem.search_tool import SearchTool
23
+ from hanzo_mcp.tools.filesystem.batch_search import BatchSearchTool
21
24
  from hanzo_mcp.tools.filesystem.directory_tree import DirectoryTreeTool
22
25
  from hanzo_mcp.tools.filesystem.content_replace import ContentReplaceTool
23
26
 
24
- # Import unified search tool (which includes legacy Grep as alias)
25
- from hanzo_mcp.tools.filesystem.search import (
26
- UnifiedSearchTool,
27
- Grep, # Legacy alias for backward compatibility
28
- create_unified_search_tool,
29
- create_grep_tool,
30
- )
31
-
32
27
  # Import new search tools
33
28
  try:
34
29
  from hanzo_mcp.tools.search import (
35
- FindTool as SearchFindTool, # Rename to avoid conflict with filesystem FindTool
30
+ FindTool,
36
31
  UnifiedSearch,
37
32
  create_find_tool,
38
33
  create_unified_search_tool,
@@ -49,15 +44,14 @@ __all__ = [
49
44
  "Edit",
50
45
  "MultiEdit",
51
46
  "DirectoryTreeTool",
52
- "Grep", # Legacy alias for UnifiedSearchTool
53
- "UnifiedSearchTool", # New unified search tool
47
+ "Grep",
54
48
  "ContentReplaceTool",
55
49
  "ASTTool",
56
50
  "GitSearchTool",
57
- "FindTool",
51
+ "BatchSearchTool",
52
+ "FindFilesTool",
58
53
  "RulesTool",
59
- "create_unified_search_tool",
60
- "create_grep_tool",
54
+ "SearchTool",
61
55
  "get_filesystem_tools",
62
56
  "register_filesystem_tools",
63
57
  ]
@@ -82,14 +76,15 @@ def get_read_only_filesystem_tools(
82
76
  Grep(permission_manager),
83
77
  ASTTool(permission_manager),
84
78
  GitSearchTool(permission_manager),
85
- FindTool(permission_manager),
79
+ FindFilesTool(permission_manager),
86
80
  RulesTool(permission_manager),
87
81
  watch_tool,
88
82
  create_diff_tool(permission_manager),
89
83
  ]
90
84
 
91
- # Add unified search tool (replaces old SearchTool and BatchSearchTool)
92
- tools.append(UnifiedSearchTool(permission_manager, project_manager))
85
+ # Add search if project manager is available
86
+ if project_manager:
87
+ tools.append(SearchTool(permission_manager, project_manager))
93
88
 
94
89
  # Add new search tools if available
95
90
  if UNIFIED_SEARCH_AVAILABLE:
@@ -118,14 +113,15 @@ def get_filesystem_tools(permission_manager: PermissionManager, project_manager=
118
113
  ContentReplaceTool(permission_manager),
119
114
  ASTTool(permission_manager),
120
115
  GitSearchTool(permission_manager),
121
- FindTool(permission_manager),
116
+ FindFilesTool(permission_manager),
122
117
  RulesTool(permission_manager),
123
118
  watch_tool,
124
119
  create_diff_tool(permission_manager),
125
120
  ]
126
121
 
127
- # Add unified search tool (replaces old SearchTool and BatchSearchTool)
128
- tools.append(UnifiedSearchTool(permission_manager, project_manager))
122
+ # Add search if project manager is available
123
+ if project_manager:
124
+ tools.append(SearchTool(permission_manager, project_manager))
129
125
 
130
126
  # Add new search tools if available
131
127
  if UNIFIED_SEARCH_AVAILABLE:
@@ -162,13 +158,14 @@ def register_filesystem_tools(
162
158
  "edit": Edit,
163
159
  "multi_edit": MultiEdit,
164
160
  "directory_tree": DirectoryTreeTool,
165
- "grep": Grep, # Legacy alias for UnifiedSearchTool
166
- "search": UnifiedSearchTool, # New unified search tool (replaces SearchTool and BatchSearchTool)
161
+ "grep": Grep,
167
162
  "ast": ASTTool, # AST-based code structure search with tree-sitter
168
163
  "git_search": GitSearchTool,
169
164
  "content_replace": ContentReplaceTool,
170
- "find": FindTool,
165
+ "batch_search": BatchSearchTool,
166
+ "find_files": FindFilesTool,
171
167
  "rules": RulesTool,
168
+ "search": SearchTool,
172
169
  "watch": lambda pm: watch_tool, # Singleton instance
173
170
  "diff": create_diff_tool,
174
171
  }
@@ -185,8 +182,8 @@ def register_filesystem_tools(
185
182
  for tool_name, enabled in enabled_tools.items():
186
183
  if enabled and tool_name in tool_classes:
187
184
  tool_class = tool_classes[tool_name]
188
- if tool_name == "search":
189
- # Unified search tool requires project_manager
185
+ if tool_name in ["batch_search", "search"]:
186
+ # Batch search and search require project_manager
190
187
  tools.append(tool_class(permission_manager, project_manager))
191
188
  elif tool_name == "watch":
192
189
  # Watch tool is a singleton
@@ -15,6 +15,7 @@ from grep_ast.grep_ast import TreeContext
15
15
  from mcp.server.fastmcp import Context as MCPContext
16
16
 
17
17
  from hanzo_mcp.tools.filesystem.base import FilesystemBaseTool
18
+ from hanzo_mcp.tools.common.auto_timeout import auto_timeout
18
19
 
19
20
  Pattern = Annotated[
20
21
  str,
@@ -97,6 +98,7 @@ ast "def test_" ./tests
97
98
  Searches code structure intelligently, understanding syntax and providing semantic context."""
98
99
 
99
100
  @override
101
+ @auto_timeout("ast")
100
102
  async def call(
101
103
  self,
102
104
  ctx: MCPContext,
@@ -112,14 +114,11 @@ Searches code structure intelligently, understanding syntax and providing semant
112
114
  Tool result
113
115
  """
114
116
  tool_ctx = self.create_tool_context(ctx)
115
- self.set_tool_context_info(tool_ctx)
117
+ await self.set_tool_context_info(tool_ctx)
116
118
 
117
119
  # Extract parameters
118
120
  pattern: Pattern = params["pattern"]
119
121
  path: SearchPath = params["path"]
120
-
121
- # Expand path (handles ~, $HOME, etc.)
122
- path = self.expand_path(path)
123
122
  ignore_case = params.get("ignore_case", False)
124
123
  line_number = params.get("line_number", False)
125
124