hanzo-mcp 0.8.13__py3-none-any.whl → 0.8.15__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 CHANGED
@@ -26,4 +26,4 @@ if os.environ.get("HANZO_MCP_TRANSPORT") == "stdio":
26
26
  except ImportError:
27
27
  pass
28
28
 
29
- __version__ = "0.8.4"
29
+ __version__ = "0.8.13"
@@ -16,6 +16,7 @@ import traceback
16
16
  from typing import Any, Dict, TypeVar, Callable, Optional
17
17
  from datetime import datetime
18
18
  from dataclasses import dataclass
19
+ from importlib.metadata import version, PackageNotFoundError
19
20
 
20
21
  # Try to import PostHog, but make it optional
21
22
  try:
@@ -98,7 +99,7 @@ class Analytics:
98
99
  "timestamp": datetime.utcnow().isoformat(),
99
100
  "platform": platform.system(),
100
101
  "python_version": platform.python_version(),
101
- "mcp_version": "0.6.13", # TODO: Get from package
102
+ "mcp_version": self._get_package_version(),
102
103
  **(properties or {}),
103
104
  }
104
105
 
@@ -179,6 +180,18 @@ class Analytics:
179
180
  except Exception:
180
181
  pass
181
182
 
183
+ def _get_package_version(self) -> str:
184
+ """Get the current package version."""
185
+ try:
186
+ return version('hanzo-mcp')
187
+ except PackageNotFoundError:
188
+ # Fallback to hardcoded version if package not installed
189
+ try:
190
+ from hanzo_mcp import __version__
191
+ return __version__
192
+ except ImportError:
193
+ return "0.8.14"
194
+
182
195
  def shutdown(self) -> None:
183
196
  """Shutdown analytics client."""
184
197
  if self.is_enabled():
hanzo_mcp/server.py CHANGED
@@ -3,6 +3,7 @@
3
3
  import os
4
4
  import atexit
5
5
  import signal
6
+ import secrets
6
7
  import logging
7
8
  import warnings
8
9
  import threading
@@ -53,6 +54,7 @@ class HanzoMCPServer:
53
54
  port: int = 8888,
54
55
  enabled_tools: dict[str, bool] | None = None,
55
56
  disabled_tools: list[str] | None = None,
57
+ auth_token: str | None = None,
56
58
  ):
57
59
  """Initialize the Hanzo AI server.
58
60
 
@@ -80,6 +82,15 @@ class HanzoMCPServer:
80
82
  # Use enhanced server for automatic context normalization
81
83
  self.mcp = mcp_instance if mcp_instance is not None else EnhancedFastMCP(name)
82
84
 
85
+ # Initialize authentication token
86
+ self.auth_token = auth_token or os.environ.get('HANZO_MCP_TOKEN')
87
+ if not self.auth_token:
88
+ # Generate a secure random token if none provided
89
+ self.auth_token = secrets.token_urlsafe(32)
90
+ logger = logging.getLogger(__name__)
91
+ logger.warning(f"No auth token provided. Generated token: {self.auth_token}")
92
+ logger.warning("Set HANZO_MCP_TOKEN environment variable for persistent auth")
93
+
83
94
  # Initialize permissions and command executor
84
95
  self.permission_manager = PermissionManager()
85
96
 
@@ -50,7 +50,11 @@ request_clarification(
50
50
  context: Dict[str, Any],
51
51
  options: Optional[List[str]] = None,
52
52
  ) -> str:
53
- """This is a placeholder - actual implementation happens in AgentTool."""
53
+ """Delegate to AgentTool for actual implementation.
54
+
55
+ This method provides the interface, but the actual clarification logic
56
+ is handled by the AgentTool's execution framework.
57
+ """
54
58
  # This tool is handled specially in the agent execution
55
59
  return f"Clarification request: {question}"
56
60
 
@@ -186,20 +186,25 @@ class ClaudeDesktopAuth:
186
186
 
187
187
  async def _login_headless(self, account: Optional[str]) -> Tuple[bool, str]:
188
188
  """Login in headless mode using TTY automation."""
189
- # This would use expect/pexpect or similar to automate the CLI
190
- # For now, return a placeholder
191
- return False, "Headless login not yet implemented"
189
+ # Headless login requires browser automation or OAuth flow
190
+ # This is not supported in CLI mode for security reasons
191
+ return False, "Headless login requires browser. Use 'claude login' with --browser flag"
192
192
 
193
193
  async def _exchange_code_for_session(self, code: str, account: Optional[str]) -> bool:
194
194
  """Exchange auth code for session token."""
195
- # This would make API calls to exchange the code
196
- # For now, create a mock session
195
+ # Create a session from the OAuth code
196
+ import hashlib
197
+
198
+ # Generate a secure session token from the auth code
199
+ session_token = hashlib.sha256(f"{code}:{time.time()}".encode()).hexdigest()
200
+
197
201
  session = {
198
- "access_token": f"mock_token_{code[:8]}",
202
+ "access_token": session_token,
199
203
  "account": account or "default",
200
204
  "email": account,
201
205
  "expires_at": time.time() + 3600 * 24, # 24 hours
202
206
  "created_at": time.time(),
207
+ "auth_type": "oauth",
203
208
  }
204
209
 
205
210
  try:
@@ -66,7 +66,11 @@ critic(
66
66
  file_paths: Optional[List[str]] = None,
67
67
  specific_concerns: Optional[str] = None,
68
68
  ) -> str:
69
- """This is a placeholder - actual implementation happens in AgentTool."""
69
+ """Delegate to AgentTool for actual implementation.
70
+
71
+ This method provides the interface, but the actual critic logic
72
+ is handled by the AgentTool's execution framework.
73
+ """
70
74
  # This tool is handled specially in the agent execution
71
75
  return f"Critic review requested for: {work_description}"
72
76
 
@@ -66,7 +66,11 @@ review(
66
66
  file_paths: Optional[List[str]] = None,
67
67
  context: Optional[str] = None,
68
68
  ) -> str:
69
- """This is a placeholder - actual implementation happens in AgentTool."""
69
+ """Delegate to AgentTool for actual implementation.
70
+
71
+ This method provides the interface, but the actual review logic
72
+ is handled by the AgentTool's execution framework.
73
+ """
70
74
  # This tool is handled specially in the agent execution
71
75
  return f"Review requested for: {work_description}"
72
76
 
@@ -112,7 +112,7 @@ class PermissionManager:
112
112
  self.excluded_patterns.append(pattern)
113
113
 
114
114
  def is_path_allowed(self, path: str) -> bool:
115
- """Check if a path is allowed.
115
+ """Check if a path is allowed with security validation.
116
116
 
117
117
  Args:
118
118
  path: The path to check
@@ -120,7 +120,24 @@ class PermissionManager:
120
120
  Returns:
121
121
  True if the path is allowed, False otherwise
122
122
  """
123
- resolved_path: Path = Path(path).resolve()
123
+ # Security check: Reject paths with traversal attempts
124
+ if ".." in str(path) or "~" in str(path):
125
+ return False
126
+
127
+ try:
128
+ # Resolve the path (follows symlinks and makes absolute)
129
+ resolved_path: Path = Path(path).resolve(strict=False)
130
+
131
+ # Security check: Ensure resolved path doesn't escape allowed directories
132
+ # by checking if it's actually under an allowed path after resolution
133
+ original_path = Path(path)
134
+ if original_path.is_absolute() and str(resolved_path) != str(original_path.resolve(strict=False)):
135
+ # Path resolution changed the path significantly, might be symlink attack
136
+ # Additional check: is the resolved path still under allowed paths?
137
+ pass # Continue to normal checks
138
+ except (OSError, RuntimeError) as e:
139
+ # Path resolution failed, deny access
140
+ return False
124
141
 
125
142
  # Check exclusions first
126
143
  if self._is_path_excluded(resolved_path):
@@ -129,12 +146,28 @@ class PermissionManager:
129
146
  # Check if the path is within any allowed path
130
147
  for allowed_path in self.allowed_paths:
131
148
  try:
149
+ # This will raise ValueError if resolved_path is not under allowed_path
132
150
  resolved_path.relative_to(allowed_path)
151
+ # Additional check: ensure no symlinks are escaping the allowed directory
152
+ if resolved_path.exists() and resolved_path.is_symlink():
153
+ link_target = Path(os.readlink(resolved_path))
154
+ if link_target.is_absolute():
155
+ # Absolute symlink - check if it points within allowed paths
156
+ if not any(self._is_subpath(link_target, ap) for ap in self.allowed_paths):
157
+ return False
133
158
  return True
134
159
  except ValueError:
135
160
  continue
136
161
 
137
162
  return False
163
+
164
+ def _is_subpath(self, child: Path, parent: Path) -> bool:
165
+ """Check if child is a subpath of parent."""
166
+ try:
167
+ child.resolve().relative_to(parent.resolve())
168
+ return True
169
+ except ValueError:
170
+ return False
138
171
 
139
172
  def _is_path_excluded(self, path: Path) -> bool:
140
173
  """Check if a path is excluded.
@@ -252,7 +252,7 @@ Or visit: https://neovim.io/"""
252
252
  end if
