parseAgentsConfig
JavaScript · package toolnexus · SPEC §7A · js/src/a2a.ts
interface AgentConfig { card: string headers?: Record<string, string> timeout?: number pollEvery?: number enabled?: boolean disabled?: boolean}type AgentsConfig = Record<string, AgentConfig>
function parseAgentsConfig(block: AgentsConfig | undefined): Agent[]Parse an agents config block — Record<name, AgentConfig>, the same shape as an mcpServers
block — into Agent[] descriptors, skipping disabled entries. The config key is only an
identifier for humans editing the file; the tool name prefix a peer’s skills get comes from the
fetched card’s name, not the key.
When to use it
Section titled “When to use it”Use parseAgentsConfig when remote peers should be data, not code — declared in the same JSON
file as your mcpServers, edited without a redeploy, one block per environment. This is what
createToolkit({ mcpConfig }) calls when the parsed config has an agents key.
Why this and not the alternative
Section titled “Why this and not the alternative”The enable/disable precedence matters and is easy to get wrong by hand:
disabled: true wins over everything, then enabled: false, default is enabled — the exact
isEnabled rule mcpServers already uses (§2), so an agents block reads the same way once you
know one.
Examples
Section titled “Examples”1. The smallest useful call — one entry, no disables
Section titled “1. The smallest useful call — one entry, no disables”import assert from "node:assert"import { parseAgentsConfig } from "toolnexus"
const parsed = parseAgentsConfig({ reviewer: { card: "http://127.0.0.1:9/.well-known/agent-card.json" },})
assert.equal(parsed.length, 1)assert.equal(parsed[0].card, "http://127.0.0.1:9/.well-known/agent-card.json")
console.log("ok:", parsed.length, "agent(s) parsed")2. A realistic case — mixed headers, timeouts, and one disabled peer
Section titled “2. A realistic case — mixed headers, timeouts, and one disabled peer”import assert from "node:assert"import { parseAgentsConfig } from "toolnexus"
const parsed = parseAgentsConfig({ reviewer: { card: "http://reviewer.internal/.well-known/agent-card.json", headers: { Authorization: "Bearer ${TN_DOCS_TOKEN}" }, timeout: 60_000, pollEvery: 500, }, planner: { card: "http://planner.internal/.well-known/agent-card.json", disabled: true, // wound down for now — kept in config, not deleted }, archivist: { card: "http://archivist.internal/.well-known/agent-card.json", enabled: true, },})
assert.deepEqual( parsed.map((a) => a.card), ["http://reviewer.internal/.well-known/agent-card.json", "http://archivist.internal/.well-known/agent-card.json"], "the disabled planner is skipped; the config key itself never appears on the Agent",)assert.equal(parsed[0].timeout, 60_000)assert.equal(parsed[0].pollEvery, 500)
console.log("ok:", parsed.length, "enabled agent(s)")3. The full surface — every precedence combination, plus malformed entries
Section titled “3. The full surface — every precedence combination, plus malformed entries”enabled+disabled together: disabled always wins. Entries missing a card, or that aren’t
objects at all, are skipped rather than throwing — a config file is user input.
import assert from "node:assert"import { parseAgentsConfig } from "toolnexus"
const parsed = parseAgentsConfig({ a: { card: "http://x/1" }, // default: enabled b: { card: "http://x/2", disabled: true }, // disabled wins c: { card: "http://x/3", enabled: false }, // enabled:false ⇒ off d: { card: "http://x/4", enabled: true, disabled: true }, // disabled STILL wins over enabled:true e: { headers: { x: "y" } } as any, // no `card` ⇒ skipped, not thrown})
assert.deepEqual(parsed.map((a) => a.card), ["http://x/1"])
// undefined block ⇒ empty array, never a throw — the "no agents configured" case.assert.deepEqual(parseAgentsConfig(undefined), [])
console.log("ok:", parsed.length, "agent(s) survived precedence + malformed-entry filtering")Options
Section titled “Options”| Field | Type | What it does |
|---|---|---|
block |
AgentsConfig | undefined |
Record<name, AgentConfig>, mirroring mcpServers. undefined ⇒ []. |
block[name].card |
string |
Agent Card URL. Entries missing this are skipped. |
block[name].headers |
Record<string, string> |
${ENV}-expanding request headers. |
block[name].timeout / .pollEvery |
number |
Same as agent()’s options. |
block[name].enabled / .disabled |
boolean |
MCP isEnabled precedence: disabled:true wins, then enabled:false, default enabled. |
What you get back
Section titled “What you get back”Agent[] — ready to hand straight to createToolkit({ agents }) or toolkit.addAgent(), one per
enabled config entry, in the block’s iteration order. Disabled and malformed entries are silently
dropped, never thrown.
See also
Section titled “See also”agent— Point at a remote agent’s card and use it exactly like a local tool.agentTools— Expand a remote agent card into one tool per advertised skill.