MCP
@0xgks/mandate-mcp is a thin stdio wrapper around @0xgks/mandate-sdk, published for MoonPay Agent, Claude Desktop, Codex, and other MCP-compatible clients.
Overview
MCP (Model Context Protocol) is an open standard that lets AI agents discover and call external tools in a structured, interoperable way. By publishing a dedicated MCP server, MANDATE ensures that any MCP-compatible agent can connect to the policy enforcement layer without modification to the agent itself.
The server reimplements no proving, commitment, Merkle, or submission logic of its own — every tool is a thin wrapper around the same MandateClient from @0xgks/mandate-sdk documented on the SDK page.
Installation
npm install @0xgks/mandate-mcpOr run the published binary directly, without installing:
npx -y @0xgks/mandate-mcp@0.1.0The repository's npm run local:start writes a complete, ready-to-paste config to .local/moonpay-agent-mandate.json automatically — see Quickstart — MCP local setup.
Client configuration
A redacted configuration example, adaptable to any MCP client that reads an mcpServers map (this shape matches MoonPay Agent, Claude Desktop, and Codex):
{
"mcpServers": {
"mandate": {
"command": "npx",
"args": ["-y", "@0xgks/mandate-mcp@0.1.0"],
"env": {
"MANDATE_RPC_URL": "<RPC_ENDPOINT>",
"MANDATE_SEQUENCER_URL": "<SEQUENCER_ENDPOINT>",
"MANDATE_AUCTION_ADDRESS": "<AUCTION_CONTRACT_ADDRESS>",
"MANDATE_REGISTRY_ADDRESS": "<REGISTRY_CONTRACT_ADDRESS>",
"MANDATE_AGENT_ID": "<REGISTERED_AGENT_ID>",
"MANDATE_SESSION_KEY": "<SESSION_PRIVATE_KEY — SECRET, NEVER COMMIT>",
"MANDATE_CIRCUIT_PATH": "<PATH_TO_circuits/policy_check>",
"MANDATE_WHITELIST_ROOT": "<WHITELIST_MERKLE_ROOT>",
"MANDATE_MAX_ORDER_NOTIONAL": "1000000",
"MANDATE_MAX_POSITION": "<MAX_POSITION>",
"MANDATE_MAX_DAILY_LOSS": "<MAX_DAILY_LOSS>",
"MANDATE_POLICY_SALT": "<POLICY_SALT — SECRET, NEVER COMMIT>"
}
}
}
}This is more environment variables than the product overview shows
MANDATE_* variables. The actual server (packages/mandate-mcp/src/config.ts) requires five more — MANDATE_CIRCUIT_PATH, MANDATE_WHITELIST_ROOT, MANDATE_MAX_ORDER_NOTIONAL, MANDATE_MAX_POSITION, and MANDATE_MAX_DAILY_LOSS — because PolicyParamsis a required field of the SDK's config and the server has no way to recover that plaintext from the chain (only its commitment is on-chain). This page follows the repository implementation.Environment variables
| Variable | Required | Description |
|---|---|---|
| MANDATE_AGENT_ID | Yes | This agent's identity — Poseidon2(session pk, salt), as a 0x bytes32 or decimal integer. |
| MANDATE_SESSION_KEY | Yes | The session private key used to sign submitOrder transactions. Secret — never commit or log. |
| MANDATE_AUCTION_ADDRESS | Yes | Deployed BatchAuction contract address. |
| MANDATE_REGISTRY_ADDRESS | Yes | Deployed MandateRegistry contract address. |
| MANDATE_CIRCUIT_PATH | Yes | Filesystem path to the policy_check Noir circuit project (containing Nargo.toml), used for proving. |
| MANDATE_WHITELIST_ROOT | Yes | Plaintext mandate parameter — root of the approved-markets Merkle tree. Part of the policy commitment opening. |
| MANDATE_MAX_ORDER_NOTIONAL | Yes | Plaintext mandate parameter — maximum notional (quantity × limit price) per order. |
| MANDATE_MAX_POSITION | Yes | Plaintext mandate parameter — maximum post-fill absolute position. |
| MANDATE_MAX_DAILY_LOSS | Yes | Plaintext mandate parameter — maximum realized daily loss. |
| MANDATE_POLICY_SALT | Yes | Plaintext mandate parameter — random salt binding the policy commitment. Secret — never commit or log. |
| MANDATE_RPC_URL | No | JSON-RPC endpoint for the target chain. Defaults to http://127.0.0.1:8545 (local Anvil). |
| MANDATE_SEQUENCER_URL | No | Sequencer HTTP base URL. Defaults to http://127.0.0.1:8787. |
| MANDATE_NARGO_PATH | No | Override path to the nargo binary. Defaults to $NARGO_BIN or "nargo" on PATH. |
| MANDATE_BB_PATH | No | Override path to the bb (Barretenberg) binary. Defaults to $BB_BIN or "bb" on PATH. |
| MANDATE_MARKET_MAP | No | Optional JSON object mapping market symbols (e.g. "ETH/USDC") to numeric circuit market ids, merged over the built-in defaults. |
Available tools
The server exposes exactly three tools — this is a deliberate design decision, not an incomplete API. There is no mandate_preview_order or dry-run tool: the SDK exposes no such method, so none was invented for the server either.
mandate_get_epoch
Read-only
mandate_get_portfolio
Read-only
mandate_submit_order
Execution (proof-gated)
Tool schemas
mandate_get_epoch
Reads the current MANDATE batch-auction epoch, phase (commit/reveal), and circuit-breaker bit directly from the auction contract. Read-only; does not submit anything.
Input schema
{}Example response
{
"epoch": "396",
"phase": "commit",
"breaker": false
}mandate_get_portfolio
Reads this agent's current anchored portfolio state: position, daily PnL, and the Merkle root anchored on-chain for this agent, cross-checked against the on-chain state root. Read-only; does not submit anything.
Input schema
{}Example response
{
"agentId": "0x2c4fd536018985d83870a4ed4a18dc1a4ded2c3343f7c3da46af50a92846b420",
"position": "0",
"dailyPnl": "0",
"stateRoot": "0x...",
"onchainStateRoot": "0x...",
"stateRootMatches": true,
"whitelistIndex": 0
}mandate_submit_order
Generates a real zero-knowledge compliance proof (Noir/UltraHonk) for this order and submits the proof-gated commitment through MANDATE. This is the only path that can put an order in the batch auction — there is no separate execution or signing tool. A mandate-violating order cannot be proven: it is rejected here, before it ever reaches the chain, with the circuit's rejection reason. This performs real proving (nargo execute + bb prove) and, on success, a real on-chain transaction — it is not a preview or simulation.
Input schema
{
market: string, // e.g. "ETH/USDC", "ETH", or a numeric circuit market id
side: "buy" | "sell",
amount: string, // positive whole-number string, no sign, no decimals
limitPrice: string, // positive whole-number string, no sign, no decimals
}Example response
// compliant order
{
"status": "submitted",
"submitted": true,
"orderCommitment": "0x12a254be...5e74d89",
"epoch": "396",
"txHash": "0xdee28569...9a60bc01"
}
// non-compliant order
{
"status": "rejected",
"submitted": false,
"reason": "order notional exceeds mandate maximum"
}Example interaction
An end-to-end walkthrough, exactly as documented in the repository README:
| Step | Prompt / action | Expected result |
|---|---|---|
| 1 | “What is the current epoch?” → mandate_get_epoch | Epoch: 396, Phase: commit, Circuit breaker: false |
| 2 | “Show me the portfolio.” → mandate_get_portfolio | Position: 0, PnL: 0, State root match: true |
| 3 | “Buy 250 units at 3,500.” → mandate_submit_order | Proof generated, order submitted, tx hash returned |
| 4 | “Buy 10,000 units at 3,500.” → mandate_submit_order | “order notional exceeds mandate maximum” — rejected, nothing submitted |
Security considerations
Limiting the tool surface area is itself a security property: an agent cannot do something the MCP server does not expose, regardless of what it is instructed to attempt. All chain/sequencer/circuit/key material is sourced only from server-side environment variables — never from MCP tool arguments — so a client can request an order but can never redirect the server at a different RPC endpoint, sequencer, signer, or circuit.
Never combine mandate_submit_order with unrestricted execution tools
mandate_submit_order. Proof-gating only works if it is the only execution path available to the agent. Session keys and policy salts are secrets — never commit them to version control, chat logs, or shared documents.