Skip to main content

Module a2a

Module a2a 

Source
Expand description

Discovery of a2a.json — the remote agents a workspace can hand work to.

The split is crate::mcp’s, and ADR-0028 draws it the same way: mentra owns the client half — fetching an Agent Card, speaking JSON-RPC, sending a task and waiting on it — and basis owns the convention half: which file names an agent, where that file lives, and what its fields mean. The model reaches a named agent through spawn (@<agent> <task>, ADR-0029); remote holds the agents an open reaches, and spawn does the talking.

§The set is live

ADR-0031: what an open connected is not fixed for its lifetime, because a host that runs for weeks outlives the configuration it opened with.

  • Workspace::reload_a2a reads the same three sources again, under every rule below, and applies the difference by name — an added agent connects, a removed one stops being routable, a changed one reconnects, an unchanged one has its card fetched again — and reports it as an A2aReload. A source that fails to read or validate changes nothing: a typo never drops an agent.
  • Workspace::watch_a2a polls the two files’ paths and reloads on a change, until its A2aWatch is dropped. basis’s long-running hosts turn it on at DEFAULT_A2A_WATCH_INTERVAL; a one-shot run does not.
  • An unreachable agent is retried when it is asked for, at most once per A2A_RETRY_BACKOFF, so one that came up after the open is reached without a reload.

Every agent a workspace mints or resumes holds the live set, so routing follows a change at once and what spawn teaches follows it from the next model request on — mentra captures tool descriptions at registration, and basis asks it to capture them again after every change. A delegation already in flight keeps the connection it started with.

§The format is basis’s

Where .mcp.json is a format other agents already wrote, this one is basis’s own, so it lives under .basis/ beside config.json and tools.json and is read the way they are: an unknown key is refused rather than tolerated, because nobody else writes this file and a key basis does not know is a typo — a misspelled headers is an agent called without the credential its operator wrote down.

{"agents": {"nous": {"url": "https://nous.example.com", "headers": {"Authorization": "Bearer ${NOUS_TOKEN}"}}}}

url is a base URL (the card is fetched from /.well-known/agent-card.json on that origin) or the card’s own URL — mentra’s rule, stated on A2aAgentConfig.

§Every rule .mcp.json follows, unchanged

  • Three places, and no fourth. Supplied by the host, then the workspace file, then the global one, by name; the first source to name an agent wins it outright rather than merging with a weaker one.
  • No parent walk, for .mcp.json’s reason and more urgently: this file names a host to send work to and the credential to send it with, and inheriting one from a directory the operator did not point basis at means handing that directory’s agent this repository’s work.
  • ${VAR} expands in url and in every header value, by the rules .mcp.json and .basis/tools.json expand by.
  • A name may not contain __ or end in _ — mentra’s validate_a2a_agent_name, which is deliberately the MCP server-name rule.
  • A file that exists but names no agents is an error, because the alternative is that a typo disables every agent and says nothing.
  • A committed file cannot grant its headers plaintext passage to a non-loopback host. mentra’s allow_plaintext_credentials override is not a key here; it stays with a host that builds an A2aAgentConfig in code.

Nothing read here is ever repeated back: see A2aError.

Structs§

A2aAgent
One remote agent, as basis hands it to mentra.
A2aAgentConfig
Configuration for one remote agent reached over A2A.
A2aConfig
Which remote agents a workspace gets: where to look for configured ones, and any the host already has in hand.
A2aLimits
Timeouts and size limits for one remote agent.
A2aReload
What Workspace::reload_a2a changed, by agent name.
A2aSource
One a2a.json that exists on disk, and what it configured.
A2aWatch
A running watch of one workspace’s A2A files. Dropping it stops the poll.
A2aWatchError
Why a watch could not start: the poll is a task on a Tokio runtime, and Workspace::watch_a2a was called on a thread that is not in one.
SecretString
A header value that is never rendered by Debug or Display.

Enums§

A2aError
Anything that can go wrong turning configuration into agents.

Constants§

A2A_RETRY_BACKOFF
How long an unreachable agent is left alone after a connect attempt before @<agent> tries it again (ADR-0031 §2).
DEFAULT_A2A_WATCH_INTERVAL
How often a long-running host polls its A2A files by default — what basis-acp and the CLI’s task driver pass to Workspace::watch_a2a.
DEFAULT_GLOBAL_A2A_FILE
Where basis looks inside the global config directory. Undotted, for the reason every file there is: a hidden file inside a directory that exists to hold configuration would be hiding it from the person who put it there.
DEFAULT_WORKSPACE_A2A_FILE
Where basis looks inside a workspace, relative to its root.
HOPS_EXTENSION
The URI of basis’s hops extension (docs/a2a/hops-v1.md, ADR-0030 §9): a message carries {"hop", "deadline"} under it in its metadata, so a loop between agents ends at a hop cap or the first deadline.

Functions§

agents
Every agent a workspace should connect, strongest source first, one per name.
discover
Every a2a.json that exists, most specific first.