Source code for ralph.mcp.transport.agy
"""Google Anti Gravity (AGY) transport helpers.
This module provides AGY-specific MCP transport helpers.
Research-confirmed facts:
- Executable: agy
- Print flag: --print
- Yolo flag: --dangerously-skip-permissions
- MCP config path: ~/.gemini/antigravity-cli/mcp_config.json
- HTTP JSON key: serverUrl
- Output format: plain text (not NDJSON) - uses JsonParserType.GENERIC
Ralph reads existing AGY upstream servers from the user config files at
~/.gemini/antigravity-cli/mcp_config.json and workspace .agents/mcp_config.json.
The agy_mcp_config() helper builds the AGY-native JSON payload for Ralph's MCP
endpoint using AGY's serverUrl field.
"""
from __future__ import annotations
import json
from collections.abc import Iterator, Mapping
from contextlib import contextmanager
from pathlib import Path
from typing import cast
from ralph.mcp.tools.names import RALPH_MCP_SERVER_NAME
from ralph.mcp.transport.common import _load_mcpservers_from_paths
from ralph.mcp.upstream.config import UpstreamMcpServer, normalize_upstream_mcp_servers
# AGY home config directory name within its default config root
_AGY_HOME_SUBDIR = "antigravity-cli"
[docs]
def agy_mcp_config(endpoint: str) -> str:
"""Return the AGY MCP JSON config string pointing to the given endpoint.
Args:
endpoint: The MCP server HTTP endpoint URL.
Returns:
JSON string with mcpServers containing the Ralph entry with serverUrl key.
"""
config_payload = {
"mcpServers": {
RALPH_MCP_SERVER_NAME: {
"serverUrl": endpoint,
}
}
}
return json.dumps(config_payload, separators=(",", ":"))
[docs]
@contextmanager
def agy_workspace_mcp_endpoint(workspace_path: Path, endpoint: str) -> Iterator[None]:
"""Write a run-scoped Ralph-only MCP config for AGY and restore it after exit."""
config_path = workspace_path / ".agents" / "mcp_config.json"
original_bytes = config_path.read_bytes() if config_path.is_file() else None
config_payload = {
"mcpServers": {
RALPH_MCP_SERVER_NAME: {
"serverUrl": endpoint,
}
}
}
try:
config_path.parent.mkdir(parents=True, exist_ok=True)
config_path.write_text(json.dumps(config_payload, indent=2), encoding="utf-8")
yield
finally:
if original_bytes is None:
if config_path.is_file():
config_path.unlink()
else:
config_path.write_bytes(original_bytes)
def _normalize_agy_server_entry(name: str, entry: object) -> tuple[str, object] | None:
"""Normalize an AGY server entry to Ralph's expected format.
AGY uses 'serverUrl' for HTTP servers; Ralph's normalize_upstream_mcp_servers
expects 'url'. This helper converts 'serverUrl' -> 'url' so the standard
normalizer can process AGY config entries.
Args:
name: Server name.
entry: Raw server entry dict from mcpServers.
Returns:
Tuple of (name, normalized_entry) if valid, None if skipped.
"""
if not isinstance(entry, Mapping):
return None
casted = cast("dict[str, object]", entry)
# AGY uses serverUrl; Ralph normalizer expects url
if "serverUrl" in casted and "url" not in casted:
casted = {**casted, "url": casted["serverUrl"]}
return name, casted
[docs]
def load_existing_agy_upstream_servers(
workspace_path: Path | None = None,
) -> tuple[UpstreamMcpServer, ...]:
"""Read AGY's MCP config files and return any upstream MCP servers found.
Args:
workspace_path: Optional workspace path for workspace-level AGY config.
Returns:
Tuple of UpstreamMcpServer objects found in AGY config files.
"""
return normalize_upstream_mcp_servers(
_load_mcpservers_from_paths(
_agy_mcp_config_paths(workspace_path), _normalize_agy_server_entry
)
)
def _agy_mcp_config_paths(workspace_path: Path | None) -> tuple[Path, ...]:
"""Return the AGY MCP config file paths to check.
Order: workspace-level .agents/mcp_config.json first (if workspace_path provided),
then global ~/.gemini/antigravity-cli/mcp_config.json.
"""
workspace_paths: tuple[Path, ...] = ()
if workspace_path is not None:
workspace_paths = (
workspace_path / ".agents" / "mcp_config.json",
)
return (
*workspace_paths,
Path.home() / ".gemini" / _AGY_HOME_SUBDIR / "mcp_config.json",
)
__all__ = [
"agy_mcp_config",
"agy_workspace_mcp_endpoint",
"load_existing_agy_upstream_servers",
]