Chio/Docs

ReferenceSDKs

TypeScript SDK

The TypeScript SDK is published as @chio-protocol/sdk for Node.js 22 or newer. It is ESM-only, pure TypeScript, and has no native dependencies.

Installation

bash
$ npm install @chio-protocol/sdk
$ yarn add @chio-protocol/sdk
$ pnpm add @chio-protocol/sdk
$ bun add @chio-protocol/sdk

Node.js 22 is the minimum. The package declares "type": "module" in its package.json; consumers built with CommonJS should use a dynamic import().

Entry Points

The SDK exposes three top-level modules. Import only what you need so tree-shaking drops unused code paths from your bundle.

ModulePurpose
@chio-protocol/sdkTop-level entry point: client, session, authorization, errors, DPoP, and receipt query.
@chio-protocol/sdk/invariantsPure verification helpers: canonical JSON, SHA-256, Ed25519, receipt/capability/manifest parsing and verification.
@chio-protocol/sdk/transportStreamable HTTP transport: RPC message parsing, session lifecycle, header builders.

Invariants Module

The invariants module is dependency-free. Every exported function is synchronous: signing, hashing, and verification run on the built-in node:crypto module, so there is nothing to await. Use these functions to verify protocol objects without network access.

Exported Functions

  • canonicalizeJson: serialize any JSON-compatible value to an RFC 8785 canonical JSON string.
  • canonicalizeJsonString: canonicalize an existing JSON string.
  • sha256Hex, sha256HexBytes, sha256HexUtf8: SHA-256 digests, lowercase hex.
  • parseReceiptJson, verifyReceipt, verifyReceiptJson: parse and verify a receipt (signature plus parameter hash).
  • parseCapabilityJson, verifyCapability, verifyCapabilityJson: signature check plus time-status classification.
  • parseSignedManifestJson, verifySignedManifest, verifySignedManifestJson: signed tool-manifest verification.
  • signUtf8MessageEd25519, verifyUtf8MessageEd25519, signJsonStringEd25519, verifyJsonStringSignatureEd25519: Ed25519 signing and verification.
  • isValidPublicKeyHex, isValidSignatureHex, publicKeyHexMatches: key and signature hex validators.

DPoP proof signing lives on the top-level @chio-protocol/sdk entry point (signDpopProof), not the invariants subpath. See DPoP Proofs.

Canonical JSON

typescript
import { canonicalizeJson, canonicalizeJsonString } from "@chio-protocol/sdk/invariants";

// Serialize a value to its RFC 8785 canonical JSON string.
const canonical: string = canonicalizeJson({ b: 2, a: 1 });
// canonical === '{"a":1,"b":2}'

// Canonicalize an existing JSON string.
const str: string = canonicalizeJsonString('{"b":2,"a":1}');
// str === '{"a":1,"b":2}'

Hashing

typescript
import { sha256Hex, sha256HexBytes, sha256HexUtf8 } from "@chio-protocol/sdk/invariants";

sha256Hex("hello world");
sha256HexBytes(Buffer.from([1, 2, 3]));
sha256HexUtf8("hello world");

Receipts

typescript
import {
  parseReceiptJson,
  verifyReceipt,
  verifyReceiptJson,
  receiptBodyCanonicalJson,
} from "@chio-protocol/sdk/invariants";
import type { ChioReceipt, ReceiptVerification } from "@chio-protocol/sdk/invariants";

const receipt: ChioReceipt = parseReceiptJson(jsonString);
const result: ReceiptVerification = verifyReceipt(receipt);
// result.signature_valid, result.parameter_hash_valid, result.receipt_id_valid

const quick: ReceiptVerification = verifyReceiptJson(jsonString);
const body: string = receiptBodyCanonicalJson(receipt);

Capabilities

typescript
import {
  parseCapabilityJson,
  verifyCapability,
  verifyCapabilityJson,
} from "@chio-protocol/sdk/invariants";
import type {
  CapabilityToken,
  CapabilityVerification,
  CapabilityTimeStatus,
} from "@chio-protocol/sdk/invariants";

const cap: CapabilityToken = parseCapabilityJson(jsonString);
const now = Math.floor(Date.now() / 1000);
const status: CapabilityVerification = verifyCapability(cap, now);
// status.signature_valid: boolean
// status.delegation_chain_shape_valid: boolean
// status.time_valid: boolean
// status.time_status: "valid" | "not_yet_valid" | "expired"

Manifests

typescript
import {
  parseSignedManifestJson,
  verifySignedManifest,
  verifySignedManifestJson,
} from "@chio-protocol/sdk/invariants";
import type { SignedManifest, ManifestVerification } from "@chio-protocol/sdk/invariants";

