hanzo-mcp 0.7.6__py3-none-any.whl → 0.8.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 +7 -1
- hanzo_mcp/__main__.py +1 -1
- hanzo_mcp/analytics/__init__.py +2 -2
- hanzo_mcp/analytics/posthog_analytics.py +76 -82
- hanzo_mcp/cli.py +31 -36
- hanzo_mcp/cli_enhanced.py +94 -72
- hanzo_mcp/cli_plugin.py +27 -17
- hanzo_mcp/config/__init__.py +2 -2
- hanzo_mcp/config/settings.py +112 -88
- hanzo_mcp/config/tool_config.py +32 -34
- hanzo_mcp/dev_server.py +66 -67
- hanzo_mcp/prompts/__init__.py +94 -12
- hanzo_mcp/prompts/enhanced_prompts.py +809 -0
- hanzo_mcp/prompts/example_custom_prompt.py +6 -5
- hanzo_mcp/prompts/project_todo_reminder.py +0 -1
- hanzo_mcp/prompts/tool_explorer.py +10 -7
- hanzo_mcp/server.py +17 -21
- hanzo_mcp/server_enhanced.py +15 -22
- hanzo_mcp/tools/__init__.py +56 -28
- hanzo_mcp/tools/agent/__init__.py +16 -19
- hanzo_mcp/tools/agent/agent.py +82 -65
- hanzo_mcp/tools/agent/agent_tool.py +152 -122
- hanzo_mcp/tools/agent/agent_tool_v1_deprecated.py +66 -62
- hanzo_mcp/tools/agent/clarification_protocol.py +55 -50
- hanzo_mcp/tools/agent/clarification_tool.py +11 -10
- hanzo_mcp/tools/agent/claude_cli_tool.py +21 -20
- hanzo_mcp/tools/agent/claude_desktop_auth.py +130 -144
- hanzo_mcp/tools/agent/cli_agent_base.py +59 -53
- hanzo_mcp/tools/agent/code_auth.py +102 -107
- hanzo_mcp/tools/agent/code_auth_tool.py +28 -27
- hanzo_mcp/tools/agent/codex_cli_tool.py +20 -19
- hanzo_mcp/tools/agent/critic_tool.py +86 -73
- hanzo_mcp/tools/agent/gemini_cli_tool.py +21 -20
- hanzo_mcp/tools/agent/grok_cli_tool.py +21 -20
- hanzo_mcp/tools/agent/iching_tool.py +404 -139
- hanzo_mcp/tools/agent/network_tool.py +89 -73
- hanzo_mcp/tools/agent/prompt.py +2 -1
- hanzo_mcp/tools/agent/review_tool.py +101 -98
- hanzo_mcp/tools/agent/swarm_alias.py +87 -0
- hanzo_mcp/tools/agent/swarm_tool.py +246 -161
- hanzo_mcp/tools/agent/swarm_tool_v1_deprecated.py +134 -92
- hanzo_mcp/tools/agent/tool_adapter.py +21 -11
- hanzo_mcp/tools/common/__init__.py +1 -1
- hanzo_mcp/tools/common/base.py +3 -5
- hanzo_mcp/tools/common/batch_tool.py +46 -39
- hanzo_mcp/tools/common/config_tool.py +120 -84
- hanzo_mcp/tools/common/context.py +1 -5
- hanzo_mcp/tools/common/context_fix.py +5 -3
- hanzo_mcp/tools/common/critic_tool.py +4 -8
- hanzo_mcp/tools/common/decorators.py +58 -56
- hanzo_mcp/tools/common/enhanced_base.py +29 -32
- hanzo_mcp/tools/common/fastmcp_pagination.py +91 -94
- hanzo_mcp/tools/common/forgiving_edit.py +91 -87
- hanzo_mcp/tools/common/mode.py +15 -17
- hanzo_mcp/tools/common/mode_loader.py +27 -24
- hanzo_mcp/tools/common/paginated_base.py +61 -53
- hanzo_mcp/tools/common/paginated_response.py +72 -79
- hanzo_mcp/tools/common/pagination.py +50 -53
- hanzo_mcp/tools/common/permissions.py +4 -4
- hanzo_mcp/tools/common/personality.py +186 -138
- hanzo_mcp/tools/common/plugin_loader.py +54 -54
- hanzo_mcp/tools/common/stats.py +65 -47
- hanzo_mcp/tools/common/test_helpers.py +31 -0
- hanzo_mcp/tools/common/thinking_tool.py +4 -8
- hanzo_mcp/tools/common/tool_disable.py +17 -12
- hanzo_mcp/tools/common/tool_enable.py +13 -14
- hanzo_mcp/tools/common/tool_list.py +36 -28
- hanzo_mcp/tools/common/truncate.py +23 -23
- hanzo_mcp/tools/config/__init__.py +4 -4
- hanzo_mcp/tools/config/config_tool.py +42 -29
- hanzo_mcp/tools/config/index_config.py +37 -34
- hanzo_mcp/tools/config/mode_tool.py +175 -55
- hanzo_mcp/tools/database/__init__.py +15 -12
- hanzo_mcp/tools/database/database_manager.py +77 -75
- hanzo_mcp/tools/database/graph.py +137 -91
- hanzo_mcp/tools/database/graph_add.py +30 -18
- hanzo_mcp/tools/database/graph_query.py +178 -102
- hanzo_mcp/tools/database/graph_remove.py +33 -28
- hanzo_mcp/tools/database/graph_search.py +97 -75
- hanzo_mcp/tools/database/graph_stats.py +91 -59
- hanzo_mcp/tools/database/sql.py +107 -79
- hanzo_mcp/tools/database/sql_query.py +30 -24
- hanzo_mcp/tools/database/sql_search.py +29 -25
- hanzo_mcp/tools/database/sql_stats.py +47 -35
- hanzo_mcp/tools/editor/neovim_command.py +25 -28
- hanzo_mcp/tools/editor/neovim_edit.py +21 -23
- hanzo_mcp/tools/editor/neovim_session.py +60 -54
- hanzo_mcp/tools/filesystem/__init__.py +31 -30
- hanzo_mcp/tools/filesystem/ast_multi_edit.py +329 -249
- hanzo_mcp/tools/filesystem/ast_tool.py +4 -4
- hanzo_mcp/tools/filesystem/base.py +1 -1
- hanzo_mcp/tools/filesystem/batch_search.py +316 -224
- hanzo_mcp/tools/filesystem/content_replace.py +4 -4
- hanzo_mcp/tools/filesystem/diff.py +71 -59
- hanzo_mcp/tools/filesystem/directory_tree.py +7 -7
- hanzo_mcp/tools/filesystem/directory_tree_paginated.py +49 -37
- hanzo_mcp/tools/filesystem/edit.py +4 -4
- hanzo_mcp/tools/filesystem/find.py +173 -80
- hanzo_mcp/tools/filesystem/find_files.py +73 -52
- hanzo_mcp/tools/filesystem/git_search.py +157 -104
- hanzo_mcp/tools/filesystem/grep.py +8 -8
- hanzo_mcp/tools/filesystem/multi_edit.py +4 -8
- hanzo_mcp/tools/filesystem/read.py +12 -10
- hanzo_mcp/tools/filesystem/rules_tool.py +59 -43
- hanzo_mcp/tools/filesystem/search_tool.py +263 -207
- hanzo_mcp/tools/filesystem/symbols_tool.py +94 -54
- hanzo_mcp/tools/filesystem/tree.py +35 -33
- hanzo_mcp/tools/filesystem/unix_aliases.py +13 -18
- hanzo_mcp/tools/filesystem/watch.py +37 -36
- hanzo_mcp/tools/filesystem/write.py +4 -8
- hanzo_mcp/tools/jupyter/__init__.py +4 -4
- hanzo_mcp/tools/jupyter/base.py +4 -5
- hanzo_mcp/tools/jupyter/jupyter.py +67 -47
- hanzo_mcp/tools/jupyter/notebook_edit.py +4 -4
- hanzo_mcp/tools/jupyter/notebook_read.py +4 -7
- hanzo_mcp/tools/llm/__init__.py +5 -7
- hanzo_mcp/tools/llm/consensus_tool.py +72 -52
- hanzo_mcp/tools/llm/llm_manage.py +101 -60
- hanzo_mcp/tools/llm/llm_tool.py +226 -166
- hanzo_mcp/tools/llm/provider_tools.py +25 -26
- hanzo_mcp/tools/lsp/__init__.py +1 -1
- hanzo_mcp/tools/lsp/lsp_tool.py +228 -143
- hanzo_mcp/tools/mcp/__init__.py +2 -3
- hanzo_mcp/tools/mcp/mcp_add.py +27 -25
- hanzo_mcp/tools/mcp/mcp_remove.py +7 -8
- hanzo_mcp/tools/mcp/mcp_stats.py +23 -22
- hanzo_mcp/tools/mcp/mcp_tool.py +129 -98
- hanzo_mcp/tools/memory/__init__.py +39 -21
- hanzo_mcp/tools/memory/knowledge_tools.py +124 -99
- hanzo_mcp/tools/memory/memory_tools.py +90 -108
- hanzo_mcp/tools/search/__init__.py +7 -2
- hanzo_mcp/tools/search/find_tool.py +297 -212
- hanzo_mcp/tools/search/unified_search.py +366 -314
- hanzo_mcp/tools/shell/__init__.py +8 -7
- hanzo_mcp/tools/shell/auto_background.py +56 -49
- hanzo_mcp/tools/shell/base.py +1 -1
- hanzo_mcp/tools/shell/base_process.py +75 -75
- hanzo_mcp/tools/shell/bash_session.py +2 -2
- hanzo_mcp/tools/shell/bash_session_executor.py +4 -4
- hanzo_mcp/tools/shell/bash_tool.py +24 -31
- hanzo_mcp/tools/shell/command_executor.py +12 -12
- hanzo_mcp/tools/shell/logs.py +43 -33
- hanzo_mcp/tools/shell/npx.py +13 -13
- hanzo_mcp/tools/shell/npx_background.py +24 -21
- hanzo_mcp/tools/shell/npx_tool.py +18 -22
- hanzo_mcp/tools/shell/open.py +19 -21
- hanzo_mcp/tools/shell/pkill.py +31 -26
- hanzo_mcp/tools/shell/process_tool.py +32 -32
- hanzo_mcp/tools/shell/processes.py +57 -58
- hanzo_mcp/tools/shell/run_background.py +24 -25
- hanzo_mcp/tools/shell/run_command.py +5 -5
- hanzo_mcp/tools/shell/run_command_windows.py +5 -5
- hanzo_mcp/tools/shell/session_storage.py +3 -3
- hanzo_mcp/tools/shell/streaming_command.py +141 -126
- hanzo_mcp/tools/shell/uvx.py +24 -25
- hanzo_mcp/tools/shell/uvx_background.py +35 -33
- hanzo_mcp/tools/shell/uvx_tool.py +18 -22
- hanzo_mcp/tools/todo/__init__.py +6 -2
- hanzo_mcp/tools/todo/todo.py +50 -37
- hanzo_mcp/tools/todo/todo_read.py +5 -8
- hanzo_mcp/tools/todo/todo_write.py +5 -7
- hanzo_mcp/tools/vector/__init__.py +40 -28
- hanzo_mcp/tools/vector/ast_analyzer.py +176 -143
- hanzo_mcp/tools/vector/git_ingester.py +170 -179
- hanzo_mcp/tools/vector/index_tool.py +96 -44
- hanzo_mcp/tools/vector/infinity_store.py +283 -228
- hanzo_mcp/tools/vector/mock_infinity.py +39 -40
- hanzo_mcp/tools/vector/project_manager.py +88 -78
- hanzo_mcp/tools/vector/vector.py +59 -42
- hanzo_mcp/tools/vector/vector_index.py +30 -27
- hanzo_mcp/tools/vector/vector_search.py +64 -45
- hanzo_mcp/types.py +6 -4
- {hanzo_mcp-0.7.6.dist-info → hanzo_mcp-0.8.0.dist-info}/METADATA +1 -1
- hanzo_mcp-0.8.0.dist-info/RECORD +185 -0
- hanzo_mcp-0.7.6.dist-info/RECORD +0 -182
- {hanzo_mcp-0.7.6.dist-info → hanzo_mcp-0.8.0.dist-info}/WHEEL +0 -0
- {hanzo_mcp-0.7.6.dist-info → hanzo_mcp-0.8.0.dist-info}/entry_points.txt +0 -0
- {hanzo_mcp-0.7.6.dist-info → hanzo_mcp-0.8.0.dist-info}/top_level.txt +0 -0
hanzo_mcp/tools/mcp/mcp_tool.py
CHANGED
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
"""Unified MCP tool for managing MCP servers."""
|
|
2
2
|
|
|
3
|
-
from typing import Annotated, TypedDict, Unpack, final, override, Optional, Dict, Any, List
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
import json
|
|
6
|
-
import subprocess
|
|
7
3
|
import os
|
|
4
|
+
import json
|
|
8
5
|
import signal
|
|
6
|
+
import subprocess
|
|
7
|
+
from typing import (
|
|
8
|
+
Any,
|
|
9
|
+
Dict,
|
|
10
|
+
List,
|
|
11
|
+
Unpack,
|
|
12
|
+
Optional,
|
|
13
|
+
Annotated,
|
|
14
|
+
TypedDict,
|
|
15
|
+
final,
|
|
16
|
+
override,
|
|
17
|
+
)
|
|
18
|
+
from pathlib import Path
|
|
9
19
|
|
|
10
|
-
from mcp.server.fastmcp import Context as MCPContext
|
|
11
20
|
from pydantic import Field
|
|
21
|
+
from mcp.server.fastmcp import Context as MCPContext
|
|
12
22
|
|
|
13
23
|
from hanzo_mcp.tools.common.base import BaseTool
|
|
14
24
|
from hanzo_mcp.tools.common.context import create_tool_context
|
|
15
25
|
|
|
16
|
-
|
|
17
26
|
# Parameter types
|
|
18
27
|
Action = Annotated[
|
|
19
28
|
str,
|
|
@@ -82,6 +91,7 @@ AutoStart = Annotated[
|
|
|
82
91
|
|
|
83
92
|
class MCPParams(TypedDict, total=False):
|
|
84
93
|
"""Parameters for MCP tool."""
|
|
94
|
+
|
|
85
95
|
action: str
|
|
86
96
|
name: Optional[str]
|
|
87
97
|
command: Optional[str]
|
|
@@ -95,29 +105,29 @@ class MCPParams(TypedDict, total=False):
|
|
|
95
105
|
@final
|
|
96
106
|
class MCPTool(BaseTool):
|
|
97
107
|
"""Tool for managing MCP servers."""
|
|
98
|
-
|
|
108
|
+
|
|
99
109
|
# Config file
|
|
100
110
|
CONFIG_FILE = Path.home() / ".hanzo" / "mcp" / "servers.json"
|
|
101
|
-
|
|
111
|
+
|
|
102
112
|
# Running servers tracking
|
|
103
113
|
_running_servers: Dict[str, subprocess.Popen] = {}
|
|
104
|
-
|
|
114
|
+
|
|
105
115
|
def __init__(self):
|
|
106
116
|
"""Initialize the MCP management tool."""
|
|
107
117
|
self.config = self._load_config()
|
|
108
|
-
|
|
118
|
+
|
|
109
119
|
# Auto-start servers if configured
|
|
110
120
|
self._auto_start_servers()
|
|
111
|
-
|
|
121
|
+
|
|
112
122
|
def _load_config(self) -> Dict[str, Any]:
|
|
113
123
|
"""Load MCP server configuration."""
|
|
114
124
|
if self.CONFIG_FILE.exists():
|
|
115
125
|
try:
|
|
116
|
-
with open(self.CONFIG_FILE,
|
|
126
|
+
with open(self.CONFIG_FILE, "r") as f:
|
|
117
127
|
return json.load(f)
|
|
118
|
-
except:
|
|
128
|
+
except Exception:
|
|
119
129
|
pass
|
|
120
|
-
|
|
130
|
+
|
|
121
131
|
# Default configuration with some examples
|
|
122
132
|
return {
|
|
123
133
|
"servers": {
|
|
@@ -128,51 +138,56 @@ class MCPTool(BaseTool):
|
|
|
128
138
|
"env": {},
|
|
129
139
|
"enabled": False,
|
|
130
140
|
"auto_start": False,
|
|
131
|
-
"description": "MCP filesystem server for /tmp access"
|
|
141
|
+
"description": "MCP filesystem server for /tmp access",
|
|
132
142
|
},
|
|
133
143
|
"github": {
|
|
134
|
-
"command": "npx",
|
|
144
|
+
"command": "npx",
|
|
135
145
|
"args": ["@modelcontextprotocol/server-github"],
|
|
136
146
|
"env": {"GITHUB_TOKEN": "${GITHUB_TOKEN}"},
|
|
137
147
|
"enabled": False,
|
|
138
148
|
"auto_start": False,
|
|
139
|
-
"description": "GitHub API access via MCP"
|
|
149
|
+
"description": "GitHub API access via MCP",
|
|
140
150
|
},
|
|
141
151
|
"postgres": {
|
|
142
152
|
"command": "npx",
|
|
143
|
-
"args": [
|
|
153
|
+
"args": [
|
|
154
|
+
"@modelcontextprotocol/server-postgres",
|
|
155
|
+
"postgresql://localhost/db",
|
|
156
|
+
],
|
|
144
157
|
"env": {},
|
|
145
158
|
"enabled": False,
|
|
146
159
|
"auto_start": False,
|
|
147
|
-
"description": "PostgreSQL database access"
|
|
148
|
-
}
|
|
160
|
+
"description": "PostgreSQL database access",
|
|
161
|
+
},
|
|
149
162
|
},
|
|
150
163
|
"global_env": {},
|
|
151
|
-
"log_dir": str(Path.home() / ".hanzo" / "mcp" / "logs")
|
|
164
|
+
"log_dir": str(Path.home() / ".hanzo" / "mcp" / "logs"),
|
|
152
165
|
}
|
|
153
|
-
|
|
166
|
+
|
|
154
167
|
def _save_config(self):
|
|
155
168
|
"""Save configuration."""
|
|
156
169
|
self.CONFIG_FILE.parent.mkdir(parents=True, exist_ok=True)
|
|
157
|
-
with open(self.CONFIG_FILE,
|
|
170
|
+
with open(self.CONFIG_FILE, "w") as f:
|
|
158
171
|
json.dump(self.config, f, indent=2)
|
|
159
|
-
|
|
172
|
+
|
|
160
173
|
def _auto_start_servers(self):
|
|
161
174
|
"""Auto-start servers configured for auto-start."""
|
|
162
175
|
for name, server_config in self.config.get("servers", {}).items():
|
|
163
|
-
if server_config.get("enabled", False) and server_config.get(
|
|
176
|
+
if server_config.get("enabled", False) and server_config.get(
|
|
177
|
+
"auto_start", False
|
|
178
|
+
):
|
|
164
179
|
self._start_server(name, server_config)
|
|
165
|
-
|
|
180
|
+
|
|
166
181
|
def _start_server(self, name: str, config: Dict[str, Any]) -> bool:
|
|
167
182
|
"""Start an MCP server."""
|
|
168
183
|
if name in self._running_servers:
|
|
169
184
|
return False # Already running
|
|
170
|
-
|
|
185
|
+
|
|
171
186
|
try:
|
|
172
187
|
# Prepare environment
|
|
173
188
|
env = os.environ.copy()
|
|
174
189
|
env.update(self.config.get("global_env", {}))
|
|
175
|
-
|
|
190
|
+
|
|
176
191
|
# Process server-specific env vars
|
|
177
192
|
server_env = config.get("env", {})
|
|
178
193
|
for key, value in server_env.items():
|
|
@@ -182,54 +197,56 @@ class MCPTool(BaseTool):
|
|
|
182
197
|
if var_name in os.environ:
|
|
183
198
|
value = os.environ[var_name]
|
|
184
199
|
env[key] = value
|
|
185
|
-
|
|
200
|
+
|
|
186
201
|
# Prepare command
|
|
187
202
|
cmd = [config["command"]] + config.get("args", [])
|
|
188
|
-
|
|
203
|
+
|
|
189
204
|
# Create log directory
|
|
190
|
-
log_dir = Path(
|
|
205
|
+
log_dir = Path(
|
|
206
|
+
self.config.get("log_dir", str(Path.home() / ".hanzo" / "mcp" / "logs"))
|
|
207
|
+
)
|
|
191
208
|
log_dir.mkdir(parents=True, exist_ok=True)
|
|
192
|
-
|
|
209
|
+
|
|
193
210
|
# Start process
|
|
194
211
|
log_file = log_dir / f"{name}.log"
|
|
195
|
-
with open(log_file,
|
|
212
|
+
with open(log_file, "a") as log:
|
|
196
213
|
process = subprocess.Popen(
|
|
197
214
|
cmd,
|
|
198
215
|
env=env,
|
|
199
216
|
stdout=log,
|
|
200
217
|
stderr=subprocess.STDOUT,
|
|
201
|
-
preexec_fn=os.setsid if os.name !=
|
|
218
|
+
preexec_fn=os.setsid if os.name != "nt" else None,
|
|
202
219
|
)
|
|
203
|
-
|
|
220
|
+
|
|
204
221
|
self._running_servers[name] = process
|
|
205
222
|
return True
|
|
206
|
-
|
|
207
|
-
except Exception
|
|
223
|
+
|
|
224
|
+
except Exception:
|
|
208
225
|
return False
|
|
209
|
-
|
|
226
|
+
|
|
210
227
|
def _stop_server(self, name: str) -> bool:
|
|
211
228
|
"""Stop an MCP server."""
|
|
212
229
|
if name not in self._running_servers:
|
|
213
230
|
return False
|
|
214
|
-
|
|
231
|
+
|
|
215
232
|
process = self._running_servers[name]
|
|
216
233
|
try:
|
|
217
|
-
if os.name ==
|
|
234
|
+
if os.name == "nt":
|
|
218
235
|
process.terminate()
|
|
219
236
|
else:
|
|
220
237
|
os.killpg(os.getpgid(process.pid), signal.SIGTERM)
|
|
221
|
-
|
|
238
|
+
|
|
222
239
|
process.wait(timeout=5)
|
|
223
|
-
except:
|
|
240
|
+
except Exception:
|
|
224
241
|
# Force kill if needed
|
|
225
242
|
try:
|
|
226
|
-
if os.name ==
|
|
243
|
+
if os.name == "nt":
|
|
227
244
|
process.kill()
|
|
228
245
|
else:
|
|
229
246
|
os.killpg(os.getpgid(process.pid), signal.SIGKILL)
|
|
230
|
-
except:
|
|
247
|
+
except Exception:
|
|
231
248
|
pass
|
|
232
|
-
|
|
249
|
+
|
|
233
250
|
del self._running_servers[name]
|
|
234
251
|
return True
|
|
235
252
|
|
|
@@ -246,7 +263,7 @@ class MCPTool(BaseTool):
|
|
|
246
263
|
servers = self.config.get("servers", {})
|
|
247
264
|
enabled = sum(1 for s in servers.values() if s.get("enabled", False))
|
|
248
265
|
running = len(self._running_servers)
|
|
249
|
-
|
|
266
|
+
|
|
250
267
|
return f"""Manage MCP servers. Actions: list (default), add, remove, enable, disable, restart, config.
|
|
251
268
|
|
|
252
269
|
Usage:
|
|
@@ -266,16 +283,16 @@ Status: {enabled} enabled, {running} running"""
|
|
|
266
283
|
# Create tool context only if we have a proper MCP context
|
|
267
284
|
tool_ctx = None
|
|
268
285
|
try:
|
|
269
|
-
if hasattr(ctx,
|
|
286
|
+
if hasattr(ctx, "client") and ctx.client and hasattr(ctx.client, "server"):
|
|
270
287
|
tool_ctx = create_tool_context(ctx)
|
|
271
288
|
if tool_ctx:
|
|
272
289
|
await tool_ctx.set_tool_info(self.name)
|
|
273
|
-
except:
|
|
290
|
+
except Exception:
|
|
274
291
|
pass
|
|
275
|
-
|
|
292
|
+
|
|
276
293
|
# Extract action
|
|
277
294
|
action = params.get("action", "list")
|
|
278
|
-
|
|
295
|
+
|
|
279
296
|
# Route to appropriate handler
|
|
280
297
|
if action == "list":
|
|
281
298
|
return self._handle_list()
|
|
@@ -290,30 +307,34 @@ Status: {enabled} enabled, {running} running"""
|
|
|
290
307
|
elif action == "restart":
|
|
291
308
|
return self._handle_restart(params.get("name"))
|
|
292
309
|
elif action == "config":
|
|
293
|
-
return self._handle_config(
|
|
310
|
+
return self._handle_config(
|
|
311
|
+
params.get("config_key"), params.get("config_value")
|
|
312
|
+
)
|
|
294
313
|
else:
|
|
295
314
|
return f"Error: Unknown action '{action}'. Valid actions: list, add, remove, enable, disable, restart, config"
|
|
296
315
|
|
|
297
316
|
def _handle_list(self) -> str:
|
|
298
317
|
"""List all MCP servers."""
|
|
299
318
|
servers = self.config.get("servers", {})
|
|
300
|
-
|
|
319
|
+
|
|
301
320
|
if not servers:
|
|
302
321
|
return "No MCP servers configured. Use 'mcp --action add' to add one."
|
|
303
|
-
|
|
322
|
+
|
|
304
323
|
output = ["=== MCP Servers ==="]
|
|
305
|
-
output.append(
|
|
324
|
+
output.append(
|
|
325
|
+
f"Total: {len(servers)} | Enabled: {sum(1 for s in servers.values() if s.get('enabled', False))} | Running: {len(self._running_servers)}"
|
|
326
|
+
)
|
|
306
327
|
output.append("")
|
|
307
|
-
|
|
328
|
+
|
|
308
329
|
for name, config in sorted(servers.items()):
|
|
309
330
|
status_parts = []
|
|
310
|
-
|
|
331
|
+
|
|
311
332
|
# Check if enabled
|
|
312
333
|
if config.get("enabled", False):
|
|
313
334
|
status_parts.append("✅ Enabled")
|
|
314
335
|
else:
|
|
315
336
|
status_parts.append("❌ Disabled")
|
|
316
|
-
|
|
337
|
+
|
|
317
338
|
# Check if running
|
|
318
339
|
if name in self._running_servers:
|
|
319
340
|
process = self._running_servers[name]
|
|
@@ -324,41 +345,43 @@ Status: {enabled} enabled, {running} running"""
|
|
|
324
345
|
del self._running_servers[name]
|
|
325
346
|
else:
|
|
326
347
|
status_parts.append("⚫ Not running")
|
|
327
|
-
|
|
348
|
+
|
|
328
349
|
# Auto-start status
|
|
329
350
|
if config.get("auto_start", False):
|
|
330
351
|
status_parts.append("🚀 Auto-start")
|
|
331
|
-
|
|
352
|
+
|
|
332
353
|
status = " | ".join(status_parts)
|
|
333
|
-
|
|
354
|
+
|
|
334
355
|
output.append(f"{name}: {status}")
|
|
335
356
|
if config.get("description"):
|
|
336
357
|
output.append(f" Description: {config['description']}")
|
|
337
|
-
output.append(
|
|
338
|
-
|
|
358
|
+
output.append(
|
|
359
|
+
f" Command: {config['command']} {' '.join(config.get('args', []))}"
|
|
360
|
+
)
|
|
361
|
+
|
|
339
362
|
if config.get("env"):
|
|
340
|
-
env_str = ", ".join([f"{k}={v}" for k, v in config[
|
|
363
|
+
env_str = ", ".join([f"{k}={v}" for k, v in config["env"].items()])
|
|
341
364
|
output.append(f" Environment: {env_str}")
|
|
342
|
-
|
|
365
|
+
|
|
343
366
|
output.append("\nUse 'mcp --action enable --name <server>' to enable a server")
|
|
344
367
|
output.append("Use 'mcp --action add' to add a new server")
|
|
345
|
-
|
|
368
|
+
|
|
346
369
|
return "\n".join(output)
|
|
347
370
|
|
|
348
371
|
def _handle_add(self, params: Dict[str, Any]) -> str:
|
|
349
372
|
"""Add a new MCP server."""
|
|
350
373
|
name = params.get("name")
|
|
351
374
|
command = params.get("command")
|
|
352
|
-
|
|
375
|
+
|
|
353
376
|
if not name:
|
|
354
377
|
return "Error: name is required for add action"
|
|
355
378
|
if not command:
|
|
356
379
|
return "Error: command is required for add action"
|
|
357
|
-
|
|
380
|
+
|
|
358
381
|
servers = self.config.get("servers", {})
|
|
359
382
|
if name in servers:
|
|
360
383
|
return f"Error: Server '{name}' already exists. Use a different name or remove it first."
|
|
361
|
-
|
|
384
|
+
|
|
362
385
|
# Create server config
|
|
363
386
|
server_config = {
|
|
364
387
|
"command": command,
|
|
@@ -366,91 +389,91 @@ Status: {enabled} enabled, {running} running"""
|
|
|
366
389
|
"env": params.get("env", {}),
|
|
367
390
|
"enabled": False,
|
|
368
391
|
"auto_start": params.get("auto_start", True),
|
|
369
|
-
"description": params.get("description", "")
|
|
392
|
+
"description": params.get("description", ""),
|
|
370
393
|
}
|
|
371
|
-
|
|
394
|
+
|
|
372
395
|
servers[name] = server_config
|
|
373
396
|
self.config["servers"] = servers
|
|
374
397
|
self._save_config()
|
|
375
|
-
|
|
398
|
+
|
|
376
399
|
return f"Successfully added MCP server '{name}'. Use 'mcp --action enable --name {name}' to enable it."
|
|
377
400
|
|
|
378
401
|
def _handle_remove(self, name: Optional[str]) -> str:
|
|
379
402
|
"""Remove an MCP server."""
|
|
380
403
|
if not name:
|
|
381
404
|
return "Error: name is required for remove action"
|
|
382
|
-
|
|
405
|
+
|
|
383
406
|
servers = self.config.get("servers", {})
|
|
384
407
|
if name not in servers:
|
|
385
408
|
return f"Error: Server '{name}' not found"
|
|
386
|
-
|
|
409
|
+
|
|
387
410
|
# Stop if running
|
|
388
411
|
if name in self._running_servers:
|
|
389
412
|
self._stop_server(name)
|
|
390
|
-
|
|
413
|
+
|
|
391
414
|
del servers[name]
|
|
392
415
|
self.config["servers"] = servers
|
|
393
416
|
self._save_config()
|
|
394
|
-
|
|
417
|
+
|
|
395
418
|
return f"Successfully removed MCP server '{name}'"
|
|
396
419
|
|
|
397
420
|
def _handle_enable(self, name: Optional[str]) -> str:
|
|
398
421
|
"""Enable an MCP server."""
|
|
399
422
|
if not name:
|
|
400
423
|
return "Error: name is required for enable action"
|
|
401
|
-
|
|
424
|
+
|
|
402
425
|
servers = self.config.get("servers", {})
|
|
403
426
|
if name not in servers:
|
|
404
427
|
return f"Error: Server '{name}' not found"
|
|
405
|
-
|
|
428
|
+
|
|
406
429
|
servers[name]["enabled"] = True
|
|
407
430
|
self.config["servers"] = servers
|
|
408
431
|
self._save_config()
|
|
409
|
-
|
|
432
|
+
|
|
410
433
|
# Start if auto-start is enabled
|
|
411
434
|
if servers[name].get("auto_start", False):
|
|
412
435
|
if self._start_server(name, servers[name]):
|
|
413
436
|
return f"Successfully enabled and started MCP server '{name}'"
|
|
414
437
|
else:
|
|
415
438
|
return f"Enabled MCP server '{name}' but failed to start it. Check the configuration."
|
|
416
|
-
|
|
439
|
+
|
|
417
440
|
return f"Successfully enabled MCP server '{name}'"
|
|
418
441
|
|
|
419
442
|
def _handle_disable(self, name: Optional[str]) -> str:
|
|
420
443
|
"""Disable an MCP server."""
|
|
421
444
|
if not name:
|
|
422
445
|
return "Error: name is required for disable action"
|
|
423
|
-
|
|
446
|
+
|
|
424
447
|
servers = self.config.get("servers", {})
|
|
425
448
|
if name not in servers:
|
|
426
449
|
return f"Error: Server '{name}' not found"
|
|
427
|
-
|
|
450
|
+
|
|
428
451
|
# Stop if running
|
|
429
452
|
if name in self._running_servers:
|
|
430
453
|
self._stop_server(name)
|
|
431
|
-
|
|
454
|
+
|
|
432
455
|
servers[name]["enabled"] = False
|
|
433
456
|
self.config["servers"] = servers
|
|
434
457
|
self._save_config()
|
|
435
|
-
|
|
458
|
+
|
|
436
459
|
return f"Successfully disabled MCP server '{name}'"
|
|
437
460
|
|
|
438
461
|
def _handle_restart(self, name: Optional[str]) -> str:
|
|
439
462
|
"""Restart an MCP server."""
|
|
440
463
|
if not name:
|
|
441
464
|
return "Error: name is required for restart action"
|
|
442
|
-
|
|
465
|
+
|
|
443
466
|
servers = self.config.get("servers", {})
|
|
444
467
|
if name not in servers:
|
|
445
468
|
return f"Error: Server '{name}' not found"
|
|
446
|
-
|
|
469
|
+
|
|
447
470
|
if not servers[name].get("enabled", False):
|
|
448
471
|
return f"Error: Server '{name}' is not enabled"
|
|
449
|
-
|
|
472
|
+
|
|
450
473
|
# Stop if running
|
|
451
474
|
if name in self._running_servers:
|
|
452
475
|
self._stop_server(name)
|
|
453
|
-
|
|
476
|
+
|
|
454
477
|
# Start again
|
|
455
478
|
if self._start_server(name, servers[name]):
|
|
456
479
|
return f"Successfully restarted MCP server '{name}'"
|
|
@@ -462,10 +485,10 @@ Status: {enabled} enabled, {running} running"""
|
|
|
462
485
|
if not key:
|
|
463
486
|
# Show all config
|
|
464
487
|
return json.dumps(self.config, indent=2)
|
|
465
|
-
|
|
488
|
+
|
|
466
489
|
# Parse nested keys (e.g., "servers.github.auto_start")
|
|
467
|
-
keys = key.split(
|
|
468
|
-
|
|
490
|
+
keys = key.split(".")
|
|
491
|
+
|
|
469
492
|
if value is None:
|
|
470
493
|
# Get value
|
|
471
494
|
current = self.config
|
|
@@ -474,8 +497,12 @@ Status: {enabled} enabled, {running} running"""
|
|
|
474
497
|
current = current[k]
|
|
475
498
|
else:
|
|
476
499
|
return f"Configuration key '{key}' not found"
|
|
477
|
-
|
|
478
|
-
return
|
|
500
|
+
|
|
501
|
+
return (
|
|
502
|
+
json.dumps(current, indent=2)
|
|
503
|
+
if isinstance(current, (dict, list))
|
|
504
|
+
else str(current)
|
|
505
|
+
)
|
|
479
506
|
else:
|
|
480
507
|
# Set value
|
|
481
508
|
# Navigate to parent
|
|
@@ -484,20 +511,24 @@ Status: {enabled} enabled, {running} running"""
|
|
|
484
511
|
if k not in current:
|
|
485
512
|
current[k] = {}
|
|
486
513
|
current = current[k]
|
|
487
|
-
|
|
514
|
+
|
|
488
515
|
# Parse value if it looks like JSON
|
|
489
|
-
if
|
|
516
|
+
if (
|
|
517
|
+
isinstance(value, str)
|
|
518
|
+
and value.startswith("{")
|
|
519
|
+
or value.startswith("[")
|
|
520
|
+
):
|
|
490
521
|
try:
|
|
491
522
|
value = json.loads(value)
|
|
492
|
-
except:
|
|
523
|
+
except Exception:
|
|
493
524
|
pass
|
|
494
|
-
|
|
525
|
+
|
|
495
526
|
# Set the value
|
|
496
527
|
current[keys[-1]] = value
|
|
497
528
|
self._save_config()
|
|
498
|
-
|
|
529
|
+
|
|
499
530
|
return f"Successfully set {key} = {json.dumps(value) if isinstance(value, (dict, list)) else value}"
|
|
500
531
|
|
|
501
532
|
def register(self, mcp_server) -> None:
|
|
502
533
|
"""Register this tool with the MCP server."""
|
|
503
|
-
pass
|
|
534
|
+
pass
|
|
@@ -2,21 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
from mcp.server import FastMCP
|
|
4
4
|
|
|
5
|
+
from hanzo_mcp.tools.common.base import BaseTool, ToolRegistry
|
|
6
|
+
from hanzo_mcp.tools.common.permissions import PermissionManager
|
|
5
7
|
from hanzo_mcp.tools.memory.memory_tools import (
|
|
6
|
-
RecallMemoriesTool,
|
|
7
8
|
CreateMemoriesTool,
|
|
8
|
-
UpdateMemoriesTool,
|
|
9
9
|
DeleteMemoriesTool,
|
|
10
10
|
ManageMemoriesTool,
|
|
11
|
+
RecallMemoriesTool,
|
|
12
|
+
UpdateMemoriesTool,
|
|
11
13
|
)
|
|
12
14
|
from hanzo_mcp.tools.memory.knowledge_tools import (
|
|
13
|
-
RecallFactsTool,
|
|
14
15
|
StoreFactsTool,
|
|
16
|
+
RecallFactsTool,
|
|
15
17
|
SummarizeToMemoryTool,
|
|
16
18
|
ManageKnowledgeBasesTool,
|
|
17
19
|
)
|
|
18
|
-
from hanzo_mcp.tools.common.base import BaseTool, ToolRegistry
|
|
19
|
-
from hanzo_mcp.tools.common.permissions import PermissionManager
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
def register_memory_tools(
|
|
@@ -24,33 +24,51 @@ def register_memory_tools(
|
|
|
24
24
|
permission_manager: PermissionManager,
|
|
25
25
|
user_id: str = "default",
|
|
26
26
|
project_id: str = "default",
|
|
27
|
-
**memory_config
|
|
27
|
+
**memory_config,
|
|
28
28
|
) -> list[BaseTool]:
|
|
29
29
|
"""Register memory tools with the MCP server.
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
Args:
|
|
32
32
|
mcp_server: The FastMCP server instance
|
|
33
33
|
permission_manager: Permission manager for access control
|
|
34
34
|
user_id: User ID for memory operations
|
|
35
35
|
project_id: Project ID for memory operations
|
|
36
36
|
**memory_config: Additional memory store configuration
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
Returns:
|
|
39
39
|
List of registered tools
|
|
40
40
|
"""
|
|
41
41
|
# Create memory tools
|
|
42
|
-
recall_tool = RecallMemoriesTool(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
recall_tool = RecallMemoriesTool(
|
|
43
|
+
user_id=user_id, project_id=project_id, **memory_config
|
|
44
|
+
)
|
|
45
|
+
create_tool = CreateMemoriesTool(
|
|
46
|
+
user_id=user_id, project_id=project_id, **memory_config
|
|
47
|
+
)
|
|
48
|
+
update_tool = UpdateMemoriesTool(
|
|
49
|
+
user_id=user_id, project_id=project_id, **memory_config
|
|
50
|
+
)
|
|
51
|
+
delete_tool = DeleteMemoriesTool(
|
|
52
|
+
user_id=user_id, project_id=project_id, **memory_config
|
|
53
|
+
)
|
|
54
|
+
manage_tool = ManageMemoriesTool(
|
|
55
|
+
user_id=user_id, project_id=project_id, **memory_config
|
|
56
|
+
)
|
|
57
|
+
|
|
48
58
|
# Create knowledge tools
|
|
49
|
-
recall_facts_tool = RecallFactsTool(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
59
|
+
recall_facts_tool = RecallFactsTool(
|
|
60
|
+
user_id=user_id, project_id=project_id, **memory_config
|
|
61
|
+
)
|
|
62
|
+
store_facts_tool = StoreFactsTool(
|
|
63
|
+
user_id=user_id, project_id=project_id, **memory_config
|
|
64
|
+
)
|
|
65
|
+
summarize_tool = SummarizeToMemoryTool(
|
|
66
|
+
user_id=user_id, project_id=project_id, **memory_config
|
|
67
|
+
)
|
|
68
|
+
manage_kb_tool = ManageKnowledgeBasesTool(
|
|
69
|
+
user_id=user_id, project_id=project_id, **memory_config
|
|
70
|
+
)
|
|
71
|
+
|
|
54
72
|
# Register tools
|
|
55
73
|
ToolRegistry.register_tool(mcp_server, recall_tool)
|
|
56
74
|
ToolRegistry.register_tool(mcp_server, create_tool)
|
|
@@ -61,7 +79,7 @@ def register_memory_tools(
|
|
|
61
79
|
ToolRegistry.register_tool(mcp_server, store_facts_tool)
|
|
62
80
|
ToolRegistry.register_tool(mcp_server, summarize_tool)
|
|
63
81
|
ToolRegistry.register_tool(mcp_server, manage_kb_tool)
|
|
64
|
-
|
|
82
|
+
|
|
65
83
|
# Return list of registered tools
|
|
66
84
|
return [
|
|
67
85
|
recall_tool,
|
|
@@ -73,4 +91,4 @@ def register_memory_tools(
|
|
|
73
91
|
store_facts_tool,
|
|
74
92
|
summarize_tool,
|
|
75
93
|
manage_kb_tool,
|
|
76
|
-
]
|
|
94
|
+
]
|