hanzo-mcp 0.1.36__py3-none-any.whl → 0.2.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 CHANGED
@@ -1,3 +1,3 @@
1
1
  """Hanzo MCP - Implementation of Hanzo capabilities using MCP."""
2
2
 
3
- __version__ = "0.1.36"
3
+ __version__ = "0.2.0"
hanzo_mcp/cli.py CHANGED
@@ -8,7 +8,6 @@ from pathlib import Path
8
8
  from typing import Any, cast
9
9
 
10
10
  from hanzo_mcp.server import HanzoServer
11
- from hanzo_mcp.tools.common.path_utils import PathUtils
12
11
 
13
12
 
14
13
  def main() -> None:
@@ -83,6 +82,14 @@ def main() -> None:
83
82
  default=False,
84
83
  help="Enable the agent tool (disabled by default)"
85
84
  )
85
+
86
+ _ = parser.add_argument(
87
+ "--disable-write-tools",
88
+ dest="disable_write_tools",
89
+ action="store_true",
90
+ default=False,
91
+ help="Disable write/edit tools (file writing, editing, notebook editing) to use IDE tools instead. Note: Shell commands can still modify files."
92
+ )
86
93
 
87
94
  _ = parser.add_argument(
88
95
  "--install",
@@ -103,29 +110,33 @@ def main() -> None:
103
110
  agent_max_iterations: int = cast(int, args.agent_max_iterations)
104
111
  agent_max_tool_uses: int = cast(int, args.agent_max_tool_uses)
105
112
  enable_agent_tool: bool = cast(bool, args.enable_agent_tool)
113
+ disable_write_tools: bool = cast(bool, args.disable_write_tools)
106
114
  allowed_paths: list[str] = (
107
115
  cast(list[str], args.allowed_paths) if args.allowed_paths else []
108
116
  )
109
117
 
110
118
  if install:
111
- install_claude_desktop_config(name, allowed_paths)
119
+ install_claude_desktop_config(name, allowed_paths, disable_write_tools)
112
120
  return
113
121
 
114
122
  # If no allowed paths are specified, use the user's home directory
115
123
  if not allowed_paths:
116
124
  allowed_paths = [str(Path.home())]
117
-
118
- # Normalize all allowed paths
119
- allowed_paths = [PathUtils.normalize_path(path) for path in allowed_paths]
120
125
 
121
- # If project directory is specified, normalize it and add to allowed paths
126
+ # If project directory is specified, add it to allowed paths
127
+ if project_dir and project_dir not in allowed_paths:
128
+ allowed_paths.append(project_dir)
129
+
130
+ # Set project directory as initial working directory if provided
122
131
  if project_dir:
123
- project_dir = PathUtils.normalize_path(project_dir)
124
- if project_dir not in allowed_paths:
125
- allowed_paths.append(project_dir)
132
+ # Expand user paths
133
+ project_dir = os.path.expanduser(project_dir)
134
+ # Make absolute
135
+ if not os.path.isabs(project_dir):
136
+ project_dir = os.path.abspath(project_dir)
126
137
 
127
138
  # If no specific project directory, use the first allowed path
128
- if not project_dir and allowed_paths:
139
+ elif allowed_paths:
129
140
  project_dir = allowed_paths[0]
130
141
 
131
142
  # Run the server
@@ -138,20 +149,25 @@ def main() -> None:
138
149
  agent_api_key=agent_api_key,
139
150
  agent_max_iterations=agent_max_iterations,
140
151
  agent_max_tool_uses=agent_max_tool_uses,
141
- enable_agent_tool=enable_agent_tool
152
+ enable_agent_tool=enable_agent_tool,
153
+ disable_write_tools=disable_write_tools
142
154
  )
143
155
  # Transport will be automatically cast to Literal['stdio', 'sse'] by the server
144
156
  server.run(transport=transport)
145
157
 
146
158
 
147
159
  def install_claude_desktop_config(
148
- name: str = "claude-code", allowed_paths: list[str] | None = None
160
+ name: str = "claude-code", allowed_paths: list[str] | None = None,
161
+ disable_write_tools: bool = False
149
162
  ) -> None:
