A2A Concentrator Mode
Purpose
This functional area lets an administrator turn ApiMapper into a governed hub for agent-to-agent traffic: a directory of remote A2A agents (internal or third-party) the platform can call on a caller's behalf, with the same credential custody, policy gating, and audit trail the platform already applies to downstream HTTP/SOAP APIs — extended to agents. It answers "who else can our agents talk to, under what rules, and how do those other agents show up to our own callers" without asking any caller to hold a remote agent's credentials directly.
Main Capabilities
- register a remote agent in the Agent Directory, bound to an existing downstream connection and credential
- grant or deny access to a registered agent, optionally scoped to one of its skills, one caller, or one client application
- publish a registered agent at its own dedicated route (the Published switch), or keep it unreachable
- narrow which client applications may address a published agent
- refresh a registered agent's Agent Card on demand or on a recurring schedule
How It Works
Registering an agent
Registering an agent does not create a new kind of connection or credential. Instead, a RegisteredAgent
row references an existing TargetSystem (the agent's base URL) and TargetAuthenticationProfile (how to
authenticate to it) — the same two building blocks every downstream HTTP/SOAP API already uses in this
platform. This is deliberate: an agent, from a networking and credential-custody point of view, is just
another downstream system the platform calls on a caller's behalf. Reusing TargetSystem/
TargetAuthenticationProfile means a registered agent gets outbound allowlist enforcement, credential
storage discipline, and rotation support for free, instead of a parallel mechanism that would need to earn
that trust separately.
An agent starts fully closed: reachable by nothing until an administrator explicitly publishes it.
Granting access
An AgentAccessPolicy governs whether a given caller may invoke a given registered agent (optionally
narrowed to one skill — which now means "one of the agent's skills this caller may use", since a caller no
longer names one). For a remote agent, whose skills belong to the peer, a policy row naming a skill can
never match; the Portal flags such a row and disables the Skill selector for new ones. It matters most for
a Deny row — an Allow row that never matches simply fails to grant, but a Deny row that never matches means
a restriction you believe is in place is not. Each policy row is a set of match dimensions — skill, caller, client application —
any of which can be left blank to mean "matches everything for that dimension" — plus an allow/deny
decision. When several policies match the same request, the most specific one wins: a policy naming a
skill and a caller beats one naming only a skill, which beats one that applies agent-wide. This mirrors how
ToolAccessPolicy already governs access to MCP tools, applied to a different resource kind (a registered
agent, not a Mapping-derived tool).
Relaying a call
Once a call is authorized, the platform is a relay, not a second agent. It resolves the agent, checks the access policy, resolves the agent's credentials and validates its address against the outbound allowlist — and then sends the caller's message to the peer and returns what the peer said.
Three things follow from that, and all three are deliberate.
The peer's answer comes back whole. Every artifact, every part — text, files, structured data — the
status.message an agent uses to ask a clarifying question, its history and its metadata are the peer's
own, passed through untouched. The relay does not summarize, re-shape, or decide on the peer's behalf what
counts as the answer. The peer's artifacts are also stored against the task here, so asking this platform
about the task later returns the answer rather than an empty record.
The identifiers are ours, and theirs is theirs. A relayed task has a task id and a context id in this platform's identity space, not the peer's — those are what you use to fetch, follow or cancel the task through us, and a peer's id would be meaningless here. The peer's own task and conversation ids are recorded alongside as soon as they are known, and every outbound message after the first carries those. Sending ours, as this did before, named a conversation the peer had never issued, so each turn started a fresh one on the far side: the exchange looked continuous here and had no history at all there. A task you are continuing works the same way — we send the identifier the peer issued, or none when we have never seen one, rather than asking it to continue a task it never started.
Following one means asking the peer. Fetching or subscribing to a relayed task goes to the agent running it, because nothing on this side can move a task somebody else owns. What it reports is written here too, so a live subscription, a later fetch and the Portal's task view all agree. A peer that cannot be reached leaves the task as it stands.
The execution mode is the peer's to honour. configuration.returnImmediately is relayed as you sent
it. A remote agent implements A2A itself, so asking it to answer immediately is a matter of asking; putting
a queue of our own in front of it would invent progress this platform does not observe. The governed part
in front of the call — agent unknown or inactive, access denied, credentials unresolvable, address refused —
is answered immediately whichever mode you asked for, because a refusal is already a finished task.
Exposing an agent
A registered agent is reachable at its own dedicated address, and nowhere else. Publishing it is a single explicit grant; until an administrator makes it, the agent exists in the directory but no route to it does.
There used to be a second way — composite inclusion, which folded an agent's skills into a client application's own Agent Card so a caller saw one unified skill list without knowing some of those skills were served by another agent. That has been withdrawn. Merging other agents' skills under our own name, and signing the result with our own key, asserts that one agent holds capabilities that in fact belong to agents we do not control and whose cards we had only cached — a claim this platform was not entitled to make, and one that goes wrong quietly as a cached card ages.
The purpose it served — letting a caller reach several agents without learning your internal topology — is better met by a hosted agent that acts as a front desk: give it skills that delegate to the remote agents behind it, and callers see one genuine agent with one genuine card. Nothing about your topology leaks, and every capability the card advertises is one that agent really has.
Publishing an agent still does not authorize anything. Every call goes through the same access-policy check and credential resolution — exposure controls whether a route to the agent exists at all, not whether an individual call on that route is allowed.
Choosing which client applications may address an agent
An agent's runtime address names a client application — /{tenant}/{clientApplication}/a2a/agents/{agent}
— and Client access on the agent decides which of them it answers on. Leave it empty and any client
application in the tenant may address the agent; add applications to narrow it to those. Empty meaning
"every one" is the same convention every blank governance dimension here follows, and it is safe for the
same reason: this narrows a published agent, it never widens one. Publishing decides whether a route
exists, the agent's access policies decide each call, and this decides which doors the agent answers at.
A call arriving on an application that is not on a non-empty list gets exactly the response an agent that does not exist gets — including for the Agent Card, since the card is how a caller learns an agent is there at all. A restriction that announced itself would defeat its own purpose.
If a client application is deleted while still named on an agent, its id stays on the list and the Portal marks it as no longer in the tenant. The restriction is still being enforced, so hiding it would be worse than showing something slightly untidy.
The endpoint-URL builder on the agent follows the same list: when an agent is narrowed, the client application picker offers only the applications it answers on, so a URL copied from the Portal is never an address the agent would refuse.
Card refresh
A registered agent's own Agent Card (the metadata it publishes about itself and its skills) is fetched and
cached, not queried live on every request — a remote agent being slow or briefly unreachable should not
block every caller who wants to see what it offers. An administrator can trigger an immediate, single-agent
refresh on demand, and a scheduled sweep refreshes every active registered agent across every tenant on a
recurring interval so the cache does not go stale indefinitely on its own. If a refresh fails, the
previously cached card is left untouched — a stale card that still describes the agent reasonably well is
preferable to no card at all. The cached card is treated purely as display content, never as
something that drives an authorization or authentication decision; those decisions always come from the
platform's own TargetAuthenticationProfile and AgentAccessPolicy configuration.
Functional Value
This gives an organization one governed place to see and control every agent-to-agent relationship its own agents participate in — the same posture the platform already gives administrators for its own MCP tool catalog — instead of individual integrations quietly holding their own credentials and bypassing every control this platform otherwise enforces.
