Enable A2A on a claude-agent
Expose an agent's real capabilities as typed A2A skills — scaffold with initializ agent init, implement an @initializ/a2a-kit executor, wire the package.json pointer, and deploy with a2a.enabled.
This guide takes a Claude Agent SDK repository from "reachable by nobody" to a governed A2A agent: the platform serves an Agent Card built from your skills, other agents and workflows invoke them, and every inbound task runs under the same governance and audit as the agent's own model calls. You write plain async functions — the A2A wire format, the server, and the auth are all handled by the governed loader already in your image.
Before you start
- A Node.js (≥ 18) agent repository built on the Claude Agent SDK, deployed from CI as a claude-agent — if you're not there yet, start with Deploy a CI-built agent and Build a governed image in CI.
- The
initializCLI locally (the scaffold step runs entirely offline).
Step 1 — scaffold
initializ agent init --name code-reviewer --provider anthropicThis writes the two files A2A needs (existing files are never
overwritten without --force):
initializ-deploy.yaml— the deploy spec withagent.type: claude-agentand A2A enabled by default.a2a-executor.ts— an@initializ/a2a-kitexecutor stub with one skill; you fill in itsrun(). If your project keeps sources undersrc/, the stub lands atsrc/a2a-executor.tssotscemits it todist/a2a-executor.js.
If a package.json is present, the scaffold also wires the executor
pointer into it automatically. See
the agent init reference for
flags and validation rules.
Step 2 — install the kit
npm install @initializ/a2a-kitThe kit has zero runtime dependencies — it's types plus two helpers.
Input validation is bring-your-own: anything with a .parse method works
(any Zod schema qualifies).
Step 3 — check the executor pointer
The loader finds your executor through this package.json field (the
scaffold adds it when it can):
"initializ": { "a2a": "./dist/a2a-executor.js" }The pointer names the compiled file, relative to the working
directory the agent runs in. Discovery order: the pointer, then the
conventional ./dist/a2a-executor.js. The module's default export must be
the executor object — which is exactly what defineAgent returns. If no
executor is found, the loader falls back to a generic chat skill that
bridges messages to the governed query().
Step 4 — implement the executor
Define one skill per capability. This example (condensed from a real
production agent) exposes a PR-review pipeline: validate the input, do the
work, return a result object.
import { defineAgent, skill } from "@initializ/a2a-kit";
import { z } from "zod";
import { queue } from "./core/queue/index.js";
import { github } from "./core/clients/github/index.js";
const ReviewInput = z.object({
repo: z.string().default("sportsbook"),
pr: z.number().int().positive(),
});
export default defineAgent({
skills: [
skill({
id: "review-pr",
name: "Review a pull request",
description:
"Fetch a pull request and run the multi-agent code review. " +
"Returns the queued review reference (trace id).",
tags: ["code-review"],
input: ReviewInput, // runtime validation
inputSchema: z.toJSONSchema(ReviewInput), // advertised on the Agent Card
examples: ['{"repo":"sportsbook","pr":123}'],
run: async ({ repo, pr }, ctx) => {
const prData = await github.getPR(repo, pr);
await queue.enqueue({
prNumber: prData.number,
repoName: repo,
commitSha: prData.headSha,
traceId: ctx.taskId, // correlate the async job with this task
});
return { queued: true, repo, pr: prData.number, title: prData.title };
},
}),
],
});How tasks map to your handler:
- Skill selection: callers name a skill via the message metadata's
skillId; without one, thedefaultSkill(or the first skill) handles the task. - Input:
run()receives the message's JSON data part as an object, or the message text as a string when there is no data part. When the skill declaresinput, the value is validated first and a parse failure fails the task with the validation error. - Output: return a string for a text reply, an object for a
structured data reply (or
{ text, data }for both). - Streaming:
ctx.emit?.("chunk")streams incremental text to callers using A2A streaming. - Correlation:
ctx.taskIdis the A2A task id — stamp it on downstream work (queue jobs, trace ids) so async results tie back to the exact invocation.
Step 5 — declare A2A in the deploy spec
The scaffolded initializ-deploy.yaml already carries this; the parts
that matter for A2A:
apiVersion: initializ.ai/v1
kind: AgentDeploy
agent:
name: code-reviewer
type: claude-agent # A2A is claude-agent-only
image: registry.initializ.ai/acme/code-reviewer:latest # --image overrides
model:
provider: anthropic # platform-managed gateway; no credentials here
a2a:
enabled: true
auth: bearer # the default — callers present a platform tokenauth: bearer (the default when the field is empty) verifies every
caller's token against the platform and accepts only same-workspace
callers, failing closed; auth: none disables the check for local
development. port, name, and description optionally override the
A2A port (default 9090) and the Agent Card identity — see
the A2A exposure reference. To make the
card fetchable off-cluster, add an
ingress block.
Step 6 — deploy from CI
initializ agent deploy -f initializ-deploy.yaml --image "$GOVERNED_IMAGE"The image is your CI-built, governance-wrapped image — the A2A server
lives in that governance layer, so
the wrap step is what makes
a2a.enabled do anything. On deploy, the platform opens the A2A port and
routing and hands the loader its A2A configuration; the loader discovers
your executor via the pointer and starts serving.
What you get
- An Agent Card built from your skills — served at
/.well-known/agent-card.json, listing each skill's id, name, description, tags, examples, andinputSchema, and advertising the bearer auth scheme. - Per-task governed invocations — each inbound task is its own audited run with lifecycle events, the caller's verified identity, and cache-inclusive token accounting; see what the platform records for every run.
- Workflow participation — the agent joins workflows like any platform agent, and its typed input schemas drive both the workflow planner's argument construction and the structured run forms shown when a skill is invoked directly.
Test skills without a server
The kit ships a direct-invocation helper for unit tests — no server, no A2A envelope:
import { invokeSkill } from "@initializ/a2a-kit";
import agent from "./a2a-executor.js";
const result = await invokeSkill(agent, "review-pr", { repo: "sportsbook", pr: 123 });Agent SDK
The initializ SDK for Claude Agent SDK agents — the governed loader that brings audit, policy enforcement, and platform identity to your image without code changes, and the optional @initializ/a2a-kit library for exposing typed skills over A2A.
Build a governed image in CI
Wrap your agent's unchanged image with the initializ governance layer in CI — the wrapper Dockerfile, the build-wrap-push-deploy pipeline, and what the governed loader records and enforces at runtime.