const manifest: SignedManifest = parseSignedManifestJson(jsonString);
const result: ManifestVerification = verifySignedManifest(manifest);

Ed25519

typescript
import {
  signJsonStringEd25519,
  signUtf8MessageEd25519,
  verifyJsonStringSignatureEd25519,
  verifyUtf8MessageEd25519,
  isValidPublicKeyHex,
  isValidSignatureHex,
  publicKeyHexMatches,
} from "@chio-protocol/sdk/invariants";

const sig = signJsonStringEd25519('{"key":"value"}', privateKeyHex);
const ok = verifyJsonStringSignatureEd25519(
  '{"key":"value"}',
  sig.public_key_hex,
  sig.signature_hex,
);

isValidPublicKeyHex(hex);  // 64 hex chars
isValidSignatureHex(hex);  // 128 hex chars

ChioClient and ChioSession

ChioClient is the top-level entry into the SDK. It composes transport, authentication, and session management. Call initialize() to open a remote MCP session against a chio edge.

typescript
import { ChioClient, staticBearerAuth } from "@chio-protocol/sdk";

// ChioClientOptions extends { authToken }, so spread the auth fields in.
const client = new ChioClient({
  baseUrl: "https://edge.example.com/mcp",
  ...staticBearerAuth(process.env.CHIO_TOKEN!),
});
// Or: ChioClient.withStaticBearer("https://edge.example.com/mcp", token)

const session = await client.initialize();

const tools = await session.listTools();
const result = await session.callTool("read_file", { path: "./README.md" });

await session.close();

ChioSession exposes the common MCP surface (listTools, callTool, listResources, readResource, listPrompts, getPrompt) along with lower-level hooks like request, notification, and sendEnvelope.

Transport Module

The transport module implements MCP-compatible Streamable HTTP with explicit session lifecycle. Use it directly to manage sessions, or letChioClient manage them.

typescript
import {
  initializeSession,
  postRpc,
  postNotification,
  deleteSession,
  buildRpcHeaders,
  parseRpcMessages,
  readRpcMessagesUntilTerminal,
} from "@chio-protocol/sdk/transport";
import type {
  SessionState,
  InitializeSessionResult,
  RpcExchange,
  JsonRpcMessage,
} from "@chio-protocol/sdk/transport";

const { sessionState }: InitializeSessionResult = await initializeSession(
  "https://edge.example.com/mcp",
  { onMessage: (msg) => console.log("recv", msg) },
);

const exchange: RpcExchange = await postRpc(
  "https://edge.example.com/mcp",
  sessionState,
  { method: "tools/list", params: {} },
);

await deleteSession("https://edge.example.com/mcp", sessionState);

DPoP Proofs

signDpopProof constructs a canonical proof body, hashes the tool arguments with SHA-256, and signs the body with the agent Ed25519 seed. The output is accepted by any chio kernel that runs verify_dpop_proof.

typescript
import { signDpopProof, DPOP_SCHEMA } from "@chio-protocol/sdk";
import type { DpopProof, DpopProofBody } from "@chio-protocol/sdk";

const proof: DpopProof = signDpopProof({
  capabilityId: "cap_7f3a...e91d",
  toolServer: "srv-files",
  toolName: "read_file",
  actionArgs: { path: "./workspace/README.md" },
  agentSeedHex: process.env.CHIO_AGENT_SEED_HEX!,
  nonce: "optional-server-nonce",
  issuedAt: 1744537862, // optional, defaults to now
});

// proof.body fields (sorted keys for canonical JSON):
//   action_hash, agent_key, capability_id, issued_at, nonce,
//   schema (= DPOP_SCHEMA), tool_name, tool_server
// proof.signature is a hex-encoded Ed25519 signature over the canonical body.

ReceiptQueryClient

ReceiptQueryClient wraps GET /v1/receipts/query with TypeScript types and automatic bearer-token injection. Filter fields are camelCase in JavaScript; the client converts them to the wire query string.

Construction

typescript
import { ReceiptQueryClient } from "@chio-protocol/sdk";
import type { ChioReceipt, ReceiptQueryResponse } from "@chio-protocol/sdk";

// The constructor is positional: (baseUrl, authToken, fetchImpl?).
const client = new ReceiptQueryClient(
  "https://receipts.example.com",
  process.env.RECEIPT_TOKEN!,
);

One-Shot Query

typescript
const page: ReceiptQueryResponse = await client.query({
  capabilityId: "cap_7f3a...e91d",
  toolServer: "srv-files",
  toolName: "read_file",
  outcome: "deny",          // "allow" | "deny" | "cancelled" | "incomplete"
  since: 1744500000,        // Unix seconds
  until: 1744600000,
  minCost: 1,
  maxCost: 1000,
  limit: 50,
  cursor: undefined,        // opaque cursor from a prior page
});

