hanzo-mcp 0.5.1__py3-none-any.whl → 0.6.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.

Files changed (118) hide show
  1. hanzo_mcp/__init__.py +1 -1
  2. hanzo_mcp/cli.py +32 -0
  3. hanzo_mcp/dev_server.py +246 -0
  4. hanzo_mcp/prompts/__init__.py +1 -1
  5. hanzo_mcp/prompts/project_system.py +43 -7
  6. hanzo_mcp/server.py +5 -1
  7. hanzo_mcp/tools/__init__.py +168 -6
  8. hanzo_mcp/tools/agent/__init__.py +1 -1
  9. hanzo_mcp/tools/agent/agent.py +401 -0
  10. hanzo_mcp/tools/agent/agent_tool.py +3 -4
  11. hanzo_mcp/tools/common/__init__.py +1 -1
  12. hanzo_mcp/tools/common/base.py +9 -4
  13. hanzo_mcp/tools/common/batch_tool.py +3 -5
  14. hanzo_mcp/tools/common/config_tool.py +1 -1
  15. hanzo_mcp/tools/common/context.py +1 -1
  16. hanzo_mcp/tools/common/palette.py +344 -0
  17. hanzo_mcp/tools/common/palette_loader.py +108 -0
  18. hanzo_mcp/tools/common/stats.py +261 -0
  19. hanzo_mcp/tools/common/thinking_tool.py +3 -5
  20. hanzo_mcp/tools/common/tool_disable.py +144 -0
  21. hanzo_mcp/tools/common/tool_enable.py +182 -0
  22. hanzo_mcp/tools/common/tool_list.py +260 -0
  23. hanzo_mcp/tools/config/__init__.py +10 -0
  24. hanzo_mcp/tools/config/config_tool.py +212 -0
  25. hanzo_mcp/tools/config/index_config.py +176 -0
  26. hanzo_mcp/tools/config/palette_tool.py +166 -0
  27. hanzo_mcp/tools/database/__init__.py +71 -0
  28. hanzo_mcp/tools/database/database_manager.py +246 -0
  29. hanzo_mcp/tools/database/graph.py +482 -0
  30. hanzo_mcp/tools/database/graph_add.py +257 -0
  31. hanzo_mcp/tools/database/graph_query.py +536 -0
  32. hanzo_mcp/tools/database/graph_remove.py +267 -0
  33. hanzo_mcp/tools/database/graph_search.py +348 -0
  34. hanzo_mcp/tools/database/graph_stats.py +345 -0
  35. hanzo_mcp/tools/database/sql.py +411 -0
  36. hanzo_mcp/tools/database/sql_query.py +229 -0
  37. hanzo_mcp/tools/database/sql_search.py +296 -0
  38. hanzo_mcp/tools/database/sql_stats.py +254 -0
  39. hanzo_mcp/tools/editor/__init__.py +11 -0
  40. hanzo_mcp/tools/editor/neovim_command.py +272 -0
  41. hanzo_mcp/tools/editor/neovim_edit.py +290 -0
  42. hanzo_mcp/tools/editor/neovim_session.py +356 -0
  43. hanzo_mcp/tools/filesystem/__init__.py +52 -13
  44. hanzo_mcp/tools/filesystem/base.py +1 -1
  45. hanzo_mcp/tools/filesystem/batch_search.py +812 -0
  46. hanzo_mcp/tools/filesystem/content_replace.py +3 -5
  47. hanzo_mcp/tools/filesystem/diff.py +193 -0
  48. hanzo_mcp/tools/filesystem/directory_tree.py +3 -5
  49. hanzo_mcp/tools/filesystem/edit.py +3 -5
  50. hanzo_mcp/tools/filesystem/find.py +443 -0
  51. hanzo_mcp/tools/filesystem/find_files.py +348 -0
  52. hanzo_mcp/tools/filesystem/git_search.py +505 -0
  53. hanzo_mcp/tools/filesystem/grep.py +2 -2
  54. hanzo_mcp/tools/filesystem/multi_edit.py +3 -5
  55. hanzo_mcp/tools/filesystem/read.py +17 -5
  56. hanzo_mcp/tools/filesystem/{grep_ast_tool.py → symbols.py} +17 -27
  57. hanzo_mcp/tools/filesystem/symbols_unified.py +376 -0
  58. hanzo_mcp/tools/filesystem/tree.py +268 -0
  59. hanzo_mcp/tools/filesystem/unified_search.py +465 -443
  60. hanzo_mcp/tools/filesystem/unix_aliases.py +99 -0
  61. hanzo_mcp/tools/filesystem/watch.py +174 -0
  62. hanzo_mcp/tools/filesystem/write.py +3 -5
  63. hanzo_mcp/tools/jupyter/__init__.py +9 -12
  64. hanzo_mcp/tools/jupyter/base.py +1 -1
  65. hanzo_mcp/tools/jupyter/jupyter.py +326 -0
  66. hanzo_mcp/tools/jupyter/notebook_edit.py +3 -4
  67. hanzo_mcp/tools/jupyter/notebook_read.py +3 -5
  68. hanzo_mcp/tools/llm/__init__.py +31 -0
  69. hanzo_mcp/tools/llm/consensus_tool.py +351 -0
  70. hanzo_mcp/tools/llm/llm_manage.py +413 -0
  71. hanzo_mcp/tools/llm/llm_tool.py +346 -0
  72. hanzo_mcp/tools/llm/llm_unified.py +851 -0
  73. hanzo_mcp/tools/llm/provider_tools.py +412 -0
  74. hanzo_mcp/tools/mcp/__init__.py +15 -0
  75. hanzo_mcp/tools/mcp/mcp_add.py +263 -0
  76. hanzo_mcp/tools/mcp/mcp_remove.py +127 -0
  77. hanzo_mcp/tools/mcp/mcp_stats.py +165 -0
  78. hanzo_mcp/tools/mcp/mcp_unified.py +503 -0
  79. hanzo_mcp/tools/shell/__init__.py +21 -23
  80. hanzo_mcp/tools/shell/base.py +1 -1
  81. hanzo_mcp/tools/shell/base_process.py +303 -0
  82. hanzo_mcp/tools/shell/bash_unified.py +134 -0
  83. hanzo_mcp/tools/shell/logs.py +265 -0
  84. hanzo_mcp/tools/shell/npx.py +194 -0
  85. hanzo_mcp/tools/shell/npx_background.py +254 -0
  86. hanzo_mcp/tools/shell/npx_unified.py +101 -0
  87. hanzo_mcp/tools/shell/open.py +107 -0
  88. hanzo_mcp/tools/shell/pkill.py +262 -0
  89. hanzo_mcp/tools/shell/process_unified.py +131 -0
  90. hanzo_mcp/tools/shell/processes.py +279 -0
  91. hanzo_mcp/tools/shell/run_background.py +326 -0
  92. hanzo_mcp/tools/shell/run_command.py +3 -4
  93. hanzo_mcp/tools/shell/run_command_windows.py +3 -4
  94. hanzo_mcp/tools/shell/uvx.py +187 -0
  95. hanzo_mcp/tools/shell/uvx_background.py +249 -0
  96. hanzo_mcp/tools/shell/uvx_unified.py +101 -0
  97. hanzo_mcp/tools/todo/__init__.py +1 -1
  98. hanzo_mcp/tools/todo/base.py +1 -1
  99. hanzo_mcp/tools/todo/todo.py +265 -0
  100. hanzo_mcp/tools/todo/todo_read.py +3 -5
  101. hanzo_mcp/tools/todo/todo_write.py +3 -5
  102. hanzo_mcp/tools/vector/__init__.py +6 -1
  103. hanzo_mcp/tools/vector/git_ingester.py +3 -0
  104. hanzo_mcp/tools/vector/index_tool.py +358 -0
  105. hanzo_mcp/tools/vector/infinity_store.py +98 -0
  106. hanzo_mcp/tools/vector/project_manager.py +27 -5
  107. hanzo_mcp/tools/vector/vector.py +311 -0
  108. hanzo_mcp/tools/vector/vector_index.py +1 -1
  109. hanzo_mcp/tools/vector/vector_search.py +12 -7
  110. hanzo_mcp-0.6.1.dist-info/METADATA +336 -0
  111. hanzo_mcp-0.6.1.dist-info/RECORD +134 -0
  112. hanzo_mcp-0.6.1.dist-info/entry_points.txt +3 -0
  113. hanzo_mcp-0.5.1.dist-info/METADATA +0 -276
  114. hanzo_mcp-0.5.1.dist-info/RECORD +0 -68
  115. hanzo_mcp-0.5.1.dist-info/entry_points.txt +0 -2
  116. {hanzo_mcp-0.5.1.dist-info → hanzo_mcp-0.6.1.dist-info}/WHEEL +0 -0
  117. {hanzo_mcp-0.5.1.dist-info → hanzo_mcp-0.6.1.dist-info}/licenses/LICENSE +0 -0
  118. {hanzo_mcp-0.5.1.dist-info → hanzo_mcp-0.6.1.dist-info}/top_level.txt +0 -0
