fix(runtime): resolve a2a_mcp_server.py path from wheel install location

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) <noreply@anthropic.com>
This commit is contained in:
Hongming Wang 2026-04-27 11:15:06 -07:00
parent 9532890f04
commit 203a4f0f91
2 changed files with 23 additions and 1 deletions

View File

@ -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

View File

@ -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"