Skip to content

A2A (Agent-to-Agent)

This page covers the provider side of the A2A channel: how mono-agent serves your agent over the A2A protocol so other agents can discover and call it. The channel is provided by the external @mono-agent/a2a-adapter package and loaded through channels.plugins[]. It publishes an Agent Card, accepts messages over JSON-RPC and REST, and streams responses. Calling remote A2A agents (the consumer side) is programmatic — see A2A consumer.

Coverage: config. The provider is fully described by the A2A plugin entry’s config.provider, config.agent, and config.skill settings in mono-agent.config.json.

When the A2A plugin entry has config.enabled set to true (or the legacy config.provider.enabled — the root flag wins when both are set), mono-agent start binds an HTTP server that exposes three endpoints relative to the bound host (or publicBaseUrl when fronted by a proxy):

PathPurpose
/.well-known/agent-card.jsonAgent Card for discovery (name, description, version, skill, capabilities)
/a2a/json-rpcJSON-RPC message endpoint (message/send, message/stream)
/a2a/restREST message endpoint

The Agent Card advertises capabilities.streaming: true, so callers can stream incremental output over JSON-RPC. Each inbound message runs one agent turn against your configured runtime, memory, and tools — the same engine that backs every other channel.

The provider is deliberately text/task only for deliverable content. It supports plain-text message exchange and task-style turns. If a responder tries to return an attachment or MCP App, answer text remains byte-for-byte unchanged and the final artifact adds one structured outcome data Part; every attempted rich part terminates explicitly as a bounded sanitized failure. Text-only clients can ignore that additive part. The following A2A protocol features are intentionally not implemented:

  • No agent registry / catalog
  • No gRPC transport (HTTP/JSON only)
  • No push notifications
  • No signed Agent Cards
  • No file exchange (no file URL or raw-byte parts)

The outcome part uses application/vnd.mono-agent.reply-part-outcomes+json. It never carries part ids, filenames, local or host-only URLs, capability values, integrity ids, producer messages, or private payload bytes. Attachments and MCP Apps report unsupported_destination; the list is capped at 20 with a counted overflow aggregate. Durable idempotency stores and replays the same structured result without rerunning the responder.

Set MONO_AGENT_A2A_BEARER_TOKEN in the agent’s .env when requireBearer is enabled. The source-config example intentionally omits the credential.

{
"channels": {
"plugins": [
{
"package": "@mono-agent/a2a-adapter",
"id": "a2a",
"config": {
"enabled": true,
"provider": {
"host": "127.0.0.1",
"port": 4201,
"publicBaseUrl": "https://agent.example.com",
"allowNonLoopback": true,
"requireBearer": true,
"maxRequestBytes": 50000000,
"idempotency": {
"namespace": "my-agent-production",
"retentionMs": 2592000000,
"maxRecords": 10000
}
},
"agent": {
"name": "My Agent",
"description": "What it does.",
"version": "0.1.0",
"providerOrganization": "Acme",
"providerUrl": "https://acme.example.com"
},
"skill": {
"id": "main",
"name": "Main",
"description": "Primary skill.",
"tags": ["agent"]
}
}
}
]
}
}
KeyTypeDefaultNotes
enabledbooleanfalseOpt-in. When off the channel reports disabled, not waiting.
hoststring127.0.0.1Bind address. Non-loopback requires allowNonLoopback: true.
portnumber4201TCP port (0–65535).
publicBaseUrlstringAbsolute URL written into the Agent Card endpoint URLs when fronted by a reverse proxy.
allowNonLoopbackbooleanfalseMust be true to bind a non-loopback host or advertise a non-loopback publicBaseUrl.
requireBearerbooleanfalseRequire Authorization: Bearer <token> on /a2a/json-rpc and /a2a/rest.
bearerTokenstringThe expected token. Required when requireBearer is true.
maxRequestBytesintegerA2A SDK default (100 KiB)Optional JSON request-body ceiling for JSON-RPC and REST. Range: 1024–100000000 bytes. Authentication runs before body parsing.
idempotency.namespacestringExplicitly enables durable logical-dispatch idempotency and defines the stable authenticated principal boundary. Never derive it from URL/version/token.
idempotency.stateDirstringderived owner-only pathDurable receipt/tombstone directory. Relative paths resolve from the agent cwd.
idempotency.retentionMsinteger2592000000Full terminal-result replay horizon; compact tombstones remain permanent.
idempotency.maxRecordsinteger10000Hard unique-key admission capacity; exhaustion fails closed.

The block is all-or-nothing: configuring any idempotency.* field requires a non-empty idempotency.namespace. Partial configuration fails validation rather than starting without protection.

Populates the identity block of the Agent Card.

