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_a2areads 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 anA2aReload. A source that fails to read or validate changes nothing: a typo never drops an agent.Workspace::watch_a2apolls the two files’ paths and reloads on a change, until itsA2aWatchis dropped. basis’s long-running hosts turn it on atDEFAULT_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 inurland in every header value, by the rules.mcp.jsonand.basis/tools.jsonexpand by.- A name may not contain
__or end in_— mentra’svalidate_a2a_agent_name, which is deliberately the MCP server-name rule. - A file that exists but names no
agentsis 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_credentialsoverride is not a key here; it stays with a host that builds anA2aAgentConfigin code.
Nothing read here is ever repeated back: see A2aError.
Structs§
- A2aAgent
- One remote agent, as basis hands it to mentra.
- A2aAgent
Config - 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_a2achanged, by agent name. - A2aSource
- One
a2a.jsonthat exists on disk, and what it configured. - A2aWatch
- A running watch of one workspace’s A2A files. Dropping it stops the poll.
- A2aWatch
Error - Why a watch could not start: the poll is a task on a Tokio runtime, and
Workspace::watch_a2awas called on a thread that is not in one. - Secret
String - A header value that is never rendered by
DebugorDisplay.
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-acpand the CLI’s task driver pass toWorkspace::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.