hanzo-mcp 0.1.35__tar.gz → 0.1.36__tar.gz
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-0.1.35 → hanzo_mcp-0.1.36}/PKG-INFO +1 -1
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/__init__.py +1 -1
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/cli.py +9 -11
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/server.py +9 -4
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/__init__.py +2 -2
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/common/__init__.py +5 -4
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/common/context.py +8 -6
- hanzo_mcp-0.1.36/hanzo_mcp/tools/common/path_utils.py +51 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/common/permissions.py +8 -6
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/filesystem/directory_tree.py +46 -7
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/shell/command_executor.py +6 -7
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp.egg-info/PKG-INFO +1 -1
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp.egg-info/SOURCES.txt +2 -1
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/pyproject.toml +1 -1
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/LICENSE +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/README.md +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/agent/__init__.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/agent/agent_tool.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/agent/prompt.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/agent/tool_adapter.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/common/base.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/common/session.py +0 -0
- /hanzo_mcp-0.1.35/hanzo_mcp/tools/common/thinking_tool.py → /hanzo_mcp-0.1.36/hanzo_mcp/tools/common/think_tool.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/common/validation.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/common/version_tool.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/filesystem/__init__.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/filesystem/base.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/filesystem/content_replace.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/filesystem/edit_file.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/filesystem/get_file_info.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/filesystem/read_files.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/filesystem/search_content.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/filesystem/write_file.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/jupyter/__init__.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/jupyter/base.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/jupyter/edit_notebook.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/jupyter/notebook_operations.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/jupyter/read_notebook.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/project/__init__.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/project/analysis.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/project/base.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/project/project_analyze.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/shell/__init__.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/shell/base.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/shell/run_command.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/shell/run_script.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp/tools/shell/script_tool.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp.egg-info/dependency_links.txt +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp.egg-info/entry_points.txt +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp.egg-info/requires.txt +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/hanzo_mcp.egg-info/top_level.txt +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/setup.cfg +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/setup.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/tests/test_cli.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/tests/test_server.py +0 -0
- {hanzo_mcp-0.1.35 → hanzo_mcp-0.1.36}/tests/test_validation.py +0 -0
|
@@ -8,6 +8,7 @@ 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
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
def main() -> None:
|
|
@@ -113,21 +114,18 @@ def main() -> None:
|
|
|
113
114
|
# If no allowed paths are specified, use the user's home directory
|
|
114
115
|
if not allowed_paths:
|
|
115
116
|
allowed_paths = [str(Path.home())]
|
|
117
|
+
|
|
118
|
+
# Normalize all allowed paths
|
|
119
|
+
allowed_paths = [PathUtils.normalize_path(path) for path in allowed_paths]
|
|
116
120
|
|
|
117
|
-
# If project directory is specified,
|
|
118
|
-
if project_dir and project_dir not in allowed_paths:
|
|
119
|
-
allowed_paths.append(project_dir)
|
|
120
|
-
|
|
121
|
-
# Set project directory as initial working directory if provided
|
|
121
|
+
# If project directory is specified, normalize it and add to allowed paths
|
|
122
122
|
if project_dir:
|
|
123
|
-
|
|
124
|
-
project_dir
|
|
125
|
-
|
|
126
|
-
if not os.path.isabs(project_dir):
|
|
127
|
-
project_dir = os.path.abspath(project_dir)
|
|
123
|
+
project_dir = PathUtils.normalize_path(project_dir)
|
|
124
|
+
if project_dir not in allowed_paths:
|
|
125
|
+
allowed_paths.append(project_dir)
|
|
128
126
|
|
|
129
127
|
# If no specific project directory, use the first allowed path
|
|
130
|
-
|
|
128
|
+
if not project_dir and allowed_paths:
|
|
131
129
|
project_dir = allowed_paths[0]
|
|
132
130
|
|
|
133
131
|
# Run the server
|
|
@@ -6,6 +6,7 @@ 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
|
|
9
10
|
from hanzo_mcp.tools.common.permissions import PermissionManager
|
|
10
11
|
from hanzo_mcp.tools.project.analysis import ProjectAnalyzer, ProjectManager
|
|
11
12
|
from hanzo_mcp.tools.shell.command_executor import CommandExecutor
|
|
@@ -70,8 +71,10 @@ class HanzoServer:
|
|
|
70
71
|
# Add allowed paths
|
|
71
72
|
if allowed_paths:
|
|
72
73
|
for path in allowed_paths:
|
|
73
|
-
|
|
74
|
-
|
|
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
78
|
|
|
76
79
|
# Store agent options
|
|
77
80
|
self.agent_model = agent_model
|
|
@@ -104,8 +107,10 @@ class HanzoServer:
|
|
|
104
107
|
# Add allowed paths if provided
|
|
105
108
|
allowed_paths_list = allowed_paths or []
|
|
106
109
|
for path in allowed_paths_list:
|
|
107
|
-
|
|
108
|
-
|
|
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)
|
|
109
114
|
|
|
110
115
|
# Run the server
|
|
111
116
|
transport_type = cast(Literal["stdio", "sse"], transport)
|
|
@@ -12,7 +12,7 @@ to delegate tasks to sub-agents for concurrent execution and specialized process
|
|
|
12
12
|
from mcp.server.fastmcp import FastMCP
|
|
13
13
|
|
|
14
14
|
from hanzo_mcp.tools.agent import register_agent_tools
|
|
15
|
-
from hanzo_mcp.tools.common import
|
|
15
|
+
from hanzo_mcp.tools.common import register_think_tool, register_version_tool
|
|
16
16
|
from hanzo_mcp.tools.common.context import DocumentContext
|
|
17
17
|
from hanzo_mcp.tools.common.permissions import PermissionManager
|
|
18
18
|
from hanzo_mcp.tools.filesystem import register_filesystem_tools
|
|
@@ -78,7 +78,7 @@ def register_all_tools(
|
|
|
78
78
|
)
|
|
79
79
|
|
|
80
80
|
# Initialize and register thinking tool
|
|
81
|
-
|
|
81
|
+
register_think_tool(mcp_server)
|
|
82
82
|
|
|
83
83
|
# Register version tool
|
|
84
84
|
register_version_tool(mcp_server)
|
|
@@ -3,11 +3,12 @@
|
|
|
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.
|
|
6
|
+
from hanzo_mcp.tools.common.path_utils import PathUtils
|
|
7
|
+
from hanzo_mcp.tools.common.think_tool import ThinkingTool
|
|
7
8
|
from hanzo_mcp.tools.common.version_tool import VersionTool
|
|
8
9
|
|
|
9
10
|
|
|
10
|
-
def
|
|
11
|
+
def register_think_tool(
|
|
11
12
|
mcp_server: FastMCP,
|
|
12
13
|
) -> None:
|
|
13
14
|
"""Register all thinking tools with the MCP server.
|
|
@@ -15,8 +16,8 @@ def register_thinking_tool(
|
|
|
15
16
|
Args:
|
|
16
17
|
mcp_server: The FastMCP server instance
|
|
17
18
|
"""
|
|
18
|
-
|
|
19
|
-
ToolRegistry.register_tool(mcp_server,
|
|
19
|
+
think_tool = ThinkingTool()
|
|
20
|
+
ToolRegistry.register_tool(mcp_server, think_tool)
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
def register_version_tool(
|
|
@@ -14,6 +14,8 @@ 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
|
+
|
|
17
19
|
|
|
18
20
|
@final
|
|
19
21
|
class ToolContext:
|
|
@@ -179,9 +181,9 @@ class DocumentContext:
|
|
|
179
181
|
Args:
|
|
180
182
|
path: The path to allow
|
|
181
183
|
"""
|
|
182
|
-
#
|
|
183
|
-
|
|
184
|
-
resolved_path: Path = Path(
|
|
184
|
+
# Normalize path (expand user paths and make absolute)
|
|
185
|
+
normalized_path = PathUtils.normalize_path(path)
|
|
186
|
+
resolved_path: Path = Path(normalized_path).resolve()
|
|
185
187
|
self.allowed_paths.add(resolved_path)
|
|
186
188
|
|
|
187
189
|
def is_path_allowed(self, path: str) -> bool:
|
|
@@ -193,9 +195,9 @@ class DocumentContext:
|
|
|
193
195
|
Returns:
|
|
194
196
|
True if the path is allowed, False otherwise
|
|
195
197
|
"""
|
|
196
|
-
#
|
|
197
|
-
|
|
198
|
-
resolved_path: Path = Path(
|
|
198
|
+
# Normalize path (expand user paths and make absolute)
|
|
199
|
+
normalized_path = PathUtils.normalize_path(path)
|
|
200
|
+
resolved_path: Path = Path(normalized_path).resolve()
|
|
199
201
|
|
|
200
202
|
# Check if the path is within any allowed path
|
|
201
203
|
for allowed_path in self.allowed_paths:
|
|
@@ -0,0 +1,51 @@
|
|
|
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(".")
|
|
@@ -6,6 +6,8 @@ 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
|
+
|
|
9
11
|
# Define type variables for better type annotations
|
|
10
12
|
T = TypeVar("T")
|
|
11
13
|
P = TypeVar("P")
|
|
@@ -69,9 +71,9 @@ class PermissionManager:
|
|
|
69
71
|
Args:
|
|
70
72
|
path: The path to allow
|
|
71
73
|
"""
|
|
72
|
-
#
|
|
73
|
-
|
|
74
|
-
resolved_path: Path = Path(
|
|
74
|
+
# Normalize path (expand user paths and make absolute)
|
|
75
|
+
normalized_path = PathUtils.normalize_path(path)
|
|
76
|
+
resolved_path: Path = Path(normalized_path).resolve()
|
|
75
77
|
self.allowed_paths.add(resolved_path)
|
|
76
78
|
|
|
77
79
|
def remove_allowed_path(self, path: str) -> None:
|
|
@@ -110,9 +112,9 @@ class PermissionManager:
|
|
|
110
112
|
Returns:
|
|
111
113
|
True if the path is allowed, False otherwise
|
|
112
114
|
"""
|
|
113
|
-
#
|
|
114
|
-
|
|
115
|
-
resolved_path: Path = Path(
|
|
115
|
+
# Normalize path (expand user paths and make absolute)
|
|
116
|
+
normalized_path = PathUtils.normalize_path(path)
|
|
117
|
+
resolved_path: Path = Path(normalized_path).resolve()
|
|
116
118
|
|
|
117
119
|
# Check exclusions first
|
|
118
120
|
if self._is_path_excluded(resolved_path):
|
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
This module provides the DirectoryTreeTool for viewing file and directory structures.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
+
import os
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
from typing import Any, final, override
|
|
8
9
|
|
|
9
10
|
from mcp.server.fastmcp import Context as MCPContext
|
|
10
11
|
from mcp.server.fastmcp import FastMCP
|
|
11
12
|
|
|
13
|
+
from hanzo_mcp.tools.common.path_utils import PathUtils
|
|
12
14
|
from hanzo_mcp.tools.filesystem.base import FilesystemBaseTool
|
|
13
15
|
|
|
14
16
|
|
|
@@ -137,17 +139,47 @@ requested. Only works within allowed directories."""
|
|
|
137
139
|
if not is_dir:
|
|
138
140
|
return error_msg
|
|
139
141
|
|
|
140
|
-
# Define filtered directories
|
|
142
|
+
# Define filtered directories based on common patterns and .gitignore
|
|
141
143
|
FILTERED_DIRECTORIES = {
|
|
142
|
-
|
|
143
|
-
"
|
|
144
|
-
".
|
|
145
|
-
".ruff_cache",".
|
|
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"
|
|
146
156
|
}
|
|
147
157
|
|
|
148
158
|
# Log filtering settings
|
|
149
159
|
await tool_ctx.info(f"Directory tree filtering: include_filtered={include_filtered}")
|
|
150
160
|
|
|
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
|
+
|
|
151
183
|
# Check if a directory should be filtered
|
|
152
184
|
def should_filter(current_path: Path) -> bool:
|
|
153
185
|
# Don't filter if it's the explicitly requested path
|
|
@@ -155,8 +187,15 @@ requested. Only works within allowed directories."""
|
|
|
155
187
|
# Don't filter explicitly requested paths
|
|
156
188
|
return False
|
|
157
189
|
|
|
158
|
-
#
|
|
159
|
-
|
|
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
|
|
160
199
|
|
|
161
200
|
# Track stats for summary
|
|
162
201
|
stats = {
|
|
@@ -14,6 +14,8 @@ 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
|
+
|
|
17
19
|
from mcp.server.fastmcp import Context as MCPContext
|
|
18
20
|
from mcp.server.fastmcp import FastMCP
|
|
19
21
|
|
|
@@ -134,8 +136,8 @@ class CommandExecutor:
|
|
|
134
136
|
path: The path to set as the current working directory
|
|
135
137
|
"""
|
|
136
138
|
session = self.get_session_manager(session_id)
|
|
137
|
-
|
|
138
|
-
session.set_working_dir(Path(
|
|
139
|
+
normalized_path = PathUtils.normalize_path(path)
|
|
140
|
+
session.set_working_dir(Path(normalized_path))
|
|
139
141
|
|
|
140
142
|
def get_working_dir(self, session_id: str) -> str:
|
|
141
143
|
"""Get the current working directory for the session.
|
|
@@ -249,11 +251,8 @@ class CommandExecutor:
|
|
|
249
251
|
session_cwd = self.get_working_dir(session_id)
|
|
250
252
|
target_dir = os.path.join(session_cwd, target_dir)
|
|
251
253
|
|
|
252
|
-
#
|
|
253
|
-
target_dir =
|
|
254
|
-
|
|
255
|
-
# Normalize path
|
|
256
|
-
target_dir = os.path.normpath(target_dir)
|
|
254
|
+
# Normalize path (expand user and make absolute)
|
|
255
|
+
target_dir = PathUtils.normalize_path(target_dir)
|
|
257
256
|
|
|
258
257
|
if os.path.isdir(target_dir):
|
|
259
258
|
self.set_working_dir(session_id, target_dir)
|
|
@@ -19,9 +19,10 @@ hanzo_mcp/tools/agent/tool_adapter.py
|
|
|
19
19
|
hanzo_mcp/tools/common/__init__.py
|
|
20
20
|
hanzo_mcp/tools/common/base.py
|
|
21
21
|
hanzo_mcp/tools/common/context.py
|
|
22
|
+
hanzo_mcp/tools/common/path_utils.py
|
|
22
23
|
hanzo_mcp/tools/common/permissions.py
|
|
23
24
|
hanzo_mcp/tools/common/session.py
|
|
24
|
-
hanzo_mcp/tools/common/
|
|
25
|
+
hanzo_mcp/tools/common/think_tool.py
|
|
25
26
|
hanzo_mcp/tools/common/validation.py
|
|
26
27
|
hanzo_mcp/tools/common/version_tool.py
|
|
27
28
|
hanzo_mcp/tools/filesystem/__init__.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|