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.
- hanzo_mcp/__init__.py +1 -1
- hanzo_mcp/cli.py +32 -0
- hanzo_mcp/dev_server.py +246 -0
- hanzo_mcp/prompts/__init__.py +1 -1
- hanzo_mcp/prompts/project_system.py +43 -7
- hanzo_mcp/server.py +5 -1
- hanzo_mcp/tools/__init__.py +168 -6
- hanzo_mcp/tools/agent/__init__.py +1 -1
- hanzo_mcp/tools/agent/agent.py +401 -0
- hanzo_mcp/tools/agent/agent_tool.py +3 -4
- hanzo_mcp/tools/common/__init__.py +1 -1
- hanzo_mcp/tools/common/base.py +9 -4
- hanzo_mcp/tools/common/batch_tool.py +3 -5
- hanzo_mcp/tools/common/config_tool.py +1 -1
- hanzo_mcp/tools/common/context.py +1 -1
- hanzo_mcp/tools/common/palette.py +344 -0
- hanzo_mcp/tools/common/palette_loader.py +108 -0
- hanzo_mcp/tools/common/stats.py +261 -0
- hanzo_mcp/tools/common/thinking_tool.py +3 -5
- hanzo_mcp/tools/common/tool_disable.py +144 -0
- hanzo_mcp/tools/common/tool_enable.py +182 -0
- hanzo_mcp/tools/common/tool_list.py +260 -0
- hanzo_mcp/tools/config/__init__.py +10 -0
- hanzo_mcp/tools/config/config_tool.py +212 -0
- hanzo_mcp/tools/config/index_config.py +176 -0
- hanzo_mcp/tools/config/palette_tool.py +166 -0
- hanzo_mcp/tools/database/__init__.py +71 -0
- hanzo_mcp/tools/database/database_manager.py +246 -0
- hanzo_mcp/tools/database/graph.py +482 -0
- hanzo_mcp/tools/database/graph_add.py +257 -0
- hanzo_mcp/tools/database/graph_query.py +536 -0
- hanzo_mcp/tools/database/graph_remove.py +267 -0
- hanzo_mcp/tools/database/graph_search.py +348 -0
- hanzo_mcp/tools/database/graph_stats.py +345 -0
- hanzo_mcp/tools/database/sql.py +411 -0
- hanzo_mcp/tools/database/sql_query.py +229 -0
- hanzo_mcp/tools/database/sql_search.py +296 -0
- hanzo_mcp/tools/database/sql_stats.py +254 -0
- hanzo_mcp/tools/editor/__init__.py +11 -0
- hanzo_mcp/tools/editor/neovim_command.py +272 -0
- hanzo_mcp/tools/editor/neovim_edit.py +290 -0
- hanzo_mcp/tools/editor/neovim_session.py +356 -0
- hanzo_mcp/tools/filesystem/__init__.py +52 -13
- hanzo_mcp/tools/filesystem/base.py +1 -1
- hanzo_mcp/tools/filesystem/batch_search.py +812 -0
- hanzo_mcp/tools/filesystem/content_replace.py +3 -5
- hanzo_mcp/tools/filesystem/diff.py +193 -0
- hanzo_mcp/tools/filesystem/directory_tree.py +3 -5
- hanzo_mcp/tools/filesystem/edit.py +3 -5
- hanzo_mcp/tools/filesystem/find.py +443 -0
- hanzo_mcp/tools/filesystem/find_files.py +348 -0
- hanzo_mcp/tools/filesystem/git_search.py +505 -0
- hanzo_mcp/tools/filesystem/grep.py +2 -2
- hanzo_mcp/tools/filesystem/multi_edit.py +3 -5
- hanzo_mcp/tools/filesystem/read.py +17 -5
- hanzo_mcp/tools/filesystem/{grep_ast_tool.py → symbols.py} +17 -27
- hanzo_mcp/tools/filesystem/symbols_unified.py +376 -0
- hanzo_mcp/tools/filesystem/tree.py +268 -0
- hanzo_mcp/tools/filesystem/unified_search.py +465 -443
- hanzo_mcp/tools/filesystem/unix_aliases.py +99 -0
- hanzo_mcp/tools/filesystem/watch.py +174 -0
- hanzo_mcp/tools/filesystem/write.py +3 -5
- hanzo_mcp/tools/jupyter/__init__.py +9 -12
- hanzo_mcp/tools/jupyter/base.py +1 -1
- hanzo_mcp/tools/jupyter/jupyter.py +326 -0
- hanzo_mcp/tools/jupyter/notebook_edit.py +3 -4
- hanzo_mcp/tools/jupyter/notebook_read.py +3 -5
- hanzo_mcp/tools/llm/__init__.py +31 -0
- hanzo_mcp/tools/llm/consensus_tool.py +351 -0
- hanzo_mcp/tools/llm/llm_manage.py +413 -0
- hanzo_mcp/tools/llm/llm_tool.py +346 -0
- hanzo_mcp/tools/llm/llm_unified.py +851 -0
- hanzo_mcp/tools/llm/provider_tools.py +412 -0
- hanzo_mcp/tools/mcp/__init__.py +15 -0
- hanzo_mcp/tools/mcp/mcp_add.py +263 -0
- hanzo_mcp/tools/mcp/mcp_remove.py +127 -0
- hanzo_mcp/tools/mcp/mcp_stats.py +165 -0
- hanzo_mcp/tools/mcp/mcp_unified.py +503 -0
- hanzo_mcp/tools/shell/__init__.py +21 -23
- hanzo_mcp/tools/shell/base.py +1 -1
- hanzo_mcp/tools/shell/base_process.py +303 -0
- hanzo_mcp/tools/shell/bash_unified.py +134 -0
- hanzo_mcp/tools/shell/logs.py +265 -0
- hanzo_mcp/tools/shell/npx.py +194 -0
- hanzo_mcp/tools/shell/npx_background.py +254 -0
- hanzo_mcp/tools/shell/npx_unified.py +101 -0
- hanzo_mcp/tools/shell/open.py +107 -0
- hanzo_mcp/tools/shell/pkill.py +262 -0
- hanzo_mcp/tools/shell/process_unified.py +131 -0
- hanzo_mcp/tools/shell/processes.py +279 -0
- hanzo_mcp/tools/shell/run_background.py +326 -0
- hanzo_mcp/tools/shell/run_command.py +3 -4
- hanzo_mcp/tools/shell/run_command_windows.py +3 -4
- hanzo_mcp/tools/shell/uvx.py +187 -0
- hanzo_mcp/tools/shell/uvx_background.py +249 -0
- hanzo_mcp/tools/shell/uvx_unified.py +101 -0
- hanzo_mcp/tools/todo/__init__.py +1 -1
- hanzo_mcp/tools/todo/base.py +1 -1
- hanzo_mcp/tools/todo/todo.py +265 -0
- hanzo_mcp/tools/todo/todo_read.py +3 -5
- hanzo_mcp/tools/todo/todo_write.py +3 -5
- hanzo_mcp/tools/vector/__init__.py +6 -1
- hanzo_mcp/tools/vector/git_ingester.py +3 -0
- hanzo_mcp/tools/vector/index_tool.py +358 -0
- hanzo_mcp/tools/vector/infinity_store.py +98 -0
- hanzo_mcp/tools/vector/project_manager.py +27 -5
- hanzo_mcp/tools/vector/vector.py +311 -0
- hanzo_mcp/tools/vector/vector_index.py +1 -1
- hanzo_mcp/tools/vector/vector_search.py +12 -7
- hanzo_mcp-0.6.1.dist-info/METADATA +336 -0
- hanzo_mcp-0.6.1.dist-info/RECORD +134 -0
- hanzo_mcp-0.6.1.dist-info/entry_points.txt +3 -0
- hanzo_mcp-0.5.1.dist-info/METADATA +0 -276
- hanzo_mcp-0.5.1.dist-info/RECORD +0 -68
- hanzo_mcp-0.5.1.dist-info/entry_points.txt +0 -2
- {hanzo_mcp-0.5.1.dist-info → hanzo_mcp-0.6.1.dist-info}/WHEEL +0 -0
- {hanzo_mcp-0.5.1.dist-info → hanzo_mcp-0.6.1.dist-info}/licenses/LICENSE +0 -0
- {hanzo_mcp-0.5.1.dist-info → hanzo_mcp-0.6.1.dist-info}/top_level.txt +0 -0
hanzo_mcp/__init__.py
CHANGED
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,
|
hanzo_mcp/dev_server.py
ADDED
|
@@ -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()
|
hanzo_mcp/prompts/__init__.py
CHANGED
|
@@ -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
|
|
67
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
113
|
-
- Use the available search tools to understand the codebase and my query. You are encouraged to use
|
|
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
|
-
|
|
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
|
hanzo_mcp/tools/__init__.py
CHANGED
|
@@ -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
|
|
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
|
-
#
|
|
62
|
-
|
|
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
|
|
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
|
|
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
|