curl -X GET "$KONNECT_PROXY_URL/a2a/.well-known/agent-card.json" \
--no-progress-meter --fail-with-body
Proxy A2A agents through Kong AI Gateway
Create a service pointing to your A2A agent, add a route, and enable the AI A2A Proxy plugin. Kong proxies A2A JSON-RPC traffic and can export A2A metrics and payloads as OpenTelemetry span attributes.
Prerequisites
Tracing environment variables
Set the following OTel tracing variables before you configure the Data Plane:
export KONG_TRACING_INSTRUMENTATIONS=all
export KONG_TRACING_SAMPLING_RATE=1.0
Kong Konnect
This is a Konnect tutorial and requires a Konnect personal access token.
-
Create a new personal access token by opening the Konnect PAT page and selecting Generate Token.
-
Export your token to an environment variable:
export KONNECT_TOKEN='YOUR_KONNECT_PAT'Copied! -
Run the quickstart script to automatically provision a Control Plane and Data Plane, and configure your environment:
curl -Ls https://get.konghq.com/quickstart | bash -s -- -k $KONNECT_TOKEN -e KONG_TRACING_INSTRUMENTATIONS -e KONG_TRACING_SAMPLING_RATE --deck-outputCopied!This sets up a Konnect Control Plane named
quickstart, provisions a local Data Plane, and prints out the following environment variable exports:export DECK_KONNECT_TOKEN=$KONNECT_TOKEN export DECK_KONNECT_CONTROL_PLANE_NAME=quickstart export KONNECT_CONTROL_PLANE_URL=https://us.api.konghq.com export KONNECT_PROXY_URL='http://localhost:8000'Copied!Copy and paste these into your terminal to configure your session.
Kong Gateway running
This tutorial requires Kong Gateway Enterprise. If you don’t have Kong Gateway set up yet, you can use the quickstart script with an enterprise license to get an instance of Kong Gateway running almost instantly.
-
Export your license to an environment variable:
export KONG_LICENSE_DATA='LICENSE-CONTENTS-GO-HERE'Copied! -
Run the quickstart script:
curl -Ls https://get.konghq.com/quickstart | bash -s -- -e KONG_LICENSE_DATA \ -e KONG_TRACING_INSTRUMENTATIONS \ -e KONG_TRACING_SAMPLING_RATECopied!Once Kong Gateway is ready, you will see the following message:
Kong Gateway Ready
decK v1.43+
decK is a CLI tool for managing Kong Gateway declaratively with state files. To complete this tutorial, install decK version 1.43 or later.
This guide uses deck gateway apply, which directly applies entity configuration to your Gateway instance.
We recommend upgrading your decK installation to take advantage of this tool.
You can check your current decK version with deck version.
Required entities
For this tutorial, you’ll need Kong Gateway entities, like Gateway Services and Routes, pre-configured. These entities are essential for Kong Gateway to function but installing them isn’t the focus of this guide. Follow these steps to pre-configure them:
-
Run the following command:
echo ' _format_version: "3.0" services: - name: a2a-currency-agent url: http://host.docker.internal:10000 routes: - name: a2a-route paths: - "/a2a" strip_path: true service: name: a2a-currency-agent protocols: - http - https ' | deck gateway apply -Copied!
To learn more about entities, you can read our entities documentation.
OpenAI API key
This tutorial uses OpenAI:
- Create an OpenAI account.
- Get an API key.
-
Create a decK variable with the API key:
export DECK_OPENAI_API_KEY='YOUR OPENAI API KEY'Copied!
OpenTelemetry Collector
In this tutorial, we’ll collect data in OpenTelemetry Collector. Use the following command to launch a Collector instance with default configuration that listens on port 4318 and writes its output to a text file:
docker run \
--name otel-collector \
-p 127.0.0.1:4319:4318 \
otel/opentelemetry-collector:0.141.0 \
2>&1 | tee collector-output.txt
In a new terminal, export the OTEL Collector host. In this example, use the following host:
export DECK_OTEL_HOST=host.docker.internal
A2A agent
You need a running A2A-compliant agent. This guide uses a sample currency conversion agent from the A2A project.
Create a docker-compose.yaml file:
cat <<'EOF' > docker-compose.yaml
services:
a2a-agent:
container_name: a2a-currency-agent
build:
context: .
dockerfile_inline: |
FROM python:3.12-slim
WORKDIR /app
RUN pip install uv && apt-get update && apt-get install -y git
RUN git clone --depth 1 https://github.com/a2aproject/a2a-samples.git /tmp/a2a && \
cp -r /tmp/a2a/samples/python/agents/langgraph/* . && \
rm -rf /tmp/a2a
ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy
RUN uv sync --frozen --no-dev
EXPOSE 10000
CMD ["uv", "run", "app", "--host", "0.0.0.0"]
environment:
- model_source=openai
- API_KEY=${DECK_OPENAI_API_KEY}
- TOOL_LLM_URL=https://api.openai.com/v1
- TOOL_LLM_NAME=gpt-5.1
ports:
- "10000:10000"
EOF
Export your OpenAI API key and start the agent:
export DECK_OPENAI_API_KEY='your-openai-key'
docker compose up --build -d
The agent listens on port 10000 and uses the A2A JSON-RPC protocol to handle currency conversion queries. In this guide, the gateway service points to host.docker.internal:10000 instead of the container name because Kong Gateway runs in its own container with a separate DNS resolver.
Enable the AI A2A Proxy plugin
The AI A2A Proxy plugin parses A2A JSON-RPC requests and proxies them to the upstream agent. With logging enabled, the plugin records A2A metrics and payloads as OpenTelemetry span attributes.
echo '
_format_version: "3.0"
plugins:
- name: ai-a2a-proxy
config:
max_request_body_size: 0
logging:
log_statistics: true
log_payloads: true
' | deck gateway apply -
log_statistics adds A2A metrics to Kong log plugin output. log_payloads records request and response bodies, and requires log_statistics to be enabled. See the AI A2A Proxy plugin reference for all available parameters.
Retrieve the Agent Card
A2A agents expose their capabilities through an Agent Card at the /.well-known/agent-card.json endpoint. Retrieve it through the gateway:
curl -X GET "http://localhost:8000/a2a/.well-known/agent-card.json" \
--no-progress-meter --fail-with-body
You should see the following response:
{"capabilities":{"pushNotifications":true,"streaming":true},"defaultInputModes":["text","text/plain"],"defaultOutputModes":["text","text/plain"],"description":"Helps with exchange rates for currencies","name":"Currency Agent","preferredTransport":"JSONRPC","protocolVersion":"0.3.0","skills":[{"description":"Helps with exchange values between various currencies","examples":["What is exchange rate between USD and GBP?"],"id":"convert_currency","name":"Currency Exchange Rates Tool","tags":["currency conversion","currency exchange"]}],"url":"http://0.0.0.0:10000/","version":"1.0.0"}%
Enable the OpenTelemetry plugin
The OpenTelemetry plugin exports distributed traces for each A2A request to your Jaeger instance. Combined with the logging configuration on the AI A2A Proxy plugin, traces include A2A-specific span attributes.
echo '
_format_version: "3.0"
plugins:
- name: opentelemetry
config:
traces_endpoint: http://${{ env "DECK_OTEL_HOST" }}:4319/v1/traces
metrics:
endpoint: http://${otel.host}:4319/v1/metrics
enable_ai_metrics: true
resource_attributes:
service.name: kong-a2a
' | deck gateway apply -
The traces_endpoint points to the OpenTelemetry Collector’s OTLP HTTP receiver on port 4318. The service.name attribute identifies this AI Gateway instance in the collector output.
Send an A2A request
Send a message/send JSON-RPC request to the gateway route:
curl -X POST "$KONNECT_PROXY_URL/a2a" \
--no-progress-meter --fail-with-body \
-H "Content-Type: application/json" \
--json '{
"jsonrpc": "2.0",
"id": "1",
"method": "message/send",
"params": {
"message": {
"kind": "message",
"messageId": "msg-001",
"role": "user",
"parts": [
{
"kind": "text",
"text": "How much is 100 USD in EUR?"
}
]
}
}
}'
curl -X POST "http://localhost:8000/a2a" \
--no-progress-meter --fail-with-body \
-H "Content-Type: application/json" \
--json '{
"jsonrpc": "2.0",
"id": "1",
"method": "message/send",
"params": {
"message": {
"kind": "message",
"messageId": "msg-001",
"role": "user",
"parts": [
{
"kind": "text",
"text": "How much is 100 USD in EUR?"
}
]
}
}
}'
Kong Gateway proxies the request to the A2A agent and returns the agent’s JSON-RPC response. A successful response contains either a completed task with artifacts, or a task in input-required state if the agent needs more information.
Validate traces
You should see data in your OpenTelemetry Collector terminal. You can also search for kong-a2a in the collector-output.txt output file. You should see the following data:
ResourceSpans #0
Resource SchemaURL:
Resource attributes:
-> service.instance.id: Str(9c214152-1621-456a-8b42-6f1309dac551)
-> service.name: Str(kong-a2a)
-> service.version: Str(3.14.0.0)
ScopeSpans #0
ScopeSpans SchemaURL:
InstrumentationScope kong-internal 0.1.0
Span #0
Trace ID : 1bfc19e17dd9121769882cd9b8bf5de1
Parent ID :
ID : 779db508077de69f
Name : kong
Kind : Server
Start time : 2026-04-03 06:48:41.446000128 +0000 UTC
End time : 2026-04-03 06:48:47.139977728 +0000 UTC
Status code : Unset
Status message :
Attributes:
-> http.flavor: Str(1.1)
-> http.route: Str(/a2a)
-> http.url: Str(http://localhost/a2a)
-> http.scheme: Str(http)
-> http.client_ip: Str(192.168.65.1)
-> http.method: Str(POST)
-> net.peer.ip: Str(192.168.65.1)
-> http.status_code: Int(200)
-> http.host: Str(localhost)
-> kong.request.id: Str(8221291c2cac1842d7c77118ca409e6a)
Span #1
Trace ID : 1bfc19e17dd9121769882cd9b8bf5de1
Parent ID : 779db508077de69f
ID : a3b699c33700feee
Name : kong.router
Kind : Internal
Start time : 2026-04-03 06:48:41.446752256 +0000 UTC
End time : 2026-04-03 06:48:41.44679424 +0000 UTC
Status code : Unset
Status message :
Span #2
Trace ID : 1bfc19e17dd9121769882cd9b8bf5de1
Parent ID : 779db508077de69f
ID : de4e6ed2c16a2dd3
Name : kong.access.plugin.ai-a2a-proxy
Kind : Internal
Start time : 2026-04-03 06:48:41.446919936 +0000 UTC
End time : 2026-04-03 06:48:41.447105024 +0000 UTC
Status code : Unset
Status message :
Span #3
Trace ID : 1bfc19e17dd9121769882cd9b8bf5de1
Parent ID : de4e6ed2c16a2dd3
ID : 240b2b9ac3ac9e38
Name : kong.a2a
Kind : Internal
Start time : 2026-04-03 06:48:41.44707456 +0000 UTC
End time : 2026-04-03 06:48:47.140356608 +0000 UTC
Status code : Unset
Status message :
Attributes:
-> kong.a2a.protocol.version: Str(unknown)
-> rpc.system: Str(jsonrpc)
-> rpc.method: Str(message/send)
-> kong.a2a.task.id: Str(8a98bbbf-7d09-4336-b3aa-afe73e3a38d3)
-> kong.a2a.task.state: Str(completed)
-> kong.a2a.context.id: Str(df2e34aa-27ce-44ee-b5d3-3130b4f10985)
-> kong.a2a.operation: Str(message/send)
Span #4
Trace ID : 1bfc19e17dd9121769882cd9b8bf5de1
Parent ID : 779db508077de69f
ID : c1573adfe53ae258
Name : kong.access.plugin.opentelemetry
Kind : Internal
Start time : 2026-04-03 06:48:41.447129088 +0000 UTC
End time : 2026-04-03 06:48:41.447464448 +0000 UTC
Status code : Unset
Status message :
Span #5
Trace ID : 1bfc19e17dd9121769882cd9b8bf5de1
Parent ID : 779db508077de69f
ID : 1c44c62490a4dc00
Name : kong.dns
Kind : Client
Start time : 2026-04-03 06:48:41.44754304 +0000 UTC
End time : 2026-04-03 06:48:41.447862272 +0000 UTC
Status code : Unset
Status message :
Attributes:
-> dns.record.port: Double(10000)
-> dns.record.ip: Str(172.18.0.2)
-> dns.record.domain: Str(a2a-currency-agent)
Span #6
Trace ID : 1bfc19e17dd9121769882cd9b8bf5de1
Parent ID : 779db508077de69f
ID : 811a109d1908068d
Name : kong.header_filter.plugin.ai-a2a-proxy
Kind : Internal
Start time : 2026-04-03 06:48:47.139697664 +0000 UTC
End time : 2026-04-03 06:48:47.139731712 +0000 UTC
Status code : Unset
Status message :
Span #7
Trace ID : 1bfc19e17dd9121769882cd9b8bf5de1
Parent ID : 779db508077de69f
ID : ff3f295f3b8cf464
Name : kong.header_filter.plugin.opentelemetry
Kind : Internal
Start time : 2026-04-03 06:48:47.139753728 +0000 UTC
End time : 2026-04-03 06:48:47.1397632 +0000 UTC
Status code : Unset
Status message :
Span #8
Trace ID : 1bfc19e17dd9121769882cd9b8bf5de1
Parent ID : 779db508077de69f
ID : f8718c5342d3bc70
Name : kong.balancer
Kind : Client
Start time : 2026-04-03 06:48:41.447897088 +0000 UTC
End time : 2026-04-03 06:48:47.139977728 +0000 UTC
Status code : Unset
Status message :
Attributes:
-> net.peer.ip: Str(172.18.0.2)
-> net.peer.port: Double(10000)
-> net.peer.name: Str(a2a-currency-agent)
-> try_count: Double(1)
-> peer.service: Str(a2a-currency-agent)
Validate metrics
You should also see metrics data in the OpenTelemetry Collector output. Search for kong.gen_ai.a2a in the collector-output.txt file. You should see the following data:
ResourceMetrics #0
Resource SchemaURL:
Resource attributes:
-> service.instance.id: Str(9c214152-1621-456a-8b42-6f1309dac551)
-> service.name: Str(kong-a2a)
-> service.version: Str(3.14.0.0)
ScopeMetrics #0
ScopeMetrics SchemaURL:
InstrumentationScope kong-internal 0.1.0
Metric #0
Descriptor:
-> Name: kong.gen_ai.a2a.request.duration
-> Description: Measures A2A request duration in seconds.
-> Unit: s
-> DataType: Histogram
-> AggregationTemporality: Cumulative
HistogramDataPoints #0
Data point attributes:
-> kong.service.name: Str(a2a-currency-agent)
-> kong.route.name: Str(a2a-route)
-> kong.gen_ai.a2a.method: Str(message/send)
-> kong.workspace.name: Str(default)
-> kong.gen_ai.a2a.binding: Str(jsonrpc)
StartTimestamp: 2026-04-03 06:40:44.823196672 +0000 UTC
Timestamp: 2026-04-03 06:48:47.141009664 +0000 UTC
Count: 3
Sum: 20.365000
Min: 5.692000
Max: 8.950000
Metric #1
Descriptor:
-> Name: kong.gen_ai.a2a.response.size
-> Description: Measures A2A response body size in bytes.
-> Unit: By
-> DataType: Histogram
-> AggregationTemporality: Cumulative
HistogramDataPoints #0
Data point attributes:
-> kong.service.name: Str(a2a-currency-agent)
-> kong.route.name: Str(a2a-route)
-> kong.gen_ai.a2a.method: Str(message/send)
-> kong.workspace.name: Str(default)
-> kong.gen_ai.a2a.binding: Str(jsonrpc)
StartTimestamp: 2026-04-03 06:40:44.823648 +0000 UTC
Timestamp: 2026-04-03 06:48:47.141217024 +0000 UTC
Count: 3
Sum: 3994.000000
Min: 1304.000000
Max: 1345.000000
Metric #2
Descriptor:
-> Name: kong.gen_ai.a2a.request.count
-> Description: Counts A2A requests.
-> Unit: {request}
-> DataType: Sum
-> IsMonotonic: true
-> AggregationTemporality: Cumulative
NumberDataPoints #0
Data point attributes:
-> kong.service.name: Str(a2a-currency-agent)
-> kong.route.name: Str(a2a-route)
-> kong.gen_ai.a2a.method: Str(message/send)
-> kong.workspace.name: Str(default)
-> kong.gen_ai.a2a.binding: Str(jsonrpc)
StartTimestamp: 2026-04-03 06:40:44.822096128 +0000 UTC
Timestamp: 2026-04-03 06:48:47.14095616 +0000 UTC
Value: 3
Metric #3
Descriptor:
-> Name: kong.gen_ai.a2a.task.state.count
-> Description: Counts A2A task state transitions.
-> Unit: {state}
-> DataType: Sum
-> IsMonotonic: true
-> AggregationTemporality: Cumulative
NumberDataPoints #0
Data point attributes:
-> kong.workspace.name: Str(default)
-> kong.service.name: Str(a2a-currency-agent)
-> kong.route.name: Str(a2a-route)
-> kong.gen_ai.a2a.task.state: Str(completed)
StartTimestamp: 2026-04-03 06:40:44.824023552 +0000 UTC
Timestamp: 2026-04-03 06:48:47.141275648 +0000 UTC
Value: 3
Cleanup
Clean up Konnect environment
If you created a new control plane and want to conserve your free trial credits or avoid unnecessary charges, delete the new control plane used in this tutorial.
Destroy the Kong Gateway container
curl -Ls https://get.konghq.com/quickstart | bash -s -- -d
Stop the A2A agent and OpenTelemetry Collector
Stop and remove the sample A2A agent and OpenTelemetry Collector containers:
docker compose down
docker rm -f otel-collector
FAQs
What is the A2A protocol?
The Agent2Agent (A2A) protocol is an open standard originally developed by Google that defines how AI agents communicate with each other. It uses JSON-RPC over HTTP and supports capability discovery through Agent Cards, task lifecycle management, multi-turn conversations, and streaming responses. See the A2A protocol documentation for the full specification.
How is A2A different from MCP?
MCP (Model Context Protocol) standardizes how agents connect to tools, APIs, and data sources. A2A standardizes how agents communicate with other agents. They are complementary: use MCP for agent-to-tool communication and A2A for agent-to-agent communication.
Can I add authentication to the A2A endpoint?
Yes. Apply any Kong Gateway authentication plugin (Key Auth, OAuth2, JWT, etc.) to the same service or route. The AI A2A Proxy plugin handles A2A protocol concerns independently of authentication.