150
163
  """Install the server configuration in Claude Desktop.
151
164
 
152
165
  Args:
153
166
  name: The name to use for the server in the config
154
167
  allowed_paths: Optional list of paths to allow
168
+ disable_write_tools: Whether to disable write/edit tools (file writing, editing, notebook editing)
169
+ to use IDE tools instead. Note: Shell commands can still modify files.
170
+ (default: False)
155
171
  """
156
172
  # Find the Claude Desktop config directory
157
173
  home: Path = Path.home()
@@ -181,6 +197,10 @@ def install_claude_desktop_config(
181
197
  else:
182
198
  # Allow home directory by default
183
199
  args.extend(["--allow-path", str(home)])
200
+
201
+ # Add disable_write_tools flag if specified
202
+ if disable_write_tools:
203
+ args.append("--disable-write-tools")
184
204
 
185
205
  # Create config object
186
206
  config: dict[str, Any] = {
hanzo_mcp/server.py CHANGED
@@ -6,7 +6,6 @@ from mcp.server.fastmcp import FastMCP
6
6
 
7
7
  from hanzo_mcp.tools import register_all_tools
8
8
  from hanzo_mcp.tools.common.context import DocumentContext
9
- from hanzo_mcp.tools.common.path_utils import PathUtils
10
9
  from hanzo_mcp.tools.common.permissions import PermissionManager
11
10
  from hanzo_mcp.tools.project.analysis import ProjectAnalyzer, ProjectManager
12
11
  from hanzo_mcp.tools.shell.command_executor import CommandExecutor
@@ -28,6 +27,7 @@ class HanzoServer:
28
27
  agent_max_iterations: int = 10,
29
28
  agent_max_tool_uses: int = 30,
30
29
  enable_agent_tool: bool = False,
30
+ disable_write_tools: bool = False,
31
31
  ):
32
32
  """Initialize the Hanzo server.
33
33
 
@@ -42,6 +42,7 @@ class HanzoServer:
42
42
  agent_max_iterations: Maximum number of iterations for agent (default: 10)
43
43
  agent_max_tool_uses: Maximum number of total tool uses for agent (default: 30)
44
44
  enable_agent_tool: Whether to enable the agent tool (default: False)
45
+ disable_write_tools: Whether to disable write/edit tools (default: False)
45
46
  """
46
47
  self.mcp = mcp_instance if mcp_instance is not None else FastMCP(name)
47
48
 
@@ -71,10 +72,8 @@ class HanzoServer:
71
72
  # Add allowed paths
72
73
  if allowed_paths:
73
74
  for path in allowed_paths:
74
- # Path should already be normalized from CLI, but normalize here for safety
75
- normalized_path = PathUtils.normalize_path(path)
76
- self.permission_manager.add_allowed_path(normalized_path)
77
- self.document_context.add_allowed_path(normalized_path)
75
+ self.permission_manager.add_allowed_path(path)
76
+ self.document_context.add_allowed_path(path)
78
77
 
79
78
  # Store agent options
80
79
  self.agent_model = agent_model
@@ -83,6 +82,7 @@ class HanzoServer:
83
82
  self.agent_max_iterations = agent_max_iterations
84
83
  self.agent_max_tool_uses = agent_max_tool_uses
85
84
  self.enable_agent_tool = enable_agent_tool
85
+ self.disable_write_tools = disable_write_tools
86
86
 
87
87
  # Register all tools
88
88
  register_all_tools(
@@ -95,6 +95,7 @@ class HanzoServer:
95
95
  agent_max_iterations=self.agent_max_iterations,
96
96
  agent_max_tool_uses=self.agent_max_tool_uses,
97
97
  enable_agent_tool=self.enable_agent_tool,
98
+ disable_write_tools=self.disable_write_tools,
98
99
  )
99
100
 
100
101
  def run(self, transport: str = "stdio", allowed_paths: list[str] | None = None):
@@ -107,10 +108,8 @@ class HanzoServer:
107
108
  # Add allowed paths if provided
108
109
  allowed_paths_list = allowed_paths or []
109
110
  for path in allowed_paths_list:
110
- # Normalize path before adding
111
- normalized_path = PathUtils.normalize_path(path)
112
- self.permission_manager.add_allowed_path(normalized_path)
113
- self.document_context.add_allowed_path(normalized_path)
111
+ self.permission_manager.add_allowed_path(path)
112
+ self.document_context.add_allowed_path(path)
114
113
 
115
114
  # Run the server
116
115
  transport_type = cast(Literal["stdio", "sse"], transport)
@@ -32,6 +32,7 @@ def register_all_tools(
32
32
  agent_max_iterations: int = 10,
33
33
  agent_max_tool_uses: int = 30,
34
34
  enable_agent_tool: bool = False,
35
+ disable_write_tools: bool = False,
35
36
  ) -> None:
36
37
  """Register all Hanzo tools with the MCP server.
37
38
 
@@ -45,12 +46,13 @@ def register_all_tools(
45
46
  agent_max_iterations: Maximum number of iterations for agent (default: 10)
46
47
  agent_max_tool_uses: Maximum number of total tool uses for agent (default: 30)
47
48
  enable_agent_tool: Whether to enable the agent tool (default: False)
49
+ disable_write_tools: Whether to disable write/edit tools (default: False)
48
50
  """
49
51
  # Register all filesystem tools
50
- register_filesystem_tools(mcp_server, document_context, permission_manager)
52
+ register_filesystem_tools(mcp_server, document_context, permission_manager, disable_write_tools)
51
53
 
52
54
  # Register all jupyter tools
53
- register_jupyter_tools(mcp_server, document_context, permission_manager)
55
+ register_jupyter_tools(mcp_server, document_context, permission_manager, disable_write_tools)
54
56
 
55
57
  # Register shell tools
56
58
  register_shell_tools(mcp_server, permission_manager)
@@ -3,7 +3,6 @@
3
3
  from mcp.server.fastmcp import FastMCP
4
4
 
5
5
  from hanzo_mcp.tools.common.base import ToolRegistry
6
- from hanzo_mcp.tools.common.path_utils import PathUtils
7
6
  from hanzo_mcp.tools.common.think_tool import ThinkingTool
8
7
  from hanzo_mcp.tools.common.version_tool import VersionTool
9
8
 
@@ -14,8 +14,6 @@ from typing import Any, ClassVar, final
14
14
  from mcp.server.fastmcp import Context as MCPContext
15
15
  from mcp.server.lowlevel.helper_types import ReadResourceContents
16
16
 
17
- from hanzo_mcp.tools.common.path_utils import PathUtils
18
-
19
17
 
20
18
  @final
21
19
  class ToolContext:
@@ -181,9 +179,9 @@ class DocumentContext:
181
179
  Args:
182
180
  path: The path to allow
183
181
  """
184
- # Normalize path (expand user paths and make absolute)
185
- normalized_path = PathUtils.normalize_path(path)
186
- resolved_path: Path = Path(normalized_path).resolve()
182
+ # Expand user path (e.g., ~/ or $HOME)
183
+ expanded_path = os.path.expanduser(path)
184
+ resolved_path: Path = Path(expanded_path).resolve()
187
185
  self.allowed_paths.add(resolved_path)
188
186
 
189
187
  def is_path_allowed(self, path: str) -> bool:
@@ -195,9 +193,9 @@ class DocumentContext:
195
193
  Returns:
196
194
  True if the path is allowed, False otherwise
197
195
  """
198
- # Normalize path (expand user paths and make absolute)
199
- normalized_path = PathUtils.normalize_path(path)
200
- resolved_path: Path = Path(normalized_path).resolve()
196
+ # Expand user path (e.g., ~/ or $HOME)
197
+ expanded_path = os.path.expanduser(path)
198
+ resolved_path: Path = Path(expanded_path).resolve()
201
199
 
202
200
  # Check if the path is within any allowed path
203
201
  for allowed_path in self.allowed_paths:
@@ -6,8 +6,6 @@ from collections.abc import Awaitable, Callable
6
6
  from pathlib import Path
7
7
  from typing import Any, TypeVar, final
8
8
 
9
- from hanzo_mcp.tools.common.path_utils import PathUtils
10
-
11
9
  # Define type variables for better type annotations
12
10
  T = TypeVar("T")
13
11
  P = TypeVar("P")
@@ -71,9 +69,9 @@ class PermissionManager:
71
69
  Args:
72
70
  path: The path to allow
73
71
  """
74
- # Normalize path (expand user paths and make absolute)
75
- normalized_path = PathUtils.normalize_path(path)
76
- resolved_path: Path = Path(normalized_path).resolve()
72
+ # Expand user path (e.g., ~/ or $HOME)
73
+ expanded_path = os.path.expanduser(path)
74
+ resolved_path: Path = Path(expanded_path).resolve()
77
75
  self.allowed_paths.add(resolved_path)
78
76
 
79
77
  def remove_allowed_path(self, path: str) -> None:
@@ -112,9 +110,9 @@ class PermissionManager:
112
110
  Returns:
113
111
  True if the path is allowed, False otherwise
114
112
  """
115
- # Normalize path (expand user paths and make absolute)
116
- normalized_path = PathUtils.normalize_path(path)
117
- resolved_path: Path = Path(normalized_path).resolve()
113
+ # Expand user path (e.g., ~/ or $HOME)
114
+ expanded_path = os.path.expanduser(path)
115
+ resolved_path: Path = Path(expanded_path).resolve()
118
116
 
119
117
  # Check exclusions first
120
118
  if self._is_path_excluded(resolved_path):
@@ -77,6 +77,7 @@ def register_filesystem_tools(
77
77
  mcp_server: FastMCP,
78
78
  document_context: DocumentContext,
79
79
  permission_manager: PermissionManager,
80
+ disable_write_tools: bool = False,
80
81
  ) -> None:
81
82
  """Register all filesystem tools with the MCP server.
82
83
 
@@ -84,6 +85,10 @@ def register_filesystem_tools(
84
85
  mcp_server: The FastMCP server instance
85
86
  document_context: Document context for tracking file contents
86
87
  permission_manager: Permission manager for access control
88
+ disable_write_tools: Whether to disable write/edit tools (default: False)
87
89
  """
88
- tools = get_filesystem_tools(document_context, permission_manager)
90
+ if disable_write_tools:
91
+ tools = get_read_only_filesystem_tools(document_context, permission_manager)
92
+ else:
93
+ tools = get_filesystem_tools(document_context, permission_manager)
89
94
  ToolRegistry.register_tools(mcp_server, tools)
@@ -3,14 +3,12 @@
3
3
  This module provides the DirectoryTreeTool for viewing file and directory structures.
4
4
  """
5
5
 
6
- import os
7
6
  from pathlib import Path
8
7
  from typing import Any, final, override
9
8
 
10
9
  from mcp.server.fastmcp import Context as MCPContext
11
10
  from mcp.server.fastmcp import FastMCP
12
11
 
13
- from hanzo_mcp.tools.common.path_utils import PathUtils
14
12
  from hanzo_mcp.tools.filesystem.base import FilesystemBaseTool
15
13
 
16
14
 
@@ -139,47 +137,17 @@ requested. Only works within allowed directories."""
139
137
  if not is_dir:
140
138
  return error_msg
141
139
 
142
- # Define filtered directories based on common patterns and .gitignore
140
+ # Define filtered directories
143
141
  FILTERED_DIRECTORIES = {
144
- # Hidden/dot directories
145
- ".git", ".github", ".gitignore", ".hg", ".svn", ".venv", ".env",
146
- ".idea", ".vscode", ".vs", ".cache", ".config", ".local",
147
- ".pytest_cache", ".ruff_cache", ".mypy_cache", ".pytype",
148
- ".coverage", ".tox", ".nox", ".circleci", ".llm-context",
149
- # Cache directories
150
- "__pycache__", ".ipynb_checkpoints", "htmlcov", ".eggs",
151
- # Build artifacts
152
- "dist", "build", "target", "out", "site", "coverage",
153
- # Dependency directories
154
- "node_modules", "venv", "env", "ENV", "lib", "libs", "vendor",
155
- "eggs", "sdist", "wheels", "share"
142
+ ".git", "node_modules", ".venv", "venv",
143
+ "__pycache__", ".pytest_cache", ".idea",
144
+ ".vs", ".vscode", "dist", "build", "target",
145
+ ".ruff_cache",".llm-context"
156
146
  }
157
147
 
158
148
  # Log filtering settings
159
149
  await tool_ctx.info(f"Directory tree filtering: include_filtered={include_filtered}")
160
150
 
161
- # Try to get additional patterns from .gitignore if it exists
162
- gitignore_patterns = set()
163
- gitignore_path = dir_path / ".gitignore"
164
- if gitignore_path.exists() and gitignore_path.is_file():
165
- try:
166
- with open(gitignore_path, "r") as f:
167
- for line in f:
168
- line = line.strip()
169
- if line and not line.startswith("#"):
170
- # Strip trailing slashes for directory patterns
171
- if line.endswith("/"):
172
- line = line[:-1]
173
- # Extract the actual pattern without path
174
- pattern = line.split("/")[-1]
175
- if pattern and "*" not in pattern and "?" not in pattern:
176
- gitignore_patterns.add(pattern)
177
- except Exception as e:
178
- await tool_ctx.warning(f"Error reading .gitignore: {str(e)}")
179
-
180
- # Add gitignore patterns to filtered directories
181
- FILTERED_DIRECTORIES.update(gitignore_patterns)
182
-
183
151
  # Check if a directory should be filtered
184
152
  def should_filter(current_path: Path) -> bool:
185
153
  # Don't filter if it's the explicitly requested path
@@ -187,15 +155,8 @@ requested. Only works within allowed directories."""
187
155
  # Don't filter explicitly requested paths
188
156
  return False
189
157
 
190
- # First check standard filtered directories
191
- if current_path.name in FILTERED_DIRECTORIES and not include_filtered:
192
- return True
193
-
194
- # Also filter hidden directories (dot directories) unless explicitly included
195
- if PathUtils.is_dot_directory(current_path) and not include_filtered:
196
- return True
197
-
198
- return False
158
+ # Filter based on directory name if filtering is enabled
159
+ return current_path.name in FILTERED_DIRECTORIES and not include_filtered
199
160
 
200
161
  # Track stats for summary
201
162
  stats = {
@@ -59,6 +59,7 @@ def register_jupyter_tools(
59
59
  mcp_server: FastMCP,
60
60
  document_context: DocumentContext,
61
61
  permission_manager: PermissionManager,
62
+ disable_write_tools: bool = False,
62
63
  ) -> None:
63
64
  """Register all Jupyter notebook tools with the MCP server.
64
65
 
@@ -66,6 +67,10 @@ def register_jupyter_tools(
66
67
  mcp_server: The FastMCP server instance
67
68
  document_context: Document context for tracking file contents
68
69
  permission_manager: Permission manager for access control
70
+ disable_write_tools: Whether to disable write/edit tools (default: False)
69
71
  """
70
- tools = get_jupyter_tools(document_context, permission_manager)
72
+ if disable_write_tools:
73
+ tools = get_read_only_jupyter_tools(document_context, permission_manager)
74
+ else:
75
+ tools = get_jupyter_tools(document_context, permission_manager)
71
76
  ToolRegistry.register_tools(mcp_server, tools)
@@ -14,8 +14,6 @@ from collections.abc import Awaitable, Callable
14
14
  from pathlib import Path
15
15
  from typing import Dict, Optional, final
16
16
 
17
- from hanzo_mcp.tools.common.path_utils import PathUtils
18
-
19
17
  from mcp.server.fastmcp import Context as MCPContext
20
18
  from mcp.server.fastmcp import FastMCP
21
19
 
@@ -136,8 +134,8 @@ class CommandExecutor:
136
134
  path: The path to set as the current working directory
137
135
  """
138
136
  session = self.get_session_manager(session_id)
139
- normalized_path = PathUtils.normalize_path(path)
140
- session.set_working_dir(Path(normalized_path))
137
+ expanded_path = os.path.expanduser(path)
138
+ session.set_working_dir(Path(expanded_path))
141
139
 
142
140
  def get_working_dir(self, session_id: str) -> str:
143
141
  """Get the current working directory for the session.
@@ -251,8 +249,11 @@ class CommandExecutor:
251
249
  session_cwd = self.get_working_dir(session_id)
252
250
  target_dir = os.path.join(session_cwd, target_dir)
253
251
 
254
- # Normalize path (expand user and make absolute)
255
- target_dir = PathUtils.normalize_path(target_dir)
252
+ # Expand user paths
253
+ target_dir = os.path.expanduser(target_dir)
254
+
255
+ # Normalize path
256
+ target_dir = os.path.normpath(target_dir)
256
257
 
257
258
  if os.path.isdir(target_dir):
258
259
  self.set_working_dir(session_id, target_dir)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hanzo-mcp
3
- Version: 0.1.36
3
+ Version: 0.2.0
4
4
  Summary: MCP implementation of Hanzo capabilities
5
5
  Author-email: Hanzo Industries Inc <dev@hanzo.ai>
6
6
  License: MIT
@@ -1,30 +1,29 @@
1
- hanzo_mcp/__init__.py,sha256=R1BsBtTu0Ll8ASecW2mPKCntuNwFh6Z8vY8U5tjQ6pE,90
2
- hanzo_mcp/cli.py,sha256=VPGky5w47_7PYYaXnryToaj7By7Sld0vn_bZ2Hxmpy8,7189
3
- hanzo_mcp/server.py,sha256=OyF-jmPCHXId7DaIk3QcGxnmkSgsnd0GLx3WAtiaOik,6032
4
- hanzo_mcp/tools/__init__.py,sha256=9bq0emNV_xQk3elWdfIkzgzRU2G1vIsV2k_neV43coc,3333
1
+ hanzo_mcp/__init__.py,sha256=crlSOFdbUf7WY8A-esMOCLI-YKa7IwfxXTsOZvFswvg,89
2
+ hanzo_mcp/cli.py,sha256=nfTY2lDvdxUR7KTk5j89CGpgLCj9YwAb2mqBzWlJBHI,8070
3
+ hanzo_mcp/server.py,sha256=rZcu0JsOQlnnbBVV128qpXEl-ocWEVToBDeA8wmt068,5913
4
+ hanzo_mcp/tools/__init__.py,sha256=9LbWAPfSntDwLaAex3dagsHO4BgZQHKj5E6UX-Gmyb4,3496
5
5
  hanzo_mcp/tools/agent/__init__.py,sha256=0eyQqqdAy7WCZEqUfV6xh66bDpQI9neB6iDjWf0_pJI,2189
6
6
  hanzo_mcp/tools/agent/agent_tool.py,sha256=qXu62ZRGM0o9mxOiRVFy-ABIZtEJ8z03DqAXshKAieI,19180
7
7
  hanzo_mcp/tools/agent/prompt.py,sha256=jmYRI4Sm2D3LwEdC2qWakpqupgfGg8CT6grmx4NEDaA,4431
8
8
  hanzo_mcp/tools/agent/tool_adapter.py,sha256=g9NKfIET0WOsm0r28xEXsibsprpI1C6ripcM6QwU-rI,2172
9
- hanzo_mcp/tools/common/__init__.py,sha256=E0rOalOkyLKsjDcmZNjuvKY4Fu0NTr81rYC1G4r51UM,853
9
+ hanzo_mcp/tools/common/__init__.py,sha256=felrGAEqLWOa8fuHjTCS7khqtlA-izz8k865ky7R-f8,797
10
10
  hanzo_mcp/tools/common/base.py,sha256=O7Lgft0XiC9Iyi3fYsmoWWrvKDK2Aa-FJLxPgnNJRJY,7341
11
- hanzo_mcp/tools/common/context.py,sha256=rXK6FvHFnJ0OAYvAKfJUEjY6TKFn_Dj8ErgvCSvGHh4,13254
12
- hanzo_mcp/tools/common/path_utils.py,sha256=eLmHxvUQimCZceUNg5W9DbUvlA3uRzU6Stal-3XHRf8,1509
13
- hanzo_mcp/tools/common/permissions.py,sha256=w_D2bIX5E0PmctmNf0tFASYebU-cUuEFICjOOiAI_WM,7703
11
+ hanzo_mcp/tools/common/context.py,sha256=ReIfcm37j5qnLQ8G_-d88ad5uC1OKkjQZKG9HdJPybI,13145
12
+ hanzo_mcp/tools/common/permissions.py,sha256=4YCfA2PJUOl-z_47n5uaRXO8gAZ_shMaPhpi1dilgRE,7594
14
13
  hanzo_mcp/tools/common/session.py,sha256=csX5ZhgBjO2bdXXXPpsUPzOCc7Tib-apYe01AP8sh8k,2774
15
14
  hanzo_mcp/tools/common/think_tool.py,sha256=I-O6ipM-PUofkNoMMzv37Y_2Yfx9mh7F1upTTsfRN4M,4046
16
15
  hanzo_mcp/tools/common/validation.py,sha256=gB3uM_cbPZsH4Ez0hnTgIcdP-AUlHZU02aRmZEpk_6I,3648
17
16
  hanzo_mcp/tools/common/version_tool.py,sha256=4bJZhqgtvwQMyVSSZ-xU-NQvr1xfnyDi_4FnOpZvuw0,3406
18
- hanzo_mcp/tools/filesystem/__init__.py,sha256=-wNhb0IjJgz05n_fRP0wDXfKgJ6fgBp4wrGo62Hpyvc,3265
17
+ hanzo_mcp/tools/filesystem/__init__.py,sha256=Jm5AmLIdRxzcZ4L8ajNJCQYObpmUeoBOzdYdFvDgQIA,3513
19
18
  hanzo_mcp/tools/filesystem/base.py,sha256=HAzuMCrS0dKOBZNoLr7y74tdbYyKpi0FGhStuRgkFTU,3917
20
19
  hanzo_mcp/tools/filesystem/content_replace.py,sha256=ZwzxyOTASUmzP-jtEnsSR-MKtNFC4R3kQunpV3KOCyg,11049
21
- hanzo_mcp/tools/filesystem/directory_tree.py,sha256=QgMjvBv9rKVNHEumHgrS0chP91IKo5xg3X5JzSiHbOY,13403
20
+ hanzo_mcp/tools/filesystem/directory_tree.py,sha256=cx-zpOeKP8DDuMt1ls3QhRk9h3RVmMhpPwpqn4wTfP4,11271
22
21
  hanzo_mcp/tools/filesystem/edit_file.py,sha256=03ku1_8X_uAUPfqGlR6fv55VEz-0Pifp9DJtkcOTFHY,10980
23
22
  hanzo_mcp/tools/filesystem/get_file_info.py,sha256=WR7uMqFcpKboS3FX3jF-MD-0-ROJJcppX7M_GtR0yLs,5392
24
23
  hanzo_mcp/tools/filesystem/read_files.py,sha256=0JYJ2kM8FIoksbnnO8V0uY3D2R1uWvR7zb7_oXV0sMM,6968
25
24
  hanzo_mcp/tools/filesystem/search_content.py,sha256=2zXke1YHYxg6GKQ_XHb0sXeaSkHI7Jx3P-YAqrpOTNM,10766
26
25
  hanzo_mcp/tools/filesystem/write_file.py,sha256=7ZNR1ygECBjT7m62QNkeIEt0OGxNZL2zijX-bASWj0Y,5303
27
- hanzo_mcp/tools/jupyter/__init__.py,sha256=xFYW8Idb4x1jLWbexN5sqFKSCd1aL820uENzJ7mb6rs,2282
26
+ hanzo_mcp/tools/jupyter/__init__.py,sha256=9mZ55Mq0JMmjOxCxbeFHS8vOqvezzh_uCst02EPB4Ng,2527
28
27
  hanzo_mcp/tools/jupyter/base.py,sha256=xtssHrkHx_u_nE12dqtZGcvuJe8kfsbSkMmq-6KQobQ,10412
29
28
  hanzo_mcp/tools/jupyter/edit_notebook.py,sha256=_ZNlsCYaO9_SbZouvrLYElvssL6nlElCc2JxNCeMdQo,11986
30
29
  hanzo_mcp/tools/jupyter/notebook_operations.py,sha256=PkZXk_PYPkBGxg2RWzqh-rN6VDHjFybImhdUm3xLLoY,23120
@@ -35,13 +34,13 @@ hanzo_mcp/tools/project/base.py,sha256=CniLAsjum5vC3cgvF9AqU-_ZY_0Nf9uaF2L_xV2ob
35
34
  hanzo_mcp/tools/project/project_analyze.py,sha256=6GLE_JcSiCy6kKdee0sMI5T2229A-Vpp98s2j_JD6yI,5711
36
35
  hanzo_mcp/tools/shell/__init__.py,sha256=lKgh0WXds4tZJ1tIL9MJbyMSzP6A9uZQALjGGBvyYc4,1679
37
36
  hanzo_mcp/tools/shell/base.py,sha256=OxKNWMp-fB-vozzWOE_hHvr5M_pFKSMIYfOX0dEOWzA,4836
38
- hanzo_mcp/tools/shell/command_executor.py,sha256=MqGxHoLB_cDOutdTv59BF5ooiLE-_2Xt-J9UOE-ReFo,32610
37
+ hanzo_mcp/tools/shell/command_executor.py,sha256=5GcJvg54uty9fl_tkGdWTBcHyjxuynQZ_Tv6qOyKgdk,32616
39
38
  hanzo_mcp/tools/shell/run_command.py,sha256=r7HBw0lqabgkGnVsDXmLnrTo0SU9g8gLvzpa-9n-cmM,6891
40
39
  hanzo_mcp/tools/shell/run_script.py,sha256=CLYnDc0Ze8plkXU6d98RgE4UrBg-fwaMVdcn9Fc6Ixw,7432
41
40
  hanzo_mcp/tools/shell/script_tool.py,sha256=s63tawIZBvwgm_kU9P7A3D4v2ulVw7j4l_rpsa_zGuc,8680
42
- hanzo_mcp-0.1.36.dist-info/licenses/LICENSE,sha256=mf1qZGFsPGskoPgytp9B-RsahfKvXsBpmaAbTLGTt8Y,1063
43
- hanzo_mcp-0.1.36.dist-info/METADATA,sha256=xVd9e-JGD2c0Rs5GdG7ZMoDu4LcsdS2jwgADGdegj30,7551
44
- hanzo_mcp-0.1.36.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
45
- hanzo_mcp-0.1.36.dist-info/entry_points.txt,sha256=aRKOKXtuQr-idSr-yH4efnRl2v8te94AcgN3ysqqSYs,49
46
- hanzo_mcp-0.1.36.dist-info/top_level.txt,sha256=eGFANatA0MHWiVlpS56fTYRIShtibrSom1uXI6XU0GU,10
47
- hanzo_mcp-0.1.36.dist-info/RECORD,,
41
+ hanzo_mcp-0.2.0.dist-info/licenses/LICENSE,sha256=mf1qZGFsPGskoPgytp9B-RsahfKvXsBpmaAbTLGTt8Y,1063
42
+ hanzo_mcp-0.2.0.dist-info/METADATA,sha256=dAdJtsK8J9wl9dVksKcMPvaj7xcxtPjcyhbIekF_BX0,7550
43
+ hanzo_mcp-0.2.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
44
+ hanzo_mcp-0.2.0.dist-info/entry_points.txt,sha256=aRKOKXtuQr-idSr-yH4efnRl2v8te94AcgN3ysqqSYs,49
45
+ hanzo_mcp-0.2.0.dist-info/top_level.txt,sha256=eGFANatA0MHWiVlpS56fTYRIShtibrSom1uXI6XU0GU,10
46
+ hanzo_mcp-0.2.0.dist-info/RECORD,,
@@ -1,51 +0,0 @@
1
- """Path utilities for Hanzo MCP.
2
-
3
- This module provides path normalization and validation utilities.
4
- """
5
-
6
- import os
7
- from pathlib import Path
8
- from typing import final
9
-
10
-
11
- @final
12
- class PathUtils:
13
- """Utilities for path handling."""
14
-
15
- @staticmethod
16
- def normalize_path(path: str) -> str:
17
- """Normalize a path by expanding user paths and making it absolute.
18
-
19
- Args:
20
- path: The path to normalize
21
-
22
- Returns:
23
- The normalized path
24
- """
25
- # Expand user paths (e.g., ~/ or $HOME)
26
- expanded_path = os.path.expanduser(path)
27
-
28
- # Make the path absolute if it isn't already
29
- if not os.path.isabs(expanded_path):
30
- expanded_path = os.path.abspath(expanded_path)
31
-
32
- # Normalize the path (resolve symlinks, etc.)
33
- try:
34
- normalized_path = os.path.normpath(expanded_path)
35
- return normalized_path
36
- except Exception:
37
- # Return the expanded path if normalization fails
38
- return expanded_path
39
-
40
- @staticmethod
41
- def is_dot_directory(path: Path) -> bool:
42
- """Check if a path is a dot directory (e.g., .git, .vscode).
43
-
44
- Args:
45
- path: The path to check
46
-
47
- Returns:
48
- True if the path is a dot directory, False otherwise
49
- """
50
- # Consider any directory starting with "." to be a dot directory
51
- return path.is_dir() and path.name.startswith(".")