npm package: @plawio/polymarket-veto-mcp
Description: Guarded MCP sidecar for Polymarket CLI powered by Veto
Latest version: 0.1.7

# @plawio/polymarket-veto-mcp

Guarded MCP server for Polymarket CLI, powered by [Veto](https://github.com/PlawIO/veto). Lets AI agents search markets, place orders, and manage positions — with every mutation validated against policy rules before execution. Simulation by default, no real money moves unless you explicitly opt in.

## Get started

### Claude Code (recommended)

```bash
# 1. Install the polymarket CLI
brew install polymarket

# 2. Install the agent skill + connect the MCP server
cp -r skills/polymarket-veto ~/.claude/skills/
bash ~/.claude/skills/polymarket-veto/scripts/setup.sh

# 3. Restart Claude Code — done.
```

The skill teaches your agent the full tool set, policy profiles, error handling, and simulation semantics. The setup script writes `.mcp.json` to your project so the MCP server starts automatically.

To use a specific profile (see [Pick a profile](#pick-a-profile)):

```bash
bash ~/.claude/skills/polymarket-veto/scripts/setup.sh user
```

### Any MCP host

Add to your MCP config file:

```json
{
  "mcpServers": {
    "polymarket-veto": {
      "command": "npm",
      "args": [
        "exec", "--yes", "--prefix", "/tmp",
        "--package", "@plawio/polymarket-veto-mcp",
        "--", "polymarket-veto-mcp", "serve",
        "--policy-profile", "defaults"
      ]
    }
  }
}
```

Or run directly:

```bash
npx -y @plawio/polymarket-veto-mcp serve --policy-profile defaults
```

### Prerequisites

This package wraps the Rust `polymarket` CLI binary. Install it before starting the server:

```bash
brew install polymarket           # Option A: Homebrew
cargo build --release             # Option B: build from repo root
polymarket --version              # verify
```

If you built locally, the binary is auto-discovered from `target/release/`. You can also set `polymarket.binaryPath` in `polymarket-veto.config.yaml`.

## Pick a profile

All profiles block wallet-mutating and raw CTF operations. They differ in how they gate order placement. Pass `--policy-profile <name>` when starting the server.

| Profile | Who it's for | What happens |
|---------|-------------|--------------|
| **`defaults`** | Getting started | Orders under $25 go through. Larger orders and cancel-all need approval. No time-of-day limits. |
| **`agent`** | Autonomous bots | Same as defaults + off-hours gate (outside 8am–8pm ET weekdays need approval). |
| **`user`** | Human delegating to AI | Hard cap $500, orders > $100 need approval, all sells need approval, price guardrails (no buys > $0.97, no sells < $0.03), FOK > $50 blocked, off-hours + weekends need approval, cancel-all blocked. |
| **`conservative`** | Testing new strategies | Every mutation needs approval. Nothing goes through automatically. |

### Decision matrix

How each profile handles representative scenarios during weekday business hours:

| Scenario | defaults | agent | user | conservative |
|----------|----------|-------|------|--------------|
| Read-only (search, book, price) | allow | allow | allow | allow |
| Buy $10 limit | allow | allow | allow | require_approval |
| Buy $30 limit | require_approval | require_approval | allow | require_approval |
| Buy $150 limit | require_approval | require_approval | require_approval | require_approval |
| Buy $600 limit | require_approval | require_approval | **deny** | require_approval |
| Buy @ $0.99 (near-resolution) | allow | allow | **deny** | require_approval |
| Sell (any size) | allow | allow | require_approval | require_approval |
| FOK buy $60 | allow | allow | **deny** | require_approval |
| Cancel all | require_approval | require_approval | **deny** | require_approval |
| CTF split/merge/redeem | **deny** | **deny** | **deny** | **deny** |
| Wallet import/reset/delete key | rejected at MCP layer | rejected at MCP layer | rejected at MCP layer | rejected at MCP layer |

## Tool set

**Read-only** (no policy gating):

`markets_list`, `markets_search`, `markets_get`, `clob_book`, `clob_midpoint`, `clob_price`, `portfolio_positions`

**Mutating** (policy-guarded):

`order_create_limit`, `order_market`, `order_cancel`, `order_cancel_all`, `approve_set`, `ctf_split`, `ctf_merge`, `ctf_redeem`

**Internal controls**:

`audit_query`, `pnl_snapshot`, `pnl_position`, `circuit_breaker_status`, `compliance_report`, `budget_status`, `runtime_status`, `approval_status`

**Paid research** (x402 + economic authorization):

`intel_search`, `intel_market_context`

**Architecturally excluded** (not registered as MCP tools):

`wallet_import`, `wallet_reset`, `clob_delete_api_key`

These return `-32601 Unknown tool` if called — the guard is never invoked.

## Commands

```bash
polymarket-veto-mcp serve [options]    # Start MCP server
polymarket-veto-mcp doctor             # Diagnose binary, config, rules
polymarket-veto-mcp status             # Show runtime readiness and capital control state
polymarket-veto-mcp approval-status --approval-id <id>
polymarket-veto-mcp print-tools        # List registered MCP tools
polymarket-veto-mcp print-config       # Dump resolved config
```

Serve options: `--config <path>`, `--policy-profile defaults|agent|user|conservative`, `--simulation on|off`, `--transport stdio|sse`, `--host <ip>`, `--port <port>`

## Simulation vs live trading

Mutating tools run in simulation mode by default. Simulation estimates shares and notional value using live midpoint data without placing real orders.

Live execution requires **all three**:

1. `--simulation off` at startup
2. `execution.allowLiveTrades: true` in `polymarket-veto.config.yaml`
3. `ALLOW_LIVE_TRADES=true` environment variable

x402 research tools also respect `simulationDefault`, but they stop at price discovery in simulation mode. No x402 payment is settled until simulation is off.

## Approval flow

Approval handling is configurable:

- `runtime.approvalMode: return` returns immediately with `approvalId`, `pending: true`, and budget context so the agent can keep going and poll with `approval_status`.
- `runtime.approvalMode: wait` preserves the original blocking behavior and polls until approval resolves or times out.

The sample config defaults to `return` for a better agent UX. If you run without a config file, the runtime still defaults to `wait` for compatibility.

## Economic authorization

The MCP runtime can treat both orders and x402 spend as budgeted actions under one authority:

- Trade notional is authorized before `order_create_limit`, `order_market`, `ctf_split`, and `ctf_merge`.
- Paid research spend is authorized before `intel_search` and `intel_market_context`.
- `budget_status` returns remaining session, agent, and category budgets plus pending approvals.
- Live priced actions fail closed if the economic authority is unavailable. Simulation can return a cached preview when recent budget state exists.

Priced tool responses now include an `economic` object with quoted or actual spend, payer, approval metadata, and the current budget snapshot.

## Configuration

Config path: `polymarket-veto.config.yaml` (searched in cwd, then defaults).

```yaml
polymarket:
  binaryPath: auto            # auto | command name | explicit path

runtime:
  agentId: ""                 # optional fixed agent id
  agentIdEnv: VETO_AGENT_ID   # env override for agent id
  sessionId: ""               # optional fixed session id
  sessionIdEnv: VETO_SESSION_ID
  approvalMode: return        # return | wait

execution:
  simulationDefault: true
  allowLiveTrades: false
  maxCommandTimeoutMs: 15000
  maxOutputBytes: 1048576

mcp:
  transport: stdio            # stdio | sse
  host: 127.0.0.1
  port: 9800

veto:
  configDir: ../veto          # path to veto/ with rules and veto.config.yaml
  policyProfile: defaults
  cloud:
    apiKeyEnv: VETO_API_KEY

economic:
  enabled: false
  defaultPayer: trader-wallet
  approvedPayers: [trader-wallet, research-wallet]
  scopes: [session, agent, category]
  cloud:
    baseUrl: https://api.veto.so
    apiKeyEnv: VETO_API_KEY
    timeoutMs: 10000
    cacheTtlMs: 30000

x402:
  enabled: false
  evmPrivateKeyEnv: X402_EVM_PRIVATE_KEY
  tools:
    intelSearch:
      enabled: false
      url: https://intel.example/search
      method: GET
      provider: intel-search
      budgetCategory: research
      maxPriceUsd: 0.05
      payer: research-wallet
      allowedNetworks: [eip155:8453]
      allowedAssets: [USDC]
    intelMarketContext:
      enabled: false
      url: https://intel.example/context
      method: GET
      provider: intel-market-context
      budgetCategory: research
      maxPriceUsd: 0.10
      payer: research-wallet
      allowedNetworks: [eip155:8453]
      allowedAssets: [USDC]
```

### Cloud mode (optional)

Local deterministic validation works out of the box. For cloud-backed validation with the Veto dashboard:

```bash
export VETO_API_KEY=veto_xxx
```

Then set `validation.mode: cloud` in `veto/veto.config.yaml`.

If you enable x402 settlement, also provide:

```bash
export X402_EVM_PRIVATE_KEY=0x...
```

The runtime only settles x402 requests when the quoted price is within `maxPriceUsd`, the payer is approved by `economic`, and simulation is off.

## Development

```bash
cd veto-agent
npm install
npm run typecheck    # type check src/
npm test             # run all tests
npm run build        # compile to dist/
npm run simulate     # run policy decision table across all profiles
```

### Testing

Tests cover three layers:

- **Policy evaluation** (`tests/policy.test.ts`) — exercises real YAML rules through `Veto.init()` with local validation. Tests all four profiles across order sizing, price discipline, sell-side, FOK limits, timestamp conditions, and CTF blocking. Includes a cross-profile CTF regression suite.
- **Runtime decisions** (`tests/runtime.test.ts`) — tests denial/approval mapping, async approval return mode, approval lookup, runtime status, simulation behavior, budget status, economic denial, x402 previews, and wallet tool boundary rejection.
- **Tool building** (`tests/tools.test.ts`) — validates CLI argument construction and guard argument computation for each tool.

### Simulate

`npm run simulate` prints a color-coded decision table showing how each profile handles a set of representative scenarios:

```
  SCENARIO                              defaults  user      agent     conservative
  ────────────────────────────────────────────────────────────────────────────────
  Search markets                        ALLOW     ALLOW     ALLOW     ALLOW
  Buy 100 shares @ $0.10 ($10 limit)    ALLOW     ALLOW     ALLOW     APPROVE
  Buy 300 shares @ $0.10 ($30 limit)    APPROVE   ALLOW     APPROVE   APPROVE
  ...
  CTF split $50                         BLOCK     BLOCK     BLOCK     BLOCK
```

Runs entirely in local validation mode with simulation on — no real orders, no cloud calls.

## License

MIT