Repository navigation
A2A Support in the API Platform #2743
Replies: 2 comments 1 reply
|
Following the design discussions, the current Agent Gateway proposal is documented below. This updates the proposed initial scope and captures the remaining design considerations. Feature coverageInitial release
Later releases
ExampleapiVersion: gateway.api-platform.wso2.com/v1
kind: Agent
metadata:
name: weather-agent-v1-0
spec:
displayName: Weather Agent
version: v1.0
context: /weather
vhost: agents.example.com
upstream:
url: https://weather.internal
resilience:
timeout: 30s
idleTimeout: 5m
a2a:
protocolVersion: "1.0"
operationConfigs:
transports:
- protocolBinding: JSONRPC
pathPrefix: /rpc
- protocolBinding: HTTP+JSON
pathPrefix: /rest
policies:
- name: jwt-auth
version: v1
params:
issuer: https://idp.example.com
requiredScopes:
- a2a.invoke
operations:
- name: SendMessage
policies:
- name: advanced-ratelimit
version: v1
params:
quotas:
- name: send-message-limit
limits:
- limit: 100
duration: 1m
agentCard:
public:
mode: managed
path: /.well-known/agent-card.json
policies:
- name: cors
version: v1
content: {
"name": "Weather Agent",
"description": "Provides weather information",
"version": "1.0.0",
"supportedInterfaces": [
{
"protocolBinding": "JSONRPC",
"protocolVersion": "1.0",
"url": "https://agents.example.com/weather/rpc"
},
{
"protocolBinding": "HTTP+JSON",
"protocolVersion": "1.0",
"url": "https://agents.example.com/weather/rest"
}
],
"capabilities": {
"streaming": true
},
"securitySchemes": {
"gateway-jwt": {
"openIdConnectSecurityScheme": {
"openIdConnectUrl": "https://idp.example.com/.well-known/openid-configuration"
}
}
},
"securityRequirements": [
{
"gateway-jwt": ["a2a.invoke"]
}
],
"defaultInputModes": ["text/plain"],
"defaultOutputModes": ["text/plain"],
"skills": [
{
"id": "get_weather",
"name": "Get weather",
"description": "Gets weather information",
"tags": ["weather"]
}
]
}
signing:
enabled: true
algorithm: ES256Behaviour
Agent Card representations and serving modes
The protected representation has no configurable path or local policy list. It is returned through the canonical A passthrough configuration needs no card content and may omit a2a:
protocolVersion: "1.0"
operationConfigs:
transports:
- protocolBinding: JSONRPC
pathPrefix: /
- protocolBinding: HTTP+JSON
pathPrefix: /
agentCard:
public:
mode: passthroughPolicy scopes
The name is binding-independent even though the standard values match the A2A JSON-RPC and gRPC method names. Within each configured transport prefix, the gateway maps an HTTP+JSON resource or a JSON-RPC method to the same name before selecting policies. For example, JSON-RPC The public Agent Card is configured separately by The resulting chains are: The For example: a2a:
operationConfigs:
policies:
- name: jwt-auth
version: v1
operations:
- name: SendMessage
policies:
- name: advanced-ratelimit
version: v1
agentCard:
public:
mode: passthrough
policies:
- name: cors
version: v1Streaming operation handlingStreaming is derived from the A2A protocol version and the resolved operation; it is not configurable in the Agent YAML. In A2A 1.0, A successful streaming response must use For JSON-RPC, unary and streaming operations share one HTTP route. Route timeout handling must therefore use the resolved operation or otherwise use a stream-safe shared timeout; a normal unary total timeout must not terminate a valid long-lived stream. Authentication enforcement and security consistencyAuthentication policies change the effective security contract exposed by the gateway, but the gateway does not generate or rewrite the security declaration of a managed public Agent Card. For a managed card, the consistency checks must ensure that:
In public When the published Agent Card declares a top-level security requirement, the gateway must authenticate every invocation request against that requirement before forwarding it upstream. A2A requires clients to send credentials with every A2A request and requires the server to authenticate every incoming request against its declared authentication requirements. This applies to every A2A operation and every exposed transport. An operation-level policy must not weaken, bypass, or replace the Agent-wide authentication requirement. A failed or missing credential is rejected before the upstream Agent or an operation-specific policy is invoked. The public Agent Card should remain outside these authentication policies so a client can discover how to authenticate. A2A operations, including the authenticated extended-card operation, remain protected. Protecting the public card makes discovery and initial credential acquisition an out-of-band concern. The gateway signs the complete, validated managed card without modifying its security fields. Changing an authentication policy requires the supplied card to remain consistent, but does not itself cause the gateway to synthesize new card content. A passthrough card is never regenerated or signed by the gateway. Policy-to-card validationThe user supplies the complete security declaration in
For OIDC and OAuth 2.0, the initial managed-card validation supports both client-credentials and authorization-code flows. The user supplies public discovery information—including authorization, token, optional refresh and OIDC discovery URLs, PKCE requirements, scopes, and scope descriptions—in the complete Agent Card. The gateway neither derives those values from Enforcement-only configuration and secrets remain in the policies or gateway system configuration. Client secrets, access tokens, API-key values, password data, signing keys, and other credentials must never appear in the Agent Card. Multiple authentication policies
Operation-specific authorizationAuthentication and authorization must remain separate concerns. A common operation authentication policy validates the credential and establishes For example, The standard Agent Card has no field for declaring security requirements per operation. It may advertise the Agent-wide authentication scheme and the OAuth scopes available from the authorization server, but it cannot state that a particular scope is required only for A2A operation rate limitingUse the existing Attach an operation-specific limit under the canonical A2A operation name. Multiple quotas in the same policy can enforce shared operation capacity and a separate limit for each authenticated consumer: a2a:
operationConfigs:
operations:
- name: SendMessage
policies:
- name: advanced-ratelimit
version: v1
params:
quotas:
# Shared SendMessage capacity across all consumers.
- name: send-message-capacity
limits:
- limit: 1000
duration: 1m
# Per-consumer SendMessage capacity. The `auth` extractor is a
# proposed generic extension to Advanced Rate Limit.
- name: send-message-consumer
keyExtraction:
- type: auth
key: credentialId
limits:
- limit: 30
duration: 1mThe canonical operation The gateway must resolve the canonical A2A operation before selecting the policy chain. It must give a common operation-policy attachment one stable Agent-wide scope and an operation-specific policy attachment one stable Agent-and-operation scope. These scopes must be used as the base limiter scope: The detected transport can still be recorded for analytics. It must not create a separate quota bucket, because a caller could otherwise evade a limit by switching transports. The existing Advanced Rate Limit policy defaults to a route-name key and does not currently expose authenticated
Authentication must execute before a consumer-keyed rate-limit quota. If the configured authenticated identity is absent, that quota must reject the request instead of placing unidentified callers into a shared placeholder bucket. A public Agent that intentionally permits anonymous access should use an explicit alternative key, such as the trusted client IP. Advanced Rate Limit evaluates configured quotas independently. In the initial phase, the gateway does not guarantee atomic rollback across quotas: a quota evaluated before another quota rejects the request may already have been consumed. If atomic multi-quota evaluation becomes a requirement, it should be implemented in Advanced Rate Limit for every gateway workload rather than in an A2A-specific wrapper. Rate-limit rejection rendering is also transport-specific, but the limiting decision is not. The gateway's transport response layer must convert an Advanced Rate Limit rejection into a JSON-RPC error response for An Agent-wide limit uses the same For streaming operations, the policy charges the stream initiation; limiting the number of simultaneous or long-lived streams requires a separate concurrency control. JSON-RPC batch requests are rejected in the initial phase; supporting them later requires resolving and applying policies independently to each batch element. AnalyticsThe initial release captures the following business-facing A2A metrics. Public Agent Card requests are reported separately and do not count as Agent invocations.
High-cardinality identifiers such as consumer ID, |
Updated Agent Gateway designThe Agent Gateway design has evolved since the previous update. The main changes and newly settled details are below. Initial scope and protected Agent Cards
The remaining later-release items are unchanged: gRPC, scope-dependent skill access, OR/AND security-requirement semantics, GenAI traces, and A2A content guardrails. Routes created by the gatewayRoutes are generated independently for each routing partition/vhost. For A2A 1.0, the gateway knows 11 canonical operations.
With the default single vhost, the route counts before CORS routes are therefore:
The two transports may use the same base path because the JSON-RPC endpoint is the base path itself while HTTP+JSON operations have non-empty resource paths beneath it. The validator rejects only real route collisions and shadowing cases.
Canonical operation resolution and policy chainsThe gateway creates one canonical policy chain per A2A operation per routing partition, regardless of which transports are enabled. For A2A 1.0 this is always 11 operation chains per vhost—not 11 chains per transport. Equivalent requests over JSON-RPC and HTTP+JSON resolve to the same operation and therefore share the same chain. For example: The operation-chain order is: The lists are concatenated without deduplication or override. If the same policy is attached in two scopes, it executes twice as two policy instances. The public Agent Card has a separate route-key chain: Operation policies do not run on the public-card route. In passthrough mode there is no card responder and the request is proxied upstream with the standard-path rewrite. CORS preflights also use separate route-key chains. A preflight is generated only when its own chain can answer it. Operation-specific policies contribute only their resolved Every operation route carries the Agent's selected protocol version. The intended request contract is to validate the Route timeouts are disabled by default for the shared JSON-RPC route and streaming HTTP+JSON routes. An explicitly configured timeout takes precedence, while Agent Card model and signing configuration
{
"securityRequirements": [
{
"schemes": {
"gateway-jwt": {
"list": ["a2a.invoke"]
}
}
}
]
}
signing:
enabled: true
Analytics event modelAgent analytics no longer use a flat {
"agentAnalytics": {
"a2a": {
"requestType": "operation",
"operation": "SendMessage",
"transport": "JSONRPC",
"protocolVersion": "1.0",
"messageId": "message-123",
"taskId": "task-existing",
"contextId": "context-123",
"inputPartCount": 2,
"returnImmediately": false,
"historyLength": 5,
"isError": false,
"isStreaming": true,
"timeToFirstEventMs": 120,
"streamDurationMs": 850,
"payloadType": "task",
"responseTaskId": "task-456",
"responseContextId": "context-123",
"taskState": "TASK_STATE_COMPLETED",
"outcome": "SUCCESS"
}
}
}The A2A event properties are:
Request and response identifiers are kept apart so a mismatch between what a caller asked for and what an Agent answered with stays diagnosable; a consumer that wants one value derives Generic fields remain outside the A2A envelope, including Agent/API identity, organization, project, environment, authenticated High-cardinality values such as consumer ID, Other clarified limitations
|
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
What problem are we solving, and why now?
Enterprises are deploying AI agents — their own and third-party — that increasingly call each other over the Agent2Agent (A2A) protocol to delegate tasks. Today a platform/API team has no consistent way to put these agent-to-agent interactions under governance: each agent re-implements its own auth, there is no central identity, authorization ("which agent may call which"), rate limiting, or cost control, and no end-to-end visibility across a multi-agent call chain. Teams want to govern A2A agents the way they already govern their other agent traffic, without modifying agent code.
A2A is now stable. A2A reached v1.0 under the Linux Foundation — its first stable release. The earlier 0.2.x/0.3.x versions changed rapidly and introduced breaking changes, making it risky to build durable gateway support against them; v1.0 provides a stable, vendor-neutral contract we can implement against with confidence. The AI Gateway governs enterprise agent traffic (LLM access, MCP tools), configured through AI Workspace; A2A is the agent-to-agent pillar and is currently the gap.
Who are we solving it for?
Primarily platform / API teams and agent developers in enterprises adopting multi-agent systems, and their security & compliance stakeholders who must audit and constrain agent interactions. Secondarily, the agents themselves (internal systems acting as callers) that need a stable, governed endpoint to reach other agents.
Solution
Introduce a new proxy type (
a2aproxy) in the gateway and the AI Workspace to onboard agents.flowchart LR subgraph CP["Control Plane — AI Workspace"] REG["A2A agent onboarding · policies<br/>security config · analytics"] end CLIENT["A2A clients /<br/>calling agents"] subgraph DP["Data Plane — AI Gateway"] GW["a2aproxy<br/>• Agent Card mediation<br/>• Transports: JSON-RPC / HTTP+JSON<br/>• Authentication<br/>• Rate limiting<br/>• Guardrails<br/>• Analytics & observability"] end UP["Upstream A2A agents<br/>(A2A v1.x)"] REG <-->|"config · sync"| GW CLIENT -- "A2A request<br/>(JSON-RPC / HTTP+JSON, SSE)" --> GW GW -- "forward request" --> UPThe platform will support the following capabilities:
securityRequirements, skills, etc. based on the user requirements.User Stories
As an agent developer, I want to publish a rewritten Agent Card at the gateway's own well-known URL, so that clients discover the gateway endpoint and all traffic flows through governance.
Acceptance Criteria
/.well-known/agent-card.jsonrelative to the base path of the A2A proxy created, with the URLs rewritten to the gateway address.As an A2A client developer, I want to call the agent over either the JSON-RPC or the HTTP+JSON binding, so that I can use whichever transport my A2A SDK supports.
Acceptance Criteria
/path as well as specific REST endpoints based on the transport protocol used.As an agent developer, I want the gateway to authenticate every caller against a declared scheme before forwarding, so that only authorized clients can reach an agent.
Acceptance Criteria
As an agent developer, I want the public Agent Card to expose only non-sensitive metadata, so that discovery does not leak internal endpoints or sensitive skills.
Acceptance Criteria
As a security & compliance officer, I want the gateway to verify and optionally re-sign Agent Card signatures, so that clients can trust the card was not tampered with.
Acceptance Criteria
As an agent developer, I want to apply rate limits, so that expensive A2A operations can be constrained independently of cheap ones.
Acceptance Criteria
SendMessagevstasks/get) and enforced independently.As an agent developer, I want end-to-end traces and per-agent metrics for A2A traffic, so that I can monitor and troubleshoot multi-agent interactions.
Acceptance Criteria
gen_ai.agent.idand the A2A operation.Challenges
allOforanyOfsort of scenarios. Need to check whether we can support such scenarios through our policy approach.Card Mediation: acquisition, computation, and curation
The gateway serves a rewritten Agent Card, not the upstream one —
supportedInterfaces[].urlrewritten to the gateway,securitySchemes/securityRequirementsderived from the attached frontend-auth policy, upstream security stripped, and (optionally) re-signed with the gateway key + published JWKS.Acquisition: live proxy-and-rewrite vs. snapshot-and-serve
Two fundamentally different ways to obtain and maintain the served card.
Option 1 — Live proxy-and-rewrite. The gateway proxies the well-known path to the upstream at request time and rewrites the card's URLs on the fly (gateway address from
X-Forwarded-*).signCardforces a full JWS operation (canonicalization + signature) on every discovery request, a hot path clients hit before every session.Option 2 — Snapshot-and-serve at deploy time (recommended). The card is fetched once, snapshotted into the deployed config, and the controller computes the served card at deploy, serving it from a local, non-forwarding (synth) route at
/{context}/.well-known/agent-card.json(the same pattern used for MCP's OAuth Protected Resource Metadata).How the served card is computed (Option 2)
The pushed
kind: A2aspec carries inputs + curation, not a frozen card:agentCardUrl/ snapshotpoliciessecuritySchemes/securityRequirements+ skill visibilitycardOverridessignCardThe controller computes the served card at deploy — identically for AI Workspace and gateway-only:
Why the controller computes it (not AI Workspace): URL rewriting needs the runtime gateway address and re-signing needs the gateway's signing key — neither exists in the control plane. Since those steps are inherently gateway-side, all derivation lives in the controller. AI Workspace's role is to author the spec and preview; the controller turns the spec into the served card. This keeps both authoring modes behaviorally identical.
Authoring modes
Recommended —
agentCardUrl(AI Workspace auto-snapshots; gateway-only fetches at deploy). The user suppliesagentCardUrl; the controller derives everything a hand-authored card would spell out — the rewrittensupportedInterfaces[].url(fromcontext+ gateway host) andsecuritySchemes/securityRequirements(from thejwt-authpolicy). NoagentCardblock needed in the common case.Gateway-only override — inline
agentCard(a hand-authored snapshot). For air-gapped / no-egress deployments where the controller can't fetch upstream, the operator supplies the card inline; the controller still URL-rewrites and signs it. In AI Workspace this inline card is produced automatically as a snapshot (stored with the curation overrides) — the operator never hand-writes it.Note the inline snapshot carries source fields only — no
securitySchemes/securityRequirements(derived from thejwt-authpolicy) and nosignatures(the controller re-signs).Snapshot & drift
name,description,skills,capabilities,supportedInterfaces. Not upstreamsecuritySchemes/securityRequirements(derived from policies) and not upstreamsignatures(invalidated by the rewrite).Card editing & devportal publishing (discovery)
Editing the card in AI Workspace is valuable beyond mediation: the controller-generated card is a baseline, and the workspace lets operators curate it — descriptions, tags, surfaced skills, public-vs-extended split, docs links. The curated card then has two destinations:
This directly addresses the registry gap (A2A has no standardized registry yet, a2aproject/A2A#741) — the devportal becomes the discovery surface for governed A2A agents, exactly as it already is for REST/MCP/LLM APIs. Curation feeds both destinations from one edited card, published from the same versioned card revision to keep them consistent.
Proposal: acquire the card by snapshot-and-serve at deploy (not live proxy-and-rewrite — signing and curation both favor it); the controller computes the served card from the pushed spec (
agentCardUrl/snapshot +policies+cardOverrides+signCard), serving it from a synth well-known route and signing once at deploy. AI Workspace adds authoring + preview + curation on top, and publishes the curated card to both the gateway and the devportal. Upstream-auth secret note: the backend credential is always manual in both modes — the card reveals only which scheme, never the secret.Transports: JSON-RPC methods ↔ HTTP+JSON paths (v1.0)
Every A2A operation maps onto both bindings the gateway must support. In JSON-RPC everything hits
POST /and the operation is selected by themethodfield; in HTTP+JSON the operation is the HTTP verb + path. These are the routes the gateway classifies, authenticates, and rate-limits.POST /)SendMessagePOST /message:sendSendStreamingMessagePOST /message:streamSubscribeToTaskGETorPOST /tasks/{id}:subscribeGetTaskGET /tasks/{id}ListTasksGET /tasksCancelTaskPOST /tasks/{id}:cancelGetExtendedAgentCardGET /extendedAgentCardGET /.well-known/agent-card.json(The push-notification / webhook config operations —
CreateTaskPushNotificationConfigetc. /…/tasks/{id}/pushNotificationConfigs— are deferred along with the gateway-mediated webhook story and omitted here.)How the two bindings differ (same operations, different envelope):
POST /; the operation is chosen by themethodfield (PascalCase), andparamscarries the request message. Responses are wrapped in the JSON-RPC envelope ({"jsonrpc","id","result"}), withresultholding the typed payload (e.g.SendMessageResponse={"task": …}).POSTs the body is the bare request message (no JSON-RPC wrapper) and the response is the bare object.{id}is the task id.SendStreamingMessage,SubscribeToTask) returntext/event-stream(SSE) in both bindings; each frame is aStreamResponseenvelope ({"statusUpdate": …}/{"artifactUpdate": …}/{"task": …}), wrapped in a JSON-RPCresultfor JSON-RPC, or emitted bare for REST.All reactions