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:
-
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. -
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_transactionis annotated as destructive, so clients prompt for confirmation
Quick Start
Run the CLI
npx @policylayer/mcp init
This will:
- Open your browser to authenticate with PolicyLayer
- Let you select or create an organisation
- Create an agent with spending policies
- Detect your MCP client (Claude Code, Claude Desktop, or Cursor)
- 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
| Variable | Required | Default | Description |
|---|---|---|---|
POLICYLAYER_API_KEY | Yes | — | Authenticates all API calls. Scopes to your org and policies. Get this from the dashboard or the CLI wizard. |
WALLET_ADAPTER | No | viem | Which wallet SDK to use for signing. Options: viem, ethers. |
WALLET_PRIVATE_KEY | No | — | Enables execution mode. The private key used to sign transactions. Never transmitted — stays in your local process. |
CHAIN | No | — | Required when execution enabled. The chain the wallet operates on (e.g. ethereum, base, arbitrum). |
RPC_URL | No | — | Required for viem and ethers adapters. Your JSON-RPC endpoint. |
POLICYLAYER_API_URL | No | https://api.policylayer.com | Override the API URL. Useful for local development against http://localhost:3001. |
Non-custodial guarantee:
WALLET_PRIVATE_KEYis 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:
| Field | Type | Required | Description |
|---|---|---|---|
chain | string | Yes | Chain identifier: "ethereum", "base", "arbitrum", etc. |
asset | string | Yes | Asset symbol: "eth", "usdc", "dai", etc. |
to | string | Yes | Recipient address (0x...) |
amount | string | Yes | Human-readable amount: "100.50", "0.5" |
memo | string | No | Description of the transaction purpose |
tokenAddress | string | No | Override 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:
| Field | Type | Required | Description |
|---|---|---|---|
chain | string | Yes | Chain identifier |
asset | string | Yes | Asset symbol |
to | string | Yes | Recipient address (0x...) |
amount | string | Yes | Human-readable amount |
memo | string | No | Description. Stored in audit log. |
tokenAddress | string | No | Override 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_transactionis 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:
| Field | Type | Required | Description |
|---|---|---|---|
asset | string | No | Filter 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:
| Field | Type | Required | Description |
|---|---|---|---|
asset | string | No | Filter 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:
| Field | Type | Required | Description |
|---|---|---|---|
timeframe | string | No | Time window: "1h", "24h" (default), "7d", "30d" |
limit | number | No | Number 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) andethers. SetWALLET_ADAPTER=ethersif you prefer ethers.js. Both requireWALLET_PRIVATE_KEYandRPC_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:
| Asset | Decimals |
|---|---|
| ETH | 18 |
| WETH | 18 |
| USDC | 6 |
| USDT | 6 |
| DAI | 18 |
| MATIC | 18 |
| SOL | 9 |
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
commandandargsformat
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
- Getting Started — Full SDK integration guide (for non-MCP setups)
- Architecture — How two-gate enforcement works under the hood
- Policy Controls Reference — All available policy types and limits
- Troubleshooting — General troubleshooting guide