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