hanzo_mcp/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """Hanzo MCP - Implementation of Hanzo capabilities using MCP."""
2
2
 
3
- __version__ = "0.5.1"
3
+ __version__ = "0.6.0"
hanzo_mcp/cli.py CHANGED
@@ -145,6 +145,12 @@ def main() -> None:
145
145
  help="Single project directory (alias for --project)",
146
146
  )
147
147
 
148
+ _ = parser.add_argument(
149
+ "--dev",
150
+ action="store_true",
151
+ help="Run in development mode with hot reload",
152
+ )
153
+
148
154
  _ = parser.add_argument(
149
155
  "--install",
150
156
  action="store_true",
@@ -156,6 +162,7 @@ def main() -> None:
156
162
  # Cast args attributes to appropriate types to avoid 'Any' warnings
157
163
  name: str = cast(str, args.name)
158
164
  install: bool = cast(bool, args.install)
165
+ dev: bool = cast(bool, args.dev)
159
166
  transport: str = cast(str, args.transport)
160
167
  agent_model: str | None = cast(str | None, args.agent_model)
161
168
  agent_max_tokens: int | None = cast(int | None, args.agent_max_tokens)
@@ -200,6 +207,31 @@ def main() -> None:
200
207
  if not allowed_paths:
201
208
  allowed_paths = [os.getcwd()]
202
209
 
210
+ # Run in dev mode if requested
211
+ if dev:
212
+ from hanzo_mcp.dev_server import DevServer
213
+
214
+ dev_server = DevServer(
215
+ name=name,
216
+ allowed_paths=allowed_paths,
217
+ project_paths=project_paths,
218
+ project_dir=project_dir,
219
+ agent_model=agent_model,
220
+ agent_max_tokens=agent_max_tokens,
221
+ agent_api_key=agent_api_key,
222
+ agent_base_url=agent_base_url,
223
+ agent_max_iterations=agent_max_iterations,
224
+ agent_max_tool_uses=agent_max_tool_uses,
225
+ enable_agent_tool=enable_agent_tool,
226
+ command_timeout=command_timeout,
227
+ disable_write_tools=disable_write_tools,
228
+ disable_search_tools=disable_search_tools,
229
+ host=host,
230
+ port=port,
231
+ )
232
+ dev_server.run(transport=transport)
233
+ return
234
+
203
235
  # Run the server
204
236
  server = HanzoMCPServer(
205
237
  name=name,
@@ -0,0 +1,246 @@
1
+ """Development server with hot reload for Hanzo MCP."""
2
+
3
+ import asyncio
4
+ import os
5
+ import sys
6
+ import time
7
+ from pathlib import Path
8
+ from typing import Optional, Set
9
+
10
+ import watchdog.events
11
+ import watchdog.observers
12
+ from watchdog.events import FileSystemEventHandler
13
+
14
+ from hanzo_mcp.server import HanzoMCPServer
15
+
16
+
17
+ class MCPReloadHandler(FileSystemEventHandler):
18
+ """Handler for file system events that triggers MCP server reload."""
19
+
20
+ def __init__(self, restart_callback, ignore_patterns: Optional[Set[str]] = None):
21
+ """Initialize the reload handler.
22
+
23
+ Args:
24
+ restart_callback: Function to call when files change
25
+ ignore_patterns: Set of patterns to ignore
26
+ """
27
+ self.restart_callback = restart_callback
28
+ self.ignore_patterns = ignore_patterns or {
29
+ "__pycache__", ".pyc", ".pyo", ".git", ".pytest_cache",
30
+ ".mypy_cache", ".ruff_cache", ".coverage", "*.log",
31
+ ".env", ".venv", "venv", "node_modules"
32
+ }
33
+ self.last_reload = 0
34
+ self.reload_delay = 0.5 # Debounce delay in seconds
35
+
36
+ def should_ignore(self, path: str) -> bool:
37
+ """Check if a path should be ignored."""
38
+ path_obj = Path(path)
39
+
40
+ # Check against ignore patterns
41
+ for pattern in self.ignore_patterns:
42
+ if pattern in str(path_obj):
43
+ return True
44
+ if path_obj.name.endswith(pattern):
45
+ return True
46
+
47
+ # Only watch Python files and config files
48
+ if path_obj.is_file():
49
+ allowed_extensions = {".py", ".json", ".yaml", ".yml", ".toml"}
50
+ if path_obj.suffix not in allowed_extensions:
51
+ return True
52
+
53
+ return False
54
+
55
+ def on_any_event(self, event):
56
+ """Handle any file system event."""
57
+ if event.is_directory:
58
+ return
59
+
60
+ if self.should_ignore(event.src_path):
61
+ return
62
+
63
+ # Debounce rapid changes
64
+ current_time = time.time()
65
+ if current_time - self.last_reload < self.reload_delay:
66
+ return
67
+
68
+ self.last_reload = current_time
69
+
70
+ print(f"\n🔄 File changed: {event.src_path}")
71
+ print("🔄 Reloading MCP server...")
72
+
73
+ self.restart_callback()
74
+
75
+
76
+ class DevServer:
77
+ """Development server with hot reload capability."""
78
+
79
+ def __init__(
80
+ self,
81
+ name: str = "hanzo-dev",
82
+ allowed_paths: Optional[list[str]] = None,
83
+ project_paths: Optional[list[str]] = None,
84
+ project_dir: Optional[str] = None,
85
+ **kwargs
86
+ ):
87
+ """Initialize the development server.
88
+
89
+ Args:
90
+ name: Server name
91
+ allowed_paths: Allowed paths for the server
92
+ project_paths: Project paths
93
+ project_dir: Project directory
94
+ **kwargs: Additional arguments for HanzoMCPServer
95
+ """
96
+ self.name = name
97
+ self.allowed_paths = allowed_paths or []
98
+ self.project_paths = project_paths
99
+ self.project_dir = project_dir
100
+ self.server_kwargs = kwargs
101
+ self.server_process = None
102
+ self.observer = None
103
+ self.running = False
104
+
105
+ def create_server(self) -> HanzoMCPServer:
106
+ """Create a new MCP server instance."""
107
+ return HanzoMCPServer(
108
+ name=self.name,
109
+ allowed_paths=self.allowed_paths,
110
+ project_paths=self.project_paths,
111
+ project_dir=self.project_dir,
112
+ **self.server_kwargs
113
+ )
114
+
115
+ def start_file_watcher(self):
116
+ """Start watching for file changes."""
117
+ # Watch the hanzo_mcp package directory
118
+ package_dir = Path(__file__).parent
119
+
120
+ # Create observer and handler
121
+ self.observer = watchdog.observers.Observer()
122
+ handler = MCPReloadHandler(self.restart_server)
123
+
124
+ # Watch the package directory
125
+ self.observer.schedule(handler, str(package_dir), recursive=True)
126
+
127
+ # Also watch any project directories
128
+ if self.project_dir:
129
+ self.observer.schedule(handler, self.project_dir, recursive=True)
130
+
131
+ for path in self.allowed_paths:
132
+ if Path(path).is_dir() and path not in [str(package_dir), self.project_dir]:
133
+ self.observer.schedule(handler, path, recursive=True)
134
+
135
+ self.observer.start()
136
+ print(f"👀 Watching for changes in: {package_dir}")
137
+ if self.project_dir:
138
+ print(f"👀 Also watching: {self.project_dir}")
139
+
140
+ def stop_file_watcher(self):
141
+ """Stop the file watcher."""
142
+ if self.observer and self.observer.is_alive():
143
+ self.observer.stop()
144
+ self.observer.join(timeout=2)
145
+
146
+ def restart_server(self):
147
+ """Restart the MCP server."""
148
+ # Since MCP servers run in the same process, we need to handle this differently
149
+ # For now, we'll print a message indicating a restart is needed
150
+ print("\n⚠️ Server restart required. Please restart the MCP client to reload changes.")
151
+ print("💡 Tip: In development, consider using the MCP test client for easier reloading.")
152
+
153
+ async def run_async(self, transport: str = "stdio"):
154
+ """Run the development server asynchronously."""
155
+ self.running = True
156
+
157
+ print(f"\n🚀 Starting Hanzo MCP in development mode...")
158
+ print(f"🔧 Hot reload enabled - watching for file changes")
159
+ print(f"📁 Project: {self.project_dir or 'current directory'}")
160
+ print(f"🌐 Transport: {transport}\n")
161
+
162
+ # Start file watcher
163
+ self.start_file_watcher()
164
+
165
+ try:
166
+ # Create and run server
167
+ server = self.create_server()
168
+
169
+ # Run the server (this will block)
170
+ server.run(transport=transport)
171
+
172
+ except KeyboardInterrupt:
173
+ print("\n\n🛑 Shutting down development server...")
174
+ finally:
175
+ self.running = False
176
+ self.stop_file_watcher()
177
+ print("👋 Development server stopped")
178
+
179
+ def run(self, transport: str = "stdio"):
180
+ """Run the development server."""
181
+ try:
182
+ # Run the async version
183
+ asyncio.run(self.run_async(transport))
184
+ except KeyboardInterrupt:
185
+ pass
186
+
187
+
188
+ def run_dev_server():
189
+ """Entry point for development server."""
190
+ import argparse
191
+
192
+ parser = argparse.ArgumentParser(description="Run Hanzo MCP in development mode with hot reload")
193
+ parser.add_argument(
194
+ "--name",
195
+ type=str,
196
+ default="hanzo-dev",
197
+ help="Name of the MCP server"
198
+ )
199
+ parser.add_argument(
200
+ "--project-dir",
201
+ type=str,
202
+ help="Project directory to serve"
203
+ )
204
+ parser.add_argument(
205
+ "--allowed-path",
206
+ type=str,
207
+ action="append",
208
+ dest="allowed_paths",
209
+ help="Additional allowed paths (can be specified multiple times)"
210
+ )
211
+ parser.add_argument(
212
+ "--transport",
213
+ type=str,
214
+ default="stdio",
215
+ choices=["stdio", "sse"],
216
+ help="Transport type (default: stdio)"
217
+ )
218
+ parser.add_argument(
219
+ "--host",
220
+ type=str,
221
+ default="127.0.0.1",
222
+ help="Host for SSE transport"
223
+ )
224
+ parser.add_argument(
225
+ "--port",
226
+ type=int,
227
+ default=3000,
228
+ help="Port for SSE transport"
229
+ )
230
+
231
+ args = parser.parse_args()
232
+
233
+ # Create and run dev server
234
+ dev_server = DevServer(
235
+ name=args.name,
236
+ allowed_paths=args.allowed_paths,
237
+ project_dir=args.project_dir,
238
+ host=args.host,
239
+ port=args.port,
240
+ )
241
+
242
+ dev_server.run(transport=args.transport)
243
+
244
+
245
+ if __name__ == "__main__":
246
+ run_dev_server()
@@ -1,6 +1,6 @@
1
1
  import os
2
2
 
3
- from fastmcp import FastMCP
3
+ from mcp.server import FastMCP
4
4
 
5
5
  from hanzo_mcp.prompts.compact_conversation import COMPACT_CONVERSATION_PROMPT
6
6
  from hanzo_mcp.prompts.create_release import CREATE_RELEASE_PROMPT
@@ -29,6 +29,42 @@ Recent commits:
29
29
  {recent_commits}
30
30
  </project_info>
31
31
 
32
+ <available_tools>
33
+ Hanzo MCP provides 65+ tools organized by category. Key tools include:
34
+
35
+ # File Operations
36
+ - read, write, edit, multi_edit: File manipulation
37
+ - tree, find: Navigation and discovery
38
+
39
+ # Search & Analysis
40
+ - grep: Fast text search
41
+ - symbols: AST-aware symbol search
42
+ - search: Multi-modal intelligent search
43
+ - git_search: Git history search
44
+ - vector_search: Semantic search
45
+
46
+ # Shell & Process
47
+ - run_command: Execute commands
48
+ - processes, pkill: Process management
49
+ - npx, uvx: Run packages directly
50
+
51
+ # Development
52
+ - jupyter: Notebook operations (read/edit actions)
53
+ - todo: Task management (read/write actions)
54
+ - agent: Delegate complex tasks
55
+ - llm: Query LLMs (query/list/consensus actions)
56
+
57
+ # Databases
58
+ - sql: SQL operations (query/search/stats actions)
59
+ - graph: Graph operations (add/remove/query/search/stats actions)
60
+
61
+ # System
62
+ - config: Configuration management
63
+ - stats: Usage statistics
64
+ - tool_enable/disable: Dynamic tool control
65
+
66
+ Tools follow the principle of one tool per task with multiple actions where appropriate.
67
+ </available_tools>
32
68
 
33
69
  <preferences>
34
70
  IMPORTANT: Always use the todo_write tool to plan and track tasks throughout the conversation.
@@ -63,21 +99,21 @@ When making changes to files, first understand the file's code conventions. Mimi
63
99
 
64
100
  <task_management>
65
101
  # Task Management
66
- You have access to the todo_write and todo_read tools to help you manage and plan tasks. Use these tools VERY frequently to ensure that you are tracking your tasks and giving me visibility into your progress.
67
- These tools are also EXTREMELY helpful for planning tasks, and for breaking down larger complex tasks into smaller steps. If you do not use this tool when planning, you may forget to do important tasks - and that is unacceptable.
102
+ You have access to the todo tool (actions: read, write) to help you manage and plan tasks. Use this tool VERY frequently to ensure that you are tracking your tasks and giving me visibility into your progress.
103
+ This tool is also EXTREMELY helpful for planning tasks, and for breaking down larger complex tasks into smaller steps. If you do not use this tool when planning, you may forget to do important tasks - and that is unacceptable.
68
104
 
69
105
  It is critical that you mark todos as completed as soon as you are done with a task. Do not batch up multiple tasks before marking them as completed.
70
106
 
71
107
  Examples:
72
108
  <example>
73
109
  user: Run the build and fix any type errors
74
- assistant: I'm going to use the todo_write tool to write the following items to the todo list:
110
+ assistant: I'm going to use the todo tool with write action to add the following items to the todo list:
75
111
  - Run the build
76
112
  - Fix any type errors
77
113
 
78
114
  I'm now going to run the build using Bash.
79
115
 
80
- Looks like I found 10 type errors. I'm going to use the todo_write tool to write 10 items to the todo list.
116
+ Looks like I found 10 type errors. I'm going to use the todo tool with write action to add 10 items to the todo list.
81
117
 
82
118
  marking the first todo as in_progress
83
119
 
@@ -91,7 +127,7 @@ In the above example, the assistant completes all the tasks, including the 10 er
91
127
  <example>
92
128
  user: Help me write a new feature that allows users to track their usage metrics and export them to various formats
93
129
 
94
- assistant: I'll help you implement a usage metrics tracking and export feature. Let me first use the todo_write tool to plan this task.
130
+ assistant: I'll help you implement a usage metrics tracking and export feature. Let me first use the todo tool with write action to plan this task.
95
131
  Adding the following todos to the todo list:
96
132
  1. Research existing metrics tracking in the codebase
97
133
  2. Design the metrics collection system
@@ -109,8 +145,8 @@ I've found some existing telemetry code. Let me mark the first todo as in_progre
109
145
 
110
146
  # Doing tasks
111
147
  I will primarily request you perform software engineering tasks. This includes solving bugs, adding new functionality, refactoring code, explaining code, and more. For these tasks the following steps are recommended:
112
- - Use the todo_write tool to plan the task if required
113
- - Use the available search tools to understand the codebase and my query. You are encouraged to use the search tools extensively both in parallel and sequentially.
148
+ - Use the todo tool with write action to plan the task if required
149
+ - Use the available search tools (grep, symbols, search, git_search) to understand the codebase and my query. The 'search' tool intelligently combines multiple search strategies. You are encouraged to use search tools extensively both in parallel and sequentially.
114
150
  - Implement the solution using all tools available to you
115
151
  - Verify the solution if possible with tests. NEVER assume specific test framework or test script. Check the README or search codebase to determine the testing approach.
116
152
  - VERY IMPORTANT: When you have completed a task, you MUST run the lint and typecheck commands (eg. npm run lint, npm run typecheck, ruff, etc.) with Bash if they were provided to you to ensure your code is correct. If you are unable to find the correct command, ask me for the command to run and if they supply it, proactively suggest writing it to CLAUDE.md so that you will know to run it next time.
hanzo_mcp/server.py CHANGED
@@ -6,7 +6,11 @@ import threading
6
6
  import time
7
7
  from typing import Literal, cast, final
8
8
 
9
- from fastmcp import FastMCP
9
+ try:
10
+ from fastmcp import FastMCP
11
+ except ImportError:
12
+ # Fallback for older MCP versions
13
+ from mcp.server import FastMCP
10
14
 
11
15
  from hanzo_mcp.prompts import register_all_prompts
12
16
  from hanzo_mcp.tools import register_all_tools
@@ -9,18 +9,27 @@ space for structured thinking. It also includes an "agent" tool that enables Cla
9
9
  to delegate tasks to sub-agents for concurrent execution and specialized processing.
10
10
  """
11
11
 
12
- from fastmcp import FastMCP
12
+ from mcp.server import FastMCP
13
13
 
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 import UnifiedMCPTool, McpAddTool, McpRemoveTool, McpStatsTool
29
+ from hanzo_mcp.tools.editor import NeovimEditTool, NeovimCommandTool, NeovimSessionTool
30
+ from hanzo_mcp.tools.llm import UnifiedLLMTool, LLMTool, ConsensusTool, LLMManageTool, create_provider_tools
31
+ from hanzo_mcp.tools.config.palette_tool import palette_tool
32
+ from hanzo_mcp.tools.common.palette_loader import PaletteLoader
24
33
 
25
34
 
26
35
  def register_all_tools(
@@ -37,6 +46,8 @@ def register_all_tools(
37
46
  disable_search_tools: bool = False,
38
47
  enabled_tools: dict[str, bool] | None = None,
39
48
  vector_config: dict | None = None,
49
+ use_palette: bool = True,
50
+ force_palette: str | None = None,
40
51
  ) -> None:
41
52
  """Register all Hanzo tools with the MCP server.
42
53
 
@@ -54,12 +65,23 @@ def register_all_tools(
54
65
  disable_search_tools: Whether to disable search tools (default: False)
55
66
  enabled_tools: Dictionary of individual tool enable/disable states (default: None)
56
67
  vector_config: Vector store configuration (default: None)
68
+ use_palette: Whether to use palette system for tool configuration (default: True)
69
+ force_palette: Force a specific palette to be active (default: None)
57
70
  """
58
71
  # Dictionary to store all registered tools
59
72
  all_tools: dict[str, BaseTool] = {}
60
73
 
61
- # Use individual tool configuration if provided, otherwise fall back to category-level flags
62
- tool_config = enabled_tools or {}
74
+ # Apply palette configuration if enabled
75
+ if use_palette:
76
+ tool_config = PaletteLoader.get_enabled_tools_from_palette(
77
+ base_enabled_tools=enabled_tools,
78
+ force_palette=force_palette
79
+ )
80
+ # Apply palette environment variables
81
+ PaletteLoader.apply_palette_environment()
82
+ else:
83
+ # Use individual tool configuration if provided, otherwise fall back to category-level flags
84
+ tool_config = enabled_tools or {}
63
85
 
64
86
  def is_tool_enabled(tool_name: str, category_enabled: bool = True) -> bool:
65
87
  """Check if a specific tool should be enabled."""
@@ -76,7 +98,10 @@ def register_all_tools(
76
98
  "directory_tree": is_tool_enabled("directory_tree", True),
77
99
  "grep": is_tool_enabled("grep", not disable_search_tools),
78
100
  "grep_ast": is_tool_enabled("grep_ast", not disable_search_tools),
101
+ "git_search": is_tool_enabled("git_search", not disable_search_tools),
79
102
  "content_replace": is_tool_enabled("content_replace", not disable_write_tools),
103
+ "batch_search": is_tool_enabled("batch_search", not disable_search_tools),
104
+ "find_files": is_tool_enabled("find_files", True),
80
105
  "unified_search": is_tool_enabled("unified_search", not disable_search_tools),
81
106
  }
82
107
 
@@ -87,8 +112,8 @@ def register_all_tools(
87
112
  "vector_search": is_tool_enabled("vector_search", False),
88
113
  }
89
114
 
90
- # Create project manager if vector tools or unified search are enabled
91
- if any(vector_enabled.values()) or filesystem_enabled.get("unified_search", False):
115
+ # Create project manager if vector tools, batch_search, or unified_search are enabled
116
+ if any(vector_enabled.values()) or filesystem_enabled.get("batch_search", False) or filesystem_enabled.get("unified_search", False):
92
117
  if vector_config:
93
118
  from hanzo_mcp.tools.vector.project_manager import ProjectVectorManager
94
119
  search_paths = [str(path) for path in permission_manager.allowed_paths]
@@ -175,3 +200,140 @@ def register_all_tools(
175
200
  # Register batch tool if enabled (batch tool is typically always enabled)
176
201
  if is_tool_enabled("batch", True):
177
202
  register_batch_tool(mcp_server, all_tools)
203
+
204
+ # Register database tools if enabled
205
+ db_manager = None
206
+ database_enabled = {
207
+ "sql_query": is_tool_enabled("sql_query", True),
208
+ "sql_search": is_tool_enabled("sql_search", True),
209
+ "sql_stats": is_tool_enabled("sql_stats", True),
210
+ "graph_add": is_tool_enabled("graph_add", True),
211
+ "graph_remove": is_tool_enabled("graph_remove", True),
212
+ "graph_query": is_tool_enabled("graph_query", True),
213
+ "graph_search": is_tool_enabled("graph_search", True),
214
+ "graph_stats": is_tool_enabled("graph_stats", True),
215
+ }
216
+
217
+ if any(database_enabled.values()):
218
+ db_manager = DatabaseManager(permission_manager)
219
+ database_tools = register_database_tools(
220
+ mcp_server,
221
+ permission_manager,
222
+ db_manager=db_manager,
223
+ )
224
+ # Filter based on enabled state
225
+ for tool in database_tools:
226
+ if database_enabled.get(tool.name, True):
227
+ all_tools[tool.name] = tool
228
+
229
+ # Register unified MCP tool if enabled
230
+ if is_tool_enabled("mcp", True):
231
+ tool = UnifiedMCPTool()
232
+ tool.register(mcp_server)
233
+ all_tools[tool.name] = tool
234
+
235
+ # Register legacy MCP tools if explicitly enabled (disabled by default)
236
+ legacy_mcp_enabled = {
237
+ "mcp_add": is_tool_enabled("mcp_add", False),
238
+ "mcp_remove": is_tool_enabled("mcp_remove", False),
239
+ "mcp_stats": is_tool_enabled("mcp_stats", False),
240
+ }
241
+
242
+ if legacy_mcp_enabled.get("mcp_add", False):
243
+ tool = McpAddTool()
244
+ tool.register(mcp_server)
245
+ all_tools[tool.name] = tool
246
+
247
+ if legacy_mcp_enabled.get("mcp_remove", False):
248
+ tool = McpRemoveTool()
249
+ tool.register(mcp_server)
250
+ all_tools[tool.name] = tool
251
+
252
+ if legacy_mcp_enabled.get("mcp_stats", False):
253
+ tool = McpStatsTool()
254
+ tool.register(mcp_server)
255
+ all_tools[tool.name] = tool
256
+
257
+ # Register system tools (always enabled)
258
+ # Tool enable/disable tools
259
+ tool_enable = ToolEnableTool()
260
+ tool_enable.register(mcp_server)
261
+ all_tools[tool_enable.name] = tool_enable
262
+
263
+ tool_disable = ToolDisableTool()
264
+ tool_disable.register(mcp_server)
265
+ all_tools[tool_disable.name] = tool_disable
266
+
267
+ tool_list = ToolListTool()
268
+ tool_list.register(mcp_server)
269
+ all_tools[tool_list.name] = tool_list
270
+
271
+ # Stats tool
272
+ stats_tool = StatsTool(db_manager=db_manager)
273
+ stats_tool.register(mcp_server)
274
+ all_tools[stats_tool.name] = stats_tool
275
+
276
+ # Palette tool (always enabled for managing tool sets)
277
+ palette_tool.register(mcp_server)
278
+ all_tools[palette_tool.name] = palette_tool
279
+
280
+ # Register editor tools if enabled
281
+ editor_enabled = {
282
+ "neovim_edit": is_tool_enabled("neovim_edit", True),
283
+ "neovim_command": is_tool_enabled("neovim_command", True),
284
+ "neovim_session": is_tool_enabled("neovim_session", True),
285
+ }
286
+
287
+ if editor_enabled.get("neovim_edit", True):
288
+ tool = NeovimEditTool(permission_manager)
289
+ tool.register(mcp_server)
290
+ all_tools[tool.name] = tool
291
+
292
+ if editor_enabled.get("neovim_command", True):
293
+ tool = NeovimCommandTool(permission_manager)
294
+ tool.register(mcp_server)
295
+ all_tools[tool.name] = tool
296
+
297
+ if editor_enabled.get("neovim_session", True):
298
+ tool = NeovimSessionTool()
299
+ tool.register(mcp_server)
300
+ all_tools[tool.name] = tool
301
+
302
+ # Register unified LLM tool if enabled
303
+ if is_tool_enabled("llm", True):
304
+ tool = UnifiedLLMTool()
305
+ if tool.available_providers: # Only register if API keys found
306
+ tool.register(mcp_server)
307
+ all_tools[tool.name] = tool
308
+
309
+ # Register legacy LLM tools if explicitly enabled (disabled by default)
310
+ legacy_llm_enabled = {
311
+ "llm_legacy": is_tool_enabled("llm_legacy", False),
312
+ "consensus": is_tool_enabled("consensus", False),
313
+ "llm_manage": is_tool_enabled("llm_manage", False),
314
+ }
315
+
316
+ if legacy_llm_enabled.get("llm_legacy", False):
317
+ tool = LLMTool()
318
+ if tool.available_providers:
319
+ tool.register(mcp_server)
320
+ all_tools["llm_legacy"] = tool
321
+
322
+ if legacy_llm_enabled.get("consensus", False):
323
+ tool = ConsensusTool()
324
+ if tool.llm_tool.available_providers:
325
+ tool.register(mcp_server)
326
+ all_tools[tool.name] = tool
327
+
328
+ if legacy_llm_enabled.get("llm_manage", False):
329
+ tool = LLMManageTool()
330
+ tool.register(mcp_server)
331
+ all_tools[tool.name] = tool
332
+
333
+ # Register provider-specific LLM tools (disabled by default)
334
+ if is_tool_enabled("provider_specific_llm", False):
335
+ provider_tools = create_provider_tools()
336
+ for tool in provider_tools:
337
+ if is_tool_enabled(tool.name, False):
338
+ tool.register(mcp_server)
339
+ all_tools[tool.name] = tool
@@ -4,7 +4,7 @@ This module provides tools that allow Claude to delegate tasks to sub-agents,
4
4
  enabling concurrent execution of multiple operations and specialized processing.
5
5
  """
6
6
 
7
- from fastmcp import FastMCP
7
+ from mcp.server import FastMCP
8
8
 
9
9
  from hanzo_mcp.tools.agent.agent_tool import AgentTool
10
10
  from hanzo_mcp.tools.common.base import BaseTool, ToolRegistry