From onex
Set up a modern AI agent on top of the six protocols that define how it connects to tools, other agents, suppliers, payments, the UI, and the user — MCP (Model Context Protocol, tools), A2A (Agent-to-Agent, discovery + delegation), UCP (Universal Commerce Protocol, supplier discovery + structured orders), AP2 (Agent Payments Protocol, typed mandates for bounded spend), A2UI (Agent-to-User Interface, interactive UI primitives), and AGUI (event streaming from agent to frontend). Confirms scope first by asking which agent framework — Claude Agent SDK or OpenAI Agent SDK — whether the agent has a commerce segment (gates UCP + AP2), whether it has a user-facing UI (gates A2UI + AGUI), whether it delegates to or is discoverable by other agents (gates A2A), runtime / language, greenfield vs existing repo, and identity / auth posture. Writes a detailed plan to `docs/agent-setup-plan.md` with concrete setup steps, code stubs, env vars, and verification steps per protocol — then offers to scaffold each protocol, one commit at a time, in dependency order. Use when the user says "/onex:setup-agent", "set up an agent", "scaffold an AI agent", "wire up the agent protocols", "add MCP / A2A / UCP / AP2 / A2UI / AGUI", "build a commerce agent", "make this app agentic", "set up agent tools", or asks how to wire up the modern agent protocols.
How this skill is triggered — by the user, by Claude, or both
Slash command
/onex:setup-agentThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
`/onex:setup-agent` plans and scaffolds an AI agent on top of the six protocols that define how a modern agent connects to tools, other agents, suppliers, payments, the UI, and the user — independent of the underlying model or framework.
/onex:setup-agent plans and scaffolds an AI agent on top of the six protocols that define how a modern agent connects to tools, other agents, suppliers, payments, the UI, and the user — independent of the underlying model or framework.
| Protocol | One-liner | Gated by |
|---|---|---|
| MCP — Model Context Protocol | Connects the agent to tools and data sources at runtime — no hard-coded tool definitions. | Always included. |
| A2A — Agent-to-Agent Protocol | Standardizes agent discovery and cross-framework delegation. | Multi-agent or "discoverable" was selected. |
| UCP — Universal Commerce Protocol | Machine-readable supplier discovery, catalog browsing, and structured ordering. | Commerce segment = yes. |
| AP2 — Agent Payments Protocol | Typed mandates that bound spend, restrict merchants, and produce auditable receipts. | Commerce segment = yes. |
| A2UI — Agent-to-User Interface Protocol | Real-time interactive UIs rendered from a fixed set of primitives the client knows how to draw. | User-facing UI = yes. |
| AGUI — Agent GUI streaming | Event stream from agent to frontend — token deltas, tool calls, progress, errors. | User-facing UI or voice. |
This skill is a build procedure. Lock the answers, write the plan, then scaffold protocol-by-protocol on confirmation. Do not write code until scope is confirmed.
AskUserQuestion)Lock these answers before scanning the repo or writing anything. Group into one or two AskUserQuestion calls (max 4 per call).
Call 1 — the four protocol-gating decisions:
Framework (single-select):
claude-agent-sdk (TypeScript / Python).@openai/agents / openai-agents Python).Commerce segment? (yes / no):
User-facing surface? (single-select):
Multi-agent or discoverable? (single-select):
Call 2 — the runtime / shape decisions (skip any already obvious from a quick scan):
Runtime / language (single-select):
Greenfield or existing repo? (single-select):
Identity / auth posture (single-select, only if commerce or multi-tenant):
Do not proceed until framework, commerce, UI, and multi-agent are locked.
A short surface scan, no deep reads:
package.json vs pyproject.toml / requirements.txt).*agent*.ts, *agent*.py, prior MCP server, prior tools/ directory.These protocols evolve quickly — package names, mandate fields, and primitive lists move. Before any code stub lands in the codebase, verify the current spec:
context7 query for /modelcontextprotocol/specification; confirm the SDK package names for the chosen runtime and the current server / client APIs.context7 for /copilotkit/copilotkit and search "AG-UI protocol events"; confirm the current event schema (token / tool-call-start / tool-call-end / done / error).Record in the plan, per protocol, the source URL / commit / spec version used. If a spec can't be verified, mark that section PENDING — verify spec and skip scaffolding it until the user confirms.
Write to docs/agent-setup-plan.md (create docs/ if missing). Structure, in this order:
End the plan with this exact follow-through prompt:
Tell me which protocols to scaffold (numbers, names, or "all"), and I'll set them up in that order with a commit per protocol.
When the user picks protocols, scaffold them in the order above (or the explicit order they give), one commit per protocol. For each protocol:
.env.example.setup-agent: add MCP server + tools wiring.If a chosen protocol depends on another that isn't scaffolded yet (e.g. UCP without AP2 mandates), stop and warn — do not fake the dependency.
Each section assumes the spec was verified in Step 3. Treat package names and code stubs as templates — re-check against the live spec before they land in the codebase.
What it is. A standard for connecting an agent to external tools and data sources at runtime. The agent enumerates and calls tools over a single MCP transport instead of hard-coding tool definitions in the model prompt.
Why we need it. Every other protocol below assumes the agent has tools. MCP is the substrate. Without it, every new capability is a hand-rolled function the model has to be told about.
Setup steps:
create_profile, look_up_order). Each tool gets a typed schema — zod (TS) / pydantic (Python). Never accept untyped tool args.Code stubs (TS — verify package names against current MCP SDK):
// lib/agent/mcp-servers.ts
export const mcpServers = [
{ name: "filesystem", command: "npx", args: ["-y", "@modelcontextprotocol/server-filesystem", "/workspace"] },
{ name: "postgres", command: "npx", args: ["-y", "@modelcontextprotocol/server-postgres", process.env.DATABASE_URL!] },
{ name: "app-tools", command: "node", args: ["./mcp/app-tools.js"] }, // custom
];
// mcp/app-tools.ts — a tiny custom MCP server
import { Server } from "@modelcontextprotocol/sdk/server";
import { z } from "zod";
// server.tool("create_profile", { schema: z.object({ … }) }, async (args) => { … });
Env vars:
ANTHROPIC_API_KEY= # Claude Agent SDK
OPENAI_API_KEY= # OpenAI Agent SDK
DATABASE_URL= # used by the postgres MCP server, if enabled
Verification: start the agent, ask it to list tools, confirm the expected ones appear, then call one tool round-trip end-to-end (e.g. read a file via the filesystem server).
(Include only if multi-agent or "discoverable" was selected.)
What it is. A standard envelope for agents to advertise themselves (Agent Card), be discovered, and exchange tasks — independent of the underlying agent framework.
Why we need it. Lets this agent delegate to specialists (research, scheduling, support) and lets other agents call this one — without sharing a runtime.
Setup steps:
/.well-known/agent.json) describing this agent's name, capabilities, auth requirements, and task endpoint.POST /a2a/tasks) that accepts an A2A message, hands it to the agent runtime, and streams the result.Code stubs (Next.js TS — verify Agent Card schema against current A2A spec):
// app/.well-known/agent.json/route.ts
export function GET() {
return Response.json({
schemaVersion: "1",
name: "Onex Concierge",
description: "Books and manages reservations.",
endpoints: { tasks: "/a2a/tasks" },
auth: { schemes: ["bearer"] },
capabilities: ["reservations.search", "reservations.book"],
});
}
// app/a2a/tasks/route.ts
export async function POST(req: Request) {
const message = await req.json();
// validate envelope, authenticate caller, dispatch to agent runtime, stream response.
}
Env vars:
A2A_REGISTRY_URL= # optional discovery registry
A2A_AGENT_BASE_URL= # this agent's public URL — required in the Agent Card
Verification: curl /.well-known/agent.json, then post a task and read it back. From another agent (or a CLI client), discover this one and round-trip a task.
(Include only if commerce = yes.)
What it is. A machine-readable contract for supplier discovery, catalog browsing, and structured order placement — the agent talks to suppliers over a stable JSON shape instead of scraping a checkout page.
Why we need it. Commerce agents that parse HTML break the moment a supplier ships a redesign. UCP makes the supplier responsible for exposing its catalog and accepting orders in a stable shape.
Setup steps:
/.well-known/commerce.json).Code stubs (TS — verify schema against current UCP spec):
// lib/agent/ucp/client.ts
export async function listCatalog(supplierUrl: string, query: { q?: string }) {
const res = await fetch(`${supplierUrl}/ucp/catalog?q=${encodeURIComponent(query.q ?? "")}`);
return CatalogSchema.parse(await res.json());
}
export async function placeOrder(supplierUrl: string, order: Order, mandate: SignedMandate) {
const res = await fetch(`${supplierUrl}/ucp/orders`, {
method: "POST",
headers: { "content-type": "application/json", "x-ap2-mandate": mandate.token },
body: JSON.stringify(order),
});
return OrderResultSchema.parse(await res.json());
}
Env vars:
UCP_REGISTRY_URL= # optional supplier registry
Verification: discover a supplier (fixture or sandbox), pull a catalog, place a test order against the sandbox using a sandbox AP2 mandate. Confirm the response validates against the UCP schema.
(Include only if commerce = yes.)
What it is. Typed mandates that the user (or their wallet) signs to bound what the agent may spend: amount cap, merchant allow-list, expiry, scope. Every payment carries a verifiable receipt back to the user — an auditable trail of who authorized what, when, against which agent.
Why we need it. A commerce agent without bounded spend authority is a liability. AP2 gives the user (and the auditor) a typed, signed trail.
Setup steps:
Code stubs (TS — verify mandate fields against current AP2 spec):
// lib/agent/ap2/mandate.ts
export type IntentMandate = {
user_id: string;
scope: { merchants: string[]; max_amount_cents: number; currency: string };
expires_at: string; // ISO
signature: string;
};
export async function issueIntent(user_id: string, scope: IntentMandate["scope"]) {
// 1. surface approval UI / check stored policy
// 2. sign via the wallet provider
// 3. persist row in `ap2_mandates`
// 4. return signed token
}
export function verifyMandate(token: string, spend: { merchant: string; amount_cents: number }) {
// verify signature, decode, check caps + expiry + merchant allow-list — throw on any failure
}
Env vars:
AP2_WALLET_PROVIDER=
AP2_WALLET_API_KEY=
AP2_AUDIT_DATABASE_URL= # optional separate DB for the audit trail
Verification: issue a sandbox intent mandate with a $10 cap; place a $5 sandbox order via UCP — should succeed and record a receipt. Place a $20 order — should fail before any provider call, surfacing the cap breach.
(Include only if the agent has a web (or otherwise interactive) UI surface.)
What it is. A fixed set of UI primitives (button, slider, text field, list, card, …) the agent emits as a JSON document and the client renders natively. The agent doesn't ship HTML — it ships a primitive tree the client knows how to draw. Verify the current primitive list in Step 3 — the count was ≈18 at brief time.
Why we need it. Lets the agent build interactive surfaces (forms, confirmations, choosers) without the client and agent agreeing on every screen in advance. Forms feel native; the agent stays simple.
Setup steps:
render_ui) that emits an A2UI document. AGUI (section F) is what actually streams it to the client.Code stubs (React — verify primitives and prop names against current A2UI spec):
// components/agent-ui/render.tsx
import type { A2UIDoc, A2UINode } from "@a2ui/react"; // package name TBD — verify
const primitives = {
button: ({ label, action }: A2UINode<"button">) => <Button onClick={() => emit(action)}>{label}</Button>,
text_field: ({ id, label, value }: A2UINode<"text_field">) => (
<Input id={id} aria-label={label} defaultValue={value} onChange={onChange(id)} />
),
// …rest of the verified primitive list
};
export function AgentUI({ doc }: { doc: A2UIDoc }) {
return doc.nodes.map(node => primitives[node.type](node.props));
}
Env vars: none unique — A2UI piggybacks on the AGUI transport.
Verification: the agent emits a small form (text_field + button); the client renders it; submitting fires a tool call the agent receives and acts on.
(Include only if UI surface = web or voice.)
What it is. A standardized event stream from agent to frontend — tool-call started, partial tokens, tool-call finished, message done, error — so the user sees live progress instead of a blocking spinner.
Why we need it. A2UI needs an incremental transport to render as the agent thinks. Without AGUI the UI either blocks until the whole response is ready or invents an ad-hoc streaming format.
Setup steps:
Code stubs (Next.js TS — verify event names against current AG-UI spec):
// app/api/agent/route.ts
export async function POST(req: Request) {
const { messages } = await req.json();
const stream = new ReadableStream({
async start(controller) {
const enc = new TextEncoder();
for await (const ev of runAgent({ messages })) {
// ev = { type: "token" | "tool_call_start" | "tool_call_end" | "done" | "error", ... }
controller.enqueue(enc.encode(`event: ${ev.type}\ndata: ${JSON.stringify(ev)}\n\n`));
}
controller.close();
},
});
return new Response(stream, { headers: { "content-type": "text/event-stream" } });
}
// components/agent-chat.tsx
useEffect(() => {
const es = new EventSource("/api/agent");
es.addEventListener("token", e => append(JSON.parse(e.data).delta));
es.addEventListener("tool_call_start", e => showToolCall(JSON.parse(e.data)));
es.addEventListener("tool_call_end", e => settleToolCall(JSON.parse(e.data)));
es.addEventListener("done", () => es.close());
return () => es.close();
}, []);
Env vars: none unique.
Verification: trigger an agent run; the client sees tokens appear word-by-word and tool calls render as they happen, not after the whole reply.
Wire these once, share across protocols:
AGENT_ENV switch (development / staging / production) and gate live calls on it. The first end-to-end run is always sandbox.docs/agent-setup-plan.md is written first; only scaffold protocols on explicit confirmation.zod (TS) / pydantic (Python) for every tool arg, mandate, UCP payload, and A2A message. No any, no dict[str, Any].Provides CDSS development patterns for drug interaction checking, dose validation, clinical scoring (NEWS2, qSOFA), and alert classification integrated into EMR workflows.
npx claudepluginhub onextech/skills --plugin onex