CAIN-42 · For developers
TypeScript SDK
Guard agent actions from Node.js and TypeScript.
Last reviewed 2026-10-01
Live Core platform · Platform feature
Where it fits
Part of For developers: SDKs, CLI, MCP server, APIs and offline verifiers. Every CAIN-42 product runs behind the same rule: an AI agent's action is checked before it runs (identity, authority, policy, risk), decided as allow, hold for a human, or block, and recorded as signed evidence. Unknown or error never becomes allow.
Use it
- Documentation
https://cainstudio.online/docs/sdk-typescript
Live now
Checked from your browser when this page opened, not from a cached list.
Fire a real decision
Send an action through the live CAIN-42 pipeline from this page, with no account, and watch every stage decide. This is the same pipeline every product here sits behind; it runs for a throwaway demo tenant and is rate limited.
For AI engineers
Every product sits behind one decision path: your agent proposes an action with the exact arguments, CAIN runs it through identity, authority, policy, risk, trust and quorum consensus, and answers ALLOW, REQUIRE_APPROVAL or DENY with an Ed25519-signed record. A timeout, outage or unknown verdict never becomes ALLOW. A brand-new agent has no trust history, so its first actions usually come back REQUIRE_APPROVAL.
Python (zero dependencies)
pip install https://cainstudio.online/cainstudio-0.3.0-py3-none-any.whl
export CAIN_API_KEY=... # free key: https://cainstudio.online/signup
import cainstudio
@cainstudio.guard()
def transfer(amount_usd: float, to: str) -> str:
... # runs only if CAIN allows this call, with these arguments
try:
transfer(5000, "acme")
except cainstudio.ApprovalRequired as e:
print("held for a human:", e.approval_id)
except cainstudio.ActionBlocked as e:
print("refused:", e.decision.reasons)
except cainstudio.CainUnavailable:
print("CAIN unreachable: not run") # fail-closedSee a real decision with no account
cainstudio try # live pipeline, stage by stage
cainstudio try --list # the other attack scenariosMCP clients (Claude Code, Cursor)
claude mcp add --transport http cain https://cainstudio.online/mcpMore: Python SDK · TypeScript SDK · framework integrations · AI quickstart · decision signing key
Tested guarantees in this area
Every rule in these niches has its own page with its recorded result.
- Tools, MCP, protocols & adapters: 684 tested invariants — How agents reach tools, APIs and each other, safely.
- Benchmarks, coverage & performance: 1269 tested invariants — How fast it runs and how much was tested.
Related
- CAIN MCP server — Plug CAIN into Claude, Cursor or any MCP client with one command.
- cainstudio CLI — Decide, explain and approve actions from your terminal.
- cainstudio Python SDK — Add one line to your Python agent and every risky action is checked first.
- Decision API — One HTTP call: send the action, get allow / hold / block and a signed record.
- Framework integrations — Works with LangChain, CrewAI, AutoGen, OpenAI Agents, MCP, Claude Code and Cursor.
- Hosted service catalog — Every hosted service you can call with one key.
Full documentation
The complete reference, also at /docs/sdk-typescript.
TypeScript and JavaScript#
There is no npm package yet. The decision API is one HTTP call, so the helper below is the whole integration: copy it into your project. Node 18+ (global fetch), Deno, Bun and edge runtimes. No dependencies.
// cain.ts
const CAIN_URL = process.env.CAIN_BASE_URL ?? "https://cainstudio.online";
export class CainRefused extends Error {
constructor(message: string, readonly decision?: any) { super(message); }
}
/** Ask CAIN whether a tool call may run. Resolves with the decision; never throws. */
export async function decide(tool: string, payload: object, agentId = "my-agent", runId?: string) {
try {
const r = await fetch(`${CAIN_URL}/fabric/decisions`, {
method: "POST",
headers: { "X-API-Key": process.env.CAIN_API_KEY ?? "", "content-type": "application/json" },
body: JSON.stringify({ path: `/tools/${tool}`, payload, agent_id: agentId, chain_id: runId }),
signal: AbortSignal.timeout(5000),
});
return await r.json();
} catch (e) {
return { verdict: "ERROR", blocked: true, detail: String(e) }; // fail closed
}
}
/** The only permit: an ALLOWED* verdict that is not blocked. */
export const allowed = (d: any) =>
d?.blocked === false && String(d?.verdict ?? "").startsWith("ALLOWED");
/** Wrap a tool: it runs only if CAIN allows this call with these arguments. */
export function guard<A extends object, R>(tool: string, fn: (args: A) => Promise<R>, agentId?: string) {
return async (args: A): Promise<R> => {
const d = await decide(tool, args, agentId);
if (!allowed(d)) throw new CainRefused(`${tool}: ${d.verdict ?? d.detail ?? "no verdict"} (decision ${d.decision_id ?? "-"})`, d);
return fn(args);
};
}
import { guard } from "./cain";
export const sendEmail = guard("send_email", async ({ to, subject }: { to: string; subject: string }) => {
// unchanged
});
A new agent has no trust history, so its first calls come back REQUIRE_APPROVAL: approve them in the console, or add a tool rule. The verdicts, the decision record and the approval flow are the same as in the Python SDK and the HTTP quickstart. The full OpenAPI schema is operator-only.
Try CAIN-42 on your own agents
Create a free account and every new account starts with a 7-day trial of the full platform. Or try the sandbox first, with no account at all.
Create a free account → · Try the sandbox · See the whole ecosystem