From c44f3adc11d52dc4ba3a8acb7206e72bc8be6413 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Fri, 19 Jun 2026 19:50:20 -0400 Subject: [PATCH] fix(mcp): context-aware project_root detection (cwd + script_root fallback) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MCP server's project_root was hardcoded to the script's parent dir. When opencode launches the MCP from a sibling clone (e.g., main repo launches the tier2 clone's MCP via the hardcoded path in main repo's opencode.json), the MCP only allowed paths inside the tier2 clone — even when the user was working in the main repo. Fix: use os.getcwd() as the primary project_root (the user's actual working dir) and fall back to the script's home. Read mcp_paths.toml from cwd first, then script home. This way: - MCP launched from tier2 + cwd=main -> allows [main, tier2] - MCP launched from main + cwd=main -> allows [main] - MCP launched from tier2 + cwd=tier2 -> allows [tier2] (preserves sandbox) Takes effect after the next opencode restart. --- scripts/mcp_server.py | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/scripts/mcp_server.py b/scripts/mcp_server.py index 9bfcd286..83f10d41 100644 --- a/scripts/mcp_server.py +++ b/scripts/mcp_server.py @@ -79,16 +79,33 @@ async def call_tool(name: str, arguments: dict) -> list[TextContent]: return [TextContent(type="text", text=f"ERROR: {e}")] async def main() -> None: - project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) + # Robust context detection: project_root is os.getcwd() (the directory + # the user is actually working in), not just where the script lives. + # The script's own home is a secondary fallback. This handles the case + # where opencode launches the MCP from a sibling clone (e.g., main repo + # launches the tier2 clone's MCP via a hardcoded path in opencode.json) + # — the MCP should allow access to the user's working directory too. + cwd = os.getcwd() + script_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) - extra_dirs = [project_root] - mcp_paths_toml = os.path.join(project_root, "mcp_paths.toml") - if os.path.exists(mcp_paths_toml): - import tomllib - with open(mcp_paths_toml, "rb") as f: - config = tomllib.load(f) - allowed = config.get("allowed_paths", {}).get("extra_dirs", []) - extra_dirs.extend(allowed) + extra_dirs: list[str] = [] + for d in (cwd, script_root): + if d and d not in extra_dirs: + extra_dirs.append(d) + + # Read mcp_paths.toml from cwd first (the user's working dir takes + # precedence), then fall back to the script's home dir. + for mcp_paths_toml in (os.path.join(cwd, "mcp_paths.toml"), + os.path.join(script_root, "mcp_paths.toml")): + if os.path.exists(mcp_paths_toml): + import tomllib + with open(mcp_paths_toml, "rb") as f: + config = tomllib.load(f) + allowed = config.get("allowed_paths", {}).get("extra_dirs", []) + for p in allowed: + if p not in extra_dirs: + extra_dirs.append(p) + break mcp_client.configure([], extra_base_dirs=extra_dirs) async with stdio_server() as (read_stream, write_stream):