hanzo-mcp 0.7.7__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.

Files changed (178) hide show
  1. hanzo_mcp/__init__.py +6 -0
  2. hanzo_mcp/__main__.py +1 -1
  3. hanzo_mcp/analytics/__init__.py +2 -2
  4. hanzo_mcp/analytics/posthog_analytics.py +76 -82
  5. hanzo_mcp/cli.py +31 -36
  6. hanzo_mcp/cli_enhanced.py +94 -72
  7. hanzo_mcp/cli_plugin.py +27 -17
  8. hanzo_mcp/config/__init__.py +2 -2
  9. hanzo_mcp/config/settings.py +112 -88
  10. hanzo_mcp/config/tool_config.py +32 -34
  11. hanzo_mcp/dev_server.py +66 -67
  12. hanzo_mcp/prompts/__init__.py +94 -12
  13. hanzo_mcp/prompts/enhanced_prompts.py +809 -0
  14. hanzo_mcp/prompts/example_custom_prompt.py +6 -5
  15. hanzo_mcp/prompts/project_todo_reminder.py +0 -1
  16. hanzo_mcp/prompts/tool_explorer.py +10 -7
  17. hanzo_mcp/server.py +17 -21
  18. hanzo_mcp/server_enhanced.py +15 -22
  19. hanzo_mcp/tools/__init__.py +56 -28
  20. hanzo_mcp/tools/agent/__init__.py +16 -19
  21. hanzo_mcp/tools/agent/agent.py +82 -65
  22. hanzo_mcp/tools/agent/agent_tool.py +152 -122
  23. hanzo_mcp/tools/agent/agent_tool_v1_deprecated.py +66 -62
  24. hanzo_mcp/tools/agent/clarification_protocol.py +55 -50
  25. hanzo_mcp/tools/agent/clarification_tool.py +11 -10
  26. hanzo_mcp/tools/agent/claude_cli_tool.py +21 -20
  27. hanzo_mcp/tools/agent/claude_desktop_auth.py +130 -144
  28. hanzo_mcp/tools/agent/cli_agent_base.py +59 -53
  29. hanzo_mcp/tools/agent/code_auth.py +102 -107
  30. hanzo_mcp/tools/agent/code_auth_tool.py +28 -27
  31. hanzo_mcp/tools/agent/codex_cli_tool.py +20 -19
  32. hanzo_mcp/tools/agent/critic_tool.py +86 -73
  33. hanzo_mcp/tools/agent/gemini_cli_tool.py +21 -20
  34. hanzo_mcp/tools/agent/grok_cli_tool.py +21 -20
  35. hanzo_mcp/tools/agent/iching_tool.py +404 -139
  36. hanzo_mcp/tools/agent/network_tool.py +89 -73
  37. hanzo_mcp/tools/agent/prompt.py +2 -1
  38. hanzo_mcp/tools/agent/review_tool.py +101 -98
  39. hanzo_mcp/tools/agent/swarm_alias.py +87 -0
  40. hanzo_mcp/tools/agent/swarm_tool.py +246 -161
  41. hanzo_mcp/tools/agent/swarm_tool_v1_deprecated.py +134 -92
  42. hanzo_mcp/tools/agent/tool_adapter.py +21 -11
  43. hanzo_mcp/tools/common/__init__.py +1 -1
  44. hanzo_mcp/tools/common/base.py +3 -5
  45. hanzo_mcp/tools/common/batch_tool.py +46 -39
  46. hanzo_mcp/tools/common/config_tool.py +120 -84
  47. hanzo_mcp/tools/common/context.py +1 -5
  48. hanzo_mcp/tools/common/context_fix.py +5 -3
  49. hanzo_mcp/tools/common/critic_tool.py +4 -8
  50. hanzo_mcp/tools/common/decorators.py +58 -56
  51. hanzo_mcp/tools/common/enhanced_base.py +29 -32
  52. hanzo_mcp/tools/common/fastmcp_pagination.py +91 -94
  53. hanzo_mcp/tools/common/forgiving_edit.py +91 -87
  54. hanzo_mcp/tools/common/mode.py +15 -17
  55. hanzo_mcp/tools/common/mode_loader.py +27 -24
  56. hanzo_mcp/tools/common/paginated_base.py +61 -53
  57. hanzo_mcp/tools/common/paginated_response.py +72 -79
  58. hanzo_mcp/tools/common/pagination.py +50 -53
  59. hanzo_mcp/tools/common/permissions.py +4 -4
  60. hanzo_mcp/tools/common/personality.py +186 -138
  61. hanzo_mcp/tools/common/plugin_loader.py +54 -54
  62. hanzo_mcp/tools/common/stats.py +65 -47
  63. hanzo_mcp/tools/common/test_helpers.py +31 -0
  64. hanzo_mcp/tools/common/thinking_tool.py +4 -8
  65. hanzo_mcp/tools/common/tool_disable.py +17 -12
  66. hanzo_mcp/tools/common/tool_enable.py +13 -14
  67. hanzo_mcp/tools/common/tool_list.py +36 -28
  68. hanzo_mcp/tools/common/truncate.py +23 -23
  69. hanzo_mcp/tools/config/__init__.py +4 -4
  70. hanzo_mcp/tools/config/config_tool.py +42 -29
  71. hanzo_mcp/tools/config/index_config.py +37 -34
  72. hanzo_mcp/tools/config/mode_tool.py +175 -55
  73. hanzo_mcp/tools/database/__init__.py +15 -12
  74. hanzo_mcp/tools/database/database_manager.py +77 -75
  75. hanzo_mcp/tools/database/graph.py +137 -91
  76. hanzo_mcp/tools/database/graph_add.py +30 -18
  77. hanzo_mcp/tools/database/graph_query.py +178 -102
  78. hanzo_mcp/tools/database/graph_remove.py +33 -28
  79. hanzo_mcp/tools/database/graph_search.py +97 -75
  80. hanzo_mcp/tools/database/graph_stats.py +91 -59
  81. hanzo_mcp/tools/database/sql.py +107 -79
  82. hanzo_mcp/tools/database/sql_query.py +30 -24
  83. hanzo_mcp/tools/database/sql_search.py +29 -25
  84. hanzo_mcp/tools/database/sql_stats.py +47 -35
  85. hanzo_mcp/tools/editor/neovim_command.py +25 -28
  86. hanzo_mcp/tools/editor/neovim_edit.py +21 -23
  87. hanzo_mcp/tools/editor/neovim_session.py +60 -54
  88. hanzo_mcp/tools/filesystem/__init__.py +31 -30
  89. hanzo_mcp/tools/filesystem/ast_multi_edit.py +329 -249
  90. hanzo_mcp/tools/filesystem/ast_tool.py +4 -4
  91. hanzo_mcp/tools/filesystem/base.py +1 -1
  92. hanzo_mcp/tools/filesystem/batch_search.py +316 -224
  93. hanzo_mcp/tools/filesystem/content_replace.py +4 -4
  94. hanzo_mcp/tools/filesystem/diff.py +71 -59
  95. hanzo_mcp/tools/filesystem/directory_tree.py +7 -7
  96. hanzo_mcp/tools/filesystem/directory_tree_paginated.py +49 -37
  97. hanzo_mcp/tools/filesystem/edit.py +4 -4
  98. hanzo_mcp/tools/filesystem/find.py +173 -80
  99. hanzo_mcp/tools/filesystem/find_files.py +73 -52
  100. hanzo_mcp/tools/filesystem/git_search.py +157 -104
  101. hanzo_mcp/tools/filesystem/grep.py +8 -8
  102. hanzo_mcp/tools/filesystem/multi_edit.py +4 -8
  103. hanzo_mcp/tools/filesystem/read.py +12 -10
  104. hanzo_mcp/tools/filesystem/rules_tool.py +59 -43
  105. hanzo_mcp/tools/filesystem/search_tool.py +263 -207
  106. hanzo_mcp/tools/filesystem/symbols_tool.py +94 -54
  107. hanzo_mcp/tools/filesystem/tree.py +35 -33
  108. hanzo_mcp/tools/filesystem/unix_aliases.py +13 -18
  109. hanzo_mcp/tools/filesystem/watch.py +37 -36
  110. hanzo_mcp/tools/filesystem/write.py +4 -8
  111. hanzo_mcp/tools/jupyter/__init__.py +4 -4
  112. hanzo_mcp/tools/jupyter/base.py +4 -5
  113. hanzo_mcp/tools/jupyter/jupyter.py +67 -47
  114. hanzo_mcp/tools/jupyter/notebook_edit.py +4 -4
  115. hanzo_mcp/tools/jupyter/notebook_read.py +4 -7
  116. hanzo_mcp/tools/llm/__init__.py +5 -7
  117. hanzo_mcp/tools/llm/consensus_tool.py +72 -52
  118. hanzo_mcp/tools/llm/llm_manage.py +101 -60
  119. hanzo_mcp/tools/llm/llm_tool.py +226 -166
  120. hanzo_mcp/tools/llm/provider_tools.py +25 -26
  121. hanzo_mcp/tools/lsp/__init__.py +1 -1
  122. hanzo_mcp/tools/lsp/lsp_tool.py +228 -143
  123. hanzo_mcp/tools/mcp/__init__.py +2 -3
  124. hanzo_mcp/tools/mcp/mcp_add.py +27 -25
  125. hanzo_mcp/tools/mcp/mcp_remove.py +7 -8
  126. hanzo_mcp/tools/mcp/mcp_stats.py +23 -22
  127. hanzo_mcp/tools/mcp/mcp_tool.py +129 -98
  128. hanzo_mcp/tools/memory/__init__.py +39 -21
  129. hanzo_mcp/tools/memory/knowledge_tools.py +124 -99
  130. hanzo_mcp/tools/memory/memory_tools.py +90 -108
  131. hanzo_mcp/tools/search/__init__.py +7 -2
  132. hanzo_mcp/tools/search/find_tool.py +297 -212
  133. hanzo_mcp/tools/search/unified_search.py +366 -314
  134. hanzo_mcp/tools/shell/__init__.py +8 -7
  135. hanzo_mcp/tools/shell/auto_background.py +56 -49
  136. hanzo_mcp/tools/shell/base.py +1 -1
  137. hanzo_mcp/tools/shell/base_process.py +75 -75
  138. hanzo_mcp/tools/shell/bash_session.py +2 -2
  139. hanzo_mcp/tools/shell/bash_session_executor.py +4 -4
  140. hanzo_mcp/tools/shell/bash_tool.py +24 -31
  141. hanzo_mcp/tools/shell/command_executor.py +12 -12
  142. hanzo_mcp/tools/shell/logs.py +43 -33
  143. hanzo_mcp/tools/shell/npx.py +13 -13
  144. hanzo_mcp/tools/shell/npx_background.py +24 -21
  145. hanzo_mcp/tools/shell/npx_tool.py +18 -22
  146. hanzo_mcp/tools/shell/open.py +19 -21
  147. hanzo_mcp/tools/shell/pkill.py +31 -26
  148. hanzo_mcp/tools/shell/process_tool.py +32 -32
  149. hanzo_mcp/tools/shell/processes.py +57 -58
  150. hanzo_mcp/tools/shell/run_background.py +24 -25
  151. hanzo_mcp/tools/shell/run_command.py +5 -5
  152. hanzo_mcp/tools/shell/run_command_windows.py +5 -5
  153. hanzo_mcp/tools/shell/session_storage.py +3 -3
  154. hanzo_mcp/tools/shell/streaming_command.py +141 -126
  155. hanzo_mcp/tools/shell/uvx.py +24 -25
  156. hanzo_mcp/tools/shell/uvx_background.py +35 -33
  157. hanzo_mcp/tools/shell/uvx_tool.py +18 -22
  158. hanzo_mcp/tools/todo/__init__.py +6 -2
  159. hanzo_mcp/tools/todo/todo.py +50 -37
  160. hanzo_mcp/tools/todo/todo_read.py +5 -8
  161. hanzo_mcp/tools/todo/todo_write.py +5 -7
  162. hanzo_mcp/tools/vector/__init__.py +40 -28
  163. hanzo_mcp/tools/vector/ast_analyzer.py +176 -143
  164. hanzo_mcp/tools/vector/git_ingester.py +170 -179
  165. hanzo_mcp/tools/vector/index_tool.py +96 -44
  166. hanzo_mcp/tools/vector/infinity_store.py +283 -228
  167. hanzo_mcp/tools/vector/mock_infinity.py +39 -40
  168. hanzo_mcp/tools/vector/project_manager.py +88 -78
  169. hanzo_mcp/tools/vector/vector.py +59 -42
  170. hanzo_mcp/tools/vector/vector_index.py +30 -27
  171. hanzo_mcp/tools/vector/vector_search.py +64 -45
  172. hanzo_mcp/types.py +6 -4
  173. {hanzo_mcp-0.7.7.dist-info → hanzo_mcp-0.8.0.dist-info}/METADATA +1 -1
  174. hanzo_mcp-0.8.0.dist-info/RECORD +185 -0
  175. hanzo_mcp-0.7.7.dist-info/RECORD +0 -182
  176. {hanzo_mcp-0.7.7.dist-info → hanzo_mcp-0.8.0.dist-info}/WHEEL +0 -0
  177. {hanzo_mcp-0.7.7.dist-info → hanzo_mcp-0.8.0.dist-info}/entry_points.txt +0 -0
  178. {hanzo_mcp-0.7.7.dist-info → hanzo_mcp-0.8.0.dist-info}/top_level.txt +0 -0
