alpaca-guard-mcp
# alpaca-guard-mcp ā Alpaca Trading Guard MCP Server
> MCP server wrapping the [Alpaca trading API](https://alpaca.markets/) with a hard daily USD cap guard. Enforced server-side ā an over-eager AI agent literally cannot exceed it. Paper trading by default; live trading requires explicit opt-in.
[](https://www.npmjs.com/package/alpaca-guard-mcp)
[](https://registry.modelcontextprotocol.io)
[](https://opensource.org/licenses/MIT)
[](https://modelcontextprotocol.io)
[](https://lemoncake.xyz/security)
[](https://lemoncake.xyz/start/v2)
[](https://lemoncake.xyz/pricing)
> š° **No monthly fee. Pay 3% only when your API earns. 3,000 calls free. MPP / Tempo interop.** [See pricing ā](https://lemoncake.xyz/pricing)
> š **Part of the LemonCake suite.** Japan FSA Q1āQ11 inquiry completed
> (2026-05); pure SDK / non-custodial distribution model confirmed
> registration-exempt. External security audit cleared.
> See [LemonCake security posture](https://lemoncake.xyz/security).
```bash
npx -y alpaca-guard-mcp
```
---
## 30-second pitch
[Alpaca's official MCP](https://github.com/alpacahq/alpaca-mcp-server) exposes the Alpaca trading API directly to LLMs. Powerful, but the single biggest objection from teams shipping agentic trading is:
> "What if the AI rage-buys $50k of meme stocks at 3am because the prompt got injected?"
Alpaca's MCP doesn't ship agent-level spending controls (correctly so ā that's not Alpaca's job). `alpaca-guard-mcp` does:
- Preflight every order against a **daily USD cap** stored in `~/.alpaca-guard/cap.json`.
- Refuse the call (with a structured hint the LLM can read) if the trade would breach the cap.
- Record the charge on success; the cap survives across MCP server restarts and rolls over at UTC midnight.
- **Paper trading is the default.** Live trading is blocked unless the operator sets `ALPACA_GUARD_ALLOW_LIVE=yes-i-understand`.
There is no agent-side override. The cap is a circuit breaker, not a suggestion.
---
## Quickstart
### 1. Install (Claude Desktop / Cursor / Cline)
Add to your MCP client config (`claude_desktop_config.json` or equivalent):
```json
{
"mcpServers": {
"alpaca-guard": {
"command": "npx",
"args": ["-y", "alpaca-guard-mcp"],
"env": {
"ALPACA_API_KEY": "PK...",
"ALPACA_SECRET_KEY": "...",
"ALPACA_PAPER_TRADE": "true"
}
}
}
}
```
Restart your MCP client. The šØ tools icon should show `alpaca-guard-mcp` tools.
Free Alpaca paper-trading account: https://app.alpaca.markets/paper/dashboard/overview
### 2. Set your daily cap
In your MCP client, ask:
> Set my alpaca-guard daily limit to $50.
The agent will call `guard_set_limit({ dailyLimitUsd: 50 })`. The first-ever default is **$10** as a safety floor.
### 3. Let the agent trade ā and watch it refuse the dumb ones
> Buy 1000 NVDA at limit $900.
The agent will call `guarded_place_order`. The guard will preflight: notional = 1000 Ć $900 = $900,000, remaining = $50, **refused with `BUDGET_EXCEEDED`**.
The hint the agent sees back:
```
This order would cost ~$900000.00 but only $50.00 remains under today's
$50.00 cap. Either (a) wait until tomorrow (UTC), (b) call guard_set_limit
to raise the cap (you decide, not the agent), or (c) split the order into
smaller qty. The agent cannot override this from inside a tool call.
```
---
## Tools
| Tool | Read-only? | Notes |
| ---------------------- | :--------: | ----- |
| `setup` | ā
| Env state, mode, current cap, ledger location |
| `guard_status` | ā
| Daily limit / used / remaining / recent 10 orders |
| `guard_set_limit` | ā | Set the daily USD cap. Idempotent. |
| `get_account` | ā
| Alpaca account snapshot |
| `get_positions` | ā
| Current open positions |
| `get_latest_quote` | ā
| Bid / ask / mid for a symbol |
| `guarded_place_order` | ā | Place an order; preflighted against the cap |
| `guarded_close_position` | ā | Close a position; preflighted on notional |
---
## Configuration
| Env var | Required | Default | Notes |
| ----------------------------- | :------: | ------- | ----- |
| `ALPACA_API_KEY` | ā
| ā | From Alpaca dashboard |
| `ALPACA_SECRET_KEY` | ā
| ā | From Alpaca dashboard |
| `ALPACA_PAPER_TRADE` | ā | `true` | Set to `false` for live trading (still requires `ALPACA_GUARD_ALLOW_LIVE`) |
| `ALPACA_GUARD_ALLOW_LIVE` | live only | ā | Must literally be `yes-i-understand` to enable real-money orders |
| `ALPACA_GUARD_LEDGER_DIR` | ā | `~/.alpaca-guard` | Where `cap.json` lives. Useful for tests / multiple ledgers. |
| `LEMON_CAKE_PERMIT` | ā | ā | Currently unused (v0.1 local-ledger mode). Future: switch the guard to LemonCake's permit-based preflight when the upstream API ships it. See [issue #4](https://github.com/evidai/lemon-cake/issues/4). |
---
## Worked example (end-to-end via stdio smoke test)
Without any Alpaca credentials, the guard still works for the preflight stage. From the test in this repo:
```bash
$ ALPACA_GUARD_LEDGER_DIR=/tmp/alpaca-guard-test \
echo '{"jsonrpc":"2.0",...,"method":"tools/call","params":{"name":"guarded_place_order",
"arguments":{"symbol":"NVDA","qty":1000,"side":"buy","type":"limit","limitPrice":900}}}' \
| node dist/index.js
```
Returns:
```json
{
"allowed": false,
"status": "BUDGET_EXCEEDED",
"tradeNotionalUsd": 900000,
"remainingUsd": 10,
"limitUsd": 10,
"hint": "This order would cost ~$900000.00 but only $10.00 remains ..."
}
```
That preflight ran before any Alpaca call ā and would refuse the order **even on a paper account**. With paper credentials added and a sensible cap, the same `guarded_place_order` against a 1-share order at $25 will succeed and record `$25` against the daily ledger.
---
## How the guard is composed (architecture)
```
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Agent (Claude / Cursor / Cline) ā
āāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāā
ā tool: guarded_place_order(symbol, qty, side, type, limitPrice?, tif?)
ā¼
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā alpaca-guard-mcp ā
ā ā
ā 1. Resolve effective price (limit_input OR get_latest_quote) ā
ā 2. Preflight against ~/.alpaca-guard/cap.json ā
ā 3. If !allowed ā refuse with BUDGET_EXCEEDED (no Alpaca call) ā
ā 4. If allowed ā forward to Alpaca REST /v2/orders ā
ā 5. On success ā record charge in ledger (cap.json + history) ā
ā 6. Return Alpaca order + x402-shaped receipt ā
āāāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā ā
ā¼ ā¼
āāāāāāāāāāāāāāāāāāāāāāāā āāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Alpaca REST ā ā ~/.alpaca-guard/cap.json ā
ā (paper or live) ā ā { dailyLimitUsd, ā
āāāāāāāāāāāāāāāāāāāāāāāā ā todayUsedUsd, ā
ā history[] } ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāā
```
Read-only tools (`get_account`, `get_positions`, `get_latest_quote`) bypass the guard ā they don't spend.
---
## Why the cap is local-file rather than LemonCake API (today)
`alpaca-guard-mcp` is built by the same team as [agent-payment-mcp](https://www.npmjs.com/package/agent-payment-mcp) at [lemoncake.xyz](https://lemoncake.xyz/start/v2). The eventual goal is for the guard to live on LemonCake's permit preflight endpoint ā same daily cap mechanic, but server-side and shared across MCP clients.
That endpoint doesn't exist yet (see [issue #4](https://github.com/evidai/lemon-cake/issues/4)). Until it does, the local ledger is the right shape: zero network dependency, survives restarts, simple to inspect.
When the LemonCake API ships, `LEMON_CAKE_PAY_TOKEN` will be honored: if set, the guard switches to remote preflight. The tool surface stays identical.
---
## Status & roadmap
| Phase | Status | Notes |
| ----- | ------ | ----- |
| Phase A: local-ledger guard + paper trading | ā
shipped v0.1.0 | This release |
| Phase B: LemonCake permit integration | ā³ gated | [issue #4](https://github.com/evidai/lemon-cake/issues/4) |
| Phase C: KYA tier multi-cap (daily + weekly + per-symbol) | ā³ | After Phase B |
| Phase D: Listed on Anthropic Connectors Directory | ā³ | Same submission flow as agent-payment-mcp |
---
## License
MIT. Source at [github.com/evidai/lemon-cake/tree/main/alpaca-guard-mcp](https://github.com/evidai/lemon-cake/tree/main/alpaca-guard-mcp).
## Related
- [Alpaca MCP server v2](https://github.com/alpacahq/alpaca-mcp-server) ā the upstream this guard wraps (logically; we talk directly to Alpaca REST so we don't depend on it at runtime)
- [agent-payment-mcp](https://www.npmjs.com/package/agent-payment-mcp) ā sibling MCP from the same team, where the permit flow originates
- [LemonCake](https://lemoncake.xyz/start/v2) ā interactive playground & docs
TDQS
Scored across 8 tools
Each tool has a clearly distinct purpose: account info, market data, positions, order placement with guard, position closing with guard, cap management, status, and setup. No overlap in functionality.
Tools follow a fairly consistent pattern with 'get_' for read-only, 'guarded_' for guarded actions, and 'guard_' for guard management. However, 'setup' breaks the pattern and 'guard_set_limit' uses an underscore after 'guard', which is a minor inconsistency.
8 tools is well-scoped for a trading guard server. Each tool earns its place by covering essential operations: account, quotes, positions, order/close with safety, cap management, status, and setup.
The tool set covers core workflows: read account/positions/quotes, place/close orders with guard, manage cap, and view status. Missing features like order cancellation or detailed order history are minor gaps that agents can work around.