console.log(page.totalCount, page.nextCursor, page.receipts.length);

Pagination

typescript
// paginate() is an async generator over pages; each yield is a receipt array.
for await (const receipts of client.paginate({ outcome: "deny" })) {
  for (const receipt of receipts) {
    const r: ChioReceipt = receipt;
    console.log(r.id, r.tool_name, r.decision?.verdict);
  }
}

paginate() drives the cursor field automatically and stops when the server omits nextCursor. Query failures throw QueryError with the HTTP status attached; transport failures throw TransportError.

Framework Adapters

Framework integrations ship as separate, independently versioned npm packages, not as subpaths of @chio-protocol/sdk. Each one depends on @chio-protocol/node-http — the shared HTTP client for a chio sidecar —. They do not depend on the in-process @chio-protocol/sdk core. Each exposes middleware or a plugin that evaluates the request against the sidecar, attaches the signed receipt to the request, and surfaces any deny reason to the handler.

FrameworkPackage
Express@chio-protocol/express
Fastify@chio-protocol/fastify
Elysia@chio-protocol/elysia
server.ts
import express from "express";
import { chio, chioErrorHandler } from "@chio-protocol/express";
import type { ChioRequest } from "@chio-protocol/express";

const app = express();

// Evaluate every request against the chio sidecar. Configuration is read
// from chio.yaml; sidecarUrl, onSidecarError, timeoutMs, and skip are optional.
app.use(chio({ config: "chio.yaml" }));

app.post("/tools/read_file", (req, res) => {
  const chioReq = req as ChioRequest;
  // chioReq.chioResult.receipt.id is set when evaluation succeeds.
  res.json({ receiptId: chioReq.chioResult?.receipt.id });
});

// Formats chio deny/error responses as structured JSON.
app.use(chioErrorHandler);

app.listen(3000);

Error Hierarchy

All SDK errors extend ChioError. Catch the base class for broad recovery, or the specific subclass when you need to branch on failure mode.

typescript
import {
  ChioError,
  DpopSignError,
  QueryError,
  TransportError,
} from "@chio-protocol/sdk";

try {
  const page = await client.query({ capabilityId: "cap_abc" });
} catch (err) {
  if (err instanceof QueryError) {
    console.error("query failed", err.status, err.message);
  } else if (err instanceof TransportError) {
    console.error("network failed", err.message);
  } else if (err instanceof ChioError) {
    console.error("chio error", err.code, err.message);
  } else {
    throw err;
  }
}

Invariant errors are separate

ChioInvariantError lives under @chio-protocol/sdk/invariants and does not extend ChioError. Catch it on its own if you call the low-level verification helpers directly.

Quickstart

The snippet below opens a session, list tools, sign a DPoP proof, call a tool, then verify a receipt offline.

quickstart.ts
import {
  ChioClient,
  signDpopProof,
  staticBearerAuth,
} from "@chio-protocol/sdk";
import { verifyReceiptJson } from "@chio-protocol/sdk/invariants";

const client = new ChioClient({
  baseUrl: "https://edge.example.com/mcp",
  ...staticBearerAuth(process.env.CHIO_TOKEN!),
});

const session = await client.initialize();

const tools = await session.listTools();
console.log("available tools:", tools);

const args = { path: "./README.md" };

// Sign a DPoP proof bound to this capability, tool, and argument hash.
const proof = signDpopProof({
  capabilityId: process.env.CHIO_CAPABILITY_ID!,
  toolServer: "srv-files",
  toolName: "read_file",
  actionArgs: args,
  agentSeedHex: process.env.CHIO_AGENT_SEED_HEX!,
});
console.log("dpop proof:", proof.signature);

// callTool(name, args) returns the MCP tool result.
const result = await session.callTool("read_file", args);
console.log("result:", result);

await session.close();

// The kernel signs a receipt for the call. Pull it from your receipt store
// (see ReceiptQueryClient) as a JSON string, then verify it offline.
const verification = verifyReceiptJson(receiptJson);
if (!verification.signature_valid || !verification.parameter_hash_valid) {
  throw new Error("receipt failed local verification");
}

Conformance

The TypeScript SDK is checked against the cross-language conformance vectors. Canonical JSON output, SHA-256 digests, Ed25519 signatures, receipt verification, capability verification, and manifest verification all produce byte-for-byte identical results against the Rust reference.

bash
$ cd sdks/typescript/chio-ts && npm test