hanzo-mcp 0.3.4__py3-none-any.whl → 0.5.0__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 +123 -160
- hanzo_mcp/cli_enhanced.py +438 -0
- hanzo_mcp/config/__init__.py +19 -0
- hanzo_mcp/config/settings.py +388 -0
- hanzo_mcp/config/tool_config.py +197 -0
- hanzo_mcp/prompts/__init__.py +117 -0
- hanzo_mcp/prompts/compact_conversation.py +77 -0
- hanzo_mcp/prompts/create_release.py +38 -0
- hanzo_mcp/prompts/project_system.py +120 -0
- hanzo_mcp/prompts/project_todo_reminder.py +111 -0
- hanzo_mcp/prompts/utils.py +286 -0
- hanzo_mcp/server.py +120 -98
- hanzo_mcp/tools/__init__.py +107 -31
- hanzo_mcp/tools/agent/__init__.py +8 -11
- hanzo_mcp/tools/agent/agent_tool.py +290 -224
- hanzo_mcp/tools/agent/prompt.py +16 -13
- hanzo_mcp/tools/agent/tool_adapter.py +9 -9
- hanzo_mcp/tools/common/__init__.py +17 -16
- hanzo_mcp/tools/common/base.py +79 -110
- hanzo_mcp/tools/common/batch_tool.py +330 -0
- hanzo_mcp/tools/common/context.py +26 -292
- hanzo_mcp/tools/common/permissions.py +12 -12
- hanzo_mcp/tools/common/thinking_tool.py +153 -0
- hanzo_mcp/tools/common/validation.py +1 -63
- hanzo_mcp/tools/filesystem/__init__.py +88 -41
- hanzo_mcp/tools/filesystem/base.py +32 -24
- hanzo_mcp/tools/filesystem/content_replace.py +114 -107
- hanzo_mcp/tools/filesystem/directory_tree.py +129 -105
- hanzo_mcp/tools/filesystem/edit.py +279 -0
- hanzo_mcp/tools/filesystem/grep.py +458 -0
- hanzo_mcp/tools/filesystem/grep_ast_tool.py +250 -0
- hanzo_mcp/tools/filesystem/multi_edit.py +362 -0
- hanzo_mcp/tools/filesystem/read.py +255 -0
- hanzo_mcp/tools/filesystem/write.py +156 -0
- hanzo_mcp/tools/jupyter/__init__.py +41 -29
- hanzo_mcp/tools/jupyter/base.py +66 -57
- hanzo_mcp/tools/jupyter/{edit_notebook.py → notebook_edit.py} +162 -139
- hanzo_mcp/tools/jupyter/notebook_read.py +152 -0
- hanzo_mcp/tools/shell/__init__.py +29 -20
- hanzo_mcp/tools/shell/base.py +87 -45
- hanzo_mcp/tools/shell/bash_session.py +731 -0
- hanzo_mcp/tools/shell/bash_session_executor.py +295 -0
- hanzo_mcp/tools/shell/command_executor.py +435 -384
- hanzo_mcp/tools/shell/run_command.py +284 -131
- hanzo_mcp/tools/shell/run_command_windows.py +328 -0
- hanzo_mcp/tools/shell/session_manager.py +196 -0
- hanzo_mcp/tools/shell/session_storage.py +325 -0
- hanzo_mcp/tools/todo/__init__.py +66 -0
- hanzo_mcp/tools/todo/base.py +319 -0
- hanzo_mcp/tools/todo/todo_read.py +148 -0
- hanzo_mcp/tools/todo/todo_write.py +378 -0
- hanzo_mcp/tools/vector/__init__.py +95 -0
- hanzo_mcp/tools/vector/infinity_store.py +365 -0
- hanzo_mcp/tools/vector/project_manager.py +361 -0
- hanzo_mcp/tools/vector/vector_index.py +115 -0
- hanzo_mcp/tools/vector/vector_search.py +215 -0
- {hanzo_mcp-0.3.4.dist-info → hanzo_mcp-0.5.0.dist-info}/METADATA +35 -3
- hanzo_mcp-0.5.0.dist-info/RECORD +63 -0
- {hanzo_mcp-0.3.4.dist-info → hanzo_mcp-0.5.0.dist-info}/WHEEL +1 -1
- hanzo_mcp/tools/agent/base_provider.py +0 -73
- hanzo_mcp/tools/agent/litellm_provider.py +0 -45
- hanzo_mcp/tools/agent/lmstudio_agent.py +0 -385
- hanzo_mcp/tools/agent/lmstudio_provider.py +0 -219
- hanzo_mcp/tools/agent/provider_registry.py +0 -120
- hanzo_mcp/tools/common/error_handling.py +0 -86
- hanzo_mcp/tools/common/logging_config.py +0 -115
- hanzo_mcp/tools/common/session.py +0 -91
- hanzo_mcp/tools/common/think_tool.py +0 -123
- hanzo_mcp/tools/common/version_tool.py +0 -120
- hanzo_mcp/tools/filesystem/edit_file.py +0 -287
- hanzo_mcp/tools/filesystem/get_file_info.py +0 -170
- hanzo_mcp/tools/filesystem/read_files.py +0 -198
- hanzo_mcp/tools/filesystem/search_content.py +0 -275
- hanzo_mcp/tools/filesystem/write_file.py +0 -162
- hanzo_mcp/tools/jupyter/notebook_operations.py +0 -514
- hanzo_mcp/tools/jupyter/read_notebook.py +0 -165
- hanzo_mcp/tools/project/__init__.py +0 -64
- hanzo_mcp/tools/project/analysis.py +0 -882
- hanzo_mcp/tools/project/base.py +0 -66
- hanzo_mcp/tools/project/project_analyze.py +0 -173
- hanzo_mcp/tools/shell/run_script.py +0 -215
- hanzo_mcp/tools/shell/script_tool.py +0 -244
- hanzo_mcp-0.3.4.dist-info/RECORD +0 -53
- {hanzo_mcp-0.3.4.dist-info → hanzo_mcp-0.5.0.dist-info}/entry_points.txt +0 -0
- {hanzo_mcp-0.3.4.dist-info → hanzo_mcp-0.5.0.dist-info}/licenses/LICENSE +0 -0
- {hanzo_mcp-0.3.4.dist-info → hanzo_mcp-0.5.0.dist-info}/top_level.txt +0 -0
hanzo_mcp/server.py
CHANGED
|
@@ -1,117 +1,189 @@
|
|
|
1
|
-
"""MCP server implementing Hanzo capabilities.
|
|
2
|
-
|
|
3
|
-
Includes improved error handling and debugging for tool execution.
|
|
4
|
-
"""
|
|
1
|
+
"""MCP server implementing Hanzo capabilities."""
|
|
5
2
|
|
|
3
|
+
import atexit
|
|
4
|
+
import signal
|
|
5
|
+
import threading
|
|
6
|
+
import time
|
|
6
7
|
from typing import Literal, cast, final
|
|
7
8
|
|
|
8
|
-
from
|
|
9
|
+
from fastmcp import FastMCP
|
|
9
10
|
|
|
11
|
+
from hanzo_mcp.prompts import register_all_prompts
|
|
10
12
|
from hanzo_mcp.tools import register_all_tools
|
|
11
|
-
|
|
13
|
+
|
|
12
14
|
from hanzo_mcp.tools.common.permissions import PermissionManager
|
|
13
|
-
from hanzo_mcp.tools.
|
|
14
|
-
from hanzo_mcp.tools.shell.command_executor import CommandExecutor
|
|
15
|
+
from hanzo_mcp.tools.shell.session_storage import SessionStorage
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
@final
|
|
18
|
-
class
|
|
19
|
-
"""MCP server implementing Hanzo capabilities.
|
|
20
|
-
|
|
21
|
-
Includes improved error handling and debugging for tool execution.
|
|
22
|
-
"""
|
|
19
|
+
class HanzoMCPServer:
|
|
20
|
+
"""MCP server implementing Hanzo capabilities."""
|
|
23
21
|
|
|
24
22
|
def __init__(
|
|
25
23
|
self,
|
|
26
|
-
name: str = "
|
|
24
|
+
name: str = "hanzo",
|
|
27
25
|
allowed_paths: list[str] | None = None,
|
|
26
|
+
project_paths: list[str] | None = None,
|
|
28
27
|
project_dir: str | None = None,
|
|
29
28
|
mcp_instance: FastMCP | None = None,
|
|
30
29
|
agent_model: str | None = None,
|
|
31
30
|
agent_max_tokens: int | None = None,
|
|
32
31
|
agent_api_key: str | None = None,
|
|
32
|
+
agent_base_url: str | None = None,
|
|
33
33
|
agent_max_iterations: int = 10,
|
|
34
34
|
agent_max_tool_uses: int = 30,
|
|
35
35
|
enable_agent_tool: bool = False,
|
|
36
|
+
command_timeout: float = 120.0,
|
|
36
37
|
disable_write_tools: bool = False,
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
disable_search_tools: bool = False,
|
|
39
|
+
host: str = "127.0.0.1",
|
|
40
|
+
port: int = 3000,
|
|
41
|
+
enabled_tools: dict[str, bool] | None = None,
|
|
42
|
+
disabled_tools: list[str] | None = None,
|
|
39
43
|
):
|
|
40
|
-
"""Initialize the Hanzo server.
|
|
44
|
+
"""Initialize the Hanzo MCP server.
|
|
41
45
|
|
|
42
46
|
Args:
|
|
43
47
|
name: The name of the server
|
|
44
48
|
allowed_paths: list of paths that the server is allowed to access
|
|
45
|
-
|
|
49
|
+
project_paths: list of project paths to generate prompts for
|
|
50
|
+
project_dir: single project directory (added to allowed_paths and project_paths)
|
|
46
51
|
mcp_instance: Optional FastMCP instance for testing
|
|
47
52
|
agent_model: Optional model name for agent tool in LiteLLM format
|
|
48
53
|
agent_max_tokens: Optional maximum tokens for agent responses
|
|
49
54
|
agent_api_key: Optional API key for the LLM provider
|
|
55
|
+
agent_base_url: Optional base URL for the LLM provider API endpoint
|
|
50
56
|
agent_max_iterations: Maximum number of iterations for agent (default: 10)
|
|
51
57
|
agent_max_tool_uses: Maximum number of total tool uses for agent (default: 30)
|
|
52
58
|
enable_agent_tool: Whether to enable the agent tool (default: False)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
59
|
+
command_timeout: Default timeout for command execution in seconds (default: 120.0)
|
|
60
|
+
disable_write_tools: Whether to disable write tools (default: False)
|
|
61
|
+
disable_search_tools: Whether to disable search tools (default: False)
|
|
62
|
+
host: Host for SSE server (default: 127.0.0.1)
|
|
63
|
+
port: Port for SSE server (default: 3000)
|
|
64
|
+
enabled_tools: Dictionary of individual tool enable states (default: None)
|
|
65
|
+
disabled_tools: List of tool names to disable (default: None)
|
|
56
66
|
"""
|
|
57
67
|
self.mcp = mcp_instance if mcp_instance is not None else FastMCP(name)
|
|
58
68
|
|
|
59
|
-
# Initialize
|
|
60
|
-
self.document_context = DocumentContext()
|
|
69
|
+
# Initialize permissions and command executor
|
|
61
70
|
self.permission_manager = PermissionManager()
|
|
62
71
|
|
|
63
|
-
#
|
|
64
|
-
self.command_executor = CommandExecutor(
|
|
65
|
-
permission_manager=self.permission_manager,
|
|
66
|
-
verbose=False, # Set to True for debugging
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
# If project_dir is specified, set it as initial working directory for all sessions
|
|
72
|
+
# Handle project_dir parameter
|
|
70
73
|
if project_dir:
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
self.document_context, self.permission_manager, self.project_analyzer
|
|
80
|
-
)
|
|
74
|
+
if allowed_paths is None:
|
|
75
|
+
allowed_paths = []
|
|
76
|
+
if project_dir not in allowed_paths:
|
|
77
|
+
allowed_paths.append(project_dir)
|
|
78
|
+
if project_paths is None:
|
|
79
|
+
project_paths = []
|
|
80
|
+
if project_dir not in project_paths:
|
|
81
|
+
project_paths.append(project_dir)
|
|
81
82
|
|
|
82
83
|
# Add allowed paths
|
|
83
84
|
if allowed_paths:
|
|
84
85
|
for path in allowed_paths:
|
|
85
86
|
self.permission_manager.add_allowed_path(path)
|
|
86
|
-
|
|
87
|
+
|
|
88
|
+
# Store paths and options
|
|
89
|
+
self.project_paths = project_paths
|
|
90
|
+
self.project_dir = project_dir
|
|
91
|
+
self.disable_write_tools = disable_write_tools
|
|
92
|
+
self.disable_search_tools = disable_search_tools
|
|
93
|
+
self.host = host
|
|
94
|
+
self.port = port
|
|
95
|
+
self.enabled_tools = enabled_tools or {}
|
|
96
|
+
self.disabled_tools = disabled_tools or []
|
|
87
97
|
|
|
88
98
|
# Store agent options
|
|
89
99
|
self.agent_model = agent_model
|
|
90
100
|
self.agent_max_tokens = agent_max_tokens
|
|
91
101
|
self.agent_api_key = agent_api_key
|
|
102
|
+
self.agent_base_url = agent_base_url
|
|
92
103
|
self.agent_max_iterations = agent_max_iterations
|
|
93
104
|
self.agent_max_tool_uses = agent_max_tool_uses
|
|
94
105
|
self.enable_agent_tool = enable_agent_tool
|
|
95
|
-
self.
|
|
106
|
+
self.command_timeout = command_timeout
|
|
96
107
|
|
|
97
|
-
#
|
|
98
|
-
self.
|
|
99
|
-
self.
|
|
108
|
+
# Initialize cleanup tracking
|
|
109
|
+
self._cleanup_thread: threading.Thread | None = None
|
|
110
|
+
self._shutdown_event = threading.Event()
|
|
111
|
+
self._cleanup_registered = False
|
|
112
|
+
|
|
113
|
+
# Apply disabled_tools to enabled_tools
|
|
114
|
+
final_enabled_tools = self.enabled_tools.copy()
|
|
115
|
+
for tool_name in self.disabled_tools:
|
|
116
|
+
final_enabled_tools[tool_name] = False
|
|
117
|
+
|
|
118
|
+
# Store the final processed tool configuration
|
|
119
|
+
self.enabled_tools = final_enabled_tools
|
|
100
120
|
|
|
101
121
|
# Register all tools
|
|
102
122
|
register_all_tools(
|
|
103
123
|
mcp_server=self.mcp,
|
|
104
|
-
document_context=self.document_context,
|
|
105
124
|
permission_manager=self.permission_manager,
|
|
106
125
|
agent_model=self.agent_model,
|
|
107
126
|
agent_max_tokens=self.agent_max_tokens,
|
|
108
127
|
agent_api_key=self.agent_api_key,
|
|
128
|
+
agent_base_url=self.agent_base_url,
|
|
109
129
|
agent_max_iterations=self.agent_max_iterations,
|
|
110
130
|
agent_max_tool_uses=self.agent_max_tool_uses,
|
|
111
131
|
enable_agent_tool=self.enable_agent_tool,
|
|
112
132
|
disable_write_tools=self.disable_write_tools,
|
|
133
|
+
disable_search_tools=self.disable_search_tools,
|
|
134
|
+
enabled_tools=final_enabled_tools,
|
|
113
135
|
)
|
|
114
136
|
|
|
137
|
+
register_all_prompts(mcp_server=self.mcp, projects=self.project_paths)
|
|
138
|
+
|
|
139
|
+
def _setup_cleanup_handlers(self) -> None:
|
|
140
|
+
"""Set up signal handlers and background cleanup thread."""
|
|
141
|
+
if self._cleanup_registered:
|
|
142
|
+
return
|
|
143
|
+
|
|
144
|
+
# Register cleanup on normal exit
|
|
145
|
+
atexit.register(self._cleanup_sessions)
|
|
146
|
+
|
|
147
|
+
# Register signal handlers for graceful shutdown
|
|
148
|
+
def signal_handler(signum, frame):
|
|
149
|
+
self._cleanup_sessions()
|
|
150
|
+
self._shutdown_event.set()
|
|
151
|
+
|
|
152
|
+
signal.signal(signal.SIGTERM, signal_handler)
|
|
153
|
+
signal.signal(signal.SIGINT, signal_handler)
|
|
154
|
+
|
|
155
|
+
# Start background cleanup thread for periodic cleanup
|
|
156
|
+
self._cleanup_thread = threading.Thread(
|
|
157
|
+
target=self._background_cleanup, daemon=True
|
|
158
|
+
)
|
|
159
|
+
self._cleanup_thread.start()
|
|
160
|
+
|
|
161
|
+
self._cleanup_registered = True
|
|
162
|
+
|
|
163
|
+
def _background_cleanup(self) -> None:
|
|
164
|
+
"""Background thread for periodic session cleanup."""
|
|
165
|
+
while not self._shutdown_event.is_set():
|
|
166
|
+
try:
|
|
167
|
+
# Clean up expired sessions every 2 minutes
|
|
168
|
+
# Using shorter TTL of 5 minutes (300 seconds)
|
|
169
|
+
SessionStorage.cleanup_expired_sessions(max_age_seconds=300)
|
|
170
|
+
|
|
171
|
+
# Wait for 2 minutes or until shutdown
|
|
172
|
+
self._shutdown_event.wait(timeout=120)
|
|
173
|
+
except Exception:
|
|
174
|
+
# Ignore cleanup errors and continue
|
|
175
|
+
pass
|
|
176
|
+
|
|
177
|
+
def _cleanup_sessions(self) -> None:
|
|
178
|
+
"""Clean up all active sessions."""
|
|
179
|
+
try:
|
|
180
|
+
cleared_count = SessionStorage.clear_all_sessions()
|
|
181
|
+
if cleared_count > 0:
|
|
182
|
+
print(f"Cleaned up {cleared_count} tmux sessions on shutdown")
|
|
183
|
+
except Exception:
|
|
184
|
+
# Ignore cleanup errors during shutdown
|
|
185
|
+
pass
|
|
186
|
+
|
|
115
187
|
def run(self, transport: str = "stdio", allowed_paths: list[str] | None = None):
|
|
116
188
|
"""Run the MCP server.
|
|
117
189
|
|
|
@@ -123,60 +195,10 @@ Includes improved error handling and debugging for tool execution.
|
|
|
123
195
|
allowed_paths_list = allowed_paths or []
|
|
124
196
|
for path in allowed_paths_list:
|
|
125
197
|
self.permission_manager.add_allowed_path(path)
|
|
126
|
-
self.document_context.add_allowed_path(path)
|
|
127
198
|
|
|
128
|
-
#
|
|
129
|
-
|
|
130
|
-
import os
|
|
131
|
-
# Set environment variables for FastMCP settings
|
|
132
|
-
os.environ["FASTMCP_PORT"] = str(self.port)
|
|
133
|
-
os.environ["FASTMCP_HOST"] = self.host
|
|
134
|
-
print(f"Starting SSE server on {self.host}:{self.port}")
|
|
199
|
+
# Set up cleanup handlers before running
|
|
200
|
+
self._setup_cleanup_handlers()
|
|
135
201
|
|
|
136
202
|
# Run the server
|
|
137
203
|
transport_type = cast(Literal["stdio", "sse"], transport)
|
|
138
204
|
self.mcp.run(transport=transport_type)
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
def main():
|
|
142
|
-
"""Run the Hanzo MCP server."""
|
|
143
|
-
import argparse
|
|
144
|
-
|
|
145
|
-
parser = argparse.ArgumentParser(
|
|
146
|
-
description="MCP server implementing Hanzo capabilities"
|
|
147
|
-
)
|
|
148
|
-
|
|
149
|
-
_ = parser.add_argument(
|
|
150
|
-
"--name",
|
|
151
|
-
default="claude-code",
|
|
152
|
-
help="Name of the MCP server (default: claude-code)",
|
|
153
|
-
)
|
|
154
|
-
|
|
155
|
-
_ = parser.add_argument(
|
|
156
|
-
"--transport",
|
|
157
|
-
choices=["stdio", "sse"],
|
|
158
|
-
default="stdio",
|
|
159
|
-
help="Transport protocol to use (default: stdio)",
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
_ = parser.add_argument(
|
|
163
|
-
"--allow-path",
|
|
164
|
-
action="append",
|
|
165
|
-
dest="allowed_paths",
|
|
166
|
-
help="Add an allowed path (can be specified multiple times)",
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
args = parser.parse_args()
|
|
170
|
-
|
|
171
|
-
# Type annotations for args to avoid Any warnings
|
|
172
|
-
name: str = args.name
|
|
173
|
-
transport: str = args.transport
|
|
174
|
-
allowed_paths: list[str] | None = args.allowed_paths
|
|
175
|
-
|
|
176
|
-
# Create and run the server
|
|
177
|
-
server = HanzoServer(name=name, allowed_paths=allowed_paths)
|
|
178
|
-
server.run(transport=transport, allowed_paths=allowed_paths or [])
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if __name__ == "__main__":
|
|
182
|
-
main()
|
hanzo_mcp/tools/__init__.py
CHANGED
|
@@ -9,78 +9,154 @@ 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 fastmcp import FastMCP
|
|
13
13
|
|
|
14
14
|
from hanzo_mcp.tools.agent import register_agent_tools
|
|
15
|
-
from hanzo_mcp.tools.common import
|
|
16
|
-
from hanzo_mcp.tools.common.
|
|
15
|
+
from hanzo_mcp.tools.common import register_batch_tool, register_thinking_tool
|
|
16
|
+
from hanzo_mcp.tools.common.base import BaseTool
|
|
17
|
+
|
|
17
18
|
from hanzo_mcp.tools.common.permissions import PermissionManager
|
|
18
19
|
from hanzo_mcp.tools.filesystem import register_filesystem_tools
|
|
19
20
|
from hanzo_mcp.tools.jupyter import register_jupyter_tools
|
|
20
|
-
from hanzo_mcp.tools.project import register_project_tools
|
|
21
21
|
from hanzo_mcp.tools.shell import register_shell_tools
|
|
22
|
-
from hanzo_mcp.tools.
|
|
22
|
+
from hanzo_mcp.tools.todo import register_todo_tools
|
|
23
|
+
from hanzo_mcp.tools.vector import register_vector_tools
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
def register_all_tools(
|
|
26
27
|
mcp_server: FastMCP,
|
|
27
|
-
document_context: DocumentContext,
|
|
28
28
|
permission_manager: PermissionManager,
|
|
29
29
|
agent_model: str | None = None,
|
|
30
30
|
agent_max_tokens: int | None = None,
|
|
31
31
|
agent_api_key: str | None = None,
|
|
32
|
+
agent_base_url: str | None = None,
|
|
32
33
|
agent_max_iterations: int = 10,
|
|
33
34
|
agent_max_tool_uses: int = 30,
|
|
34
35
|
enable_agent_tool: bool = False,
|
|
35
36
|
disable_write_tools: bool = False,
|
|
37
|
+
disable_search_tools: bool = False,
|
|
38
|
+
enabled_tools: dict[str, bool] | None = None,
|
|
39
|
+
vector_config: dict | None = None,
|
|
36
40
|
) -> None:
|
|
37
41
|
"""Register all Hanzo tools with the MCP server.
|
|
38
42
|
|
|
39
43
|
Args:
|
|
40
44
|
mcp_server: The FastMCP server instance
|
|
41
|
-
document_context: Document context for tracking file contents
|
|
42
45
|
permission_manager: Permission manager for access control
|
|
43
46
|
agent_model: Optional model name for agent tool in LiteLLM format
|
|
44
47
|
agent_max_tokens: Optional maximum tokens for agent responses
|
|
45
48
|
agent_api_key: Optional API key for the LLM provider
|
|
49
|
+
agent_base_url: Optional base URL for the LLM provider API endpoint
|
|
46
50
|
agent_max_iterations: Maximum number of iterations for agent (default: 10)
|
|
47
51
|
agent_max_tool_uses: Maximum number of total tool uses for agent (default: 30)
|
|
48
52
|
enable_agent_tool: Whether to enable the agent tool (default: False)
|
|
49
|
-
disable_write_tools: Whether to disable write
|
|
53
|
+
disable_write_tools: Whether to disable write tools (default: False)
|
|
54
|
+
disable_search_tools: Whether to disable search tools (default: False)
|
|
55
|
+
enabled_tools: Dictionary of individual tool enable/disable states (default: None)
|
|
56
|
+
vector_config: Vector store configuration (default: None)
|
|
50
57
|
"""
|
|
51
|
-
#
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
#
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
# Dictionary to store all registered tools
|
|
59
|
+
all_tools: dict[str, BaseTool] = {}
|
|
60
|
+
|
|
61
|
+
# Use individual tool configuration if provided, otherwise fall back to category-level flags
|
|
62
|
+
tool_config = enabled_tools or {}
|
|
63
|
+
|
|
64
|
+
def is_tool_enabled(tool_name: str, category_enabled: bool = True) -> bool:
|
|
65
|
+
"""Check if a specific tool should be enabled."""
|
|
66
|
+
if tool_name in tool_config:
|
|
67
|
+
return tool_config[tool_name]
|
|
68
|
+
return category_enabled
|
|
59
69
|
|
|
60
|
-
# Register
|
|
61
|
-
|
|
70
|
+
# Register filesystem tools with individual configuration
|
|
71
|
+
filesystem_enabled = {
|
|
72
|
+
"read": is_tool_enabled("read", True),
|
|
73
|
+
"write": is_tool_enabled("write", not disable_write_tools),
|
|
74
|
+
"edit": is_tool_enabled("edit", not disable_write_tools),
|
|
75
|
+
"multi_edit": is_tool_enabled("multi_edit", not disable_write_tools),
|
|
76
|
+
"directory_tree": is_tool_enabled("directory_tree", True),
|
|
77
|
+
"grep": is_tool_enabled("grep", not disable_search_tools),
|
|
78
|
+
"grep_ast": is_tool_enabled("grep_ast", not disable_search_tools),
|
|
79
|
+
"content_replace": is_tool_enabled("content_replace", not disable_write_tools),
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
filesystem_tools = register_filesystem_tools(
|
|
62
83
|
mcp_server,
|
|
63
84
|
permission_manager,
|
|
64
|
-
|
|
65
|
-
CommandExecutor(permission_manager)
|
|
85
|
+
enabled_tools=filesystem_enabled,
|
|
66
86
|
)
|
|
87
|
+
for tool in filesystem_tools:
|
|
88
|
+
all_tools[tool.name] = tool
|
|
67
89
|
|
|
68
|
-
# Register
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
90
|
+
# Register jupyter tools if enabled
|
|
91
|
+
jupyter_enabled = {
|
|
92
|
+
"notebook_read": is_tool_enabled("notebook_read", True),
|
|
93
|
+
"notebook_edit": is_tool_enabled("notebook_edit", True),
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if any(jupyter_enabled.values()):
|
|
97
|
+
jupyter_tools = register_jupyter_tools(mcp_server, permission_manager, enabled_tools=jupyter_enabled)
|
|
98
|
+
for tool in jupyter_tools:
|
|
99
|
+
all_tools[tool.name] = tool
|
|
100
|
+
|
|
101
|
+
# Register shell tools if enabled
|
|
102
|
+
if is_tool_enabled("run_command", True):
|
|
103
|
+
shell_tools = register_shell_tools(mcp_server, permission_manager)
|
|
104
|
+
for tool in shell_tools:
|
|
105
|
+
all_tools[tool.name] = tool
|
|
106
|
+
|
|
107
|
+
# Register agent tools if enabled
|
|
108
|
+
agent_enabled = enable_agent_tool or is_tool_enabled("dispatch_agent", False)
|
|
109
|
+
if agent_enabled:
|
|
110
|
+
agent_tools = register_agent_tools(
|
|
111
|
+
mcp_server,
|
|
73
112
|
permission_manager,
|
|
74
|
-
CommandExecutor(permission_manager),
|
|
75
113
|
agent_model=agent_model,
|
|
76
114
|
agent_max_tokens=agent_max_tokens,
|
|
77
115
|
agent_api_key=agent_api_key,
|
|
116
|
+
agent_base_url=agent_base_url,
|
|
78
117
|
agent_max_iterations=agent_max_iterations,
|
|
79
|
-
agent_max_tool_uses=agent_max_tool_uses
|
|
118
|
+
agent_max_tool_uses=agent_max_tool_uses,
|
|
80
119
|
)
|
|
120
|
+
for tool in agent_tools:
|
|
121
|
+
all_tools[tool.name] = tool
|
|
122
|
+
|
|
123
|
+
# Register todo tools if enabled
|
|
124
|
+
todo_enabled = {
|
|
125
|
+
"todo_read": is_tool_enabled("todo_read", True),
|
|
126
|
+
"todo_write": is_tool_enabled("todo_write", True),
|
|
127
|
+
}
|
|
81
128
|
|
|
82
|
-
|
|
83
|
-
|
|
129
|
+
if any(todo_enabled.values()):
|
|
130
|
+
todo_tools = register_todo_tools(mcp_server, enabled_tools=todo_enabled)
|
|
131
|
+
for tool in todo_tools:
|
|
132
|
+
all_tools[tool.name] = tool
|
|
133
|
+
|
|
134
|
+
# Register thinking tool if enabled
|
|
135
|
+
if is_tool_enabled("think", True):
|
|
136
|
+
thinking_tool = register_thinking_tool(mcp_server)
|
|
137
|
+
for tool in thinking_tool:
|
|
138
|
+
all_tools[tool.name] = tool
|
|
139
|
+
|
|
140
|
+
# Register vector tools if enabled
|
|
141
|
+
vector_enabled = {
|
|
142
|
+
"vector_index": is_tool_enabled("vector_index", False),
|
|
143
|
+
"vector_search": is_tool_enabled("vector_search", False),
|
|
144
|
+
}
|
|
84
145
|
|
|
85
|
-
|
|
86
|
-
|
|
146
|
+
if any(vector_enabled.values()) and vector_config:
|
|
147
|
+
# Get allowed paths for project detection
|
|
148
|
+
search_paths = [str(path) for path in permission_manager.allowed_paths]
|
|
149
|
+
|
|
150
|
+
vector_tools = register_vector_tools(
|
|
151
|
+
mcp_server,
|
|
152
|
+
permission_manager,
|
|
153
|
+
vector_config=vector_config,
|
|
154
|
+
enabled_tools=vector_enabled,
|
|
155
|
+
search_paths=search_paths,
|
|
156
|
+
)
|
|
157
|
+
for tool in vector_tools:
|
|
158
|
+
all_tools[tool.name] = tool
|
|
159
|
+
|
|
160
|
+
# Register batch tool if enabled (batch tool is typically always enabled)
|
|
161
|
+
if is_tool_enabled("batch", True):
|
|
162
|
+
register_batch_tool(mcp_server, all_tools)
|
|
@@ -4,23 +4,21 @@ 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 fastmcp import FastMCP
|
|
8
8
|
|
|
9
9
|
from hanzo_mcp.tools.agent.agent_tool import AgentTool
|
|
10
|
-
from hanzo_mcp.tools.common.base import BaseTool, ToolRegistry
|
|
11
|
-
|
|
10
|
+
from hanzo_mcp.tools.common.base import BaseTool, ToolRegistry
|
|
11
|
+
|
|
12
12
|
from hanzo_mcp.tools.common.permissions import PermissionManager
|
|
13
|
-
from hanzo_mcp.tools.shell.command_executor import CommandExecutor
|
|
14
13
|
|
|
15
14
|
|
|
16
15
|
def register_agent_tools(
|
|
17
16
|
mcp_server: FastMCP,
|
|
18
|
-
document_context: DocumentContext,
|
|
19
17
|
permission_manager: PermissionManager,
|
|
20
|
-
command_executor: CommandExecutor,
|
|
21
18
|
agent_model: str | None = None,
|
|
22
19
|
agent_max_tokens: int | None = None,
|
|
23
20
|
agent_api_key: str | None = None,
|
|
21
|
+
agent_base_url: str | None = None,
|
|
24
22
|
agent_max_iterations: int = 10,
|
|
25
23
|
agent_max_tool_uses: int = 30,
|
|
26
24
|
) -> list[BaseTool]:
|
|
@@ -28,12 +26,12 @@ def register_agent_tools(
|
|
|
28
26
|
|
|
29
27
|
Args:
|
|
30
28
|
mcp_server: The FastMCP server instance
|
|
31
|
-
|
|
29
|
+
|
|
32
30
|
permission_manager: Permission manager for access control
|
|
33
|
-
command_executor: Command executor for running shell commands
|
|
34
31
|
agent_model: Optional model name for agent tool in LiteLLM format
|
|
35
32
|
agent_max_tokens: Optional maximum tokens for agent responses
|
|
36
33
|
agent_api_key: Optional API key for the LLM provider
|
|
34
|
+
agent_base_url: Optional base URL for the LLM provider API endpoint
|
|
37
35
|
agent_max_iterations: Maximum number of iterations for agent (default: 10)
|
|
38
36
|
agent_max_tool_uses: Maximum number of total tool uses for agent (default: 30)
|
|
39
37
|
|
|
@@ -42,14 +40,13 @@ def register_agent_tools(
|
|
|
42
40
|
"""
|
|
43
41
|
# Create agent tool
|
|
44
42
|
agent_tool = AgentTool(
|
|
45
|
-
document_context=document_context,
|
|
46
43
|
permission_manager=permission_manager,
|
|
47
|
-
command_executor=command_executor,
|
|
48
44
|
model=agent_model,
|
|
49
45
|
api_key=agent_api_key,
|
|
46
|
+
base_url=agent_base_url,
|
|
50
47
|
max_tokens=agent_max_tokens,
|
|
51
48
|
max_iterations=agent_max_iterations,
|
|
52
|
-
max_tool_uses=agent_max_tool_uses
|
|
49
|
+
max_tool_uses=agent_max_tool_uses,
|
|
53
50
|
)
|
|
54
51
|
|
|
55
52
|
# Register agent tool
|