| title | AI A2A Proxy | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| name | AI A2A Proxy | ||||||||||
| content_type | plugin | ||||||||||
| tier | ai_gateway_enterprise | ||||||||||
| publisher | kong-inc | ||||||||||
| description | Provide observability and gateway control for Agent-to-Agent (A2A) protocol traffic, supporting both JSON-RPC and REST bindings | ||||||||||
| products |
|
||||||||||
| works_on |
|
||||||||||
| topologies |
|
||||||||||
| min_version |
|
||||||||||
| categories |
|
||||||||||
| icon | ai-a2a-proxy.png | ||||||||||
| search_aliases |
|
||||||||||
| notes | This plugin cannot be scoped to individual consumers or consumer groups. Apply it at the service or route level only. |
The AI A2A Proxy plugin provides observability and control for Agent-to-Agent (A2A) protocol traffic routed through {{site.ai_gateway}}. It detects and processes A2A requests using both JSON-RPC and REST protocol bindings, rewrites agent card URLs to the gateway address, and feeds structured A2A metrics into the Konnect analytics pipeline and OpenTelemetry tracing.
The plugin operates as a transparent proxy. It does not modify request routing, aggregate responses, or manage task state. When config.logging.log_statistics is enabled, it removes the Accept-Encoding request header to prevent compressed upstream responses. Agent card responses have their url field rewritten to the {{site.ai_gateway}} address; all other traffic passes through without modification.
The A2A protocol defines the following fundamental communication elements between Agents. The plugin surfaces data tied to these elements in its log output and OpenTelemetry spans.
{% table %} columns:
- title: Element key: element
- title: Description key: description
- title: Purpose key: purpose rows:
- element: Agent Card description: A JSON metadata document describing an agent's identity, capabilities, endpoint, skills, and authentication requirements. purpose: Enables clients to discover agents and understand how to interact with them.
- element: Task description: A stateful unit of work initiated by an agent, with a unique ID and defined lifecycle. purpose: Tracks long-running operations and supports multi-turn interactions.
- element: Message
description: A single turn of communication between a client and an agent, containing content and a role (
useroragent). purpose: Conveys instructions, context, questions, answers, or status updates that are not formal artifacts. - element: Part
description: The fundamental content container (for example,
TextPart,FilePart,DataPart) used within messages and artifacts. purpose: Provides flexibility for agents to exchange different content types within messages and artifacts. - element: Artifact description: A tangible output generated by an agent during a task (for example, a document, image, or structured data). purpose: Carries the concrete output of a task in a structured, retrievable form. {% endtable %}
The plugin runs in four phases:
- Access phase: Detects A2A protocol binding (JSON-RPC or REST). Starts an OpenTelemetry span when
config.logging.log_statisticsis enabled. Records the request body for payload logging whenconfig.logging.log_payloadsis enabled. - Header filter phase: Detects streaming responses (
Content-Type: text/event-stream) and records time to first byte (TTFB). Buffers agent card responses for URL rewriting. - Body filter phase: Streams SSE chunks through to the client without buffering, preserving low latency. Buffers non-streaming responses to extract task metadata. Rewrites agent card URLs to the gateway address. Emits analytics data to the Konnect pipeline at end-of-response.
- Log phase: Finalizes the OpenTelemetry span with task state, task ID, and error information.
{% mermaid %}
sequenceDiagram
autonumber
participant Client as A2A Client
participant Kong as Kong Gateway
(AI A2A Proxy)
participant Agent as Upstream A2A Agent
Client->>Kong: A2A request (JSON-RPC or REST)
Note over Kong: Detect A2A binding and method<br>Start OTel span (if logging enabled)
Kong->>Agent: Proxied request<br>(Accept-Encoding removed if logging enabled)
alt Streaming response (SSE)
Agent-->>Kong: text/event-stream chunks
Note over Kong: Pass through each chunk<br>Count SSE events, track TTFB
Kong-->>Client: SSE chunks (unchanged)
Note over Kong: On final chunk:<br>Extract task state, set analytics
else Non-streaming response
Agent->>Kong: JSON response
Note over Kong: Buffer response<br>Extract task metadata
Kong->>Client: Response (unchanged)
end
Note over Kong: Finish OTel span<br>Emit ai.a2a metrics to log plugins
{% endmermaid %}
The plugin auto-detects A2A traffic without requiring explicit configuration per route. It inspects each request and applies A2A processing only when a match is found; non-A2A traffic passes through without overhead. Detection works across two protocol bindings:
REST binding. The plugin detects A2A endpoints by path suffix and HTTP method. The match anchors to the end of the request path, so any prefix added by the Kong Route is ignored. For example, both /v1/message:send and /api/agents/v1/message:send match SendMessage:
{% table %} columns:
- title: HTTP method key: method
- title: Path suffix key: path
- title: A2A operation key: operation
- title: Canonical method key: canonical rows:
- method: "
POST" path: "/v1/message:send" operation: SendMessage canonical: "message/send" - method: "
POST" path: "/v1/message:stream" operation: SendStreamingMessage canonical: "message/stream" - method: "
GET" path: "/.well-known/agent-card.json" operation: GetAgentCard canonical: "agent/getCard" - method: "
GET" path: "/v1/extendedAgentCard" operation: GetExtendedAgentCard canonical: "agent/getExtendedAgentCard" - method: "
GET" path: "/v1/tasks/{id}" operation: GetTask canonical: "tasks/get" - method: "
GET" path: "/v1/tasks" operation: ListTasks canonical: "tasks/list" - method: "
POST" path: "/v1/tasks/{id}:cancel" operation: CancelTask canonical: "tasks/cancel" - method: "
POST" path: "/v1/tasks/{id}:subscribe" operation: SubscribeToTask canonical: "tasks/resubscribe" - method: "
POST" path: "/v1/tasks" operation: ListTasks canonical: "tasks/list" {% endtable %}
The canonical method name is used in OTel span attributes and log output.
JSON-RPC binding. Detected by the "jsonrpc" field in the request body, combined with a recognized method name or an A2A-Version request header. Recognized methods: message/send, message/stream, tasks/get, tasks/list, tasks/cancel, tasks/resubscribe, tasks/pushNotificationConfig/set, tasks/pushNotificationConfig/get, tasks/pushNotificationConfig/list, tasks/pushNotificationConfig/delete, agent/getExtendedAgentCard.
A request carrying an A2A-Version header is treated as JSON-RPC even if the method is not in the known list. When an unrecognized method is accepted this way, the method field in log output is recorded as "unknown" to bound metric cardinality. The OTel span's kong.a2a.operation attribute still receives the actual method name.
When an upstream agent returns an agent card document at /.well-known/agent-card.json, the plugin rewrites the url field — and any additionalInterfaces[].url fields — to the gateway address. A2A clients then discover the gateway endpoint rather than the upstream agent's direct address.
The rewrite uses X-Forwarded-* headers to construct the correct scheme, host, and port when Kong is deployed behind a load balancer or reverse proxy.
For responses with Content-Type: text/event-stream, the plugin passes SSE chunks through to the client without buffering, preserving low latency. It counts SSE events and extracts task state from the final event for analytics. TTFB is measured from request receipt to the first response header.
Enable observability with config.logging.log_statistics.
When enabled, the plugin:
- Starts an OpenTelemetry span per A2A request
- Records A2A method, binding type, task state, task ID, context ID, latency, TTFB (streaming), SSE event count, and response size
- Emits structured data to the
ai.a2anamespace consumed by Konnect analytics and attached Kong log plugins
{:.info}
When
log_statisticsis enabled, the plugin removes theAccept-Encodingrequest header before forwarding to the upstream agent. This prevents compressed responses that the plugin cannot parse for metadata extraction.
To also capture request and response bodies, enable
config.logging.log_payloads.
This field requires log_statistics to also be enabled. Payloads are truncated at
config.logging.max_payload_size (default 1 MB).
max_payload_size must be greater than 0; set max_request_body_size to 0 instead if you need
unlimited capture for request detection.
{:.warning}
Payload logging may expose sensitive data. Enable with care in production environments.
You can view AI A2A Proxy analytics in {{site.konnect_short_name}} Explorer and Dashboards using the agentic usage analytics.
{% include /plugins/ai-a2a-proxy/log-output-fields.md %}
Task state values are normalized to lowercase A2A spec format regardless of the upstream SDK version:
submitted, working, input-required, completed, canceled, failed, rejected, auth-required, unknown.
When config.logging.log_statistics is enabled and Kong tracing is configured, the plugin creates a kong.a2a child span with the following attributes:
{% include /plugins/ai-a2a-proxy/otel-span-attributes.md %}
The plugin reads the request body to detect JSON-RPC A2A requests. Use
config.max_request_body_size to control the maximum body size parsed for detection (default 1 MB). Set to 0 for no limit. REST requests are detected by path and HTTP method without reading the body, so this setting applies to JSON-RPC detection only.
If a request body exceeds the limit, the plugin logs a warning and skips A2A detection for that request; the request is still proxied upstream.