From 203a4f0f91319c53d3a8e2c672118438020672f2 Mon Sep 17 00:00:00 2001 From: Hongming Wang Date: Mon, 27 Apr 2026 11:15:06 -0700 Subject: [PATCH] fix(runtime): resolve a2a_mcp_server.py path from wheel install location MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DEFAULT_MCP_SERVER_PATH was hardcoded to /app/a2a_mcp_server.py, which was correct under the pre-#87 monolithic-template Docker layout where the workspace/ tree was COPY'd into /app/. After the universal-runtime refactor (#87, #117), workspace modules ship inside the molecule-ai-workspace-runtime wheel under site-packages/molecule_runtime/, while /app/ now holds only template-specific files (adapter.py + the runtime-native executor for that template). Net effect: in every workspace built since the wheel cutover, Claude Code SDK's mcp_servers={"a2a": {"command": python, "args": ["/app/a2a_mcp_server.py"]}} pointed at a missing file. The subprocess launch failed silently, the SDK registered zero MCP tools, and the agent's list_peers / delegate_task / a2a_send_message / a2a_send_signal all disappeared. Symptom observed today: Design Director said "I tried to reach the perf auditor via the inter-agent MCP tools (list_peers, delegate_task) but those tools didn't resolve in this environment" and fell back to running the audit itself with WebFetch. Why this slipped through E2E: the priority-runtimes harness sends a single message and verifies a reply — it does not exercise inter-agent delegation, so the missing MCP tools are invisible at that layer. Fix: resolve the path relative to executor_helpers.py via __file__, which tracks wherever the wheel is installed (site-packages today, anywhere else tomorrow). The A2A_MCP_SERVER_PATH env override is preserved for tests / non-default layouts. Regression test: assert os.path.exists(DEFAULT_MCP_SERVER_PATH) so any future move of a2a_mcp_server.py out of the package directory fails at unit-test time instead of silently disabling delegation in production. Co-Authored-By: Claude Opus 4.7 (1M context) --- workspace/executor_helpers.py | 11 ++++++++++- workspace/tests/test_executor_helpers.py | 13 +++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/workspace/executor_helpers.py b/workspace/executor_helpers.py index 35c8e7ca..95e73857 100644 --- a/workspace/executor_helpers.py +++ b/workspace/executor_helpers.py @@ -48,7 +48,16 @@ logger = logging.getLogger(__name__) WORKSPACE_MOUNT = "/workspace" CONFIG_MOUNT = "/configs" -DEFAULT_MCP_SERVER_PATH = "/app/a2a_mcp_server.py" +# Resolved relative to this module so it tracks the wheel install +# location. The hardcoded "/app/a2a_mcp_server.py" was correct under +# the pre-#87 monolithic-template layout, but post-universal-runtime +# the file ships inside the molecule-ai-workspace-runtime wheel at +# site-packages/molecule_runtime/, while /app/ now holds only +# template-specific modules (adapter.py + the runtime-native executor). +# Stale path → Claude Code SDK silently fails to spawn the MCP +# subprocess → list_peers / delegate_task / a2a_send_message all +# disappear from the agent's toolset. +DEFAULT_MCP_SERVER_PATH = str(Path(__file__).parent / "a2a_mcp_server.py") DEFAULT_DELEGATION_RESULTS_FILE = "/tmp/delegation_results.jsonl" PLATFORM_HTTP_TIMEOUT_S = 5.0 MEMORY_RECALL_LIMIT = 10 diff --git a/workspace/tests/test_executor_helpers.py b/workspace/tests/test_executor_helpers.py index d9dd35fa..688f6044 100644 --- a/workspace/tests/test_executor_helpers.py +++ b/workspace/tests/test_executor_helpers.py @@ -23,6 +23,7 @@ Covers 100% of the public surface: from __future__ import annotations import json +import os from pathlib import Path from types import SimpleNamespace from unittest.mock import AsyncMock, MagicMock, patch @@ -88,6 +89,18 @@ def test_get_mcp_server_path_default(monkeypatch): assert get_mcp_server_path() == DEFAULT_MCP_SERVER_PATH +def test_get_mcp_server_path_default_resolves_to_existing_file(): + # Locks in the wheel-relative resolution: if a future refactor moves + # a2a_mcp_server.py out of the package directory or breaks the + # __file__-based lookup, Claude Code SDK silently fails to spawn the + # MCP subprocess and inter-agent tools (list_peers, delegate_task) + # vanish at runtime. This assertion catches that at unit-test time. + assert os.path.exists(DEFAULT_MCP_SERVER_PATH), ( + f"DEFAULT_MCP_SERVER_PATH points at a missing file: " + f"{DEFAULT_MCP_SERVER_PATH}" + ) + + def test_get_mcp_server_path_env_override(monkeypatch): monkeypatch.setenv("A2A_MCP_SERVER_PATH", "/custom/mcp.py") assert get_mcp_server_path() == "/custom/mcp.py"