DEV Community

Kuldeep Paul
Kuldeep Paul

Posted on Edited on

Agent Gateway for A2A Agents: A Bifrost Setup Guide

TL;DR

  • An agent gateway places one controlled endpoint between applications and A2A agents, so access checks and logs live in one place instead of inside each agent.
  • Bifrost v2.3.0 added Agent Gateway: register an agent by its agent card URL and callers reach it at /agents/a2a/<name>/ over JSON-RPC, REST, or gRPC.
  • Agent access is granted per virtual key and is separate from model and tool access, so an agent grant never authorizes the agent's own LLM or MCP calls.
  • Two headers, x-bf-session-id (A2A context ID) and x-bf-agent-correlation-id (A2A task ID), link every model and tool call back to the agent task that made it.

An agent gateway is a proxy layer that authenticates callers, routes requests to registered agents, and records each agent operation, applying to agent-to-agent traffic what an LLM gateway applies to model calls. Bifrost, the open-source AI gateway written in Go, shipped Agent Gateway in v2.3.0, so the same deployment that governs model and MCP traffic now governs calls to agents that speak the A2A protocol. This tutorial registers one A2A agent, locks down who can call it, connects its model and tool calls, and follows a full session in Agent Logs.

What an agent gateway does for A2A traffic

An agent gateway gives every A2A agent a stable, governed address. Clients call the gateway instead of the agent; the gateway checks the caller's credential against the agent's grants, forwards the request over the agent's preferred transport, and writes one log row per A2A operation and stream event, so access and audit stop being per-agent code.

Without a gateway, each agent implements its own authentication, each caller holds a separate credential per agent, and the only record of a task lives in that agent's logs. The A2A protocol standardizes how agents exchange messages, tasks, and artifacts, but it does not decide which applications may call which agent, or where the evidence of a run is kept. That gap is what the gateway closes.

An application calls Bifrost over A2A, Bifrost forwards to the registered agent, and the agent's model and tool calls return through the Bifrost LLM and MCP gateway

Figure 1: One deployment sits on both sides of the agent: inbound A2A calls and the agent's own model and tool calls.

As Figure 1 shows, the agent's outbound model and tool calls can pass through the same Bifrost instance. That is what makes a single session traceable end to end, from the caller's message to every LLM completion and MCP tool call the agent made to answer it. The agent gateway versus LLM gateway comparison covers where the two layers differ.

A2A protocol terms you will see in the gateway

The A2A protocol describes agent communication with five objects: an agent card, messages, tasks, contexts, and artifacts. Bifrost uses all five: it reads the card at registration, logs each message and task, groups operations by context ID into sessions, and stores artifacts for inspection in Agent Logs.

A2A term What it is Where it shows up in Bifrost
Agent card The agent's directory entry: endpoints, skills, capabilities, and authentication Fetched at registration and rewritten with gateway endpoints on every card request
Message Something a user or agent says, with text, files, or structured data The Conversation view of an operation
Task A unit of work with an ID and a state Task state filters and the x-bf-agent-correlation-id header
Context The conversation that connects related work The session you open in Agent Logs, and the x-bf-session-id header
Artifact An output of the work, such as a report or file Artifact updates in the Events tab

The messages, tasks, and events guide walks through the A2A lifecycle in more depth, and the A2A specification defines each object. The gateway accepts A2A v1 and rejects an explicitly incompatible A2A-Version header.

Prerequisites

You need three things before the first registration:

  • A running Bifrost gateway on v2.3.0 or later. Start one locally with npx -y @maximhq/bifrost or docker run -p 8080:8080 -v $(pwd)/data:/app/data maximhq/bifrost, as described in the gateway setup guide.
  • A reachable A2A agent that serves its agent card, for example at https://research.example.com/.well-known/agent-card.json. The examples below use an agent named library-research.
  • A virtual key for the calling application, created under Governance → Virtual Keys. Virtual keys are the credential callers present to Bifrost.

Agents on a private network need one more setting: add their hostnames, IPs, or CIDR ranges to Agents → Agent Settings → Private Network Allowlist. Bifrost checks every destination before connecting, and link-local and cloud metadata addresses stay blocked even when an entry matches them.

