A2A Integration
Operator runbook for agent-to-agent capability discovery and delegation.Purpose
Advertise the UniFi MCP Server in a machine-readable way so orchestration systems and peer agents can discover its capabilities, auth model and tool surface without manual inspection, and let a peer agent delegate a tool call to it.What this is, and what it is not
The A2A specification (a2a-protocol.org) defines a task-based protocol:message/send, a Task lifecycle, streaming updates, push notifications and
an A2A-Version header. This server does not implement that protocol.
There is no a2a-sdk dependency, and no Task, Message, Part or Artifact
data model exists in the codebase.
What the server implements is a smaller surface that serves the same
purpose:
- Discovery. An Agent Card and a capability manifest, both built from the live tool registry.
- Delegation. A request/response endpoint that runs a registered tool, with the server’s own confirmation, role and audit controls in force.
streaming: false and pushNotifications: false because the
server implements neither. The card never claims a capability the server
does not have.
The gap and the decision behind it are recorded in
docs/AUDIT_A2A_SKILLS.md. Implementing the task
protocol would mean adopting the a2a-sdk dependency and rebuilding the
delegation path around Task objects — a substantial architectural change
that this repository gives no direction for, so it is not implemented and
not advertised.
What is served
On thehttp, sse and streamable_http transports the server registers
these routes. Every one of them is behind the same bearer token as /mcp
(MCP_AUTH_TOKEN, or a fleet token). On stdio, no HTTP is served at all.
/a2a/audit answers 403 to a viewer or operator token, and 401 to
a request with no valid token.
The Agent Card
The card is built bysrc/a2a/capabilities.py and src/a2a/agent_card.py
from the live tool registry. A card therefore describes the tools this
deployment actually registered, and it changes when the tool surface or the
configuration changes.
The
metadata block is where the configuration shows through. A
read-only server reports readOnly: true; a server in minimal profile
reports only the domains that profile registers; a fleet server reports
fleet: true.
The card carries no credential, no controller address and no filesystem
path. It is public metadata, and a unit test enforces that.
The repository artifact
agent-card.json at the repository root is the reference card for a
default local deployment. The served card legitimately differs between
configurations, so the artifact is a pinned reference rather than a
byte-for-byte copy of whatever a given server serves. Regenerate it after
changing the card, the tool surface or the package version:
tests/unit/a2a/test_agent_card.py checks the artifact against the card
the builder produces for the reference configuration, so the two cannot
drift apart.
The capability manifest
POST /a2a/discover returns the card plus the inventory an orchestrator
needs to choose a task:
include_audit adds the audit
endpoint, and include_rate_limits adds the per-tier delegation limits.
The tier of a tool is the one the server enforces, derived from the tool’s
own function rather than from its name. A client can therefore trust the
counts: a tool listed as write needs the operator role, and a tool listed
as destructive needs the admin role and a confirmation.
Delegation
POST /a2a/delegate runs a registered tool on the caller’s behalf.
- the caller’s role must grant the tool’s tier,
- a mutating tool still needs its
confirm=True, UNIFI_DRY_RUNstill forces a preview,- the call is audited.
scopes, which narrow what the role
already grants; they cannot widen it. A viewer token asking for
destructive access in the payload is refused.
The confirmation step
A destructive call, or a write that the safety policy holds, is not run directly. The endpoint answersconfirmation_required with a token and
the held call:
POST /a2a/confirm and the held call runs:
Outcomes
unavailable is deliberately distinct from ok: a delegation that cannot
run must not report success.
Security
- Every route requires the bearer token. An unauthenticated request is
answered
401with aWWW-Authenticate: Bearerheader. /a2a/auditadditionally requires theadminrole.- The caller’s role is resolved from the verified token and bound to the request, so the A2A layer cannot be talked into a wider access than the token carries.
- The card and the manifest carry no secret, no controller address and no private path.
- Delegated calls are audited with the principal, the tool, the tier and the redacted arguments. The tool’s response is never recorded.
Operational objectives
- Make the server discoverable to orchestration platforms.
- Keep the advertised capabilities synchronized with the real tool surface.
- Avoid exposing secrets or internal-only paths in the manifest.
- Keep the card valid JSON and easy to validate automatically.
Validation checklist
- JSON parses without errors.
- The card’s
skillsare the tools this deployment registered. - The capability list follows the configuration.
agent-card.jsonmatches the reference card (pytest tests/unit/a2a/test_agent_card.py).- No secret, controller address or internal path appears in the card.
- Every route refuses an unauthenticated request.
Common failure modes
- The agent card drifts from the actual tool surface — prevented by
building the card from the live registry, and by regenerating
agent-card.jsonwhen the tool surface changes. - The HTTP endpoint serves stale content after a release — the card is built per request, so it cannot be stale; only the repository artifact can be, and a test fails when it is.
- Discovery clients cache an outdated manifest — the client should re-read the card after a version change.
- The manifest includes implementation details that should remain private — prevented by the card carrying no address, path or credential.
Operator notes
- Treat the agent card as part of the public contract.
- Keep changes in sync with the tool exposure profiles, the controller capabilities and the auth policy.
- Re-validate after every release that changes the tool surface.
- The card describes what the server can do. It does not authorise anything: a delegated call is still subject to the role check, the confirmation gate and the audit trail.
