hanzo-mcp 0.5.2__py3-none-any.whl → 0.6.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 (114) 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 +66 -35
  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 +2 -2
  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 +1 -1
  19. hanzo_mcp/tools/common/thinking_tool.py +3 -5
  20. hanzo_mcp/tools/common/tool_disable.py +1 -1
  21. hanzo_mcp/tools/common/tool_enable.py +1 -1
  22. hanzo_mcp/tools/common/tool_list.py +49 -52
  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 +1 -1
  28. hanzo_mcp/tools/database/graph.py +482 -0
  29. hanzo_mcp/tools/database/graph_add.py +1 -1
  30. hanzo_mcp/tools/database/graph_query.py +1 -1
  31. hanzo_mcp/tools/database/graph_remove.py +1 -1
  32. hanzo_mcp/tools/database/graph_search.py +1 -1
  33. hanzo_mcp/tools/database/graph_stats.py +1 -1
  34. hanzo_mcp/tools/database/sql.py +411 -0
  35. hanzo_mcp/tools/database/sql_query.py +1 -1
  36. hanzo_mcp/tools/database/sql_search.py +1 -1
  37. hanzo_mcp/tools/database/sql_stats.py +1 -1
  38. hanzo_mcp/tools/editor/neovim_command.py +1 -1
  39. hanzo_mcp/tools/editor/neovim_edit.py +1 -1
  40. hanzo_mcp/tools/editor/neovim_session.py +1 -1
  41. hanzo_mcp/tools/filesystem/__init__.py +42 -13
  42. hanzo_mcp/tools/filesystem/base.py +1 -1
  43. hanzo_mcp/tools/filesystem/batch_search.py +4 -4
  44. hanzo_mcp/tools/filesystem/content_replace.py +3 -5
  45. hanzo_mcp/tools/filesystem/diff.py +193 -0
  46. hanzo_mcp/tools/filesystem/directory_tree.py +3 -5
  47. hanzo_mcp/tools/filesystem/edit.py +3 -5
  48. hanzo_mcp/tools/filesystem/find.py +443 -0
  49. hanzo_mcp/tools/filesystem/find_files.py +1 -1
  50. hanzo_mcp/tools/filesystem/git_search.py +1 -1
  51. hanzo_mcp/tools/filesystem/grep.py +2 -2
  52. hanzo_mcp/tools/filesystem/multi_edit.py +3 -5
  53. hanzo_mcp/tools/filesystem/read.py +17 -5
  54. hanzo_mcp/tools/filesystem/{grep_ast_tool.py → symbols.py} +17 -27
  55. hanzo_mcp/tools/filesystem/symbols_unified.py +376 -0
  56. hanzo_mcp/tools/filesystem/tree.py +268 -0
  57. hanzo_mcp/tools/filesystem/unified_search.py +711 -0
  58. hanzo_mcp/tools/filesystem/unix_aliases.py +99 -0
  59. hanzo_mcp/tools/filesystem/watch.py +174 -0
  60. hanzo_mcp/tools/filesystem/write.py +3 -5
  61. hanzo_mcp/tools/jupyter/__init__.py +9 -12
  62. hanzo_mcp/tools/jupyter/base.py +1 -1
  63. hanzo_mcp/tools/jupyter/jupyter.py +326 -0
  64. hanzo_mcp/tools/jupyter/notebook_edit.py +3 -4
  65. hanzo_mcp/tools/jupyter/notebook_read.py +3 -5
  66. hanzo_mcp/tools/llm/__init__.py +4 -0
  67. hanzo_mcp/tools/llm/consensus_tool.py +1 -1
  68. hanzo_mcp/tools/llm/llm_manage.py +1 -1
  69. hanzo_mcp/tools/llm/llm_tool.py +1 -1
  70. hanzo_mcp/tools/llm/llm_unified.py +851 -0
  71. hanzo_mcp/tools/llm/provider_tools.py +1 -1
  72. hanzo_mcp/tools/mcp/__init__.py +4 -0
  73. hanzo_mcp/tools/mcp/mcp_add.py +1 -1
  74. hanzo_mcp/tools/mcp/mcp_remove.py +1 -1
  75. hanzo_mcp/tools/mcp/mcp_stats.py +1 -1
  76. hanzo_mcp/tools/mcp/mcp_unified.py +503 -0
  77. hanzo_mcp/tools/shell/__init__.py +20 -42
  78. hanzo_mcp/tools/shell/base.py +1 -1
  79. hanzo_mcp/tools/shell/base_process.py +303 -0
  80. hanzo_mcp/tools/shell/bash_unified.py +134 -0
  81. hanzo_mcp/tools/shell/logs.py +1 -1
  82. hanzo_mcp/tools/shell/npx.py +1 -1
  83. hanzo_mcp/tools/shell/npx_background.py +1 -1
  84. hanzo_mcp/tools/shell/npx_unified.py +101 -0
  85. hanzo_mcp/tools/shell/open.py +107 -0
  86. hanzo_mcp/tools/shell/pkill.py +1 -1
  87. hanzo_mcp/tools/shell/process_unified.py +131 -0
  88. hanzo_mcp/tools/shell/processes.py +1 -1
  89. hanzo_mcp/tools/shell/run_background.py +1 -1
  90. hanzo_mcp/tools/shell/run_command.py +3 -4
  91. hanzo_mcp/tools/shell/run_command_windows.py +3 -4
  92. hanzo_mcp/tools/shell/uvx.py +1 -1
  93. hanzo_mcp/tools/shell/uvx_background.py +1 -1
  94. hanzo_mcp/tools/shell/uvx_unified.py +101 -0
  95. hanzo_mcp/tools/todo/__init__.py +1 -1
  96. hanzo_mcp/tools/todo/base.py +1 -1
  97. hanzo_mcp/tools/todo/todo.py +265 -0
  98. hanzo_mcp/tools/todo/todo_read.py +3 -5
  99. hanzo_mcp/tools/todo/todo_write.py +3 -5
  100. hanzo_mcp/tools/vector/__init__.py +1 -1
  101. hanzo_mcp/tools/vector/index_tool.py +1 -1
  102. hanzo_mcp/tools/vector/project_manager.py +27 -5
  103. hanzo_mcp/tools/vector/vector.py +311 -0
  104. hanzo_mcp/tools/vector/vector_index.py +1 -1
  105. hanzo_mcp/tools/vector/vector_search.py +1 -1
  106. hanzo_mcp-0.6.2.dist-info/METADATA +336 -0
  107. hanzo_mcp-0.6.2.dist-info/RECORD +134 -0
  108. hanzo_mcp-0.6.2.dist-info/entry_points.txt +3 -0
  109. hanzo_mcp-0.5.2.dist-info/METADATA +0 -276
  110. hanzo_mcp-0.5.2.dist-info/RECORD +0 -106
  111. hanzo_mcp-0.5.2.dist-info/entry_points.txt +0 -2
  112. {hanzo_mcp-0.5.2.dist-info → hanzo_mcp-0.6.2.dist-info}/WHEEL +0 -0
  113. {hanzo_mcp-0.5.2.dist-info → hanzo_mcp-0.6.2.dist-info}/licenses/LICENSE +0 -0
  114. {hanzo_mcp-0.5.2.dist-info → hanzo_mcp-0.6.2.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.2"
3
+ __version__ = "0.6.2"
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,7 +9,7 @@ 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
@@ -25,11 +25,11 @@ from hanzo_mcp.tools.shell import register_shell_tools
25
25
  from hanzo_mcp.tools.todo import register_todo_tools
26
26
  from hanzo_mcp.tools.vector import register_vector_tools
27
27
  from hanzo_mcp.tools.database import register_database_tools, DatabaseManager
28
- from hanzo_mcp.tools.mcp.mcp_add import McpAddTool
29
- from hanzo_mcp.tools.mcp.mcp_remove import McpRemoveTool
30
- from hanzo_mcp.tools.mcp.mcp_stats import McpStatsTool
28
+ from hanzo_mcp.tools.mcp import UnifiedMCPTool, McpAddTool, McpRemoveTool, McpStatsTool
31
29
  from hanzo_mcp.tools.editor import NeovimEditTool, NeovimCommandTool, NeovimSessionTool
32
- from hanzo_mcp.tools.llm import LLMTool, ConsensusTool, LLMManageTool, create_provider_tools
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
33
33
 
34
34
 
35
35
  def register_all_tools(
@@ -46,6 +46,8 @@ def register_all_tools(
46
46
  disable_search_tools: bool = False,
47
47
  enabled_tools: dict[str, bool] | None = None,
48
48
  vector_config: dict | None = None,
49
+ use_palette: bool = True,
50
+ force_palette: str | None = None,
49
51
  ) -> None:
50
52
  """Register all Hanzo tools with the MCP server.
51
53
 
@@ -63,12 +65,23 @@ def register_all_tools(
63
65
  disable_search_tools: Whether to disable search tools (default: False)
64
66
  enabled_tools: Dictionary of individual tool enable/disable states (default: None)
65
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)
66
70
  """
67
71
  # Dictionary to store all registered tools
68
72
  all_tools: dict[str, BaseTool] = {}
69
73
 
70
- # Use individual tool configuration if provided, otherwise fall back to category-level flags
71
- 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 {}
72
85
 
73
86
  def is_tool_enabled(tool_name: str, category_enabled: bool = True) -> bool:
74
87
  """Check if a specific tool should be enabled."""
@@ -89,6 +102,7 @@ def register_all_tools(
89
102
  "content_replace": is_tool_enabled("content_replace", not disable_write_tools),
90
103
  "batch_search": is_tool_enabled("batch_search", not disable_search_tools),
91
104
  "find_files": is_tool_enabled("find_files", True),
105
+ "unified_search": is_tool_enabled("unified_search", not disable_search_tools),
92
106
  }
93
107
 
94
108
  # Vector tools setup (needed for unified search)
@@ -98,8 +112,8 @@ def register_all_tools(
98
112
  "vector_search": is_tool_enabled("vector_search", False),
99
113
  }
100
114
 
101
- # Create project manager if vector tools or batch_search are enabled
102
- if any(vector_enabled.values()) or filesystem_enabled.get("batch_search", False):
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):
103
117
  if vector_config:
104
118
  from hanzo_mcp.tools.vector.project_manager import ProjectVectorManager
105
119
  search_paths = [str(path) for path in permission_manager.allowed_paths]
@@ -212,24 +226,30 @@ def register_all_tools(
212
226
  if database_enabled.get(tool.name, True):
213
227
  all_tools[tool.name] = tool
214
228
 
215
- # Register MCP management tools if enabled
216
- mcp_enabled = {
217
- "mcp_add": is_tool_enabled("mcp_add", True),
218
- "mcp_remove": is_tool_enabled("mcp_remove", True),
219
- "mcp_stats": is_tool_enabled("mcp_stats", True),
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),
220
240
  }
221
241
 
222
- if mcp_enabled.get("mcp_add", True):
242
+ if legacy_mcp_enabled.get("mcp_add", False):
223
243
  tool = McpAddTool()
224
244
  tool.register(mcp_server)
225
245
  all_tools[tool.name] = tool
226
246
 
227
- if mcp_enabled.get("mcp_remove", True):
247
+ if legacy_mcp_enabled.get("mcp_remove", False):
228
248
  tool = McpRemoveTool()
229
249
  tool.register(mcp_server)
230
250
  all_tools[tool.name] = tool
231
251
 
232
- if mcp_enabled.get("mcp_stats", True):
252
+ if legacy_mcp_enabled.get("mcp_stats", False):
233
253
  tool = McpStatsTool()
234
254
  tool.register(mcp_server)
235
255
  all_tools[tool.name] = tool
@@ -253,6 +273,10 @@ def register_all_tools(
253
273
  stats_tool.register(mcp_server)
254
274
  all_tools[stats_tool.name] = stats_tool
255
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
+
256
280
  # Register editor tools if enabled
257
281
  editor_enabled = {
258
282
  "neovim_edit": is_tool_enabled("neovim_edit", True),
@@ -275,34 +299,41 @@ def register_all_tools(
275
299
  tool.register(mcp_server)
276
300
  all_tools[tool.name] = tool
277
301
 
278
- # Register LLM tools if enabled
279
- llm_enabled = {
280
- "llm": is_tool_enabled("llm", True),
281
- "consensus": is_tool_enabled("consensus", True),
282
- "llm_manage": is_tool_enabled("llm_manage", True),
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),
283
314
  }
284
315
 
285
- if llm_enabled.get("llm", True):
316
+ if legacy_llm_enabled.get("llm_legacy", False):
286
317
  tool = LLMTool()
287
- if tool.available_providers: # Only register if API keys found
318
+ if tool.available_providers:
288
319
  tool.register(mcp_server)
289
- all_tools[tool.name] = tool
320
+ all_tools["llm_legacy"] = tool
290
321
 
291
- if llm_enabled.get("consensus", True):
322
+ if legacy_llm_enabled.get("consensus", False):
292
323
  tool = ConsensusTool()
293
- if tool.llm_tool.available_providers: # Only register if API keys found
324
+ if tool.llm_tool.available_providers:
294
325
  tool.register(mcp_server)
295
326
  all_tools[tool.name] = tool
296
327
 
297
- if llm_enabled.get("llm_manage", True):
328
+ if legacy_llm_enabled.get("llm_manage", False):
298
329
  tool = LLMManageTool()
299
330
  tool.register(mcp_server)
300
331
  all_tools[tool.name] = tool
301
332
 
302
- # Register provider-specific LLM tools
303
- # These are only registered if their API keys are available
304
- provider_tools = create_provider_tools()
305
- for tool in provider_tools:
306
- if is_tool_enabled(tool.name, True):
307
- tool.register(mcp_server)
308
- all_tools[tool.name] = tool
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