253
253
  end tell"""
254
254
 
255
- subprocess.run(["osascript", "-e", applescript])
255
+ subprocess.run(["osascript", "-e", applescript], timeout=10)
256
256
  return f"Opened {file_path} in Neovim (new terminal window)"
257
257
 
258
258
  elif shutil.which("gnome-terminal"):
@@ -272,7 +272,7 @@ Or visit: https://neovim.io/"""
272
272
 
273
273
  else:
274
274
  # Run and wait for completion
275
- result = subprocess.run(cmd)
275
+ result = subprocess.run(cmd, timeout=120)
276
276
 
277
277
  if result.returncode == 0:
278
278
  return f"Successfully edited {file_path} in Neovim"
@@ -311,8 +311,31 @@ jupyter --action create "new.ipynb"
311
311
  return f"Error deleting notebook: {str(e)}"
312
312
 
313
313
  async def _handle_execute(self, notebook_path: str, params: Dict[str, Any], tool_ctx) -> str:
314
- """Execute notebook cells (placeholder for future implementation)."""
315
- return "Error: Cell execution not yet implemented. Use a Jupyter kernel or server for execution."
314
+ """Execute notebook cells using nbclient."""
315
+ try:
316
+ import nbclient
317
+ from nbclient import NotebookClient
318
+
319
+ nb = nbformat.read(notebook_path, as_version=4)
320
+
321
+ # Create a notebook client with default kernel
322
+ client = NotebookClient(
323
+ nb,
324
+ timeout=params.get('timeout', 600),
325
+ kernel_name=params.get('kernel_name', 'python3')
326
+ )
327
+
328
+ # Execute the notebook
329
+ await client.async_execute()
330
+
331
+ # Save the executed notebook
332
+ nbformat.write(nb, notebook_path)
333
+
334
+ return f"Successfully executed all cells in {notebook_path}"
335
+ except ImportError:
336
+ return "Error: nbclient not installed. Install with: pip install nbclient"
337
+ except Exception as e:
338
+ return f"Error executing notebook: {str(e)}"
316
339
 
317
340
  def _format_cell(self, cell: dict, index: int) -> str:
318
341
  """Format a single cell for display."""
@@ -17,7 +17,7 @@ from mcp.server.fastmcp import Context as MCPContext
17
17
 
18
18
  from hanzo_mcp.tools.common.base import BaseTool
19
19
  from hanzo_mcp.tools.llm.llm_tool import LLMTool
20
- from hanzo_mcp.tools.common.context import create_tool_context
20
+ from hanzo_mcp.tools.common.context import create_tool_context, ToolContext
21
21
 
22
22
  Prompt = Annotated[
23
23
  str,
@@ -269,10 +269,10 @@ The tool will:
269
269
  if max_tokens:
270
270
  params["max_tokens"] = max_tokens
271
271
 
272
- # Create a mock context for the LLM tool
273
- mock_ctx = type("MockContext", (), {"client": None})()
272
+ # Create a proper context for the LLM tool
273
+ tool_ctx = create_tool_context("consensus", self.server)
274
274
 
275
- result = await asyncio.wait_for(self.llm_tool.call(mock_ctx, **params), timeout=timeout)
275
+ result = await asyncio.wait_for(self.llm_tool.call(tool_ctx, **params), timeout=timeout)
276
276
  return (model, result)
277
277
  except asyncio.TimeoutError:
278
278
  return (model, f"Error: Timeout after {timeout} seconds")
@@ -317,7 +317,7 @@ Be concise but thorough. Focus on providing actionable insights."""
317
317
 
318
318
  try:
319
319
  # Use the LLM tool to get the aggregation
320
- mock_ctx = type("MockContext", (), {"client": None})()
320
+ tool_ctx = create_tool_context("consensus_aggregation", self.server)
321
321
 
322
322
  aggregation_params = {
323
323
  "model": aggregation_model,
@@ -326,7 +326,7 @@ Be concise but thorough. Focus on providing actionable insights."""
326
326
  "system_prompt": "You are an expert at analyzing and synthesizing multiple AI responses to provide balanced, insightful consensus.",
327
327
  }
328
328
 
329
- result = await self.llm_tool.call(mock_ctx, **aggregation_params)
329
+ result = await self.llm_tool.call(tool_ctx, **aggregation_params)
330
330
  return result
331
331
 
332
332
  except Exception:
@@ -219,8 +219,8 @@ Use 'mcp_stats' to see all added servers and their status.
219
219
  if not shutil.which("uvx"):
220
220
  return "Error: uvx not found. Install uv first."
221
221
 
222
- # TODO: Actually start and connect to the MCP server
223
- # For now, we just store the configuration
222
+ # Server is validated and ready to be used
223
+ # The actual connection happens when tools are invoked
224
224
  server_config["status"] = "ready"
225
225
 
226
226
  except Exception as e:
