Skip to content

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.

  • You keep remote peers declared in config (a file or an inline dict) instead of building Agent descriptors by hand in code.
  • You want the same enabled/disabled precedence MCP servers already use, so one mental model covers both mcpServers and agents blocks.
  • You are about to hand the result straight to create_toolkit(agents=...) or loop over it and call toolkit.add_agent(...) yourself.

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.

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) == 1
assert agents[0].card == "http://127.0.0.1:9999/.well-known/agent-card.json"
assert agents[0].headers is None
assert agents[0].timeout is None
assert 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) == 1
only = agents[0]
assert only.card == "http://127.0.0.1:9001/.well-known/agent-card.json"
assert only.poll_every == 250
assert 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")
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.
  • 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.