@@ -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, 'r') as f:
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": ["@modelcontextprotocol/server-postgres", "postgresql://localhost/db"],
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, 'w') as f:
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("auto_start", False):
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(self.config.get("log_dir", str(Path.home() / ".hanzo" / "mcp" / "logs")))
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, 'a') as log:
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 != 'nt' else None
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 as e:
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 == 'nt':
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 == 'nt':
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, 'client') and ctx.client and hasattr(ctx.client, 'server'):
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(params.get("config_key"), params.get("config_value"))
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(f"Total: {len(servers)} | Enabled: {sum(1 for s in servers.values() if s.get('enabled', False))} | Running: {len(self._running_servers)}")
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(f" Command: {config['command']} {' '.join(config.get('args', []))}")
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['env'].items()])
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 json.dumps(current, indent=2) if isinstance(current, (dict, list)) else str(current)
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 isinstance(value, str) and value.startswith('{') or value.startswith('['):
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(user_id=user_id, project_id=project_id, **memory_config)
43
- create_tool = CreateMemoriesTool(user_id=user_id, project_id=project_id, **memory_config)
44
- update_tool = UpdateMemoriesTool(user_id=user_id, project_id=project_id, **memory_config)
45
- delete_tool = DeleteMemoriesTool(user_id=user_id, project_id=project_id, **memory_config)
46
- manage_tool = ManageMemoriesTool(user_id=user_id, project_id=project_id, **memory_config)
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(user_id=user_id, project_id=project_id, **memory_config)
50
- store_facts_tool = StoreFactsTool(user_id=user_id, project_id=project_id, **memory_config)
51
- summarize_tool = SummarizeToMemoryTool(user_id=user_id, project_id=project_id, **memory_config)
52
- manage_kb_tool = ManageKnowledgeBasesTool(user_id=user_id, project_id=project_id, **memory_config)
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
+ ]