Catalog npm
PACKAGE EXPLORERView in registry

@0xgks/mandate-mcp

npm

MCP server for proof-gated MANDATE agent execution — a thin stdio wrapper around @0xgks/mandate-sdk for MoonPay Agent, Claude, Codex, and other MCP-compatible clients.

Versions collected1
Direct dependencies6From release 0.1.0
Last observed21 Oct, 08:55 UTC

README

Current package documentation

@0xgks/mandate-mcp

An MCP (Model Context Protocol) server that exposes MANDATE — a proof-gated, policy-constrained order execution protocol — to MoonPay Agent, Claude, Codex, or any other MCP-compatible AI client.

This package is a thin stdio wrapper around @0xgks/mandate-sdk. It does not reimplement any prover, commitment, Merkle-tree, or submission logic — every order that goes through it is proven with a real Noir/Barretenberg zero-knowledge proof and submitted through the real MANDATE contracts and sequencer, exactly as @0xgks/mandate-sdk's MandateClient.proveAndSubmit does it.

⚠️ MANDATE is unaudited research software. Do not use it, or this MCP server, with real funds or real private keys. The demo/test keys referenced anywhere in this repository are Anvil's public, well-known test keys — never use them (or this software) outside a local, throwaway chain.

Architecture

MoonPay Agent / Claude / Codex
        ↓  (MCP over stdio)
@0xgks/mandate-mcp        (this package — tool surface + env config only)
        ↓  (proveAndSubmit)
@0xgks/mandate-sdk        (prover wrapper, commitments, Merkle, submission)
        ↓
Noir prover (nargo + bb)  +  MANDATE sequencer  +  MANDATE smart contracts

MoonPay is the agent interface; MANDATE is the execution-policy layer. This server does not talk to MoonPay's trading APIs at all — "MoonPay Agent" here means the MCP-compatible agent runtime that will have this server registered as one of its tools. Whatever gives that agent read-only market data, quotes, or balances (MoonPay's own read-only tools, if you wire them up) is a separate concern from whether an order is allowed to execute. Execution only ever happens through this server's mandate_submit_order tool, which is proof-gated: see the security warning below.

What this server does NOT do

  • It does not expose any MoonPay swap, transfer, bridge, buy/sell, or wallet-signing tool. There is no way to move funds through this server except by proving compliance and going through mandate_submit_order → MandateClient.proveAndSubmit.
  • It does not implement a previewOrder/dry-run tool. @0xgks/mandate-sdk does not expose one, and this integration does not invent one — a "preview" that isn't backed by the real prover would be misleading about what authorizes execution.
  • It does not modify the Noir circuit, the contracts, the sequencer, or @0xgks/mandate-sdk. It only calls the SDK's public API and a small number of already-public, read-only contract views / sequencer endpoints.

⚠️ Security warning: do not also expose unrestricted MoonPay execution tools

If the same agent session also has access to unrestricted MoonPay transaction tools (swap, transfer, bridge, arbitrary signing), the agent can simply bypass MANDATE entirely by calling those tools directly instead of mandate_submit_order. MANDATE's proof-gating only has teeth if it is the only execution path available to the agent.

For a meaningful demo/deployment of the security model, an agent using this server should be given:

  • ✅ MANDATE's tools from this package (mandate_get_portfolio, mandate_get_epoch, mandate_submit_order)
  • ✅ read-only MoonPay tools if useful (balances, prices, token metadata, quotes)
  • ❌ not MoonPay's (or any other) unrestricted execution/signing tools

Prerequisites

This server calls out to the same toolchain the rest of the MANDATE repo needs — it does not bundle or replace any of it:

  • Node.js ≥ 20
  • nargo (matching the version used by circuits/policy_check) reachable on PATH, or via MANDATE_NARGO_PATH
  • bb (Barretenberg), reachable on PATH, or via MANDATE_BB_PATH
  • A running MANDATE stack to talk to: an EVM RPC endpoint with the MandateRegistry/BatchAuction contracts deployed, and a running MANDATE sequencer — see the repo root README / demo/run.sh for how to stand up a local Anvil-based stack
  • The compiled circuit artifacts (target/policy_check.json, target/vk) for whatever circuit directory MANDATE_CIRCUIT_PATH points at

