Skip to content

Instantly share code, notes, and snippets.

@rvennam
Created February 26, 2026 03:39
Show Gist options
  • Select an option

  • Save rvennam/0c8e4bd560803cd1102628716dc2ef56 to your computer and use it in GitHub Desktop.

Select an option

Save rvennam/0c8e4bd560803cd1102628716dc2ef56 to your computer and use it in GitHub Desktop.

Configure Route to Remote A2A Agent

Pre-requisites

This lab assumes that you have completed the setup in 001 and 002

Lab Objectives

  • Deploy a mock A2A agent and expose it as a remote service
  • Route to the remote A2A agent through AgentGateway
  • Validate A2A connectivity through the gateway
  • Observe A2A traffic in access logs

Overview

In this lab, we'll route to a remote Agent-to-Agent (A2A) server through AgentGateway. While we deploy the mock agent on the same cluster for simplicity, we expose it via a LoadBalancer and route to it by external IP — simulating a remote A2A server running outside the cluster.

What is A2A?

Agent-to-Agent (A2A) is an open protocol developed by Google that enables communication and interoperability between agentic applications. A2A defines a common language that enables agents to discover each other's capabilities, negotiate interaction modalities, and securely collaborate on tasks — regardless of the framework or vendor they are built on.

Why Route A2A Through AgentGateway?

Routing A2A traffic through AgentGateway provides:

  1. Centralized Observability: View metrics, logs, and traces for all agent-to-agent interactions
  2. Security Policies: Add authentication, authorization, and rate limiting to agent communication
  3. Unified Access: Single gateway endpoint for multiple agents, MCP servers, and LLM providers
  4. Traffic Management: Apply retry policies, timeouts, and other traffic controls

Step 1: Deploy the Mock A2A Agent

Deploy a simple echo agent that implements the A2A protocol. We expose it via a LoadBalancer to simulate a remote agent:

kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: a2a-agent
  namespace: default
  labels:
    app: a2a-agent
spec:
  selector:
    matchLabels:
      app: a2a-agent
  template:
    metadata:
      labels:
        app: a2a-agent
    spec:
      containers:
        - name: a2a-agent
          image: gcr.io/solo-public/docs/test-a2a-agent:latest
          ports:
            - containerPort: 9090
---
apiVersion: v1
kind: Service
metadata:
  name: a2a-agent-lb
  namespace: default
spec:
  selector:
    app: a2a-agent
  type: LoadBalancer
  ports:
    - protocol: TCP
      port: 9090
      targetPort: 9090
EOF

Wait for the pod to be ready and the LoadBalancer IP to be assigned:

kubectl wait --for=condition=ready pod -l app=a2a-agent -n default --timeout=60s

Get the external IP of the A2A agent:

export A2A_AGENT_IP=$(kubectl get svc a2a-agent-lb -n default -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo "A2A Agent IP: $A2A_AGENT_IP"

Verify Direct Connectivity

Before routing through AgentGateway, verify the agent works directly:

# Check the agent card
curl -s http://$A2A_AGENT_IP:9090/.well-known/agent.json | jq

You should see the agent card describing the Echo Agent and its capabilities:

{
  "name": "Echo Agent",
  "description": "This agent echos the input given",
  "url": "http://0.0.0.0:9090/",
  "version": "0.1.0",
  "capabilities": {
    "streaming": true,
    "pushNotifications": false,
    "stateTransitionHistory": false
  },
  "skills": [
    {
      "id": "my-project-echo-skill",
      "name": "Echo Tool",
      "description": "Echos the input given"
    }
  ]
}

Send a test task directly:

curl -s -X POST http://$A2A_AGENT_IP:9090/ \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tasks/send",
    "params": {
      "id": "direct-test",
      "message": {
        "role": "user",
        "parts": [{"type": "text", "text": "hello direct"}]
      }
    }
  }' | jq

Expected response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "id": "direct-test",
    "status": {
      "state": "completed",
      "message": {
        "role": "agent",
        "parts": [{"type": "text", "text": "on_send_task received: hello direct"}]
      }
    }
  }
}

Step 2: Create the Remote A2A Routing

Since there is no AgentgatewayBackend type for A2A, we use a headless Kubernetes Service with an Endpoints resource pointing to the remote agent's external IP. The key is setting appProtocol: kgateway.dev/a2a on the Service — this tells AgentGateway to handle the traffic using the A2A protocol.

kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  name: remote-a2a-agent
  namespace: agentgateway-system
spec:
  clusterIP: None
  ports:
    - protocol: TCP
      port: 9090
      targetPort: 9090
      appProtocol: kgateway.dev/a2a
---
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: remote-a2a-agent-1
  namespace: agentgateway-system
  labels:
    kubernetes.io/service-name: remote-a2a-agent
addressType: IPv4
ports:
  - port: 9090
    protocol: TCP
endpoints:
  - addresses:
      - "${A2A_AGENT_IP}"
EOF

Key Configuration Details:

  • clusterIP: None — Headless service since we provide our own endpoints
  • appProtocol: kgateway.dev/a2a — Tells AgentGateway to use A2A protocol handling
  • EndpointSlice — Points to the remote agent's external IP address

Create the HTTPRoute

Create an HTTPRoute with a /a2a path prefix. We use a URLRewrite filter to strip the prefix, since the A2A agent expects requests at the root path:

kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: remote-a2a
  namespace: agentgateway-system
spec:
  parentRefs:
  - name: agentgateway-proxy
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /a2a
    filters:
    - type: URLRewrite
      urlRewrite:
        path:
          type: ReplacePrefixMatch
          replacePrefixMatch: /
    backendRefs:
      - name: remote-a2a-agent
        port: 9090
EOF

Get Gateway IP

export GATEWAY_IP=$(kubectl get svc -n agentgateway-system --selector=gateway.networking.k8s.io/gateway-name=agentgateway-proxy -o jsonpath='{.items[*].status.loadBalancer.ingress[0].ip}{.items[*].status.loadBalancer.ingress[0].hostname}')

echo $GATEWAY_IP

Step 3: Verify A2A Through the Gateway

Fetch the Agent Card

curl -s http://$GATEWAY_IP:8080/a2a/.well-known/agent.json | jq

You should see the Echo Agent card returned through the gateway. Notice that AgentGateway automatically rewrites the agent's url field to reflect the gateway address.

Send a Task Through the Gateway

curl -s -X POST http://$GATEWAY_IP:8080/a2a \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tasks/send",
    "params": {
      "id": "gateway-test",
      "message": {
        "role": "user",
        "parts": [{"type": "text", "text": "hello through the gateway"}]
      }
    }
  }' | jq

Expected response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "id": "gateway-test",
    "status": {
      "state": "completed",
      "message": {
        "role": "agent",
        "parts": [{"type": "text", "text": "on_send_task received: hello through the gateway"}]
      }
    }
  }
}

Observability

View Access Logs

AgentGateway automatically logs A2A requests with protocol-specific metadata:

kubectl logs deploy/agentgateway-proxy -n agentgateway-system --tail 5 | grep a2a

You should see log entries with "protocol":"a2a" and A2A-specific fields like a2a.method:

{
  "level": "info",
  "scope": "request",
  "route": "agentgateway-system/remote-a2a",
  "http.method": "POST",
  "http.path": "/a2a",
  "http.status": 200,
  "protocol": "a2a",
  "a2a.method": "tasks/send"
}

View in Grafana

If you set up the monitoring stack in lab 002, you can view A2A request metrics and traces in the Grafana dashboard:

  1. Port-forward to Grafana:
kubectl port-forward svc/grafana-prometheus -n monitoring 3000:3000
  1. Open http://localhost:3000 and navigate to Dashboards > AgentGateway Dashboard

  2. Navigate to Home > Explore and select Tempo to view distributed traces for A2A requests

Cleanup

kubectl delete httproute -n agentgateway-system remote-a2a
kubectl delete endpointslice -n agentgateway-system remote-a2a-agent-1
kubectl delete svc -n agentgateway-system remote-a2a-agent
kubectl delete svc -n default a2a-agent-lb
kubectl delete deployment -n default a2a-agent

Key Takeaways

  • AgentGateway supports A2A protocol routing using the appProtocol: kgateway.dev/a2a annotation on Kubernetes Services
  • Remote A2A agents can be reached by creating a headless Service with an EndpointSlice pointing to the external IP
  • A URLRewrite filter is needed when using path-prefix routing, since most A2A agents expect requests at the root path
  • AgentGateway automatically detects the A2A protocol and provides protocol-aware logging with A2A-specific metadata
  • The same observability, security, and traffic management policies available for LLM and MCP traffic also apply to A2A traffic
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment