Source code for ralph.mcp.tool_contract
"""Shared MCP tool-surface contract helpers.
This module owns the canonical Ralph MCP tool naming contract so startup,
runtime, prompts, and provider integrations all derive from the same rules.
"""
from __future__ import annotations
from typing import TYPE_CHECKING
from ralph.mcp.protocol.session import AgentSession
from ralph.mcp.tools.bridge import build_ralph_tool_registry
from ralph.mcp.tools.names import RALPH_MCP_SERVER_NAME, RalphToolName, claude_tool_name_prefix
from ralph.workspace.memory import MemoryWorkspace
if TYPE_CHECKING:
from collections.abc import Iterable
from ralph.mcp.upstream.registry import UpstreamRegistry
[docs]
def claude_alias_for_tool_name(tool_name: str) -> str | None:
"""Return the strict-MCP alias for a canonical Ralph tool name."""
try:
return str(RalphToolName(tool_name).as_claude_alias(server_name=RALPH_MCP_SERVER_NAME))
except ValueError:
return None
[docs]
def canonical_tool_name(tool_name: str) -> str:
"""Collapse a raw or aliased tool name to its canonical raw Ralph name."""
prefix = claude_tool_name_prefix(server_name=RALPH_MCP_SERVER_NAME)
if tool_name.startswith(prefix):
raw_name = tool_name[len(prefix) :]
try:
return str(RalphToolName(raw_name))
except ValueError:
return tool_name
return tool_name
[docs]
def canonicalize_tool_names(tool_names: Iterable[str]) -> tuple[str, ...]:
"""Return deduped canonical raw Ralph tool names in input order."""
canonical: list[str] = []
seen: set[str] = set()
for tool_name in tool_names:
raw_name = canonical_tool_name(tool_name)
if raw_name in seen:
continue
seen.add(raw_name)
canonical.append(raw_name)
return tuple(canonical)
[docs]
def expand_tool_names_with_aliases(tool_names: Iterable[str]) -> list[str]:
"""Return each canonical tool name plus its strict-MCP alias, deduped."""
expanded: list[str] = []
for tool_name in canonicalize_tool_names(tool_names):
if tool_name not in expanded:
expanded.append(tool_name)
alias = claude_alias_for_tool_name(tool_name)
if alias is not None and alias not in expanded:
expanded.append(alias)
return expanded
[docs]
def visible_owned_tool_names(
session: object,
workspace: object,
*,
upstream_registry: UpstreamRegistry | None = None,
include_aliases: bool = True,
) -> list[str]:
"""Return the live visible Ralph-owned tool surface for a session."""
registry = build_ralph_tool_registry(
session, workspace, upstream_registry=upstream_registry, mcp_config=None
)
tool_names = [definition.name for definition in registry.list_definitions()]
if not include_aliases:
return list(canonicalize_tool_names(tool_names))
return expand_tool_names_with_aliases(tool_names)
[docs]
def visible_tool_names_for_capabilities(capability_ids: Iterable[str], *, drain: str) -> list[str]:
"""Project capabilities onto the real runtime registry tool surface."""
session = AgentSession(
session_id=f"prompt-{drain}",
run_id=f"prompt-{drain}",
drain=drain,
capabilities=set(capability_ids),
)
return visible_owned_tool_names(session, MemoryWorkspace(), include_aliases=False)
__all__ = [
"canonical_tool_name",
"canonicalize_tool_names",
"claude_alias_for_tool_name",
"expand_tool_names_with_aliases",
"visible_owned_tool_names",
"visible_tool_names_for_capabilities",
]