Step 1: Set the external client URL

Agent Gateway protocol endpoints stay disabled until you tell Bifrost its public base URL. Set External Client URL under Agents → Agent Settings, or client.a2a_external_client_url in config.json. Without it, agent card, JSON-RPC, REST, and push callback endpoints return 503 Service Unavailable.

{
  "client": {
    "a2a_external_client_url": "https://bifrost.example.com",
    "enforce_auth_on_inference": true
  }
}
Enter fullscreen mode Exit fullscreen mode

Bifrost uses this URL in the endpoints it advertises inside served agent cards and in push notification callback URLs. It does not infer the value from Host or forwarded headers, so set it explicitly even behind a reverse proxy. The enforce_auth_on_inference flag matters for access control: without it, requests that present no identity can pass anonymously, and explicit agent grants do not make anonymous traffic private. The gateway settings and transports page lists the Helm equivalents.

Step 2: Register the agent from its agent card

Registration takes the full agent card URL, not the agent's base URL. In the dashboard, open Agents → Agent Catalog → New Agent, name the registration (lowercase letters, digits, and single hyphens), paste the card URL, and select Fetch to preview the agent's skills, interfaces, and advertised authentication.

The registration name becomes part of every gateway route, so pick it deliberately. For declarative deployments, the same registration goes in a top-level agents array, which Bifrost reconciles by name at startup:

{
  "agents": [
    {
      "name": "library-research",
      "agent_card_url": "https://research.example.com/.well-known/agent-card.json",
      "enabled": true,
      "allow_by_default": false,
      "virtual_key_ids": ["YOUR_VIRTUAL_KEY_ID"],
      "forward_accepted_credential": true,
      "runtime_auth": {
        "type": "headers",
        "headers": { "Authorization": "env.RESEARCH_AGENT_AUTHORIZATION" }
      }
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

A few details from the agent registration guide prevent most first-run failures:

  • virtual_key_ids takes key IDs, never the secret sk-bf-... values.
  • runtime_auth is the credential Bifrost sends to the agent. Card fetch credentials for a private card are configured separately.
  • forward_accepted_credential: true passes the caller's Bifrost credential through to the agent, which Step 4 relies on.
  • PUT /api/agents/{name} replaces the whole registration, so include every setting you want to keep.

After registration, each agent has these client-facing endpoints:

Transport Endpoint for library-research
Agent card https://bifrost.example.com/agents/a2a/library-research/.well-known/agent-card.json
JSON-RPC (with SSE streaming) https://bifrost.example.com/agents/a2a/library-research/json-rpc
HTTP+JSON (REST) https://bifrost.example.com/agents/a2a/library-research/rest
gRPC, when enabled library-research.a2a.example.com:443

Point clients at the gateway's agent card, not the original server card, so discovery returns gateway routes. Each request for that card fetches the current card from the agent server and rewrites its endpoints and authentication; Bifrost does not serve a cached copy if the fetch fails. Because the rewrite invalidates any signature over the upstream document, Bifrost removes the original card signatures from the card it serves.

Step 3: Grant agent access to virtual keys

Agent access is a grant from a virtual key to a named agent. A key granted library-research can call that agent's gateway routes and no other agent. Turn on Allow any virtual key only when every valid key should reach the agent; otherwise keep allow_by_default false and list keys explicitly.

An A2A request passes credential validation, the agent grant check, and transport selection before reaching the agent, while refused calls return an error

Figure 2: The grant check runs before Bifrost opens any connection to the agent, so a denied caller never reaches it.

Grants are managed from either side: the agent's Access section or the virtual key's Agent access field. In config.json, a virtual key can also declare agent_grants: [{ "agent_name": "library-research" }] under governance.virtual_keys. The agent access guide covers both forms.

Two rules shape the design of your keys:

  • Agent access is separate from model and tool access. An agent can pass its gateway access check and still have its own LLM or MCP request denied, because those requests use the caller's provider and tool grants.
  • Denials surface as protocol errors. A JSON-RPC client that is refused receives an error field with a code such as -31403, so check the response body as well as the HTTP status.

Teams on Bifrost Enterprise can grant agents through access profiles, which apply the same agent list to users, teams, business units, and customers, and can scope a single request to a project with the x-bf-project-name header. The governance resource page summarizes how these grants combine with budgets and rate limits.

Step 4: Send the first A2A request through Bifrost

With the agent registered and a key granted, fetch the gateway card and send a message over JSON-RPC. The examples use A2A v1 method and field names, and the x-bf-vk header carries the virtual key.

curl http://localhost:8080/agents/a2a/library-research/.well-known/agent-card.json \
  -H "x-bf-vk: $BIFROST_VK"

curl http://localhost:8080/agents/a2a/library-research/json-rpc \
  -H "x-bf-vk: $BIFROST_VK" \
  -H "Content-Type: application/json" \
  -H "A2A-Version: 1.0" \
  -d '{
    "jsonrpc": "2.0",
    "id": "research-1",
    "method": "SendMessage",
    "params": {
      "message": {
        "messageId": "question-1",
        "role": "user",
        "parts": [{"text": "Explain how to reuse one HTTP client across requests."}]
      }
    }
  }'
Enter fullscreen mode Exit fullscreen mode

The agent may answer with a message or a task. For long-running work, register a push notification callback through the gateway: Bifrost gives the agent its own callback URL at <base-url>/agents/a2a/<agent-name>/push/callback and relays updates to your application, which keeps delivery attempts observable. The push notifications guide shows the registration call.

Step 5: Connect the agent's model and tool calls

To see the LLM and MCP calls an agent made for a task, the agent must send them through Bifrost with two correlation headers and the caller's credential. Set x-bf-session-id to the A2A context ID and x-bf-agent-correlation-id to the A2A task ID on every model and tool request.

The A2A context ID becomes x-bf-session-id and the task ID becomes x-bf-agent-correlation-id on the agent's LLM and MCP calls, which Agent Logs links together

Figure 3: Two IDs the agent already has become two headers, and that is all Agent Logs needs to join a task to its model and tool calls.

With credential forwarding enabled in Step 2, the agent reads the caller's accepted credential and reuses it, so spend and permissions stay attributed to the original caller rather than to a shared agent key:

curl http://localhost:8080/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "x-bf-vk: $CALLER_BIFROST_VK" \
  -H "x-bf-session-id: $A2A_CONTEXT_ID" \
  -H "x-bf-agent-correlation-id: $A2A_TASK_ID" \
  -d '{
    "model": "openai/gpt-4o-mini",
    "messages": [{"role": "user", "content": "Explain how to reuse one HTTP client across requests."}]
  }'
Enter fullscreen mode Exit fullscreen mode

MCP tool calls take the same three headers on POST /v1/mcp/tool/execute. The model and tool call integration guide adds one warning worth repeating: an agent can keep working after the inbound HTTP request ends, so store each task's credential and IDs in that task's own execution state. A shared, mutable header map can attribute one caller's work to another. Tools reached this way are governed by the MCP gateway, with the same per-key tool filtering as any other MCP call.

Step 6: Read the session in Agent Logs

Agent Logs, under Observability, shows every A2A operation and stream event the gateway handled. Group by session to follow one A2A context across its tasks, then open the LLM Logs and MCP Logs tabs to see the model and tool calls that carried the matching session and correlation IDs.

The operation detail view exposes these tabs, depending on what the operation produced:

View What to look for
Conversation The message, returned task, status text, and artifacts
Events Ordered task-state and artifact updates for streaming operations
LLM Logs Model calls carrying this task ID, with latency, tokens, and cost
MCP Logs Tool calls carrying this task ID
Raw Capture Stored HTTP request and response bytes, when raw capture is on

Tokens and model cost belong to the correlated LLM logs; the A2A operation itself carries timing, not a separate token charge. Raw capture is off by default and applies only to new operations, so turn it on under Agent Settings before reproducing an issue. The Agent Logs guide also covers filters by agent, status, task state, and caller, and Bifrost exports A2A client spans to the same connectors used for AI observability.

Transports and recovery behavior

Bifrost picks the agent-facing transport from the agent card's supportedInterfaces list, in card order, independent of the client's transport. A REST client can reach a JSON-RPC-only agent, and a JSON-RPC client can reach a gRPC agent. To change the preference, reorder the interfaces in the card your agent serves.

Recovery is deliberately conservative, because replaying an A2A request can create a second task:

  • Dial failures and 404, 405, 410, or 426 responses move the current unary operation to the next eligible interface.
  • 5xx responses and ambiguous network failures return the error without replaying the operation, since the agent may have started work.
  • Streaming operations are never replayed automatically. If you have a task ID, use GetTask and SubscribeToTask to resume observation.
  • A client disconnect stops the upstream stream but does not cancel the agent's task; call CancelTask to stop the work.

Enabling the shared gRPC listener requires a base domain, a public port, and wildcard DNS, because each agent gets its own hostname such as library-research.a2a.example.com. The transport selection and recovery page has the full failure table.

Agent gateway setup checklist

Before sending production traffic, confirm each control is in place. These checks map directly to the most common failure causes in the access-control and troubleshooting guides.

  • client.a2a_external_client_url is set, so protocol endpoints do not return 503.
  • client.enforce_auth_on_inference is true, so anonymous requests are refused.
  • Each calling application has its own virtual key, and allow_by_default is off for restricted agents.
  • The agent forwards the caller's credential and both correlation headers on its LLM and MCP calls.
  • The caller's virtual key also allows the models and MCP tools the agent uses.
  • Private agent hosts are on the Private Network Allowlist, and push callback routes are reachable through your ingress.

For a broader evaluation of where an agent gateway fits in an enterprise stack, the guide to choosing an agent gateway and the A2A protocol gateway overview cover selection criteria, and the Agent Gateway launch post explains why Bifrost puts models, tools, and agents behind one gateway.

Frequently asked questions

What is an agent gateway?

An agent gateway is a proxy between applications and AI agents that authenticates callers, routes requests to registered agents, and logs each operation. In Bifrost, the agent gateway handles A2A traffic in the same deployment that governs LLM and MCP traffic, so one set of virtual keys and one log store cover all three.

What is the A2A protocol?

Agent2Agent (A2A) is an open protocol for communication between AI agents. It defines agent cards for discovery, messages, tasks with states, contexts that group related work, and artifacts that carry outputs, over JSON-RPC, HTTP+JSON, or gRPC bindings. Bifrost supports A2A v1 and can bridge any of those transports, as the A2A gateway article explains.

What is an A2A agent card?

An agent card is a JSON document, usually served at /.well-known/agent-card.json, that describes an agent's endpoints, skills, capabilities, and authentication. Bifrost registers an agent from its card URL and serves a rewritten card at the gateway route, replacing upstream endpoints with gateway endpoints, as the registration guide describes.

Does an agent grant let the agent call models and tools?

No. Agent access only controls which callers may reach an agent. The agent's own LLM and MCP calls are checked against the forwarded caller credential's provider and tool permissions. Grant the models and tools separately, and enable credential forwarding so those calls stay attributed to the caller, as covered in the enterprise agent gateway guide.

Can a client use a different A2A transport than the agent?

Yes. The client-facing and agent-facing transports are chosen independently. A client can call Bifrost over JSON-RPC while Bifrost calls the agent over gRPC, with the agent-facing choice taken from the order of interfaces in the agent card. The agent gateway configuration page lists the endpoints for each transport.

Is Agent Gateway available in open-source Bifrost?

Yes. Registration, virtual key grants, all three transports, push notifications, and Agent Logs are available in the open-source gateway. Bifrost Enterprise adds agent grants through access profiles and projects, user attribution from identity providers, and scoped log access, as listed on the Bifrost Enterprise page.

Put Bifrost in front of your agents

An agent gateway turns access control and audit for A2A agents into configuration: one registration per agent, one grant per caller, and one session view that joins agent operations to the model and tool calls behind them. To run Bifrost as the agent gateway for your organization's agents, book a demo with the Bifrost team, or start from the Bifrost GitHub repository and register your first agent today.

Top comments (0)