parse_agents_config
Python · package toolnexus · SPEC §7A · python/src/toolnexus/a2a.py
def parse_agents_config(block: dict[str, dict] | None) -> list[Agent]Parses an agents config block — the outbound-A2A mirror of mcpServers (§2) — into
a list[Agent] descriptors, skipping disabled entries. The block’s keys are only
identifiers; a tool’s name prefix comes from the fetched card’s name, never the
config key.
When to use it
Section titled “When to use it”- You keep remote peers declared in config (a file or an inline dict) instead of
building
Agentdescriptors by hand in code. - You want the same
enabled/disabledprecedence MCP servers already use, so one mental model covers bothmcpServersandagentsblocks. - You are about to hand the result straight to
create_toolkit(agents=...)or loop over it and calltoolkit.add_agent(...)yourself.
Why this and not the alternative
Section titled “Why this and not the alternative”parse_agents_config never connects to anything — it is pure config parsing, exactly
like parse_mcp_config for MCP servers. Fetching the
card and building tools is a separate step: agent_tools
per descriptor, or create_toolkit(agents=parse_agents_config(block)) to do both at
once.
Examples
Section titled “Examples”1. The smallest useful call — one enabled peer
Section titled “1. The smallest useful call — one enabled peer”from toolnexus import parse_agents_config
block = { "video-desk": {"card": "http://127.0.0.1:9999/.well-known/agent-card.json"},}
agents = parse_agents_config(block)
assert len(agents) == 1assert agents[0].card == "http://127.0.0.1:9999/.well-known/agent-card.json"assert agents[0].headers is Noneassert agents[0].timeout is Noneassert agents[0].poll_every is None
print("ok:", agents[0].card)2. The realistic case — mixed enabled/disabled peers, pollEvery
Section titled “2. The realistic case — mixed enabled/disabled peers, pollEvery”The config key ("desk-a" / "desk-b") never becomes the tool prefix — only the
fetched card’s name does. disabled: true and enabled: false both drop an entry.
from toolnexus import parse_agents_config
block = { "desk-a": { "card": "http://127.0.0.1:9001/.well-known/agent-card.json", "pollEvery": 250, "timeout": 5000, }, "desk-b": { "card": "http://127.0.0.1:9002/.well-known/agent-card.json", "disabled": True, }, "desk-c": { "card": "http://127.0.0.1:9003/.well-known/agent-card.json", "enabled": False, }, "desk-d": { # missing "card" — silently skipped, not an error. "pollEvery": 100, },}
agents = parse_agents_config(block)
assert len(agents) == 1only = agents[0]assert only.card == "http://127.0.0.1:9001/.well-known/agent-card.json"assert only.poll_every == 250assert only.timeout == 5000
print("ok:", len(agents), "of", len(block), "peers enabled")3. The full surface — precedence, empty/None blocks, headers pass through
Section titled “3. The full surface — precedence, empty/None blocks, headers pass through”disabled: true wins over enabled: true (MCP’s own precedence, reused verbatim).
A None or empty block is valid and yields no agents — never an error.
from toolnexus import parse_agents_config
# disabled:true beats enabled:true.contradictory = { "peer": { "card": "http://127.0.0.1:9009/.well-known/agent-card.json", "enabled": True, "disabled": True, }}assert parse_agents_config(contradictory) == []
# Absent block, empty block: both valid, both empty.assert parse_agents_config(None) == []assert parse_agents_config({}) == []
# Headers pass through untouched here — ${ENV} expansion happens at call time# (agent_tools / the tool's execute), never during parsing, so secrets never sit# resolved in a parsed config object.with_headers = parse_agents_config( { "peer": { "card": "http://127.0.0.1:9010/.well-known/agent-card.json", "headers": {"Authorization": "Bearer ${DESK_TOKEN}"}, } })assert with_headers[0].headers == {"Authorization": "Bearer ${DESK_TOKEN}"}
print("ok:", "precedence + empty-block + headers-passthrough all hold")Options
Section titled “Options”| Key (in each entry) | Type | What it does |
|---|---|---|
card |
str |
Required. The peer’s Agent Card URL. An entry without it is skipped. |
headers |
dict[str, str] | None |
Passed through unresolved; ${ENV} values expand at call time, never during parsing. |
timeout |
int | None |
Milliseconds. Falls back to agent()’s own default (300000ms) when unset. |
pollEvery |
int | None |
Milliseconds, wire key (camelCase even in the Python dict). Maps to Agent.poll_every. |
enabled / disabled |
bool | None |
MCP isEnabled precedence: disabled: true wins, then enabled: false; default is enabled. |
See also
Section titled “See also”agent— Point at a remote agent’s card and use it exactly like a local tool.agent_tools— Expand a remote agent card into one tool per advertised skill.