{"_id":"@0xgks/mandate-mcp","name":"@0xgks/mandate-mcp","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@0xgks/mandate-mcp","version":"0.1.0","description":"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.","type":"module","bin":{"mandate-mcp":"dist/index.js"},"main":"./dist/index.js","types":"./dist/index.d.ts","scripts":{"build":"tsc && chmod +x dist/index.js","dev":"tsx src/index.ts","start":"node dist/index.js","test":"tsx src/index.test.ts","prepublishOnly":"npm run build && npm test"},"dependencies":{"@0xgks/mandate-sdk":"*","@modelcontextprotocol/sdk":"^1.29.0","zod":"^4.4.3"},"devDependencies":{"@types/node":"^20.11.0","tsx":"^4.19.0","typescript":"^5.6.0"},"engines":{"node":">=20"},"publishConfig":{"access":"public"},"_id":"@0xgks/mandate-mcp@0.1.0","_nodeVersion":"22.14.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-EvF7qcU5JSFXlDu7bFmPU7y+94AcQs+CVamVo2xop0/OXJ31xp/jOX7ym45XHNbIl9cjt4hdvlLeloJv96zjxw==","shasum":"f2cc2954615c016f38c387bb5514b3c88281867a","tarball":"https://registry.npmjs.org/@0xgks/mandate-mcp/-/mandate-mcp-0.1.0.tgz","fileCount":14,"unpackedSize":34317,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCICcwDkMEmrwRKctjlaZUtQ4QnYjP1KzYOq15D7UZQEHYAiAo1sxNdBaGnVcxqtKWvh2im0BwKET35kYISg6UOdZ4Hw=="}]},"_npmUser":{"name":"0xgks","email":"goksualcinkaya@gmail.com"},"directories":{},"maintainers":[{"name":"0xgks","email":"goksualcinkaya@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/mandate-mcp_0.1.0_1784768568979_0.43976449597548894"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-23T01:02:48.863Z","0.1.0":"2026-07-23T01:02:49.126Z","modified":"2026-07-23T01:02:49.294Z"},"maintainers":[{"name":"0xgks","email":"goksualcinkaya@gmail.com"}],"description":"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.","readme":"# @0xgks/mandate-mcp\n\nAn MCP (Model Context Protocol) server that exposes **MANDATE** — a\nproof-gated, policy-constrained order execution protocol — to MoonPay\nAgent, Claude, Codex, or any other MCP-compatible AI client.\n\nThis package is a **thin stdio wrapper** around\n[`@0xgks/mandate-sdk`](https://www.npmjs.com/package/@0xgks/mandate-sdk). It\ndoes not reimplement any prover, commitment, Merkle-tree, or submission\nlogic — every order that goes through it is proven with a real Noir/Barretenberg\nzero-knowledge proof and submitted through the real MANDATE contracts and\nsequencer, exactly as `@0xgks/mandate-sdk`'s `MandateClient.proveAndSubmit`\ndoes it.\n\n> ⚠️ **MANDATE is unaudited research software.** Do not use it, or this MCP\n> server, with real funds or real private keys. The demo/test keys referenced\n> anywhere in this repository are Anvil's public, well-known test keys —\n> never use them (or this software) outside a local, throwaway chain.\n\n## Architecture\n\n```\nMoonPay Agent / Claude / Codex\n        ↓  (MCP over stdio)\n@0xgks/mandate-mcp        (this package — tool surface + env config only)\n        ↓  (proveAndSubmit)\n@0xgks/mandate-sdk        (prover wrapper, commitments, Merkle, submission)\n        ↓\nNoir prover (nargo + bb)  +  MANDATE sequencer  +  MANDATE smart contracts\n```\n\n**MoonPay is the agent interface; MANDATE is the execution-policy layer.**\nThis server does not talk to MoonPay's trading APIs at all — \"MoonPay Agent\"\nhere means the MCP-compatible agent runtime that will have this server\nregistered as one of its tools. Whatever gives that agent read-only market\ndata, quotes, or balances (MoonPay's own read-only tools, if you wire them\nup) is a separate concern from whether an order is allowed to execute.\n**Execution only ever happens through this server's `mandate_submit_order`\ntool**, which is proof-gated: see the security warning below.\n\n## What this server does NOT do\n\n- It does **not** expose any MoonPay swap, transfer, bridge, buy/sell, or\n  wallet-signing tool. There is no way to move funds through this server\n  except by proving compliance and going through\n  `mandate_submit_order` → `MandateClient.proveAndSubmit`.\n- It does **not** implement a `previewOrder`/dry-run tool. `@0xgks/mandate-sdk`\n  does not expose one, and this integration does not invent one — a \"preview\"\n  that isn't backed by the real prover would be misleading about what\n  authorizes execution.\n- It does **not** modify the Noir circuit, the contracts, the sequencer, or\n  `@0xgks/mandate-sdk`. It only calls the SDK's public API and a small number\n  of already-public, read-only contract views / sequencer endpoints.\n\n## ⚠️ Security warning: do not also expose unrestricted MoonPay execution tools\n\n**If the same agent session also has access to unrestricted MoonPay\ntransaction tools (swap, transfer, bridge, arbitrary signing), the agent can\nsimply bypass MANDATE entirely** by calling those tools directly instead of\n`mandate_submit_order`. MANDATE's proof-gating only has teeth if it is the\n**only** execution path available to the agent.\n\nFor a meaningful demo/deployment of the security model, an agent using this\nserver should be given:\n\n- ✅ MANDATE's tools from this package (`mandate_get_portfolio`,\n  `mandate_get_epoch`, `mandate_submit_order`)\n- ✅ read-only MoonPay tools if useful (balances, prices, token metadata,\n  quotes)\n- ❌ **not** MoonPay's (or any other) unrestricted execution/signing tools\n\n## Prerequisites\n\nThis server calls out to the same toolchain the rest of the MANDATE repo\nneeds — it does not bundle or replace any of it:\n\n- Node.js ≥ 20\n- [`nargo`](https://noir-lang.org) (matching the version used by\n  `circuits/policy_check`) reachable on `PATH`, or via `MANDATE_NARGO_PATH`\n- [`bb`](https://github.com/AztecProtocol/aztec-packages) (Barretenberg),\n  reachable on `PATH`, or via `MANDATE_BB_PATH`\n- A running MANDATE stack to talk to: an EVM RPC endpoint with the\n  `MandateRegistry`/`BatchAuction` contracts deployed, and a running MANDATE\n  sequencer — see the repo root README / `demo/run.sh` for how to stand up a\n  local Anvil-based stack\n- The compiled circuit artifacts (`target/policy_check.json`, `target/vk`)\n  for whatever circuit directory `MANDATE_CIRCUIT_PATH` points at\n\n## Installation\n\nInside this monorepo (workspace-local development):\n\n```bash\nnpm install\nnpm run build -w @0xgks/mandate-mcp\n```\n\nAs a published package (once published):\n\n```bash\nnpm install -g @0xgks/mandate-mcp\n# or run without installing:\nnpx @0xgks/mandate-mcp\n```\n\n## Configuration\n\nAll configuration is read from **environment variables only** — never from\nMCP tool arguments. This is intentional: an MCP client can ask this server to\nsubmit an order, but it can never redirect the server at a different RPC\nendpoint, a different sequencer, a different signer, or a different circuit.\n\nSee [`.env.example`](./.env.example) for a placeholder-only template (never\ncommit a real `.env` — it's git-ignored, along with `.env.*`, everywhere\nexcept this example file).\n\n### Required\n\n| Variable | Description |\n|---|---|\n| `MANDATE_AGENT_ID` | This agent's identity in the registry. A `0x`-prefixed bytes32 hex value, or a plain decimal integer (padded automatically). |\n| `MANDATE_SESSION_KEY` | The 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. |\n| `MANDATE_AUCTION_ADDRESS` | `BatchAuction` contract address. |\n| `MANDATE_REGISTRY_ADDRESS` | `MandateRegistry` contract address. |\n| `MANDATE_CIRCUIT_PATH` | Absolute path to the Noir circuit project directory (containing `Nargo.toml` and a compiled `target/`) used for proving. |\n\n### Also required: the plaintext mandate (policy) parameters\n\n`@0xgks/mandate-sdk`'s `MandateClient` requires the **plaintext** mandate\nparameters — the opening of the on-chain policy commitment — to prove\nagainst. There is no way to recover this from the chain (only the one-way\nPoseidon2 commitment is stored on-chain), so it must be supplied here. These\nfive variables must match **exactly** what is registered on-chain for\n`MANDATE_AGENT_ID` (`MandateClient` checks this and refuses to prove\notherwise — see `PolicyMismatchError`):\n\n| Variable | Description |\n|---|---|\n| `MANDATE_WHITELIST_ROOT` | Merkle root of the market whitelist (decimal or `0x`-hex). |\n| `MANDATE_MAX_ORDER_NOTIONAL` | Max per-order notional (`size * limitPrice`). |\n| `MANDATE_MAX_POSITION` | Max post-fill absolute position. |\n| `MANDATE_MAX_DAILY_LOSS` | Max daily loss before the mandate rejects further risk-increasing orders. |\n| `MANDATE_POLICY_SALT` | The policy commitment's salt. |\n\n> These five variables are **not** part of the original brief's short env\n> var list — they were added after inspecting `@0xgks/mandate-sdk`'s actual\n> `MandateClientConfig` type (`packages/mandate-sdk/src/types.ts`), which\n> declares `policy: PolicyParams` as a required (non-optional) field. See\n> the top of `src/config.ts` for the same note in code.\n\n### Optional (with defaults)\n\n| Variable | Default | Description |\n|---|---|---|\n| `MANDATE_RPC_URL` | `http://127.0.0.1:8545` | EVM JSON-RPC endpoint. |\n| `MANDATE_SEQUENCER_URL` | `http://127.0.0.1:8787` | MANDATE sequencer base URL. |\n| `MANDATE_NARGO_PATH` | `$NARGO_BIN` or `nargo` on `PATH` | Override the `nargo` binary. |\n| `MANDATE_BB_PATH` | `$BB_BIN` or `bb` on `PATH` | Override the `bb` binary. |\n| `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. |\n\n## Local development\n\n```bash\n# run directly against source with tsx (no build step)\nMANDATE_AGENT_ID=0x... MANDATE_SESSION_KEY=0x... ... npm run dev -w @0xgks/mandate-mcp\n\n# or build once, then run the compiled binary\nnpm run build -w @0xgks/mandate-mcp\nMANDATE_AGENT_ID=0x... MANDATE_SESSION_KEY=0x... ... node packages/mandate-mcp/dist/index.js\n```\n\nThe server communicates exclusively over **stdio** — it prints nothing to\nstdout except MCP protocol frames. All diagnostics (startup errors, proving\nfailures, unexpected errors) go to **stderr**.\n\n## MCP Inspector\n\n[`@modelcontextprotocol/inspector`](https://github.com/modelcontextprotocol/inspector)\nis the standard way to interactively exercise an MCP server over stdio:\n\n```bash\ncd packages/mandate-mcp\nnpx @modelcontextprotocol/inspector node dist/index.js\n```\n\nThis opens a local web UI where you can browse the exposed tools and call\nthem by hand. It also has a scriptable `--cli` mode, e.g.:\n\n```bash\nnpx @modelcontextprotocol/inspector --cli node dist/index.js --method tools/list\n```\n\n(Note: the Inspector CLI's `--tool-arg` coerces numeric-looking values to\nJSON numbers, which will fail this server's schemas — `amount`/`limitPrice`\nare intentionally strings. Use the interactive web UI, or a raw JSON-RPC/stdio\nclient, to pass them correctly quoted as strings.)\n\n## MoonPay Agent configuration\n\nRegister this server as an MCP tool provider. **Restart the MCP client fully\nafter any configuration change** — most clients only read this file at\nstartup.\n\n### Development (local build)\n\n```json\n{\n  \"mcpServers\": {\n    \"mandate\": {\n      \"command\": \"node\",\n      \"args\": [\n        \"/ABSOLUTE/PATH/TO/mandate/packages/mandate-mcp/dist/index.js\"\n      ],\n      \"env\": {\n        \"MANDATE_AGENT_ID\": \"1\",\n        \"MANDATE_RPC_URL\": \"http://127.0.0.1:8545\",\n        \"MANDATE_SEQUENCER_URL\": \"http://127.0.0.1:8787\",\n        \"MANDATE_SESSION_KEY\": \"0x...\",\n        \"MANDATE_AUCTION_ADDRESS\": \"0x...\",\n        \"MANDATE_REGISTRY_ADDRESS\": \"0x...\",\n        \"MANDATE_CIRCUIT_PATH\": \"/ABSOLUTE/PATH/TO/mandate/circuits/policy_check\",\n        \"MANDATE_WHITELIST_ROOT\": \"0x...\",\n        \"MANDATE_MAX_ORDER_NOTIONAL\": \"1000000\",\n        \"MANDATE_MAX_POSITION\": \"1000\",\n        \"MANDATE_MAX_DAILY_LOSS\": \"500\",\n        \"MANDATE_POLICY_SALT\": \"42\"\n      }\n    }\n  }\n}\n```\n\n### Published npm package\n\n```json\n{\n  \"mcpServers\": {\n    \"mandate\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@0xgks/mandate-mcp\"],\n      \"env\": {\n        \"MANDATE_AGENT_ID\": \"1\",\n        \"MANDATE_RPC_URL\": \"http://127.0.0.1:8545\",\n        \"MANDATE_SEQUENCER_URL\": \"http://127.0.0.1:8787\",\n        \"MANDATE_SESSION_KEY\": \"0x...\",\n        \"MANDATE_AUCTION_ADDRESS\": \"0x...\",\n        \"MANDATE_REGISTRY_ADDRESS\": \"0x...\",\n        \"MANDATE_CIRCUIT_PATH\": \"/ABSOLUTE/PATH/TO/mandate/circuits/policy_check\",\n        \"MANDATE_WHITELIST_ROOT\": \"0x...\",\n        \"MANDATE_MAX_ORDER_NOTIONAL\": \"1000000\",\n        \"MANDATE_MAX_POSITION\": \"1000\",\n        \"MANDATE_MAX_DAILY_LOSS\": \"500\",\n        \"MANDATE_POLICY_SALT\": \"42\"\n      }\n    }\n  }\n}\n```\n\nGive the agent **only** this MCP server (plus, if desired, read-only MoonPay\ntools) — see the security warning above.\n\n### Test prompts\n\nOnce registered, these four prompts exercise every tool this server exposes.\nUse them verbatim in the MoonPay Agent (or Claude/Codex) chat:\n\n**Epoch test**\n```text\nUse the mandate_get_epoch tool and tell me the current epoch, phase, and breaker status.\n```\n\n**Portfolio test**\n```text\nUse the mandate_get_portfolio tool and summarize the current portfolio state for the configured agent.\n```\n\n**Valid order test**\n```text\nUse 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.\n```\n\n**Invalid order test**\n```text\nUse 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.\n```\n\n> Whether the \"valid order\" prompt's 500 units actually clears depends on the\n> configured agent's registered `MANDATE_MAX_ORDER_NOTIONAL` — see the note\n> under `mandate_submit_order` below. Against the demo's default mandate\n> (cap `1,000,000`), 500 × 3500 = 1,750,000 would actually be **rejected**;\n> either raise the amount's own agent's cap or use a smaller compliant\n> amount (e.g. 250) when testing against the default demo policy.\n\n## Tools\n\n### `mandate_get_portfolio`\n\nRead-only. No arguments. Returns this agent's current position, daily PnL,\nand anchored state root (cross-checked against the on-chain root).\n\n### `mandate_get_epoch`\n\nRead-only. No arguments. Returns the current batch-auction epoch, phase\n(`\"commit\"` or `\"reveal\"`), and circuit-breaker bit.\n\n### `mandate_submit_order`\n\nGenerates a **real** zero-knowledge compliance proof and submits the\nproof-gated order through MANDATE. This is the only tool in this server that\ncan put an order in the batch auction; there is no separate execution or\nsigning tool, and there is no preview/dry-run mode. **A preview is\ninformational only** does not apply here because there is no preview tool —\nonly successful proof generation and protocol acceptance authorize an order.\n\nInput:\n\n```json\n{\n  \"market\": \"ETH/USDC\",\n  \"side\": \"buy\",\n  \"amount\": \"250\",\n  \"limitPrice\": \"3500\"\n}\n```\n\n`amount` and `limitPrice` are positive whole-number strings (the circuit's\n`size`/`limitPrice` fields are `u32`; fractional amounts are rejected by\ninput validation, not silently truncated). `market` may be a symbol from\n`MANDATE_MARKET_MAP` (default: `ETH/USDC`, `ETH`, `WBTC/USDC`, `WBTC`) or a\nraw numeric circuit market id. Note that `amount * limitPrice` must stay\nwithin the agent's registered `MANDATE_MAX_ORDER_NOTIONAL` — with the demo's\ndefault mandate (`1,000,000`), `250 × 3500 = 875,000` is compliant while\n`10,000 × 3500 = 35,000,000` (see the rejected example below) is not.\n\n#### Valid order → real proof + submission\n\n```json\n{\n  \"status\": \"submitted\",\n  \"submitted\": true,\n  \"orderCommitment\": \"0x022eb705af984a8e1682756a02238b58d065777e9588fa4fc477e58bb4be9912\",\n  \"epoch\": \"7\",\n  \"txHash\": \"0x39ad1cb0736b93eac40cd808861f878abd6032406fb85b88480119cd1ee7b26d\"\n}\n```\n\n(Captured from a real local run against a freshly deployed Anvil stack —\nindependently confirmed on-chain via\n`cast call <auction> \"committedIn(uint64,bytes32)(bool)\" 7 0x022eb7... → true`.)\n\n#### Rejected order → mandate violation, nothing touches the chain\n\n```json\n{\n  \"status\": \"rejected\",\n  \"submitted\": false,\n  \"reason\": \"Assertion failed: order notional exceeds mandate maximum\"\n}\n```\n\n`isError: true` is set on the MCP tool result whenever `submitted` is\n`false`. The `reason` string is the real Noir circuit assertion message —\nthis server preserves it rather than replacing it with a generic error.\n\n## Error handling\n\n- Full error detail (including stack traces) is only ever written to\n  **stderr**, never returned to the MCP client.\n- Tool results carry a short, sanitized `reason` string. For the SDK's own\n  error types (`MandateViolationError`, `PolicyMismatchError`,\n  `PortfolioMismatchError`, `EpochClosedError`) this is the real, meaningful\n  message (none of them embed key material). For anything else, it's the\n  first line of the error message, length-capped.\n- The session private key is never included in any tool response or log\n  line.\n\n## Known limitations\n\n- `MandateClient` does not expose a public portfolio- or epoch-reading\n  method, so `mandate_get_portfolio`/`mandate_get_epoch` call the same\n  read-only contract views and sequencer endpoint that\n  `MandateClient.proveAndSubmit` uses internally, directly from this\n  package — not through the SDK. No proving, commitment, or Merkle logic is\n  duplicated; these are plain reads.\n- There is no `mandate_preview_order` tool: `@0xgks/mandate-sdk` exposes no\n  preview/dry-run method, and one is not invented here.\n- `MANDATE_MARKET_MAP` is a convenience layer translating human-readable\n  market symbols to the protocol's numeric market ids; it is not part of the\n  MANDATE protocol itself.\n- This server assumes a single configured agent identity per process (one\n  `MANDATE_AGENT_ID`/session key pair). Running multiple agents means\n  running multiple server processes with different environments.\n","readmeFilename":"README.md","_rev":"1-2d24febab39983a52113fe933aa1669d"}