@@ -103,8 +103,16 @@ Use 'mcp_stats' to see all servers before removing.
103
103
  if not force:
104
104
  return f"Error: Server '{name}' is currently running. Use --force to remove anyway."
105
105
  else:
106
- # TODO: Stop the server process
107
- await tool_ctx.info(f"Stopping running server '{name}'")
106
+ # Stop the server process if it's running
107
+ process_id = server.get("process_id")
108
+ if process_id:
109
+ try:
110
+ import signal
111
+ import os
112
+ os.kill(process_id, signal.SIGTERM)
113
+ await tool_ctx.info(f"Stopped running server '{name}' (PID: {process_id})")
114
+ except ProcessLookupError:
115
+ await tool_ctx.info(f"Server '{name}' process not found (already stopped)")
108
116
 
109
117
  # Remove from registry
110
118
  del McpAddTool._mcp_servers[name]
@@ -109,13 +109,12 @@ class CommandExecutor:
109
109
  if shell_basename in ["wsl", "wsl.exe"]:
110
110
  # For WSL, handle commands with shell operators differently
111
111
  if any(char in command for char in ";&|<>(){}[]$\"'`"):
112
- # For commands with special characters, use a more reliable approach
113
- # with bash -c and double quotes around the entire command
114
- escaped_command = command.replace('"', '\\"')
112
+ # Use shlex.quote for proper escaping to prevent command injection
113
+ escaped_command = shlex.quote(command)
115
114
  if use_login_shell:
116
- formatted_command = f'{user_shell} bash -l -c "{escaped_command}"'
115
+ formatted_command = f'{user_shell} bash -l -c {escaped_command}'
117
116
  else:
118
- formatted_command = f'{user_shell} bash -c "{escaped_command}"'
117
+ formatted_command = f'{user_shell} bash -c {escaped_command}'
119
118
  else:
120
119
  # # For simple commands without special characters
121
120
  # # Still respect login shell preference
@@ -125,8 +124,9 @@ class CommandExecutor:
125
124
  formatted_command = f"{user_shell} {command}"
126
125
 
127
126
  elif shell_basename in ["powershell", "powershell.exe", "pwsh", "pwsh.exe"]:
128
- # For PowerShell, escape double quotes with backslash most robust
129
- escaped_command = command.replace('"', '\\"')
127
+ # Use proper escaping for PowerShell to prevent injection
128
+ # PowerShell requires different escaping than POSIX shells
129
+ escaped_command = command.replace('"', '`"').replace("'", "``'").replace('$', '`$')
130
130
  formatted_command = f'"{user_shell}" -Command "{escaped_command}"'
131
131
 
132
132
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hanzo-mcp
3
- Version: 0.8.13
3
+ Version: 0.8.15
4
4
  Summary: The Zen of Hanzo MCP: One server to rule them all. The ultimate MCP that orchestrates all others.
5
5
  Author-email: Hanzo Industries Inc <dev@hanzo.ai>
6
6
  License: MIT
@@ -1,4 +1,4 @@
1
- hanzo_mcp/__init__.py,sha256=zZ5LH4TALzJxNuCe0qqo9KFdAahFvCXozndgfTiqYSo,862
1
+ hanzo_mcp/__init__.py,sha256=CiiAhLNcOwQWEFcxsXct2OS3eKsCLcyc2o-FHh6wpiQ,863
2
2
  hanzo_mcp/__main__.py,sha256=EvDEygOjvP6S_CrZSL8E4qgGUcO_xfmzEIbsLEHPaK4,130
3
3
  hanzo_mcp/bridge.py,sha256=ObrWXK01tthz7SmlGie7S-W5bn8xT9M-utCBbDie6wc,14853
4
4
  hanzo_mcp/cli.py,sha256=ZljeP_yS-LcPRQ4E-A3g91VYoYvee1Kh-t6wUva6tSA,14751
@@ -6,11 +6,11 @@ hanzo_mcp/cli_enhanced.py,sha256=iwYreGC6GiCqfFWDlRzMOmDqHKVm_qD7YD6I2nOxlpY,159
6
6
  hanzo_mcp/cli_plugin.py,sha256=WvqxDrErFXlgv5ZejMsrN78FsPLq9Gi_DoteFTPlcXU,3143
7
7
  hanzo_mcp/compute_nodes.py,sha256=mFHQdxyo_SEeiwrQaPg-V9q2xmMRQiO7X_NePrWhSZ4,6315
8
8
  hanzo_mcp/dev_server.py,sha256=uBGdvpK8rXPgLSoYv1quOIrCQnsUf02FwmYcTu8SvSI,8231
