← All Docs

CLI & MCP Server

CLI & MCP Server

Add spending controls to any MCP-compatible AI agent in under a minute. No SDK integration required — one config block and your agent gets policy-governed tools.


Overview

The @policylayer/mcp package provides two things:

  1. CLI (npx @policylayer/mcp init) — Interactive setup wizard that authenticates via browser, walks you through org/agent creation, configures policies, and outputs the correct MCP config for your client.

  2. MCP Server — Runs locally on your machine, exposing PolicyLayer as five native tools that any MCP-compatible client can call. The server translates between human-readable amounts and the policy engine’s base-unit format, resolves token addresses automatically, and signs transactions locally with your keys.

What the agent sees:

Tools available:
- validate_transaction  — Check if a transaction would be allowed (dry-run)
- send_transaction      — Execute a policy-enforced transaction
- check_budget          — View remaining spending capacity
- list_policies         — See active spending rules
- transaction_history   — Review recent policy decisions

What stays safe:

  • Private keys never leave your machine — the MCP server signs locally
  • PolicyLayer only sees transaction intents (chain, asset, recipient, amount)
  • If the API is unreachable, all tools fail closed — no cached approvals
  • send_transaction is annotated as destructive, so clients prompt for confirmation

Quick Start

Run the CLI

npx @policylayer/mcp init

This will:

  1. Open your browser to authenticate with PolicyLayer
  2. Let you select or create an organisation
  3. Create an agent with spending policies
  4. Detect your MCP client (Claude Code, Claude Desktop, or Cursor)
  5. Output the config block to paste into your client

The CLI handles everything — API key generation, policy configuration, and client-specific config formatting.

What it produces

After completing the wizard, you’ll get a config block like this (example for Claude Desktop):

{
  "mcpServers": {
    "policylayer": {
      "command": "npx",
      "args": ["-y", "@policylayer/mcp"],
      "env": {
        "POLICYLAYER_API_KEY": "pl_live_xxxxxxxxxxxxx"
      }
    }
  }
}

Paste it into the right file for your client (see MCP Server Configuration below), restart, and your agent has spending controls.


MCP Server Configuration

If you prefer manual setup or need to customise beyond what the CLI produces, here’s the config for each supported client.

Claude Code

Add to your project’s .mcp.json or run:

claude mcp add policylayer -- npx -y @policylayer/mcp

Then set the environment variable:

export POLICYLAYER_API_KEY=pl_live_xxxxxxxxxxxxx

Or add to .mcp.json directly:

{
  "mcpServers": {
    "policylayer": {
      "command": "npx",
      "args": ["-y", "@policylayer/mcp"],
      "env": {
        "POLICYLAYER_API_KEY": "pl_live_xxxxxxxxxxxxx"
      }
    }
  }
}

Claude Desktop

Add to your Claude Desktop config file:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json
{
  "mcpServers": {
    "policylayer": {
      "command": "npx",
      "args": ["-y", "@policylayer/mcp"],
      "env": {
        "POLICYLAYER_API_KEY": "pl_live_xxxxxxxxxxxxx"
      }
    }
  }
}

Cursor

Add to .cursor/mcp.json in your project root:

{
  "mcpServers": {
    "policylayer": {
      "command": "npx",
      "args": ["-y", "@policylayer/mcp"],
      "env": {
        "POLICYLAYER_API_KEY": "pl_live_xxxxxxxxxxxxx"
      }
    }
  }
}

Enabling execution mode

The examples above give your agent read-only tools (validate, budget, policies, history). To enable send_transaction, add wallet environment variables:

{
  "mcpServers": {
    "policylayer": {
      "command": "npx",
      "args": ["-y", "@policylayer/mcp"],
      "env": {
        "POLICYLAYER_API_KEY": "pl_live_xxxxxxxxxxxxx",
        "WALLET_PRIVATE_KEY": "0x...",
        "CHAIN": "base",
        "RPC_URL": "https://mainnet.base.org"
      }
    }
  }
}

See Read-Only vs Execution Mode for guidance on when to use each.


Environment Variables

VariableRequiredDefaultDescription
POLICYLAYER_API_KEYYesAuthenticates all API calls. Scopes to your org and policies. Get this from the dashboard or the CLI wizard.
WALLET_ADAPTERNoviemWhich wallet SDK to use for signing. Options: viem, ethers.
WALLET_PRIVATE_KEYNoEnables execution mode. The private key used to sign transactions. Never transmitted — stays in your local process.
CHAINNoRequired when execution enabled. The chain the wallet operates on (e.g. ethereum, base, arbitrum).
RPC_URLNoRequired for viem and ethers adapters. Your JSON-RPC endpoint.
POLICYLAYER_API_URLNohttps://api.policylayer.comOverride the API URL. Useful for local development against http://localhost:3001.

