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

> Discover the A2A protocol for agent-to-agent communication in BuilderIO Agent-Native. Learn about its JSON-RPC, JWT, and task-store architecture for secure and efficient remote calls.

- Repository: [Builder.io/agent-native](https://github.com/BuilderIO/agent-native)
- Tags: deep-dive
- Published: 2026-06-28

---

**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](https://github.com/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`](https://github.com/BuilderIO/agent-native/blob/main/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`](https://github.com/BuilderIO/agent-native/blob/main/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`](https://github.com/BuilderIO/agent-native/blob/main/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`](https://github.com/BuilderIO/agent-native/blob/main/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`](https://github.com/BuilderIO/agent-native/blob/main/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`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/a2a/client.ts) | Implements `signA2AToken` and `callAgent` for remote invocation |
| **Invocation** | [`packages/core/src/a2a/invoke.ts`](https://github.com/BuilderIO/agent-native/blob/main/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`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/a2a/task-store.ts) | Persists tasks via `createTask`, `getTask`, and `updateTaskStatusMessage` |
| **Types** | [`packages/core/src/a2a/types.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/a2a/types.ts) | Defines `AgentCard`, `Artifact`, `Message`, and JSON-RPC interfaces |
| **Auth Policy** | [`packages/core/src/a2a/auth-policy.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/a2a/auth-policy.ts) | Determines if JWT auth is required via `hasConfiguredA2ASecret` and `shouldAdvertiseJwtA2AAuth` |
| **Handlers** | [`packages/core/src/a2a/handlers.ts`](https://github.com/BuilderIO/agent-native/blob/main/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:

```typescript
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:

```typescript
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`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/a2a/handlers.ts).

### Creating and Tracking Tasks

Queue a long-running task and poll for completion:

```typescript
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`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/a2a/server.ts).
- **Tasks** are persisted in the task store ([`packages/core/src/a2a/task-store.ts`](https://github.com/BuilderIO/agent-native/blob/main/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`](https://github.com/BuilderIO/agent-native/blob/main/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`](https://github.com/BuilderIO/agent-native/blob/main/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`](https://github.com/BuilderIO/agent-native/blob/main/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.