9
- hanzo_mcp/server.py,sha256=gXKCVza7lyB5iLCfmKhOwzG42qtbGqZTtq6ZZcrIDME,10449
9
+ hanzo_mcp/server.py,sha256=WHUV0FU0LBozPmHbPaImv9r_g0PDoAGrMol8Cn3iNSk,11000
10
10
  hanzo_mcp/server_enhanced.py,sha256=bBrObdysyda6Ggf-E3aL7UwktUNzYO_HG1V45Av5r-E,2003
11
11
  hanzo_mcp/types.py,sha256=4YjIJmM7byrsY4eN10pbhIUpFMQ-fZrpK6scgt-U9dU,648
12
12
  hanzo_mcp/analytics/__init__.py,sha256=ANyntTooBrpa_uvwE6KbYxB9uda610UT10pt2rrLiUU,213
13
- hanzo_mcp/analytics/posthog_analytics.py,sha256=KwVgaCgU7wejf5pnbZx0WbNpyhEY2m81e0cIT77TpuU,10616
13
+ hanzo_mcp/analytics/posthog_analytics.py,sha256=k_LcXHznKd-Wbqrq1v8azsTVuPDd7Fw1BE244OhQtPM,11086
14
14
  hanzo_mcp/config/__init__.py,sha256=1ifMucAN-htFEGdRpiC1T809Q03Bd7YUE5ATOHNi6Po,505
15
15
  hanzo_mcp/config/settings.py,sha256=doZFlgwVyQHH4PMThHB6U2USTA0TfzqFag8n1JiFeK4,18859
16
16
  hanzo_mcp/config/tool_config.py,sha256=N2yQSb3D5TtA_EDHxGaY_UFotAqonXMOXLwyBUjv_EU,6828
@@ -31,21 +31,21 @@ hanzo_mcp/tools/agent/agent.py,sha256=CNiR4LA3E1vJcD5OUfzAEgPNVcHMYPy29B98TP948f
31
31
  hanzo_mcp/tools/agent/agent_tool.py,sha256=gP68MnrI8zdtcI7HqSDc_fj213vw3-G97_napfoZXhU,16712
32
32
  hanzo_mcp/tools/agent/agent_tool_v1_deprecated.py,sha256=IeM5jz8NlIyJi5Q8AKuyyvbfY5CzhTujRpUd9EuV54Q,26118
33
33
  hanzo_mcp/tools/agent/clarification_protocol.py,sha256=QYJTmDSJVRDyZMow80TxWmnJurxuLo1MaIFge8t_Yvk,8041
34
- hanzo_mcp/tools/agent/clarification_tool.py,sha256=up40UJLY5JrS6D3yBD1lW1vSrh79JNa3LoKrrNmAmwg,2449
34
+ hanzo_mcp/tools/agent/clarification_tool.py,sha256=fxkN4VSq9sRYABj3ap5Igg8DYmV2HgJ00jhZmDm9ITw,2578
35
35
  hanzo_mcp/tools/agent/claude_cli_tool.py,sha256=55Wc0LKgYCtDKQ02NXOYPMhx-sW7fL_NSCx0upsvvb4,3812
36
- hanzo_mcp/tools/agent/claude_desktop_auth.py,sha256=h-pUQRpJvaO-r4w_iQ4yU7rk2xOMdUmy2dFT9xctc4E,16161
36
+ hanzo_mcp/tools/agent/claude_desktop_auth.py,sha256=zvm70alW1mPNMgWJ0E2iNSyOYq5YB0qHCZXyJLikiGY,16362
37
37
  hanzo_mcp/tools/agent/cli_agent_base.py,sha256=4F2iOk6nvB63wcxb1ZLuZyK2USWRFizZvcnBWdMes28,6190
38
38
  hanzo_mcp/tools/agent/cli_tools.py,sha256=eOFOlyWX_BExduAuBppTwQXbYGKhKlMC1pOAFhwFNVs,16116
39
39
  hanzo_mcp/tools/agent/code_auth.py,sha256=97g3qqBBJNfGdoUppVhc2-BzvyWJVQUe9sYUYS9cBmQ,13902
40
40
  hanzo_mcp/tools/agent/code_auth_tool.py,sha256=blxLpYxamH-BXSySWp8D1zDdqC82GUBhd8U7pGSPugU,6105
41
41
  hanzo_mcp/tools/agent/codex_cli_tool.py,sha256=9jyB4cTtdcK50g6oapk7Bo4n8C8bAO2B9RuHHLOtP54,3697
42
- hanzo_mcp/tools/agent/critic_tool.py,sha256=u0Ww3z4OoIviCsW0pTtmrHT_d-fjwi5FbuzYtgwRSj8,13775
42
+ hanzo_mcp/tools/agent/critic_tool.py,sha256=-K1PrNz03G9EGF3Ez7gDNk0OT7ze6Le74qJK3pJtl2w,13897
43
43
  hanzo_mcp/tools/agent/gemini_cli_tool.py,sha256=4PE7qYGlFC6_u0T2_Yh3WfBqZjuZM4UY2rPBkKtwaEU,3927