Installation

Inside this monorepo (workspace-local development):

npm install
npm run build -w @0xgks/mandate-mcp

As a published package (once published):

npm install -g @0xgks/mandate-mcp
# or run without installing:
npx @0xgks/mandate-mcp

Configuration

All configuration is read from environment variables only — never from MCP tool arguments. This is intentional: an MCP client can ask this server to submit an order, but it can never redirect the server at a different RPC endpoint, a different sequencer, a different signer, or a different circuit.

See .env.example for a placeholder-only template (never commit a real .env — it's git-ignored, along with .env.*, everywhere except this example file).

Required

VariableDescription
MANDATE_AGENT_IDThis agent's identity in the registry. A 0x-prefixed bytes32 hex value, or a plain decimal integer (padded automatically).
MANDATE_SESSION_KEYThe session EOA's private key (0x-prefixed, 32 bytes). Never logged. The session key has zero authority over funds in MANDATE's design — only over proof-gated order submission.
MANDATE_AUCTION_ADDRESSBatchAuction contract address.
MANDATE_REGISTRY_ADDRESSMandateRegistry contract address.
MANDATE_CIRCUIT_PATHAbsolute path to the Noir circuit project directory (containing Nargo.toml and a compiled target/) used for proving.

Also required: the plaintext mandate (policy) parameters

@0xgks/mandate-sdk's MandateClient requires the plaintext mandate parameters — the opening of the on-chain policy commitment — to prove against. There is no way to recover this from the chain (only the one-way Poseidon2 commitment is stored on-chain), so it must be supplied here. These five variables must match exactly what is registered on-chain for MANDATE_AGENT_ID (MandateClient checks this and refuses to prove otherwise — see PolicyMismatchError):

VariableDescription
MANDATE_WHITELIST_ROOTMerkle root of the market whitelist (decimal or 0x-hex).
MANDATE_MAX_ORDER_NOTIONALMax per-order notional (size * limitPrice).
MANDATE_MAX_POSITIONMax post-fill absolute position.
MANDATE_MAX_DAILY_LOSSMax daily loss before the mandate rejects further risk-increasing orders.
MANDATE_POLICY_SALTThe policy commitment's salt.

These five variables are not part of the original brief's short env var list — they were added after inspecting @0xgks/mandate-sdk's actual MandateClientConfig type (packages/mandate-sdk/src/types.ts), which declares policy: PolicyParams as a required (non-optional) field. See the top of src/config.ts for the same note in code.

Optional (with defaults)

VariableDefaultDescription
MANDATE_RPC_URLhttp://127.0.0.1:8545EVM JSON-RPC endpoint.
MANDATE_SEQUENCER_URLhttp://127.0.0.1:8787MANDATE sequencer base URL.
MANDATE_NARGO_PATH$NARGO_BIN or nargo on PATHOverride the nargo binary.
MANDATE_BB_PATH$BB_BIN or bb on PATHOverride the bb binary.
MANDATE_MARKET_MAP{"ETH/USDC":"1","ETH":"1","WBTC/USDC":"2","WBTC":"2"}JSON object mapping human-readable market symbols (as used in tool calls, e.g. "ETH/USDC") to the circuit's numeric market id. Merged over the default map. A plain numeric string (e.g. "1") always passes through unchanged.

Local development

# run directly against source with tsx (no build step)
MANDATE_AGENT_ID=0x... MANDATE_SESSION_KEY=0x... ... npm run dev -w @0xgks/mandate-mcp

# or build once, then run the compiled binary
npm run build -w @0xgks/mandate-mcp
MANDATE_AGENT_ID=0x... MANDATE_SESSION_KEY=0x... ... node packages/mandate-mcp/dist/index.js

The server communicates exclusively over stdio — it prints nothing to stdout except MCP protocol frames. All diagnostics (startup errors, proving failures, unexpected errors) go to stderr.

MCP Inspector

@modelcontextprotocol/inspector is the standard way to interactively exercise an MCP server over stdio:

cd packages/mandate-mcp
npx @modelcontextprotocol/inspector node dist/index.js

This opens a local web UI where you can browse the exposed tools and call them by hand. It also has a scriptable --cli mode, e.g.:

npx @modelcontextprotocol/inspector --cli node dist/index.js --method tools/list

(Note: the Inspector CLI's --tool-arg coerces numeric-looking values to JSON numbers, which will fail this server's schemas — amount/limitPrice are intentionally strings. Use the interactive web UI, or a raw JSON-RPC/stdio client, to pass them correctly quoted as strings.)

MoonPay Agent configuration

Register this server as an MCP tool provider. Restart the MCP client fully after any configuration change — most clients only read this file at startup.

Development (local build)

{
  "mcpServers": {
    "mandate": {
      "command": "node",
      "args": [
        "/ABSOLUTE/PATH/TO/mandate/packages/mandate-mcp/dist/index.js"
      ],
      "env": {
        "MANDATE_AGENT_ID": "1",
        "MANDATE_RPC_URL": "http://127.0.0.1:8545",
        "MANDATE_SEQUENCER_URL": "http://127.0.0.1:8787",
        "MANDATE_SESSION_KEY": "0x...",
        "MANDATE_AUCTION_ADDRESS": "0x...",
        "MANDATE_REGISTRY_ADDRESS": "0x...",
        "MANDATE_CIRCUIT_PATH": "/ABSOLUTE/PATH/TO/mandate/circuits/policy_check",
        "MANDATE_WHITELIST_ROOT": "0x...",
        "MANDATE_MAX_ORDER_NOTIONAL": "1000000",
        "MANDATE_MAX_POSITION": "1000",
        "MANDATE_MAX_DAILY_LOSS": "500",
        "MANDATE_POLICY_SALT": "42"
      }
    }
  }
}

Published npm package

{
  "mcpServers": {
    "mandate": {
      "command": "npx",
      "args": ["-y", "@0xgks/mandate-mcp"],
      "env": {
        "MANDATE_AGENT_ID": "1",
        "MANDATE_RPC_URL": "http://127.0.0.1:8545",
        "MANDATE_SEQUENCER_URL": "http://127.0.0.1:8787",
        "MANDATE_SESSION_KEY": "0x...",
        "MANDATE_AUCTION_ADDRESS": "0x...",
        "MANDATE_REGISTRY_ADDRESS": "0x...",
        "MANDATE_CIRCUIT_PATH": "/ABSOLUTE/PATH/TO/mandate/circuits/policy_check",
        "MANDATE_WHITELIST_ROOT": "0x...",
        "MANDATE_MAX_ORDER_NOTIONAL": "1000000",
        "MANDATE_MAX_POSITION": "1000",
        "MANDATE_MAX_DAILY_LOSS": "500",
        "MANDATE_POLICY_SALT": "42"
      }
    }
  }
}

Give the agent only this MCP server (plus, if desired, read-only MoonPay tools) — see the security warning above.

Test prompts

Once registered, these four prompts exercise every tool this server exposes. Use them verbatim in the MoonPay Agent (or Claude/Codex) chat:

Epoch test

Use the mandate_get_epoch tool and tell me the current epoch, phase, and breaker status.

Portfolio test

Use the mandate_get_portfolio tool and summarize the current portfolio state for the configured agent.

Valid order test

Use only mandate_submit_order to submit a buy order for 500 units at a price of 3500. Do not use any unrestricted wallet, swap, transfer, or MoonPay execution tool.

Invalid order test

Use only mandate_submit_order to attempt a buy order for 10000 units at a price of 3500. Return the exact policy or proof rejection reason. Do not bypass the MANDATE policy engine.

Whether the "valid order" prompt's 500 units actually clears depends on the configured agent's registered MANDATE_MAX_ORDER_NOTIONAL — see the note under mandate_submit_order below. Against the demo's default mandate (cap 1,000,000), 500 × 3500 = 1,750,000 would actually be rejected; either raise the amount's own agent's cap or use a smaller compliant amount (e.g. 250) when testing against the default demo policy.

Tools

mandate_get_portfolio

Read-only. No arguments. Returns this agent's current position, daily PnL, and anchored state root (cross-checked against the on-chain root).

mandate_get_epoch

Read-only. No arguments. Returns the current batch-auction epoch, phase ("commit" or "reveal"), and circuit-breaker bit.

mandate_submit_order

Generates a real zero-knowledge compliance proof and submits the proof-gated order through MANDATE. This is the only tool in this server that can put an order in the batch auction; there is no separate execution or signing tool, and there is no preview/dry-run mode. A preview is informational only does not apply here because there is no preview tool — only successful proof generation and protocol acceptance authorize an order.

Input:

{
  "market": "ETH/USDC",
  "side": "buy",
  "amount": "250",
  "limitPrice": "3500"
}

amount and limitPrice are positive whole-number strings (the circuit's size/limitPrice fields are u32; fractional amounts are rejected by input validation, not silently truncated). market may be a symbol from MANDATE_MARKET_MAP (default: ETH/USDC, ETH, WBTC/USDC, WBTC) or a raw numeric circuit market id. Note that amount * limitPrice must stay within the agent's registered MANDATE_MAX_ORDER_NOTIONAL — with the demo's default mandate (1,000,000), 250 × 3500 = 875,000 is compliant while 10,000 × 3500 = 35,000,000 (see the rejected example below) is not.

Valid order → real proof + submission

{
  "status": "submitted",
  "submitted": true,
  "orderCommitment": "0x022eb705af984a8e1682756a02238b58d065777e9588fa4fc477e58bb4be9912",
  "epoch": "7",
  "txHash": "0x39ad1cb0736b93eac40cd808861f878abd6032406fb85b88480119cd1ee7b26d"
}

(Captured from a real local run against a freshly deployed Anvil stack — independently confirmed on-chain via cast call <auction> "committedIn(uint64,bytes32)(bool)" 7 0x022eb7... → true.)

Rejected order → mandate violation, nothing touches the chain

{
  "status": "rejected",
  "submitted": false,
  "reason": "Assertion failed: order notional exceeds mandate maximum"
}

isError: true is set on the MCP tool result whenever submitted is false. The reason string is the real Noir circuit assertion message — this server preserves it rather than replacing it with a generic error.

Error handling

  • Full error detail (including stack traces) is only ever written to stderr, never returned to the MCP client.
  • Tool results carry a short, sanitized reason string. For the SDK's own error types (MandateViolationError, PolicyMismatchError, PortfolioMismatchError, EpochClosedError) this is the real, meaningful message (none of them embed key material). For anything else, it's the first line of the error message, length-capped.
  • The session private key is never included in any tool response or log line.

Known limitations

  • MandateClient does not expose a public portfolio- or epoch-reading method, so mandate_get_portfolio/mandate_get_epoch call the same read-only contract views and sequencer endpoint that MandateClient.proveAndSubmit uses internally, directly from this package — not through the SDK. No proving, commitment, or Merkle logic is duplicated; these are plain reads.
  • There is no mandate_preview_order tool: @0xgks/mandate-sdk exposes no preview/dry-run method, and one is not invented here.
  • MANDATE_MARKET_MAP is a convenience layer translating human-readable market symbols to the protocol's numeric market ids; it is not part of the MANDATE protocol itself.
  • This server assumes a single configured agent identity per process (one MANDATE_AGENT_ID/session key pair). Running multiple agents means running multiple server processes with different environments.

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Versions 1

Collected releases
VersionPublishedDirect dependenciesOpen version
0.1.0Registry latest23 Jul 20266

Package advisories

0
No matching advisories collected

This describes our collected data; it is not a security assessment.

Metadata history

Meaningful changes
Current collected state Current

Observed since 21 Oct, 08:55 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package:7e8ee9f06f367fd3a39fc8462650f4c54303b1d913c78d4c03a5de089d2b3dcb