Expand description
The declarative config file + its JSON Schema.
One document, two syntaxes: YAML (.yaml/.yml, read by the
hand-rolled super::yaml subset reader — no serde_yaml, the minimalism
moat) or JSON with comments (.json/.jsonc); an unknown extension is
sniffed ({/[ ⇒ JSON, else YAML). Both parse to the same
serde_json::Value document (read_document) and then to the typed
ConfigFile (ConfigFile::from_document) — so validation, the schema,
the env/flag path bindings (super::paths) and hot reload are all
format-agnostic.
The file carries only verbose structural config: the MCP-server inventory, declared subscriptions, A2A peers, limits, and the model/log knobs. It never carries secrets or per-environment scalars (those stay env/flag).
Precedence: built-in default < FILE < env < flag. The file is loaded
first, then Config::load applies env and flags over it; a flag/env for the
same key wins. List-valued keys (mcp_servers, subscribe, a2a_peers)
seed the list — repeatable --mcp/--subscribe/--a2a-peer flags add
to the file’s list rather than replacing it, matching the repeatable-flag
semantics operators already expect.
deny_unknown_fields makes a typo’d key (max_token vs max_tokens) a hard
config error (exit 2) instead of a silently-ignored value — the single most
common config footgun, closed at parse time.
The schema is hand-written (no schemars — a forbidden dependency) and
kept faithful to this struct by a unit test asserting the schema’s top-level
properties match the struct’s fields, so the two cannot diverge unnoticed.
Structs§
- A2aPeer
File - One A2A peer — maps to
--a2a-peer name=endpoint. - Config
File - The deserialized config-file shape — one source of truth for the loader, the
validator, and the
--config-schemagenerator.serdeonly. - Limits
File - The
limitssub-object — maps to the per-run limit flags. - McpServer
File - One MCP server, reached over the Streamable HTTP transport: a remote
endpoint(https://host[:port][/path], loopbackhttp://for dev) with optional secret-free authheaders. There is no local process spawn — every server is a network peer, so config can never turn into command execution.tagsis the glob→tags wire (the loader flattens a{"*": ["sensitive"]}map to the server’s tag set).
Enums§
- Format
- The two config-file syntaxes.
Constants§
- CONFIG_
FILE_ FIELDS - The list of
ConfigFilefield names, in declaration order — the single source both the schema generator and its unit test read, so the schema’spropertiescan never silently diverge from the struct. - SCHEMA_
CONTRACT_ VERSION - The
x-agentd-contract-versionthe schema carries. It is the same value as the capabilities manifest’scontract_version— a tool that validated a document against this schema knows exactly which runtime contract it targets — andtests::schema_contract_version_matches_manifestholds the two equal.
Functions§
- config_
schema - Emit the hand-written JSON Schema (Draft 2020-12) of the config file.
No
schemars— a schema library is binary weight the moat forbids. Kept faithful toConfigFilebytests::schema_properties_match_struct_fields. - merge_
into - JSON Merge Patch (RFC 7396):
overlayontobase. Objects merge key by key (recursively); any other value — a scalar or a list — replaces what was there; an explicitnullremoves the key. A non-object overlay replaces the base wholesale. - parse_
document - Parse config text of the given format into its document (a JSON value). A syntax error names the line/column; the document must be a mapping (object) at the top level.
- read_
document - Read + parse a config file from a local path into its document, deciding the format from the extension (else by sniffing the text). Errors name the path.
- read_
documents - Read several config files, in order, into ONE effective document: each later
file is merged over the previous ones with JSON Merge Patch semantics
(RFC 7396) — objects merge recursively, scalars and lists are REPLACED by the
later file, and a
nullvalue UNSETS the key. Every file is type-checked on its own first (so an unknown key is reported against the file that carries it), then the merged document is returned with the(path, format)list. - read_
documents_ checked read_documentswith a caller-supplied per-file check (the v2 settings typing, or none) —check(doc, "config file <path>")runs before the merge so an unknown key is attributed to its file.