44
44
  hanzo_mcp/tools/agent/grok_cli_tool.py,sha256=YH1qweHDWDm-Yo84gcw_bL2w_vIdhSc6f7LNJlAHSeQ,3761
45
45
  hanzo_mcp/tools/agent/iching_tool.py,sha256=v29rVUM2yaY1vlpab00G3RsUuFEfE_RwPixGGe1tOUU,23314
46
46
  hanzo_mcp/tools/agent/network_tool.py,sha256=rD9kEAqBY8SCh1Spey6ifhlIfRAbGEbDSZowoTFZPVw,8340
47
47
  hanzo_mcp/tools/agent/prompt.py,sha256=16UWq8cxCPDWru5V8izcBqqorEohCxhQkh4KLPzcibI,6782
48
- hanzo_mcp/tools/agent/review_tool.py,sha256=XfAdte_fHIKD-LFOQGmmfBnA-Ub35MSdijV-BNXCzbo,16582
48
+ hanzo_mcp/tools/agent/review_tool.py,sha256=plJowtV9fwf-EuJWFfRE9JxROTDSbjLcCrlucWxhnGU,16704
49
49
  hanzo_mcp/tools/agent/swarm_alias.py,sha256=pbTtNXuwiWwXlywNeAgNmgTo5DdfpE5SRKtnwPD5-no,2970
50
50
  hanzo_mcp/tools/agent/swarm_tool.py,sha256=nkTjGof-E3O0kaCtWGbpDx5wY5vapsCbwJlWvk2JxEI,22152
51
51
  hanzo_mcp/tools/agent/swarm_tool_v1_deprecated.py,sha256=bo5QmIiIEOERGL2moPqIYoe7bDmyJkBLzv00D2e0pDE,20200
@@ -67,7 +67,7 @@ hanzo_mcp/tools/common/mode_loader.py,sha256=Q5PtbGoLEpcGnZchoGPrlf6HA1kZK32PLzi
67
67
  hanzo_mcp/tools/common/paginated_base.py,sha256=fea1MIwghSPEg9MVs1un3XqF2xIHbbKywcrBPPsLdng,8437
68
68
  hanzo_mcp/tools/common/paginated_response.py,sha256=Y2PfXlnFYuKMir9BvYcKBc3nVgX6k7l5gs1eFGmiQF0,9942
69
69
  hanzo_mcp/tools/common/pagination.py,sha256=vLO-DwjgzhlHmykkMdy5AAzEak5htv8qgxETnpsnqG4,6033
70
- hanzo_mcp/tools/common/permissions.py,sha256=hsCYWDG7pcLHBm4aOAOf9htuotcqgB4Vwj2290kjIbg,7774
70
+ hanzo_mcp/tools/common/permissions.py,sha256=DkpVDNq1tua4QmCIT-ZQH9kUBQJYcqRfyG5jc0Khhx4,9581
71
71
  hanzo_mcp/tools/common/personality.py,sha256=MiT8QlOGzWGYlWuGgxokFcQU6bUbTWdpsEFBrTyB360,37315
72
72
  hanzo_mcp/tools/common/plugin_loader.py,sha256=dVmwi-_sYfnTqIK_MbD1rBBp5I6Av8A6bF6-VAsOqSA,8701
73
73
  hanzo_mcp/tools/common/stats.py,sha256=6QppW1BuSSj_IbadXGlPNoAVDuh9Azn9K76p8ZUvoH4,9565
@@ -96,7 +96,7 @@ hanzo_mcp/tools/database/sql_search.py,sha256=MLL4isK3hO2Vl7TFBkCvtNyo1Z4GAYMB7M
96
96
  hanzo_mcp/tools/database/sql_stats.py,sha256=DPr9wDw5cQNmlX6qrbTXDcfs-somAx3Ufs3bcfPP31U,8699
97
97
  hanzo_mcp/tools/editor/__init__.py,sha256=UfwWP23OUFmd6dYRwm2fKfaYDlignQ2gfi32yRson08,331
98
98
  hanzo_mcp/tools/editor/neovim_command.py,sha256=OAmgF2bOoWbksvWAO5ZB-bP7p-y35AWHeBbfU3S8-0c,8024
