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
$ npm install @chio-protocol/sdk
$ yarn add @chio-protocol/sdk
$ pnpm add @chio-protocol/sdk
$ bun add @chio-protocol/sdkNode.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.
| Module | Purpose |
|---|---|
@chio-protocol/sdk | Top-level entry point: client, session, authorization, errors, DPoP, and receipt query. |
@chio-protocol/sdk/invariants | Pure verification helpers: canonical JSON, SHA-256, Ed25519, receipt/capability/manifest parsing and verification. |
@chio-protocol/sdk/transport | Streamable 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
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
import { sha256Hex, sha256HexBytes, sha256HexUtf8 } from "@chio-protocol/sdk/invariants";
sha256Hex("hello world");
sha256HexBytes(Buffer.from([1, 2, 3]));
sha256HexUtf8("hello world");Receipts
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
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
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
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 charsChioClient 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.
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.
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.
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
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
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
// 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.
| Framework | Package |
|---|---|
| Express | @chio-protocol/express |
| Fastify | @chio-protocol/fastify |
| Elysia | @chio-protocol/elysia |
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.
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.
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.
$ cd sdks/typescript/chio-ts && npm test