Non-custodial guarantee: WALLET_PRIVATE_KEY is read by the MCP server process running on your machine. It is used exclusively for local transaction signing. PolicyLayer’s API never receives, stores, or has access to your private key.


Available Tools

validate_transaction

Advisory policy check. Evaluates whether a transaction would be allowed without reserving budget or consuming limits. Use this to preview decisions before committing.

Parameters:

FieldTypeRequiredDescription
chainstringYesChain identifier: "ethereum", "base", "arbitrum", etc.
assetstringYesAsset symbol: "eth", "usdc", "dai", etc.
tostringYesRecipient address (0x...)
amountstringYesHuman-readable amount: "100.50", "0.5"
memostringNoDescription of the transaction purpose
tokenAddressstringNoOverride token contract address. Auto-resolved for well-known assets.

Example response (approved):

Transaction would be approved.

Chain: Base
Asset: USDC
Amount: 100.50 USDC
To: 0x742d...bD18

Policy checks:
- Per-transaction limit (500 USDC): PASS
- Daily limit (350/1,000 USDC spent): PASS
- Recipient whitelist: PASS

Remaining daily budget: 549.50 USDC

Note: This is an advisory check. No budget has been reserved.
Use send_transaction to execute.

Example response (denied):

Transaction would be denied.

Reason: Amount exceeds per-transaction limit.
Limit: 500 USDC
Requested: 750 USDC

send_transaction

Validates, verifies, signs, and broadcasts a transaction in one call. This is the only tool that reserves budget and executes on-chain. Only available when wallet environment variables are configured.

Internally, this runs the full two-gate flow: Gate 1 (policy evaluation + budget reservation) then Gate 2 (token verification + tamper detection) then local signing then broadcast.

Parameters:

FieldTypeRequiredDescription
chainstringYesChain identifier
assetstringYesAsset symbol
tostringYesRecipient address (0x...)
amountstringYesHuman-readable amount
memostringNoDescription. Stored in audit log.
tokenAddressstringNoOverride token contract address

Example response:

Transaction sent.

Chain: Base
Asset: USDC
To: 0x742d...bD18
Amount: 250 USDC
Tx hash: 0xabc123...def456
Block explorer: https://basescan.org/tx/0xabc123...def456

Remaining daily budget: 400 USDC

No double-reservation: validate_transaction is a dry-run with no side effects. Calling validate then send creates exactly one reservation.


check_budget

Returns remaining spending capacity across all policy dimensions.

Parameters:

FieldTypeRequiredDescription
assetstringNoFilter to a specific asset. Omit for all.

Example response:

Budget status

USDC:
- Per-transaction max: 500 USDC
- Hourly: 200/500 USDC used (300 remaining)
  - Including 1 pending reservation (100 USDC)
- Daily: 350/1,000 USDC used (650 remaining, resets midnight UTC)
- Transactions this hour: 3/20

ETH:
- Per-transaction max: 0.5 ETH
- Daily: 0.2/2.0 ETH used (1.8 remaining)

list_policies

Returns all active spending policies configured for the current API key.

Parameters:

FieldTypeRequiredDescription
assetstringNoFilter to a specific asset. Omit for all.

Example response:

{
  "policies": [
    {
      "id": "pol_abc123",
      "name": "USDC on Base",
      "asset": "USDC",
      "limits": {
        "dailyLimit": "1000000000",
        "perTransactionLimit": "500000000",
        "hourlyLimit": "500000000",
        "maxTxPerHour": 20,
        "recipientWhitelist": ["0x742d...bD18"]
      },
      "createdAt": "2026-01-15T10:00:00Z",
      "updatedAt": "2026-02-10T14:30:00Z"
    }
  ]
}

transaction_history

Returns recent policy-governed transaction decisions for audit and context.

Parameters:

FieldTypeRequiredDescription
timeframestringNoTime window: "1h", "24h" (default), "7d", "30d"
limitnumberNoNumber of events to return. Default 20, max 100.

Example response:

{
  "events": [
    {
      "id": "evt_001",
      "timestamp": "2026-02-21T09:15:00Z",
      "eventType": "validate_intent",
      "decision": "allow",
      "intent": {
        "chain": "base",
        "asset": "usdc",
        "to": "0x742d...bD18",
        "amount": "100500000"
      }
    },
    {
      "id": "evt_002",
      "timestamp": "2026-02-21T09:10:00Z",
      "eventType": "validate_intent",
      "decision": "deny",
      "reason": "PER_TX_LIMIT",
      "intent": {
        "chain": "base",
        "asset": "usdc",
        "to": "0x742d...bD18",
        "amount": "750000000"
      }
    }
  ],
  "timeframe": "24h",
  "count": 2
}

Read-Only vs Execution Mode

The MCP server operates in one of two modes depending on which environment variables are set.

Read-only mode

When: Only POLICYLAYER_API_KEY is set. No wallet variables.

Tools available: validate_transaction, check_budget, list_policies, transaction_history

Use this when:

  • Your agent only needs to check whether transactions would be allowed, without executing them
  • You want to give an agent visibility into budgets and policies for planning purposes
  • A separate system (or human) handles actual transaction execution
  • You’re evaluating PolicyLayer before enabling execution

Read-only mode is completely safe — no budget is consumed, no transactions are signed, no on-chain effects. The agent can call tools freely to reason about spending.

Execution mode

When: POLICYLAYER_API_KEY plus WALLET_PRIVATE_KEY, CHAIN, and RPC_URL are all set.

Tools available: All five, including send_transaction

Use this when:

  • Your agent needs to autonomously execute policy-enforced transactions
  • You trust the agent to operate within the spending limits you’ve configured in the dashboard

In execution mode, send_transaction is annotated with destructiveHint: true, which means MCP clients will prompt for human confirmation before executing. The full two-gate enforcement model applies: Gate 1 evaluates policy and reserves budget, Gate 2 verifies the intent hasn’t been tampered with, and only then does the server sign locally with your key.

Wallet adapters: v1 ships with viem (default) and ethers. Set WALLET_ADAPTER=ethers if you prefer ethers.js. Both require WALLET_PRIVATE_KEY and RPC_URL.


Amount Handling

The MCP server handles amount conversion automatically. Agents work with human-readable values; the policy engine works with base units internally.

Input: Agent sends "100.50" USDC. The MCP server converts to "100500000" (6 decimals) before calling the API.

Output: API returns "100500000". The MCP server converts to "100.50 USDC" in the response.

Known asset decimals:

AssetDecimals
ETH18
WETH18
USDC6
USDT6
DAI18
MATIC18
SOL9

For assets not in this table, the server returns base-unit values with a note (e.g. "100500000 TOKEN (base units)").


Troubleshooting

INVALID_API_KEY

Cause: The API key is incorrect, revoked, or not set.

Fix: Check POLICYLAYER_API_KEY is set correctly. Generate a new key from the dashboard if needed. Keys are shown once at creation — if lost, create a new one.

send_transaction not available

Cause: Wallet environment variables are missing or incomplete.

Fix: Ensure all four variables are set: POLICYLAYER_API_KEY, WALLET_PRIVATE_KEY, CHAIN, RPC_URL. The send_transaction tool is only registered when the server detects a complete wallet configuration.

NO_POLICY_FOR_ASSET

Cause: No spending policy is configured for the asset you’re trying to validate or send.

Fix: Add a policy for the asset in the dashboard. Policies are scoped to the API key — make sure you’re configuring the correct agent.

Unknown decimals for {asset}

Cause: The MCP server doesn’t know the decimal precision for this asset, so it can’t convert human-readable amounts.

Fix: Either provide the amount in base units directly, or include the tokenAddress parameter so the server can look up the contract.

Server not starting

Cause: Common reasons include npx not being available, Node.js version too old, or network issues downloading the package.

Fix:

  • Ensure Node.js 18+ is installed (node --version)
  • Try installing globally first: npm install -g @policylayer/mcp
  • Check that your client config has the correct command and args format

Rate limiting

Cause: MCP read endpoints allow 200 requests per minute per organisation. Validate/send endpoints allow 100 per minute.

Fix: If your agent is hitting limits, reduce polling frequency. The check_budget and list_policies responses change infrequently — cache them in your agent logic rather than calling on every turn.

Connection to API failed

Cause: The MCP server can’t reach api.policylayer.com (or your custom POLICYLAYER_API_URL).

Fix: Check network connectivity. If using a local API for development, ensure POLICYLAYER_API_URL=http://localhost:3001 is set and the API server is running.


Next Steps