From 5b63455315fc71b2ae8646d956b07445652e2e9d Mon Sep 17 00:00:00 2001 From: Osamaali313 <86572800+Osamaali313@users.noreply.github.com> Date: Mon, 15 Jun 2026 23:10:19 +0300 Subject: [PATCH 1/2] fix(utils): check nearest existing ancestor for output-dir writeability `build_output_path` creates the output directory with `mkdir(parents=True, exist_ok=True)`, which may create several missing levels. But its `is_file_writeable` guard only checked the *immediate* parent: for a multi-level new path (e.g. `/a/b/c` where `/a` does not exist), `os.access(parent, W_OK)` is False, so it raised "Directory (...) is not writeable" even though `mkdir(parents=True)` would create it fine. Check the writeability of the nearest *existing* ancestor instead -- the level `mkdir(parents=True)` actually starts writing into. Single-level new paths (immediate parent exists) behave exactly as before. Adds tests for a multi-level `is_file_writeable` path and for `build_output_path` creating a nested directory. --- minimax_mcp/utils.py | 10 ++++++++-- tests/test_utils.py | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/minimax_mcp/utils.py b/minimax_mcp/utils.py index 0cfdfdf..1303c0f 100644 --- a/minimax_mcp/utils.py +++ b/minimax_mcp/utils.py @@ -12,8 +12,14 @@ def is_file_writeable(path: Path) -> bool: if path.exists(): return os.access(path, os.W_OK) - parent_dir = path.parent - return os.access(parent_dir, os.W_OK) + # The path does not exist yet. Callers create it with + # ``mkdir(parents=True)``, which may create several missing levels, so + # check writeability of the nearest existing ancestor rather than only the + # immediate parent (which is itself missing for a multi-level new path). + for ancestor in path.parents: + if ancestor.exists(): + return os.access(ancestor, os.W_OK) + return False def build_output_file( diff --git a/tests/test_utils.py b/tests/test_utils.py index 13691a1..30503b7 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -16,6 +16,20 @@ def test_is_file_writeable(): temp_path = Path(temp_dir) assert is_file_writeable(temp_path) is True assert is_file_writeable(temp_path / "nonexistent.txt") is True + # Multi-level new path: the immediate parent does not exist yet, but + # mkdir(parents=True) can create it, so it must be reported writeable + # (regression: only the immediate parent used to be checked). + assert is_file_writeable(temp_path / "a" / "b" / "c") is True + + +def test_build_output_path_creates_nested_dir(): + # A nested, not-yet-existing output directory must be created rather than + # rejected as "not writeable". + with tempfile.TemporaryDirectory() as temp_dir: + result = build_output_path("a/b/c", temp_dir) + assert result == Path(temp_dir) / "a" / "b" / "c" + assert result.exists() + assert result.is_dir() def test_make_output_file(): From e5f0ab82f2a37886210f0e005a86367337ff0751 Mon Sep 17 00:00:00 2001 From: Osamaali313 <86572800+Osamaali313@users.noreply.github.com> Date: Mon, 15 Jun 2026 23:30:02 +0300 Subject: [PATCH 2/2] fix(utils): require write+execute on the existing ancestor directory Address review: creating a child entry in a directory needs both write and search (execute) permission, so check os.W_OK | os.X_OK on the nearest existing ancestor (always a directory) rather than os.W_OK alone. No change on platforms where directories are always searchable; on POSIX it avoids a false positive for a write-but-not-executable directory. --- minimax_mcp/utils.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/minimax_mcp/utils.py b/minimax_mcp/utils.py index 1303c0f..acee8dc 100644 --- a/minimax_mcp/utils.py +++ b/minimax_mcp/utils.py @@ -14,11 +14,14 @@ def is_file_writeable(path: Path) -> bool: return os.access(path, os.W_OK) # The path does not exist yet. Callers create it with # ``mkdir(parents=True)``, which may create several missing levels, so - # check writeability of the nearest existing ancestor rather than only the - # immediate parent (which is itself missing for a multi-level new path). + # check the nearest existing ancestor (the directory ``mkdir`` will + # actually create entries in) rather than only the immediate parent (which + # is itself missing for a multi-level new path). Creating a child in a + # directory needs both write and search (execute) permission, so check + # ``W_OK | X_OK``. for ancestor in path.parents: if ancestor.exists(): - return os.access(ancestor, os.W_OK) + return os.access(ancestor, os.W_OK | os.X_OK) return False