guardian-mcp

Lớp thẩm quyền của con người dành cho tác nhân AI — ALLOW/ASK/DENY xác định trên mọi lệnh gọi công cụ, biên nhận có chữ ký

Tài liệu

Guardian — human authority for AI

Guardian SDK

npm version MCP registry Guardian MCP server on mcprush License

Human Authority Infrastructure for Autonomous AI. The agent proposes; the human decides. Every tool call an agent makes gets a deterministic ALLOW / ASK / DENY decision before it runs, and every execution produces a tamper-evident receipt you can verify without an account. The receipt format is an open specification: greceipt.v1.

Hosted Guardian: guardian.odingard.com — free tier, no card required. Pricing: Free, Developer $99/month with 25,000 billable executions included, Business via sales.

Set up with your coding agent

Open Claude Code, Codex or another coding agent in your project folder and paste:

Set up Guardian using https://odingard.com/guardian/skill.md

The agent reads skill.md: it checks your project read-only, shows you its plan and waits for a yes, sends you to the console to create the agent and its credential (you keep the credential out of the chat), installs pinned package versions, and runs one test that waits for your approval. It cannot create the account, the agent or the credential for you. Versions are listed in skill/CHANGELOG.md with the SHA-256 of each file.

Packages

PackageUse it whenDocs
@odingard/guardian-sdkYou call Guardian from your own agent code: createGuard for your own action code, plus the core client, receipts and approvalspackages/sdk
odingard-guardian (PyPI)You build in Python (LangChain, CrewAI, OpenAI Agents, Claude Agent SDK, plain code), sync or asyncpackages/python
@odingard/guardian-mcpYour agent speaks MCP (Claude, Cursor, …), with no code needed. guardian-mcp proxy governs an existing MCP server: local (stdio) is proven on production; remote (--url) is tested against Guardian, production proof pendingpackages/mcp
@odingard/guardian-langchainYou build with LangChain.js / LangGraphpackages/langchain
@odingard/guardian-vercel-aiYou build with the Vercel AI SDKpackages/vercel-ai
@odingard/guardian-steelYour browser agent checks out on Steel. Not yet available on hosted Guardianpackages/steel
@odingard/guardian-hookYour coding agent runs shell commands and edits files (Claude Code, Claude Agent SDK, Codex CLI, DeepSeek Harness)packages/hook
@odingard/guardian-openai-agentsYou build with the OpenAI Agents SDK (TypeScript): every tool you wrap with guardianTool is decided by Guardian before it runs. Tested, but not yet proven against a live Guardian and a live modelpackages/openai-agents
@odingard/guardian-openclawYou run an OpenClaw agent: a native plugin governs exec, file edits, messages and MCP toolspackages/openclaw
@odingard/guardian-x402Your agent pays over x402: Guardian decides payments made through the guarded official x402 TypeScript clients, before the wallet signs. Works with USDC on Base Sepolia (testnet). Base mainnet is off by default.packages/x402
@odingard/guardian-ap2Your agent pays over AP2 v0.2: Guardian decides closed Checkout and Payment Mandates before the agent key signs them (pinned merchant keys, USD). Not yet proven livepackages/ap2

Works with

Any agent can use Guardian by one of three routes: put its MCP servers behind the MCP proxy (guardian-mcp proxy), add the coding-agent hook (guardian-hook), or call the SDK / HTTP API in front of any action (createGuard, Python @guard). The table lists the integrations we ship or document, and how far each has been tested.

IntegrationPackageStatus
Claude Code@odingard/guardian-hookProven on production
Claude Agent SDK (TypeScript)@odingard/guardian-hookProven on production
Any MCP server, local (stdio)@odingard/guardian-mcpProven on production
Any MCP server, remote (--url)@odingard/guardian-mcpTested against Guardian; production proof pending
LangChain.js / LangGraph@odingard/guardian-langchainTested against Guardian; production proof pending
Vercel AI SDK@odingard/guardian-vercel-aiTested against Guardian; production proof pending
OpenAI Agents SDK (TypeScript)@odingard/guardian-openai-agentsTested, but not yet proven against a live Guardian and a live model
Python with LangChain / OpenAI Agents SDKodingard-guardianTested against Guardian; production proof pending
Python with CrewAIodingard-guardianExample, not yet tested
Claude Agent SDK (Python)odingard-guardianExample, not yet tested
Codex CLI@odingard/guardian-hookExample, not yet tested
DeepSeek Harness@odingard/guardian-hookExample, not yet tested
Hermes Agentodingard-guardianExample, not yet tested
ZeroClaw@odingard/guardian-mcp (recipe)Example, not yet tested
Your own container (CrewAI, LangGraph, AutoGen)@odingard/guardian-mcp (recipe)Example, not yet tested
Steel browser checkout@odingard/guardian-steelNot yet available on hosted Guardian
OpenClaw@odingard/guardian-openclawProven on production (shell commands)
NVIDIA NemoClaw (guide)@odingard/guardian-openclaw (guide)Verified on staging with NemoClaw v0.0.143 (shell commands); not tamper-proof
x402 payments (TypeScript)@odingard/guardian-x402Works with USDC on Base Sepolia (testnet). Base mainnet is off by default.
AP2 mandates (TypeScript)@odingard/guardian-ap2Live proof pending (no public AP2 sandbox)

