fix: file sys tools reference fixes
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import os
|
||||
import diff_match_patch as dmp_module
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
from file_server.security import get_secure_path
|
||||
from ..security import get_secure_path
|
||||
|
||||
def register_tools(mcp: FastMCP) -> None:
|
||||
"""Register diff application tools with the MCP server."""
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import os
|
||||
import diff_match_patch as dmp_module
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
from file_server.security import get_secure_path
|
||||
from ..security import get_secure_path
|
||||
|
||||
def register_tools(mcp: FastMCP) -> None:
|
||||
"""Register patch application tools with the MCP server."""
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import os
|
||||
import subprocess
|
||||
from typing import Optional
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
from file_server.security import get_secure_path, WORKSPACES_DIR
|
||||
from ..security import get_secure_path, WORKSPACES_DIR
|
||||
|
||||
def register_tools(mcp: FastMCP) -> None:
|
||||
"""Register command execution tools with the MCP server."""
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import os
|
||||
import re
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
from file_server.security import get_secure_path, WORKSPACES_DIR
|
||||
from ..security import get_secure_path, WORKSPACES_DIR
|
||||
|
||||
def register_tools(mcp: FastMCP) -> None:
|
||||
"""Register grep search tools with the MCP server."""
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import os
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
from file_server.security import get_secure_path
|
||||
from ..security import get_secure_path
|
||||
|
||||
def register_tools(mcp: FastMCP) -> None:
|
||||
"""Register directory listing tools with the MCP server."""
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import os
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
from file_server.security import get_secure_path
|
||||
from ..security import get_secure_path
|
||||
|
||||
def register_tools(mcp: FastMCP) -> None:
|
||||
"""Register file content replacement tools with the MCP server."""
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import os
|
||||
|
||||
WORKSPACES_DIR = os.path.abspath(os.path.join(os.getcwd(), "workspaces"))
|
||||
|
||||
def get_secure_path(path: str, workspace_id: str, agent_id: str, session_id: str) -> str:
|
||||
"""Resolve and verify a path within a 3-layer sandbox (workspace/agent/session)."""
|
||||
if not workspace_id or not agent_id or not session_id:
|
||||
raise ValueError("workspace_id, agent_id, and session_id are all required")
|
||||
|
||||
# Ensure session directory exists: workspaces/workspace_id/agent_id/session_id
|
||||
session_dir = os.path.join(WORKSPACES_DIR, workspace_id, agent_id, session_id)
|
||||
os.makedirs(session_dir, exist_ok=True)
|
||||
|
||||
# Resolve absolute path
|
||||
if os.path.isabs(path):
|
||||
# Treat absolute paths as relative to the session root if they start with /
|
||||
rel_path = path.lstrip(os.sep)
|
||||
final_path = os.path.abspath(os.path.join(session_dir, rel_path))
|
||||
else:
|
||||
final_path = os.path.abspath(os.path.join(session_dir, path))
|
||||
|
||||
# Verify path is within session_dir
|
||||
common_prefix = os.path.commonpath([final_path, session_dir])
|
||||
if common_prefix != session_dir:
|
||||
raise ValueError(f"Access denied: Path '{path}' is outside the session sandbox.")
|
||||
|
||||
return final_path
|
||||
@@ -1,6 +1,6 @@
|
||||
import os
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
from file_server.security import get_secure_path
|
||||
from ..security import get_secure_path
|
||||
|
||||
def register_tools(mcp: FastMCP) -> None:
|
||||
"""Register file view tools with the MCP server."""
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import os
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
from file_server.security import get_secure_path
|
||||
from ..security import get_secure_path
|
||||
|
||||
def register_tools(mcp: FastMCP) -> None:
|
||||
"""Register file write tools with the MCP server."""
|
||||
|
||||
@@ -70,6 +70,30 @@ class TestViewFileTool:
|
||||
assert "error" in result
|
||||
assert "not found" in result["error"].lower()
|
||||
|
||||
def test_view_multiline_file(self, view_file_fn, mock_workspace, mock_secure_path, tmp_path):
|
||||
"""Viewing a multiline file returns correct line count."""
|
||||
test_file = tmp_path / "multiline.txt"
|
||||
content = "Line 1\nLine 2\nLine 3\nLine 4\n"
|
||||
test_file.write_text(content)
|
||||
|
||||
result = view_file_fn(path="multiline.txt", **mock_workspace)
|
||||
|
||||
assert result["success"] is True
|
||||
assert result["content"] == content
|
||||
assert result["lines"] == 4
|
||||
|
||||
def test_view_empty_file(self, view_file_fn, mock_workspace, mock_secure_path, tmp_path):
|
||||
"""Viewing an empty file returns empty content."""
|
||||
test_file = tmp_path / "empty.txt"
|
||||
test_file.write_text("")
|
||||
|
||||
result = view_file_fn(path="empty.txt", **mock_workspace)
|
||||
|
||||
assert result["success"] is True
|
||||
assert result["content"] == ""
|
||||
assert result["size_bytes"] == 0
|
||||
assert result["lines"] == 0
|
||||
|
||||
|
||||
class TestWriteToFileTool:
|
||||
"""Tests for write_to_file tool."""
|
||||
@@ -113,6 +137,48 @@ class TestWriteToFileTool:
|
||||
assert result["mode"] == "appended"
|
||||
assert test_file.read_text() == "Line 1\nLine 2\n"
|
||||
|
||||
def test_write_overwrite_existing(self, write_to_file_fn, mock_workspace, mock_secure_path, tmp_path):
|
||||
"""Writing to existing file overwrites it by default."""
|
||||
test_file = tmp_path / "overwrite.txt"
|
||||
test_file.write_text("Original content")
|
||||
|
||||
result = write_to_file_fn(
|
||||
path="overwrite.txt",
|
||||
content="New content",
|
||||
**mock_workspace
|
||||
)
|
||||
|
||||
assert result["success"] is True
|
||||
assert result["mode"] == "written"
|
||||
assert test_file.read_text() == "New content"
|
||||
|
||||
def test_write_creates_parent_directories(self, write_to_file_fn, mock_workspace, mock_secure_path, tmp_path):
|
||||
"""Writing creates parent directories if they don't exist."""
|
||||
result = write_to_file_fn(
|
||||
path="nested/dir/file.txt",
|
||||
content="Test",
|
||||
**mock_workspace
|
||||
)
|
||||
|
||||
assert result["success"] is True
|
||||
created_file = tmp_path / "nested" / "dir" / "file.txt"
|
||||
assert created_file.exists()
|
||||
assert created_file.read_text() == "Test"
|
||||
|
||||
def test_write_empty_content(self, write_to_file_fn, mock_workspace, mock_secure_path, tmp_path):
|
||||
"""Writing empty content creates empty file."""
|
||||
result = write_to_file_fn(
|
||||
path="empty.txt",
|
||||
content="",
|
||||
**mock_workspace
|
||||
)
|
||||
|
||||
assert result["success"] is True
|
||||
assert result["bytes_written"] == 0
|
||||
created_file = tmp_path / "empty.txt"
|
||||
assert created_file.exists()
|
||||
assert created_file.read_text() == ""
|
||||
|
||||
|
||||
class TestListDirTool:
|
||||
"""Tests for list_dir tool."""
|
||||
@@ -287,3 +353,172 @@ class TestApplyDiffTool:
|
||||
|
||||
assert "error" in result
|
||||
assert "not found" in result["error"].lower()
|
||||
|
||||
def test_apply_diff_successful(self, apply_diff_fn, mock_workspace, mock_secure_path, tmp_path):
|
||||
"""Applying a valid diff successfully modifies the file."""
|
||||
test_file = tmp_path / "diff_test.txt"
|
||||
test_file.write_text("Hello World")
|
||||
|
||||
# Create a simple diff using diff_match_patch format
|
||||
import diff_match_patch as dmp_module
|
||||
dmp = dmp_module.diff_match_patch()
|
||||
patches = dmp.patch_make("Hello World", "Hello Universe")
|
||||
diff_text = dmp.patch_toText(patches)
|
||||
|
||||
result = apply_diff_fn(
|
||||
path="diff_test.txt",
|
||||
diff_text=diff_text,
|
||||
**mock_workspace
|
||||
)
|
||||
|
||||
assert result["success"] is True
|
||||
assert result["all_successful"] is True
|
||||
assert result["patches_applied"] > 0
|
||||
assert test_file.read_text() == "Hello Universe"
|
||||
|
||||
def test_apply_diff_multiline(self, apply_diff_fn, mock_workspace, mock_secure_path, tmp_path):
|
||||
"""Applying diff to multiline content works correctly."""
|
||||
test_file = tmp_path / "multiline.txt"
|
||||
original = "Line 1\nLine 2\nLine 3\n"
|
||||
test_file.write_text(original)
|
||||
|
||||
import diff_match_patch as dmp_module
|
||||
dmp = dmp_module.diff_match_patch()
|
||||
modified = "Line 1\nModified Line 2\nLine 3\n"
|
||||
patches = dmp.patch_make(original, modified)
|
||||
diff_text = dmp.patch_toText(patches)
|
||||
|
||||
result = apply_diff_fn(
|
||||
path="multiline.txt",
|
||||
diff_text=diff_text,
|
||||
**mock_workspace
|
||||
)
|
||||
|
||||
assert result["success"] is True
|
||||
assert result["all_successful"] is True
|
||||
assert test_file.read_text() == modified
|
||||
|
||||
def test_apply_diff_invalid_patch(self, apply_diff_fn, mock_workspace, mock_secure_path, tmp_path):
|
||||
"""Applying an invalid diff handles gracefully."""
|
||||
test_file = tmp_path / "test.txt"
|
||||
original_content = "Original content"
|
||||
test_file.write_text(original_content)
|
||||
|
||||
# Invalid diff text
|
||||
result = apply_diff_fn(
|
||||
path="test.txt",
|
||||
diff_text="invalid diff format",
|
||||
**mock_workspace
|
||||
)
|
||||
|
||||
# Should either error or show no patches applied
|
||||
if "error" not in result:
|
||||
assert result.get("patches_applied", 0) == 0
|
||||
# File should remain unchanged
|
||||
assert test_file.read_text() == original_content
|
||||
|
||||
|
||||
class TestApplyPatchTool:
|
||||
"""Tests for apply_patch tool."""
|
||||
|
||||
@pytest.fixture
|
||||
def apply_patch_fn(self, mcp):
|
||||
from aden_tools.tools.file_system_toolkits.apply_patch import register_tools
|
||||
register_tools(mcp)
|
||||
return mcp._tool_manager._tools["apply_patch"].fn
|
||||
|
||||
def test_apply_patch_file_not_found(self, apply_patch_fn, mock_workspace, mock_secure_path):
|
||||
"""Applying patch to non-existent file returns error."""
|
||||
result = apply_patch_fn(
|
||||
path="nonexistent.txt",
|
||||
patch_text="some patch",
|
||||
**mock_workspace
|
||||
)
|
||||
|
||||
assert "error" in result
|
||||
assert "not found" in result["error"].lower()
|
||||
|
||||
def test_apply_patch_successful(self, apply_patch_fn, mock_workspace, mock_secure_path, tmp_path):
|
||||
"""Applying a valid patch successfully modifies the file."""
|
||||
test_file = tmp_path / "patch_test.txt"
|
||||
test_file.write_text("Hello World")
|
||||
|
||||
# Create a simple patch using diff_match_patch format
|
||||
import diff_match_patch as dmp_module
|
||||
dmp = dmp_module.diff_match_patch()
|
||||
patches = dmp.patch_make("Hello World", "Hello Python")
|
||||
patch_text = dmp.patch_toText(patches)
|
||||
|
||||
result = apply_patch_fn(
|
||||
path="patch_test.txt",
|
||||
patch_text=patch_text,
|
||||
**mock_workspace
|
||||
)
|
||||
|
||||
assert result["success"] is True
|
||||
assert result["all_successful"] is True
|
||||
assert result["patches_applied"] > 0
|
||||
assert test_file.read_text() == "Hello Python"
|
||||
|
||||
def test_apply_patch_multiline(self, apply_patch_fn, mock_workspace, mock_secure_path, tmp_path):
|
||||
"""Applying patch to multiline content works correctly."""
|
||||
test_file = tmp_path / "multiline.txt"
|
||||
original = "Line 1\nLine 2\nLine 3\n"
|
||||
test_file.write_text(original)
|
||||
|
||||
import diff_match_patch as dmp_module
|
||||
dmp = dmp_module.diff_match_patch()
|
||||
modified = "Line 1\nModified Line 2\nLine 3\n"
|
||||
patches = dmp.patch_make(original, modified)
|
||||
patch_text = dmp.patch_toText(patches)
|
||||
|
||||
result = apply_patch_fn(
|
||||
path="multiline.txt",
|
||||
patch_text=patch_text,
|
||||
**mock_workspace
|
||||
)
|
||||
|
||||
assert result["success"] is True
|
||||
assert result["all_successful"] is True
|
||||
assert test_file.read_text() == modified
|
||||
|
||||
def test_apply_patch_invalid_patch(self, apply_patch_fn, mock_workspace, mock_secure_path, tmp_path):
|
||||
"""Applying an invalid patch handles gracefully."""
|
||||
test_file = tmp_path / "test.txt"
|
||||
original_content = "Original content"
|
||||
test_file.write_text(original_content)
|
||||
|
||||
# Invalid patch text
|
||||
result = apply_patch_fn(
|
||||
path="test.txt",
|
||||
patch_text="invalid patch format",
|
||||
**mock_workspace
|
||||
)
|
||||
|
||||
# Should either error or show no patches applied
|
||||
if "error" not in result:
|
||||
assert result.get("patches_applied", 0) == 0
|
||||
# File should remain unchanged
|
||||
assert test_file.read_text() == original_content
|
||||
|
||||
def test_apply_patch_multiple_changes(self, apply_patch_fn, mock_workspace, mock_secure_path, tmp_path):
|
||||
"""Applying patch with multiple changes works correctly."""
|
||||
test_file = tmp_path / "complex.txt"
|
||||
original = "Function foo() {\n return 42;\n}\n"
|
||||
test_file.write_text(original)
|
||||
|
||||
import diff_match_patch as dmp_module
|
||||
dmp = dmp_module.diff_match_patch()
|
||||
modified = "Function bar() {\n return 100;\n}\n"
|
||||
patches = dmp.patch_make(original, modified)
|
||||
patch_text = dmp.patch_toText(patches)
|
||||
|
||||
result = apply_patch_fn(
|
||||
path="complex.txt",
|
||||
patch_text=patch_text,
|
||||
**mock_workspace
|
||||
)
|
||||
|
||||
assert result["success"] is True
|
||||
assert result["all_successful"] is True
|
||||
assert test_file.read_text() == modified
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
"command": "python",
|
||||
"args": ["-m", "framework.mcp.agent_builder_server"],
|
||||
"cwd": "/home/timothy/aden/worker-bee"
|
||||
},
|
||||
"aden-tools": {
|
||||
"command": "python",
|
||||
"args": ["-m", "aden_tools.mcp_server", "--stdio"],
|
||||
"cwd": "/Users/guangjitang/acho/hive/aden-tools"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user