SDK
The reference TypeScript client
A minimal client for the vendor agent side of ANP/0.1. It lives in this site’s repository under src/lib/anp/, doubles as the crypto behind the playground, and is written to be published as @anp/client. Exports: generateIdentity, register, openSession, sendEvent, fetchLog, verifyLog, plus the canonical JSON and signing primitives.
1Install
The client is a single dependency free module. It uses fetch and globalThis.crypto only, so the same code runs in Node 18+ and in browsers.
# The reference client is MIT licensed TypeScript with zero runtime
# dependencies. The publishable package lives in packages/anp-client of
# the repository; until it lands on npm, build it from source or vendor
# src/lib/anp/ directly.
git clone https://github.com/fredrikfilipsson-svg/agent-negotiation-protocol
cd agent-negotiation-protocol/packages/anp-client
npm install && npm run build # emits dist/ with types
npm pack # or: npm link2Generate an identity
An agent identity is a raw 32-byte Ed25519 public key. Its fingerprint, the SHA-256 of the raw key bytes, is the identity humans see.
import { generateIdentity } from "@anp/client";
// A fresh Ed25519 keypair via WebCrypto. Works in Node 18+ and in
// browsers; the private key is non extractable and never leaves the
// process.
const identity = await generateIdentity();
console.log(identity.publicKey); // raw 32-byte key, base64url
console.log(identity.fingerprint); // sha256 hex, shown to humans3Register with a buyer host
Registration is unauthenticated but proves you hold the private key. The returned agent id goes into the x-anp-agent header of every later request; the client stores it on the identity for you.
import { register } from "@anp/client";
// Registration proves key possession by signing
// "ANP/0.1\nregister\n<public_key_base64url>". Unverified agents get
// sandbox access; a human at the host verifies you before live sessions.
const host = "https://app.example.com";
const registration = await register(host, identity, {
agent_name: "Acme Selling Agent",
vendor_name: "Acme Software",
contact_email: "agents@acme.example",
});
console.log(registration.agentId); // host assigned UUID
console.log(registration.status); // "sandbox" until verified4Open a session
Sessions open against the sandbox, or against a buyer-issued org handle for live negotiations. The host appends your session_open and the buyer's envelope as events 1 and 2.
import { openSession } from "@anp/client";
// Both sides open with a mandate envelope. declared_ai must be true:
// ANP has no undeclared bots.
const opened = await openSession(
host,
identity,
{
party: "Acme Software",
agent: { name: "Acme Selling Agent", declared_ai: true },
may_discuss: ["renewal pricing", "term length"],
may_disclose: ["list pricing", "standard discount bands"],
will_not_disclose: ["floor pricing"],
offer_authority: "propose_only",
human_contact: "sales@acme.example",
},
{ sandbox: true }, // or { org_handle: "anp_org_..." } for a live buyer
);
console.log(opened.sessionId);
console.log(opened.buyerEnvelope); // what the buyer's agent may do5Send an offer
Offers are strict JSON per offer.schema.json. Validate before sending; hosts reject unknown keys rather than ignoring them.
import { sendEvent } from "@anp/client";
// The payload is hashed over its canonical JSON and signed with the
// authorship signature "ANP/0.1\n<kind>\n<payload_hash>" before it is
// sent. The returned log may already contain the buyer's response.
const log = await sendEvent(host, identity, opened.sessionId, "offer", {
currency: "USD",
term_months: 12,
expires_at: "2026-08-01T00:00:00Z",
line_items: [
{
sku: "CRM-ENT",
description: "CRM Enterprise seats",
quantity: 500,
unit: "seat/year",
unit_price: 1200,
currency: "USD",
},
],
conditions: ["Net 30 payment"],
});6Verify the chain
Verification is a pure function over the log document. Run it after every append; if it fails, stop negotiating and involve a human.
import { fetchLog, verifyLog } from "@anp/client";
// Re-verify after every append, per the spec: contiguous sequence
// numbers, payload hashes, chain linkage, event hashes, and every
// Ed25519 authorship signature.
const latest = await fetchLog(host, identity, opened.sessionId);
const verdict = await verifyLog(latest);
if (!verdict.ok) {
// A broken chain is a disputed session. The verdict names each
// failing check per event.
for (const event of verdict.events.filter((e) => !e.ok)) {
console.error(event.seq, event.checks.filter((c) => !c.ok));
}
throw new Error("chain verification failed");
}
console.log(`verified ${verdict.verifiedCount}/${verdict.eventCount} events`);For MCP-capable agents
The repository also ships an MCP server, packages/anp-mcp, that exposes the same client as tools: anp_generate_identity, anp_register, anp_open_session, anp_send_offer, anp_send_message, anp_fetch_log, and anp_verify_log. Every log is re-verified locally before it reaches the model, and the private key stays in the server process. The server is a transport: offer decisions stay with your agent’s mandate and its human approval process.
The full verification rules live in section 4 of the specification. The client’s test suite includes byte-for-byte canonical JSON checks and verifies the published example session log green. To check a log without writing code, use the verifier.