"Tested against Guardian" means the integration ran end to end against the real Guardian server code, with real approvals and receipts verified offline, but not yet on hosted production.

Quickstart

npm install @odingard/guardian-sdk
import {
  classifyRequestResult,
  createGuardianClient,
} from "@odingard/guardian-sdk";

const guardian = createGuardianClient({
  baseUrl: "https://guardian-api.odingard.com",
  credential: process.env.GUARDIAN_AGENT_CREDENTIAL!, // gak_… issued per agent
});

const d = classifyRequestResult(
  await guardian.request({
    action: "tool.invoke",
    connectionId: "pcn_…",
    toolName: "refund_order",
    arguments: { orderId: "ord_123" },
  }),
);

if (d.decision === "ALLOW") {
  await guardian.execute({
    requestId: d.requestId,
    capabilityToken: d.capabilityToken,
  });
}
// ASK → held for a human (keep d.requestId, then guardian.claim). DENY → never runs.

Govern your own action code (any framework)

import { createGuard } from "@odingard/guardian-sdk";

const guard = createGuard({ client: guardian, connectionId: "pcn_…" });
const payInvoice = guard("pay_invoice", async (inv) =>
  myPaymentProvider.pay(inv),
); // your code
await payInvoice({
  invoiceId: "INV-1002",
  amountCents: 1_240_000,
  payeeId: "acme",
});
// ALLOW → your code runs + receipt. ASK → GuardianApprovalPending (resume() after approval). DENY → never runs.
from odingard_guardian import Guard, GuardianClient

guard = Guard(GuardianClient(credential=...), connection_id="pcn_…")

@guard("pay_invoice")
def pay_invoice(invoice_id: str, amount_cents: int, payee_id: str): ...  # your code

Pin the contents of file arguments (fileArgs, 0.4)

Approval binds the exact arguments. A file path is an argument; the file's contents are not, unless you declare the argument in fileArgs (file_args in Python).

const guard = createGuard({
  client: guardian,
  connectionId: "pcn_…",
  mode: "inProcess",
  fileArgs: { send_report: ["attachmentPath"] }, // bytes: no mode named
});

With fileArgs, the SDK on your machine reads the file, puts its SHA-256 in the approved arguments, and checks it again just before the tool runs. Guardian's servers never see the file. A changed file is refused with CONTENT_CHANGED_SINCE_APPROVAL and nothing runs.

  • bytes is the default when you don't name a mode. fileArgs: { send_report: ["attachmentPath"] } (or { attachmentPath: true }) means bytes: your tool receives the verified bytes themselves, so there is no gap between the check and the read. Name "copy" or "recheck" only when a tool needs a path; explicit modes work as before.
  • bytes passes your tool the verified bytes. copy passes a private copy; another process running as the same user could still change it, and the copy is deleted when your tool returns, so a tool that keeps using the path afterwards (for example a background upload) must use bytes. recheck checks the file and then lets your tool open it, so a short window remains.
  • File paths must be absolute. The SDK does not resolve relative paths or ~.
  • fileArgs needs inProcess mode. The LangChain and Vercel AI wrappers default to remote, where Guardian runs the tool and cannot read your files.
  • Opt-in preview (sdk 0.5, Python 0.3): { attachmentPath: { mode: "bytes", preview: true } } (Python: {"attachment_path": {"mode": "bytes", "preview": True}}) also shows the approver the file's type, size and, for a text file, its first 20 lines (at most 2,048 bytes). That text leaves your machine and is stored with the approval like any other argument (also on deny); the agent picks the path, so add previewRoots to preview only files in your folders (others get a size-only withheld preview). Nothing decides on it, the check before the run is still the SHA-256. See the SDK README.
  • Content pinning is supported on Linux and macOS. On Windows a declared file argument is refused (CONTENT_PIN_UNSUPPORTED_PLATFORM).
  • Digest privacy: the SHA-256 of each pinned file is sent to Guardian, shown to the approver and kept in receipts. For a small or low-entropy file (a token, a PIN, a short config value), anyone who sees the digest can guess the contents offline by hashing candidates. Do not pin files like that, or treat the digest as sensitive as the file.
  • This stops a changed file from running under an approval given for different contents. It does not protect against code that bypasses the SDK.
  • Python: the same short form, @guard("send_report", file_args=["attachment_path"]) (or {"attachment_path": True}), means bytes. Guard.run supports recheck only; use the @guard decorator for bytes and copy. A file argument declared on Guard.run without a mode means bytes, so it is refused there rather than checked in a weaker mode; name "recheck". The OpenAI Agents helper works with @guard(file_args=...) tools unchanged.
  • The MCP proxy pins declared arguments of a stdio upstream (--pin-file <tool>:<arg>, recheck only), and the coding-agent hook pins the target of Edit-type tools (not Write, not shell commands); in both a short window remains because the tool opens the file itself. Neither can hand a tool bytes, so neither takes the bytes default: you name no mode there, and the mode is always recheck. A LangChain tool reachable only through invoke() cannot take bytes either, so declaring a file argument without a mode on it is refused when the tool is wrapped (name "copy").
  • Not pinned: URLs, provider file ids, paths on a remote MCP server, files named inside shell commands, and files a tool opens without naming them. The Steel integration, the OpenClaw plugin and the Hermes adapter do not pin the contents of files their tools read. The path is bound; the file's bytes are not.