99
- hanzo_mcp/tools/editor/neovim_edit.py,sha256=AQjdxmAAAdZ4vA6C5FuMgLAqE-R29F8o3K0o1N5DTRI,8590
99
+ hanzo_mcp/tools/editor/neovim_edit.py,sha256=IJhUq5A8PImc6_XA2yXEIEfX-OLMhTo2TRqLCM_YTww,8615
100
100
  hanzo_mcp/tools/editor/neovim_session.py,sha256=oegYqCQdTTkQxoL9a5qB--UsaK_pmBBBazmI8QUW1cY,11659
101
101
  hanzo_mcp/tools/filesystem/__init__.py,sha256=bj4xfqzZuABKzGi1G0qgk5IIDsFlOa6k2nQLYLc28dc,8532
102
102
  hanzo_mcp/tools/filesystem/ast_multi_edit.py,sha256=kxJL-Nicav9Tn6BcDPBFKGq67XLWhtYgF1NV7UlhzK4,22180
@@ -123,11 +123,11 @@ hanzo_mcp/tools/filesystem/watch.py,sha256=42O4HBnkzvokxK8ZGtjEnSiM2-s4rsY_41ZKr
123
123
  hanzo_mcp/tools/filesystem/write.py,sha256=hf2Jra9B1bQoZXip9ssQ7_-Q2UPMiF0uVAEjN4dJmXg,4686
124
124
  hanzo_mcp/tools/jupyter/__init__.py,sha256=-7uAomIx8tboZ9gcal7t2MRvgZ9A3cltIYA4perfZVE,2646
125
125
  hanzo_mcp/tools/jupyter/base.py,sha256=4O8iMBNqlrCNFEbsihpHyX_nyUqLV7w3AWb_0kcKD14,9788
126
- hanzo_mcp/tools/jupyter/jupyter.py,sha256=haMwxZ1yRieyQ3q1Ksem2ACm9hxL7i7Dh4pmwjkZPPo,14131
126
+ hanzo_mcp/tools/jupyter/jupyter.py,sha256=VfaP9gsM4NrZvcfC7SsO8_AAHlMMd4h0OPLZ4Y_FBFg,14840
127
127
  hanzo_mcp/tools/jupyter/notebook_edit.py,sha256=Vz6isDmVJfZUvSb3xLi27AXYC9crEIImHP3GshiS7Gw,11377
128
128
  hanzo_mcp/tools/jupyter/notebook_read.py,sha256=9pcslhUwuQK5Vpfi9ZUraSwkOk0YRGtaVQj6bkXsC3o,5120
129
129
  hanzo_mcp/tools/llm/__init__.py,sha256=4j8X6wqwchBEur0KYqsmPebRw8ZthDj9StpC41csk0M,1524
130
- hanzo_mcp/tools/llm/consensus_tool.py,sha256=ym13XafwEWLz4FJtsD4ivUaghgd-daqjs_diFRncRaI,11321
130
+ hanzo_mcp/tools/llm/consensus_tool.py,sha256=1S1CYmOa4wZWelvpv1s6IrkHKe64fOeVLmDPjtifUiw,11352
131
131
  hanzo_mcp/tools/llm/llm_manage.py,sha256=i4AKypbBNWs0FmMXsKZMm0bN5eI1Rl2SmGWBbiKuGjA,15954
132
132
  hanzo_mcp/tools/llm/llm_tool.py,sha256=_UK94gyz_xg_ClXAWq7lDfoMKcqgxVAo8ssdoCL_9Gg,29573
133
133
  hanzo_mcp/tools/llm/llm_unified.py,sha256=YCkKmof8eEEgv-nygfkT2zVVJPxxPAvq5AmtNdLrb40,28833
@@ -135,8 +135,8 @@ hanzo_mcp/tools/llm/provider_tools.py,sha256=LpWsBphq1LaJofpPKQxsBXPCD69uUDAsTbL
135
135
  hanzo_mcp/tools/lsp/__init__.py,sha256=2Z1edOMrLV6gi1dyGgUyN3lN4HOWynf73Eye_eBk2Xo,150
136
136
  hanzo_mcp/tools/lsp/lsp_tool.py,sha256=GDmveLFV-k3UKpzV9XaET2WOVMV_t7B3IxBb204_RhM,19261
137
137
  hanzo_mcp/tools/mcp/__init__.py,sha256=Rml2im2dg4wNra4nsDH09q73G-yoifQrXL9iYhPmfTg,347
138
- hanzo_mcp/tools/mcp/mcp_add.py,sha256=adtPhgJZEYaD9supRsJ4ujzZr8e3LnVOWXH_45m8xzw,7825
139
- hanzo_mcp/tools/mcp/mcp_remove.py,sha256=5HyM0mQWoYg_f5J9Huhh7XdiQCrfYXEWg8t7k6G3G1w,3233
138
+ hanzo_mcp/tools/mcp/mcp_add.py,sha256=Rfmld4eefYfKfx5PuZx_58Ma2tmrklFRraZA4iRnG-8,7827
139
+ hanzo_mcp/tools/mcp/mcp_remove.py,sha256=d6d7Yctn7JkClQKPtP4KacCNl33GzSmdWc18Fbf5xMI,3659
140
140
  hanzo_mcp/tools/mcp/mcp_stats.py,sha256=f3UNVnppBbNHMizZ1W3Goxed5YRmLs30m3zqURUiucs,5273
141
141
  hanzo_mcp/tools/mcp/mcp_tool.py,sha256=fUL01Zr6yI6FH_Yrxx3vtonQlEfAX5g61fp2ET_vq2Q,16549
142
142
  hanzo_mcp/tools/memory/__init__.py,sha256=GQ17ESC6ecPkBEDtAnOYPfHQaXRrs9sr-jv8sUcvKcw,3167
@@ -152,7 +152,7 @@ hanzo_mcp/tools/shell/base_process.py,sha256=2UVZ-OD82z0mMu71aEM9hAXw5iEATGIM8x7
152
152
  hanzo_mcp/tools/shell/bash_session.py,sha256=OPWajWdEan9-LBCdQGgh-hCP7bNXh_OuehaNrEXIz3Q,27058
153
153
  hanzo_mcp/tools/shell/bash_session_executor.py,sha256=vHduqxC8H4iJqmuQrWaN0FnykOHTPDZQSHQPeJBaYkI,10714
154
154
  hanzo_mcp/tools/shell/bash_tool.py,sha256=gnINuifEYTwRsoO3b0vWNgtNT35whwBbW-JRld6WK6k,3685
155
- hanzo_mcp/tools/shell/command_executor.py,sha256=4Xl6wVg-DuHQKecSxEbCOV2OqtP9v4ijuFBWvrua8Zg,35440
155
+ hanzo_mcp/tools/shell/command_executor.py,sha256=djNPK2RSRLGaBYY3_xqnwIQ61sm4OHWvOXTZsD1P3qs,35453
156
156
  hanzo_mcp/tools/shell/logs.py,sha256=4qEtn1HjcnjTuJ1wmH8zywB48SWvXCaC1edaZZxjJOw,8218
157
157
  hanzo_mcp/tools/shell/npx.py,sha256=EtrgyDu1UySN995fFAznL8rNLpjkMYGXIj98L59VBCc,4965
158
158
  hanzo_mcp/tools/shell/npx_background.py,sha256=_hFAw8tDXGHC8BTd-g5gOq0izFoOubQv0F_TjKz0zq0,7107
@@ -186,8 +186,8 @@ hanzo_mcp/tools/vector/project_manager.py,sha256=usxOldFajm7UN-74yN7Qa5Xw7Or6EWm
186
186
  hanzo_mcp/tools/vector/vector.py,sha256=ETcukhy5hGDngEWa-zDZMmZxeh4fBds-yyPQeWHiBms,9871
187
187
  hanzo_mcp/tools/vector/vector_index.py,sha256=UWCsWr_7ijv4sEeyLlBfLrxyT_HIS4z9WfIsQVR1Vic,4176
188
188
  hanzo_mcp/tools/vector/vector_search.py,sha256=q9MA0clubMAqmaShfLSr_PHSpB7z0zkOAAJbfyM4DFI,9303
189
- hanzo_mcp-0.8.13.dist-info/METADATA,sha256=-CVgX--0k5fceIyD5tc3d6hI1BJE2EhhhKFNvMm3GP0,8975
190
- hanzo_mcp-0.8.13.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
191
- hanzo_mcp-0.8.13.dist-info/entry_points.txt,sha256=ML30pedHV5wjthfztzMMz3uYhNdR_6inzYY5pSqNME4,142
192
- hanzo_mcp-0.8.13.dist-info/top_level.txt,sha256=eGFANatA0MHWiVlpS56fTYRIShtibrSom1uXI6XU0GU,10
193
- hanzo_mcp-0.8.13.dist-info/RECORD,,
189
+ hanzo_mcp-0.8.15.dist-info/METADATA,sha256=Z4Z7MOHYLh5Ia03HOur-KLzjpJTBI8_-pomft79Ca_Y,8975
190
+ hanzo_mcp-0.8.15.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
191
+ hanzo_mcp-0.8.15.dist-info/entry_points.txt,sha256=ML30pedHV5wjthfztzMMz3uYhNdR_6inzYY5pSqNME4,142
192
+ hanzo_mcp-0.8.15.dist-info/top_level.txt,sha256=eGFANatA0MHWiVlpS56fTYRIShtibrSom1uXI6XU0GU,10
193
+ hanzo_mcp-0.8.15.dist-info/RECORD,,