Understanding the A2A Protocol for Agent-to-Agent Communication in BuilderIO Agent-Native

The A2A protocol is a JSON-RPC-over-HTTP mechanism that enables secure, stateless remote procedure calls between distributed Agent-Native applications, using HS256 JWT tokens for authentication and a task-store architecture for asynchronous execution.

The A2A (Agent-to-Agent) protocol powers the peer-to-peer communication layer in the BuilderIO/agent-native repository. This specification allows separate micro-services—each running as an autonomous "agent"—to invoke each other's exported actions, share artifacts, and coordinate multi-step workflows without custom REST wrappers.

Core Architecture and Design Goals

The protocol prioritizes language-agnostic interoperability and secure cross-app delegation through three foundational pillars.

JSON-RPC Over HTTP

All A2A traffic travels over standard HTTP POST requests to a fixed endpoint path. In packages/core/src/a2a/server.ts, the constant A2A_ENDPOINT_PATH is defined as /_agent-native/a2a, where the server expects a JSON-RPC 2.0 envelope containing jsonrpc, method, params, and id fields.

JWT-Based Authentication

Security relies on short-lived HS256 JWTs signed with a shared A2A secret. The signA2AToken function in packages/core/src/a2a/client.ts generates tokens containing the caller's identity (sub), organization (org), and an exp claim typically set to 60 seconds. The server validates these using verifyA2ABearerToken, ensuring cross-app calls originate from trusted peers within the same organization.

Task-Oriented Execution Model

Rather than blocking until completion, A2A calls create persistent task records. The createTask function in packages/core/src/a2a/task-store.ts stores request metadata, owner information, and status in the shared database. Remote agents poll or stream updates while the callee populates the task with Artifact objects—files, images, or data structures defined in packages/core/src/a2a/types.ts.

Key Components and Source Files

The A2A stack is modularized across the core package, with each file handling a specific layer of the communication stack.

Component File Path Responsibility
Server packages/core/src/a2a/server.ts Exports mountA2A to register the POST route and verifyA2ABearerToken for JWT validation
Client packages/core/src/a2a/client.ts Implements signA2AToken and callAgent for remote invocation
Invocation packages/core/src/a2a/invoke.ts High-level invokeAgent helper that resolves URLs and builds JSON-RPC payloads
Task Store packages/core/src/a2a/task-store.ts Persists tasks via createTask, getTask, and updateTaskStatusMessage
Types packages/core/src/a2a/types.ts Defines AgentCard, Artifact, Message, and JSON-RPC interfaces
Auth Policy packages/core/src/a2a/auth-policy.ts Determines if JWT auth is required via hasConfiguredA2ASecret and shouldAdvertiseJwtA2AAuth
Handlers packages/core/src/a2a/handlers.ts Implements JSON-RPC methods like listArtifacts, getArtifact, and cancelTask

The A2A Request Lifecycle

Understanding the flow from caller to callee clarifies how the protocol maintains stateless communication while supporting long-running operations.

  1. Token Generation: The caller invokes signA2AToken with the user's email and organization domain, creating a Bearer token signed with the organization's A2A secret.
  2. Request Construction: callAgent assembles a JSON-RPC payload targeting the remote agent's A2A_ENDPOINT_PATH and includes the JWT in the Authorization header.
  3. Server Validation: The remote server—initialized via mountA2A—extracts and verifies the token using verifyA2ABearerToken, rejecting untrusted cross-org requests.
  4. Task Creation: The handler calls createTask to queue the work, returning a task ID immediately while processing continues asynchronously.
  5. Execution: processA2ATaskFromQueue runs the requested skill, streaming progress updates back to the task store.
  6. Artifact Retrieval: Upon completion, the caller fetches results using getArtifact or lists available artifacts via listArtifacts, referencing the task ID from the initial response.

Security Implementation

The protocol implements defense-in-depth through shared secrets and scope-restricted tokens.

Shared Secret Configuration: The system checks for A2A_SECRET (global) or organization-specific secrets via getA2ASecretByDomain. The hasConfiguredA2ASecret helper ensures endpoints only advertise JWT auth when properly configured.