Govern an MCP server you already use

guardian-mcp proxy -- npx -y your-whatsapp-mcp-server

Govern Hermes Agent (Nous Research)

pip install odingard-guardian   # in Hermes' Python; ships the Hermes plugin "guardian"
# then list guardian under plugins.enabled in Hermes' config.yaml,
# with plugins.hook_callback_timeout: 130

Hermes calls the plugin before every tool call (terminal, file edits, execute_code, the browser, MCP tools, …). Guardian decides; a block holds. Read-only tools are not routed, and anything outside Hermes' tool dispatch is not governed. Example, not yet tested against Guardian. The install step is unverified: hermes plugins enable guardian failed with "not installed or bundled" in the only real attempt, so check that Hermes loads the plugin. See examples/hermes.

Full guides: packages/sdk · packages/python · packages/mcp.

Examples

Runnable demos in examples/ — each prints SIMULATED (local demo policy) or LIVE (real hosted Guardian or real published keys).

npm install && npm run build
cd examples
node verify-receipt.mjs         # LIVE, no account: verify a real receipt offline, then tamper
node demo-classes.mjs           # the 7 demo classes
node steel-checkout.mjs         # Steel checkout → ASK → resume
node shopping-agent.mjs         # purchase → ASK, human approves, receipt
node prompt-injection-deny.mjs  # injected "export contacts" → DENY
node coding-agent-ask.mjs       # consequential command → ASK
node invoice-agent.mjs          # B2B invoices: ALLOW under $5k, ASK above, DENY unknown payee (createGuard)
python python/invoice_agent.py  # the same, in Python (pip install odingard-guardian)
node remote-mcp.mjs             # guardian-mcp in front of a remote (HTTP) MCP server; SIMULATED fail-closed, LIVE with Guardian env (tested against Guardian; production proof pending)
node agent-hooks.mjs codex       # Codex CLI hook: shell, apply_patch, MCP → ALLOW / ASK / DENY (guardian-hook codex)
node agent-hooks.mjs dsh         # DeepSeek Harness hook via its Claude Code bridge (guardian-hook dsh)
python hermes/hermes_hook_demo.py  # Hermes Agent's pre_tool_call hook: ALLOW / ASK / DENY (SIMULATED unless env set)
(cd openclaw && npm install && npm run simulated)  # OpenClaw plugin in OpenClaw's real hook runtime (SIMULATED Guardian)
node x402/simulated-payments.mjs  # x402 payments: ALLOW, ASK above a cap, DENY, before the wallet signs (SIMULATED Guardian, no network)
node ap2/simulated-payments.mjs   # AP2 mandates: delegation, ALLOW, ASK above a cap, DENY, before the agent key signs (SIMULATED, no network)

Recipes (LIVE, examples not yet tested): ZeroClaw · your own container: CrewAI, LangGraph, AutoGen.

Repository layout

packages/sdk            @odingard/guardian-sdk            core client
packages/mcp            @odingard/guardian-mcp            MCP server
packages/hook           @odingard/guardian-hook           coding-agent hooks
packages/langchain      @odingard/guardian-langchain
packages/vercel-ai      @odingard/guardian-vercel-ai
packages/openai-agents  @odingard/guardian-openai-agents  OpenAI Agents SDK adapter
packages/steel          @odingard/guardian-steel
packages/openclaw       @odingard/guardian-openclaw       OpenClaw plugin
packages/x402           @odingard/guardian-x402           x402 payment guard
packages/ap2            @odingard/guardian-ap2            AP2 mandate guard
packages/python         odingard-guardian (PyPI)          Python SDK
examples/               runnable demos

packages/sdk/src/contracts, packages/sdk/src/evidence, the SDK client, guard, lifecycle and receipts sources, and the Steel adapter sources are generated — don't edit them here. See CONTRIBUTING.md.

License

Apache-2.0