Files
fullstack-engineer fb0a35f22c
CI / Canvas Deploy Reminder (push) Blocked by required conditions
CI / all-required (push) Blocked by required conditions
Block internal-flavored paths / Block forbidden paths (push) Successful in 26s
E2E Chat / detect-changes (push) Waiting to run
E2E Chat / E2E Chat (push) Blocked by required conditions
publish-runtime-autobump / pr-validate (push) Waiting to run
Runtime PR-Built Compatibility / PR-built wheel + import smoke (push) Blocked by required conditions
Secret scan / Scan diff for credential-shaped strings (push) Waiting to run
MCP Stdio Transport Regression / MCP stdio with regular-file stdout (push) Successful in 2m3s
CI / Detect changes (push) Successful in 2m33s
E2E API Smoke Test / detect-changes (push) Successful in 2m43s
CI / Shellcheck (E2E scripts) (push) Successful in 13s
Handlers Postgres Integration / detect-changes (push) Successful in 2m44s
publish-runtime-autobump / bump-and-tag (push) Failing after 2m44s
Runtime PR-Built Compatibility / detect-changes (push) Successful in 2m40s
E2E API Smoke Test / E2E API Smoke Test (push) Successful in 17s
Handlers Postgres Integration / Handlers Postgres Integration (push) Successful in 6m37s
CI / Python Lint & Test (push) Successful in 8m14s
publish-runtime / cascade (push) Has been skipped
CI / Canvas (Next.js) (push) Successful in 19m35s
publish-runtime / publish (push) Failing after 2m1s
CI / Platform (Go) (push) Failing after 20m43s
feat(workspace): add get_runtime_identity + update_agent_card MCP tools (T4 follow-up; relocated from runtime mirror PR#17) (#1240)
Co-authored-by: Molecule AI · fullstack-engineer <fullstack-engineer@agents.moleculesai.app>
Co-committed-by: Molecule AI · fullstack-engineer <fullstack-engineer@agents.moleculesai.app>
2026-05-15 22:37:55 +00:00
..

Platform tool registry

Single source of truth for every tool the platform exposes to agents (A2A delegation, hierarchical memory, broadcast, introspection).

Why this exists

Pre-#2240, three places independently declared each tool:

  1. MCP server (workspace/a2a_mcp_server.py) — the TOOLS JSON list
  2. LangChain @tool wrappers (workspace/builtin_tools/{delegation,memory}.py)
  3. Agent-facing system-prompt docs (workspace/executor_helpers.py)

Adding a tool to one and forgetting the others happened repeatedly. The canonical case: send_message_to_user was registered in MCP TOOLS but the executor_helpers doc string never mentioned it, so agents saw the tool as available but had no usage guidance — a silent capability regression.

What the registry does

registry.py defines each tool ONCE as a frozen ToolSpec:

ToolSpec(
    name="delegate_task",
    short="Delegate a task to a peer workspace via A2A and WAIT for the response.",
    when_to_use="Use for QUICK questions and small sub-tasks where you can afford to wait inline...",
    input_schema={...},          # JSON Schema, consumed by MCP server
    impl=tool_delegate_task,     # the actual coroutine
    section="a2a",               # which prompt section it belongs to
)

Adapters consume specs; no hardcoded names anywhere else:

  • MCP server builds its TOOLS list from _PLATFORM_TOOL_SPECS at import time
  • LangChain @tool wrappers read name=spec.name from the registry
  • Doc generator (executor_helpers._render_section()) produces the system-prompt block from spec.short (bullet) + spec.when_to_use (heading + paragraph)

CLI subprocess block — special case

Non-MCP runtimes (ollama, custom subprocess adapters) use a separate hand-maintained block in executor_helpers._A2A_INSTRUCTIONS_CLI because the CLI subcommand vocabulary (peers, delegate, status, info) differs from the MCP tool names (list_peers, delegate_task, etc.). Auto-generation would lose the readable invocation syntax.

Alignment is enforced via _CLI_A2A_COMMAND_KEYWORDS (in executor_helpers.py): every a2a-section spec must be keyed there with either a CLI subcommand keyword OR an explicit None if the tool is intentionally not exposed via subprocess (e.g. send_message_to_user because its structured attachments field doesn't survive positional-arg shell invocation).

Tests that catch drift

workspace/tests/test_platform_tools.py:

Test What it catches
test_mcp_server_registers_every_registry_tool MCP TOOLS list out of sync with registry
test_mcp_tool_descriptions_match_registry_short hand-edited MCP description that drifted
test_mcp_tool_input_schemas_match_registry schema duplicated in server file
test_a2a_instructions_text_includes_every_a2a_tool doc generator missed a tool
test_old_pre_rename_names_not_present_in_docs stale name leaked back in
test_a2a_mcp_instructions_match_snapshot rendered shape (bullet ordering, headings, footers) drifted
test_a2a_cli_instructions_match_snapshot CLI block edited in a way that changes shape
test_hma_instructions_match_snapshot HMA section drifted
test_cli_keyword_mapping_covers_every_a2a_tool tool added to registry without a CLI mapping decision
test_cli_keyword_substrings_appear_in_cli_block CLI keyword in the mapping but missing from the doc block

The snapshot files at workspace/tests/snapshots/*.txt are LF-pinned in .gitattributes so a Windows contributor with core.autocrlf=true doesn't get mysterious test failures.

Adding a new tool

  1. Append a ToolSpec(...) to TOOLS in registry.py.
  2. Add the LangChain @tool wrapper in workspace/builtin_tools/ (the wrapper body just calls spec.impl).
  3. Update _CLI_A2A_COMMAND_KEYWORDS in executor_helpers.py — set the value to the CLI subcommand keyword, or to None if the tool isn't exposed via the subprocess interface.
  4. Regenerate snapshots — see the comment block at the top of workspace/tests/test_platform_tools.py for the one-liner.
  5. Run pytest workspace/tests/test_platform_tools.py --no-cov.

Renaming a tool

Edit name in registry.py only. Then:

  1. The MCP TOOLS list rebuilds automatically.
  2. The doc generator regenerates automatically (snapshots will fail the diff — regenerate them).
  3. Search workspace/ for the old literal in case a non-adapter consumer (tests, plugin code) hardcoded the old name; update those.
  4. Update any _CLI_A2A_COMMAND_KEYWORDS key + the literal substring in _A2A_INSTRUCTIONS_CLI if applicable.

Removing a tool

Delete the ToolSpec and the _CLI_A2A_COMMAND_KEYWORDS key. Adapters and doc generators stop registering it automatically; the structural tests prevent stale references from surviving.