Every agent framework — Claude Agent SDK, OpenAI Agents SDK, Google ADK, LangGraph, Vercel AI SDK — streams its own shape of events. If you're building a client, a UI, or a tool that needs to work across more than one of them, you end up writing (and maintaining) a bespoke adapter per framework.
AgJSON is the wire format that ends that. It normalizes any framework's native event stream into one typed, versioned, forward-compatible shape — so a client written against AgJSON works with every framework a normalizer exists for, today and after the next SDK release.
Two SDKs. One event vocabulary.
Here's the same “call the echo tool” interaction as each framework actually emits it — a single fat message from Claude, a stream of deltas from OpenAI. Different shapes, different field names. Watch them land on the same AgJSON events.
// Claude Agent SDK — one message carries the whole tool call
{
"type": "assistant",
"message": {
"role": "assistant",
"content": [
{ "type": "tool_use", "id": "toolu_echo",
"name": "echo", "input": { "text": "hi" } }
]
}
}// OpenAI Agents SDK — the same call arrives as streamed deltas
{ "type": "response.output_item.added",
"item": { "type": "function_call", "name": "echo",
"call_id": "call_Ewi…", "arguments": "" } }
{ "type": "response.function_call_arguments.delta", "delta": "{\"" }
{ "type": "response.function_call_arguments.delta", "delta": "text" }
{ "type": "response.function_call_arguments.delta", "delta": "\":\"hi\"}" }// …both normalize to the exact same AgJSON events
{ "type": "tool.start", "name": "echo", "toolCallId": "…" }
{ "type": "tool.args.delta", "delta": "{\"text\":\"hi\"}" }
{ "type": "tool.args.assembled", "input": { "text": "hi" } }
{ "type": "tool.done", "outcome": "ok",
"content": [{ "type": "text", "text": "echo: hi" }] }Write your client against these events once — every framework with a normalizer speaks them. That's the whole idea.
Typed, not any
Every event and block is a discriminated union member — no Record<string, unknown> escape hatches for shaped data.
Streaming-first
push(native): AgEvent[] / flush(): AgEvent[]— a stateful, per-invoke normalizer that folds a live stream into a persisted AgReduceResult.
Respects the UI layer
AgJSON carries content and interaction anchors (Layer-A); rendering is left to MCP Apps and A2UI — no reinvented component schema.
Forward-compatible by rule
Unknown event types and unknown fields are always safe to ignore, so a v1.0 client keeps working as the spec grows.
FAQ
What is AgJSON?
AgJSON is an open, neutral, typed wire format that normalizes the event streams of different agent frameworks — Claude Agent SDK, OpenAI Agents SDK, Google ADK, and others — into one consistent shape, so a client written once works across all of them.
How is AgJSON different from MCP?
MCP (Model Context Protocol) connects a model to tools and data sources. AgJSON is a different layer: it normalizes the agent framework's own event stream — turns, messages, tool calls, reasoning, HITL asks — for a client to consume. AgJSON's resource blocks and ui.* events carry MCP Apps surfaces opaquely, so the two specs compose rather than compete.
How is it different from A2UI or other agent-UI protocols?
AgJSON defines zero component schema and zero render layer, deliberately. A2UI and MCP Apps own UI rendering; AgJSON just carries their payloads as opaque Layer-A content so a client can route them to the right renderer.
How is AgJSON different from AG-UI?
AG-UI is an agent–user interaction protocol: alongside a typed event stream it also prescribes shared read/write state sync, generative UI, and human-in-the-loop, and pairs with CopilotKit for a turnkey React UI. AgJSON is deliberately narrower — a normalization and transport layer. It translates each framework's native output into typed AgJSON events and gives you a normative reduce() into messages and turns, then stops: it owns nothing about state or rendering. Reach for AG-UI when you want a batteries-included agent-UI stack; reach for AgJSON when you already have a frontend — or a non-UI consumer like a logger, eval harness, or gateway — and just want clean, framework-agnostic, typed events to feed it. The two can even compose: an AG-UI-style renderer can sit on top of AgJSON's reduced graph.
Do I need a UI framework to use AgJSON?
No. AgJSON's reduce() folds the event stream into a plain object graph — messages, turns, tool calls, artifacts — that any UI framework can render directly, so you can wire it into an existing frontend in a few lines. There's no required renderer or component library: AgJSON normalizes the stream and hands you the data; the UI stays entirely yours, or a renderer built on top.
Which agent frameworks does AgJSON support today?
Normalizers ship today for Claude Agent SDK, OpenAI Agents SDK, Google ADK (official @google/adk), and the Vercel AI SDK as per-framework @silverprotocol packages — all four live-verified end-to-end, including tool calls. LangGraph and Pydantic AI informed the spec's design, and more normalizers are community-contributable. See the framework support matrix for the full picture.
Is AgJSON production-ready?
The spec is Draft (wire version 1.0.0-draft.1) — stable enough to build on, but it may still change before the v1 freeze. Feedback from real integrations is exactly what's useful right now.
Is AgJSON open source?
Yes — MIT licensed. The canonical AgJSON spec lives in the flagship AgJSON repository; the reference TypeScript SDK (@silverprotocol/core plus the per-framework normalizers) lives in the open typescript-sdk repository.
Get started
1 Install
The AgJSON core, a normalizer for your framework, and your framework's own SDK — the normalizer sits on top of it, it doesn't replace it.
npm install @silverprotocol/core @silverprotocol/claude-agent-sdk
# ...and the framework SDK you already use:
npm install @anthropic-ai/claude-agent-sdk
# (OpenAI / ADK: @silverprotocol/openai-agents · @silverprotocol/google-adk)2 Normalize a framework stream → AgJSON
You keep producing events with your framework's own SDK — the normalizer just wraps its stream. It's stateful per run: feed it each native event with push(), then flush() at end-of-stream to seal anything still open. You get back plain AgEvents.
// `query` is the Claude Agent SDK's own streaming call — you keep
// using your framework as-is; AgJSON just normalizes what it emits.
import { query } from "@anthropic-ai/claude-agent-sdk";
import { createClaudeNormalizer } from "@silverprotocol/claude-agent-sdk";
const normalizer = createClaudeNormalizer();
const agEvents = [];
for await (const native of query({ prompt: "call the echo tool" })) {
agEvents.push(...normalizer.push(native)); // one native event → 0+ AgEvents
}
agEvents.push(...normalizer.flush()); // close dangling turns/messages
// agEvents are now framework-neutral — send them over the wire,
// persist them, or hand them straight to the reducer below.3 Consume AgJSON → messages & turns
On the client, validate incoming events and fold them into the normative object graph — the same code regardless of which framework produced the stream.
import { ingestAgEvents, Reducer } from "@silverprotocol/core";
const events = ingestAgEvents(rawWireObjects); // parse-known-else-skip
const reducer = new Reducer();
for (const ev of events) reducer.push(ev);
const { messages, turns, artifacts, memory } = reducer.result();
// render `messages`, inspect `turns` — your UI never sees a raw SDK shape.The push()/flush() contract and the normative reduce() fold are the whole surface — see the spec for the details, or the framework matrix for every normalizer.