KeyRequiredNotes
nameyes when no root name is availableHuman-readable Agent Card name. Defaults from root agent.name / MONO_AGENT_NAME; plugin config.agent.name / MONO_AGENT_A2A_AGENT_NAME wins.
descriptionyesWhat the agent does.
versionyesAgent version string (e.g. 0.1.0).
providerOrganizationnoOrganization that operates the agent. Emitted only when providerUrl is also set.
providerUrlnoURL for the operating organization. Emitted only when providerOrganization is also set.

A single advertised skill on the Agent Card.

KeyRequiredNotes
idyesStable skill identifier (e.g. main).
nameyesDisplay name.
descriptionyesWhat the skill does.
tagsnoString array for categorization.

Every key has a MONO_AGENT_* override. Strings split on commas where the value is a list.

Env varJSON key
MONO_AGENT_A2A_ENABLEDplugin config.enabled (canonical; wins over the legacy form)
MONO_AGENT_A2A_PROVIDER_ENABLEDplugin config.provider.enabled (legacy; still honored)
MONO_AGENT_A2A_HOSTplugin config.provider.host
MONO_AGENT_A2A_PORTplugin config.provider.port
MONO_AGENT_A2A_PUBLIC_BASE_URLplugin config.provider.publicBaseUrl
MONO_AGENT_A2A_ALLOW_NON_LOOPBACKplugin config.provider.allowNonLoopback
MONO_AGENT_A2A_REQUIRE_BEARERplugin config.provider.requireBearer
MONO_AGENT_A2A_BEARER_TOKENplugin config.provider.bearerToken
MONO_AGENT_A2A_MAX_REQUEST_BYTESplugin config.provider.maxRequestBytes
MONO_AGENT_A2A_IDEMPOTENCY_NAMESPACEplugin config.provider.idempotency.namespace
MONO_AGENT_A2A_IDEMPOTENCY_STATE_DIRplugin config.provider.idempotency.stateDir
MONO_AGENT_A2A_IDEMPOTENCY_RETENTION_MSplugin config.provider.idempotency.retentionMs
MONO_AGENT_A2A_IDEMPOTENCY_MAX_RECORDSplugin config.provider.idempotency.maxRecords
MONO_AGENT_A2A_AGENT_NAMEplugin config.agent.name (wins over root agent.name / MONO_AGENT_NAME)
MONO_AGENT_A2A_AGENT_DESCRIPTIONplugin config.agent.description
MONO_AGENT_A2A_AGENT_VERSIONplugin config.agent.version
MONO_AGENT_A2A_PROVIDER_ORGANIZATIONplugin config.agent.providerOrganization
MONO_AGENT_A2A_PROVIDER_URLplugin config.agent.providerUrl
MONO_AGENT_A2A_SKILL_IDplugin config.skill.id
MONO_AGENT_A2A_SKILL_NAMEplugin config.skill.name
MONO_AGENT_A2A_SKILL_DESCRIPTIONplugin config.skill.description
MONO_AGENT_A2A_SKILL_TAGSplugin config.skill.tags (comma-separated)
MONO_AGENT_A2A_REMOTE_AGENT_URLSplugin config.consumer.remoteAgentUrls (comma-separated)
MONO_AGENT_A2A_DEFAULT_REMOTE_AGENT_URLplugin config.consumer.defaultRemoteAgentUrl
MONO_AGENT_A2A_CONSUMER_BEARER_TOKENplugin config.consumer.bearerToken
MONO_AGENT_A2A_TIMEOUT_MSplugin config.consumer.timeoutMs

By default the provider binds loopback (127.0.0.1) and runs without auth — safe for local development and same-host agent-to-agent calls.

To expose the provider publicly you must opt in on two axes:

  1. Set allowNonLoopback: true to bind a non-loopback host or advertise a non-loopback publicBaseUrl. Without it, start fails with an explicit error rather than silently binding 0.0.0.0.
  2. Set requireBearer: true with a bearerToken so callers must present Authorization: Bearer <token>. When requireBearer is on but no token is configured, start fails.

mono-agent start prints one status line for the A2A channel:

  • running with the bound endpoint facts (Agent Card / JSON-RPC / REST URLs) when enabled and valid.
  • waiting_for_config naming the exact missing setting (e.g. a required config.agent.name).
  • disabled when plugin config.enabled (or the legacy config.provider.enabled) is false.
  • failed with the reason (e.g. non-loopback bind without allowNonLoopback).

Run mono-agent validate first for a per-section report. Config is JSON-first — edit mono-agent.config.json and run mono-agent restart to apply.

Inbound A2A messages run the same turn pipeline as other channels, so tool policy, MCP servers, sandbox, memory, and sessions/concurrency all apply. See the channels overview for cross-channel concepts.

The provider only serves your agent. To have your agent call other A2A agents, put consumer defaults under the same A2A plugin entry’s config.consumer and invoke them programmatically with createA2AConsumerResponder, sendA2AMessage, or dispatchA2AMessage. Loading consumer config does not add a tool or autonomously delegate work; this remains a code path — see A2A consumer.