Merge pull request #6 from Molecule-AI/fix/507-mcp-server-path-absolute-imports
fix: resolve MCP server path from package + absolute imports (2nd half of #507)
This commit is contained in:
commit
ceeec69c8c
@ -10,7 +10,7 @@ import uuid
|
||||
|
||||
import httpx
|
||||
|
||||
from platform_auth import auth_headers
|
||||
from molecule_runtime.platform_auth import auth_headers
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -17,7 +17,23 @@ import json
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from a2a_tools import (
|
||||
# Absolute imports so the installed-package location works too. Previously
|
||||
# the script relied on `/app` being on sys.path (legacy template layout),
|
||||
# which broke silently when the current template dropped that copy —
|
||||
# claude-code then initialised with zero MCP tools and every agent
|
||||
# reported "search_memory / commit_memory / list_peers / delegate_task
|
||||
# not available" (second half of #507). The /app launch path is still
|
||||
# supported via a sys.path shim below for anyone running the script
|
||||
# with `python /app/a2a_mcp_server.py`.
|
||||
import os as _os
|
||||
if __package__ in (None, ""):
|
||||
# Running as a script (python path/to/a2a_mcp_server.py) — put the
|
||||
# package root on sys.path so the absolute imports below resolve.
|
||||
_pkg_root = _os.path.dirname(_os.path.dirname(_os.path.abspath(__file__)))
|
||||
if _pkg_root not in sys.path:
|
||||
sys.path.insert(0, _pkg_root)
|
||||
|
||||
from molecule_runtime.a2a_tools import (
|
||||
tool_check_task_status,
|
||||
tool_commit_memory,
|
||||
tool_delegate_task,
|
||||
@ -32,7 +48,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
# Re-export constants and client functions so existing imports
|
||||
# (e.g. tests that do `import a2a_mcp_server`) still work.
|
||||
from a2a_client import ( # noqa: F401, E402
|
||||
from molecule_runtime.a2a_client import ( # noqa: F401, E402
|
||||
PLATFORM_URL,
|
||||
WORKSPACE_ID,
|
||||
_A2A_ERROR_PREFIX,
|
||||
@ -42,7 +58,7 @@ from a2a_client import ( # noqa: F401, E402
|
||||
get_workspace_info,
|
||||
send_a2a_message,
|
||||
)
|
||||
from a2a_tools import report_activity # noqa: F401, E402
|
||||
from molecule_runtime.a2a_tools import report_activity # noqa: F401, E402
|
||||
|
||||
# --- Tool definitions (schemas) ---
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ import uuid
|
||||
|
||||
import httpx
|
||||
|
||||
from a2a_client import (
|
||||
from molecule_runtime.a2a_client import (
|
||||
PLATFORM_URL,
|
||||
WORKSPACE_ID,
|
||||
_A2A_ERROR_PREFIX,
|
||||
@ -24,7 +24,7 @@ def _auth_headers_for_heartbeat() -> dict[str, str]:
|
||||
"""Return Phase 30.1 auth headers; tolerate platform_auth being absent
|
||||
in older installs (e.g. during rolling upgrade)."""
|
||||
try:
|
||||
from platform_auth import auth_headers
|
||||
from molecule_runtime.platform_auth import auth_headers
|
||||
return auth_headers()
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
@ -14,7 +14,7 @@ import os
|
||||
|
||||
import httpx
|
||||
|
||||
from platform_auth import auth_headers
|
||||
from molecule_runtime.platform_auth import auth_headers
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -36,7 +36,10 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
WORKSPACE_MOUNT = "/workspace"
|
||||
CONFIG_MOUNT = "/configs"
|
||||
DEFAULT_MCP_SERVER_PATH = "/app/a2a_mcp_server.py"
|
||||
# Legacy template layout copied a2a_mcp_server.py into /app. Current
|
||||
# templates don't — the script lives inside the installed runtime package.
|
||||
# Kept as a last-resort fallback only.
|
||||
LEGACY_MCP_SERVER_PATH = "/app/a2a_mcp_server.py"
|
||||
DEFAULT_DELEGATION_RESULTS_FILE = "/tmp/delegation_results.jsonl"
|
||||
PLATFORM_HTTP_TIMEOUT_S = 5.0
|
||||
MEMORY_RECALL_LIMIT = 10
|
||||
@ -44,12 +47,38 @@ MEMORY_CONTENT_MAX_CHARS = 200
|
||||
BRIEF_SUMMARY_MAX_LEN = 80
|
||||
|
||||
|
||||
def _default_mcp_server_path() -> str:
|
||||
"""Resolve the installed ``a2a_mcp_server.py`` path from the package.
|
||||
|
||||
Fix for the secondary half of #507: when agents started producing text
|
||||
again (after the CRLF hook fix), the a2a MCP server failed to start
|
||||
because the hard-coded ``/app/a2a_mcp_server.py`` doesn't exist in the
|
||||
current workspace-template image — the template's Dockerfile copies
|
||||
``adapter.py`` into /app but not the MCP server script. claude-code
|
||||
then initialised with zero MCP tools, so every agent reported
|
||||
"search_memory / commit_memory / list_peers / delegate_task not
|
||||
available" on the first post-fix pulse.
|
||||
|
||||
Resolve the path from the package itself so it always points at the
|
||||
real installed script, regardless of which template layout imported
|
||||
the runtime. Legacy /app/ path kept only as last-resort fallback.
|
||||
"""
|
||||
try:
|
||||
from molecule_runtime import a2a_mcp_server as _mcp_mod
|
||||
path = getattr(_mcp_mod, "__file__", None)
|
||||
if path and os.path.isfile(path):
|
||||
return path
|
||||
except Exception:
|
||||
pass
|
||||
return LEGACY_MCP_SERVER_PATH
|
||||
|
||||
|
||||
def get_mcp_server_path() -> str:
|
||||
"""Return the path to the stdio MCP server script.
|
||||
|
||||
Overridable via A2A_MCP_SERVER_PATH for tests and non-default layouts.
|
||||
"""
|
||||
return os.environ.get("A2A_MCP_SERVER_PATH", DEFAULT_MCP_SERVER_PATH)
|
||||
return os.environ.get("A2A_MCP_SERVER_PATH", _default_mcp_server_path())
|
||||
|
||||
|
||||
# ========================================================================
|
||||
|
||||
@ -17,7 +17,7 @@ from pathlib import Path
|
||||
|
||||
import httpx
|
||||
|
||||
from platform_auth import auth_headers
|
||||
from molecule_runtime.platform_auth import auth_headers
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -39,7 +39,7 @@ from initial_prompt import (
|
||||
mark_initial_prompt_attempted,
|
||||
resolve_initial_prompt_marker,
|
||||
)
|
||||
from platform_auth import auth_headers
|
||||
from molecule_runtime.platform_auth import auth_headers
|
||||
|
||||
|
||||
def get_machine_ip() -> str: # pragma: no cover
|
||||
|
||||
Loading…
Reference in New Issue
Block a user