Token Verification: verifyA2ABearerToken validates the HS256 signature, expiry, and audience claims. The implementation resides in the core auth layer, ensuring consistent policy enforcement across all A2A endpoints.

Same-Org Enforcement: When apps query peer directories, the org-apps-directory plugin verifies that the requesting token's organization matches the target domain, preventing cross-tenant invocation.

Practical Implementation Examples

Signing Tokens and Calling Remote Agents

Generate a JWT and invoke a remote image generation agent:

import { signA2AToken, callAgent } from "@agent-native/core/a2a";

// Create a 60-second token for alice@example.com
const token = await signA2AToken(
  "alice@example.com",
  "example.com",
  undefined, // uses default 60s expiration
  { scopes: ["a2a"] }
);

// Define the JSON-RPC request
const request = {
  jsonrpc: "2.0",
  method: "generateImage",
  params: { prompt: "sunrise over mountains", width: 1024, height: 768 },
  id: "req-uuid-123"
};

// POST to the remote agent
const response = await callAgent(
  "https://images.example.com/_agent-native/a2a",
  request,
  { apiKey: token }
);

if (response.error) {
  throw new Error(`A2A call failed: ${response.error.message}`);
}
console.log("Artifact ID:", response.result.artifactId);

Mounting the A2A Server Endpoint

Register the A2A handler in an Agent-Native application:

import { mountA2A } from "@agent-native/core/a2a";
import { createApp } from "h3";

const app = createApp();

mountA2A(app, {
  // Optional: restrict which skills are exposed publicly
  publicSkills: ["listArtifacts", "getArtifact"]
});

This registers the POST route at /_agent-native/a2a and wires it to the JSON-RPC dispatcher defined in packages/core/src/a2a/handlers.ts.

Creating and Tracking Tasks

Queue a long-running task and poll for completion:

import { createTask, getTask } from "@agent-native/core/a2a/task-store";

const task = await createTask({
  type: "generateImage",
  payload: { prompt: "a red apple on a wooden table" },
  owner: "alice@example.com"
});

console.log("Task created:", task.id);

// Poll for status
const status = await getTask(task.id);
if (status?.state === "completed") {
  console.log("Result:", status.artifacts);
}

Summary

  • The A2A protocol uses JSON-RPC 2.0 over HTTP POST to enable stateless communication between Agent-Native apps.
  • Authentication relies on HS256 JWTs signed with a shared A2A secret, verified via verifyA2ABearerToken in packages/core/src/a2a/server.ts.
  • Tasks are persisted in the task store (packages/core/src/a2a/task-store.ts), allowing asynchronous execution and artifact retrieval.
  • Server setup requires calling mountA2A to register the endpoint, while client calls use signA2AToken and callAgent from packages/core/src/a2a/client.ts.
  • Security enforces same-org validation and short-lived tokens to prevent unauthorized cross-tenant access.

Frequently Asked Questions

What is the A2A protocol?

The A2A protocol is an internal communication specification in the BuilderIO/agent-native repository that allows separate agent applications to invoke each other's capabilities via JSON-RPC over HTTP. It standardizes authentication, task management, and artifact exchange so that distributed micro-services can collaborate without custom API integrations.

How does authentication work in A2A?

Authentication uses HS256 JWT tokens signed with a shared secret (either global A2A_SECRET or organization-specific). The client generates tokens via signA2AToken, and the server validates them using verifyA2ABearerToken. Tokens include expiry claims (default 60 seconds) and scope restrictions to minimize the window of vulnerability.

How do agents discover each other?

Agents discover peers through the apps directory plugin (templates/dispatch/server/plugins/org-apps-directory.ts), which publishes base URLs and A2A endpoints for all apps in an organization. When an app needs to invoke another, it constructs the full URL by appending A2A_ENDPOINT_PATH to the discovered base URL, then verifies the target belongs to the same organization before transmitting the JWT.

What happens if an A2A call fails?

If the remote A2A endpoint is unreachable or returns an error, the caller receives a JSON-RPC error response that can be handled gracefully. In production scenarios, implementations often fall back to local services—for example, the image generation action in templates/slides/actions/generate-image.ts falls back to a built-in image service when the remote A2A agent is unavailable, ensuring continuity of service.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →