Order limits, phone approvals and a kill switch in front of any broker or exchange MCP server.
A trading-aware safety proxy for AI agents. Put it between your AI client (Claude, ChatGPT, Cursor, your own agent) and any broker or exchange MCP server. It enforces per-order and daily limits, symbol allowlists, human approvals, a kill switch and a tamper-evident audit log, before an order ever reaches your broker.

AI client ──► TAS Guard ──► Alpaca / CCXT / any trading MCP server ──► your account
│
├─ blocks: over-limit orders, off-list symbols, withdrawals, leverage changes
├─ asks you: large orders, live money, "close everything"
└─ logs: every decision, hash-chained
Zero dependencies. About 800 lines of plain JavaScript you can read in one sitting.
tas-guard demo runs the guard in front of a built-in paper broker with simulated prices. Add it to your AI app as an MCP server:
{ "mcpServers": { "tas-guard-demo": { "command": "npx", "args": ["-y", "tas-guard", "demo"] } } }
(On Windows use "command": "cmd", "args": ["/c", "npx", "-y", "tas-guard", "demo"].) Then ask your agent to buy 1 SPY at $580 (allowed), 2 SPY at $580 (held for your approval), 10 SPY (blocked: over the $2,000 per-order limit) or 1 TSLA (blocked: not on the allow list). Approve held orders with npx tas-guard approve <id>.
Generic gateways decide per tool: "createOrder is critical, require approval." They can't tell a $50 order from a $50,000 one, so you end up approving everything or nothing.
TAS Guard reads the order itself. It knows that Alpaca's place_option_order has a 100x contract multiplier, that ccxt's cost is a quote-currency value while amount is base units, and that ccxt answers the first live order with a preview. That's what makes "allow small orders, ask for big ones, never exceed $2,000 a day" possible.
| Broker connector | What it does on its own | With TAS Guard |
|---|---|---|
| Alpaca MCP (TAS safety score 68) | AI can place orders directly; toolsets can limit it | Per-order and daily caps, symbol allowlist, approval above a threshold |
| CCXT MCP | Has its own caps and confirm step | One policy and one audit log across every exchange you use |
| Any other trading MCP | Varies | Heuristic profile; anything unrecognized is blocked |
Safety scores and order-gating facts: tradingagentstack.com.
~/tas-guard/alpaca.json (or run npx tas-guard init alpaca-mcp-server):{
"name": "alpaca",
"profile": "alpaca-mcp-server",
"upstream": {
"command": "uvx",
"args": ["alpaca-mcp-server"],
"env": {
"ALPACA_API_KEY": "${ALPACA_API_KEY}",
"ALPACA_SECRET_KEY": "${ALPACA_SECRET_KEY}",
"ALPACA_PAPER_TRADE": "true"
}
},
"policy": {
"max_order_value": 500,
"max_daily_value": 2000,
"approve_over": 250,
"symbols_allow": ["SPY", "QQQ", "AAPL", "MSFT"]
}
}
${VAR} is read from the environment, so keys don't have to live in the file.
claude_desktop_config.json):{
"mcpServers": {
"alpaca": {
"command": "npx",
"args": ["-y", "tas-guard", "run", "--config", "/Users/you/tas-guard/alpaca.json"],
"env": { "ALPACA_API_KEY": "...", "ALPACA_SECRET_KEY": "..." }
}
}
}
For Claude Code: claude mcp add alpaca -- npx -y tas-guard run --config ~/tas-guard/alpaca.json
tas_guard_status to see the limits it's working under.No config file? npx tas-guard run --profile alpaca-mcp-server -- uvx alpaca-mcp-server uses the default policy.
Every tool is put in a class, from a verified connector profile or, for servers without one, from name heuristics and MCP annotations.
| Class | Examples | Default |
|---|---|---|
read | quotes, positions, balances | allow |
benign_write | watchlists | allow |
cancel, close | cancel one order, close one position | allow (also during kill switch) |
order | place an order | check limits |
modify, cancel_all, close_all, other_write | replace order, cancel everything (stop-losses too), exercise options | ask a human |
leverage, funds_out, account, raw_write, unknown | set leverage, withdraw, transfer, account settings, "call any endpoint", anything unrecognized | block (and hide from the agent) |
For order calls, in this order:
symbols_block, or not on symbols_allow (if set) → block. Globs work: "BTC/*".sides_allow (if set) → block.max_orders_per_minute or max_orders_per_day → block.max_order_value, or today's total would pass max_daily_value → block. These are hard limits; approval can't override them.approve_over, value unknown (market order by quantity, multi-leg), or live money with live_requires_approval → ask a human.Only orders the broker actually accepted count toward daily totals. Rejected orders and ccxt previews don't. Orders execute one at a time and limits are re-checked just before sending, so parallel calls can't slip past the daily cap.
{
"max_order_value": 500, // hard, per order, in the profile's quote currency
"max_daily_value": 2000, // hard, sum of executed orders today (local time)
"max_orders_per_day": 20,
"max_orders_per_minute": 3,
"approve_over": 250, // soft: above this, a human approves
"symbols_allow": [], // empty = any symbol
"symbols_block": [],
"sides_allow": [], // e.g. ["buy"] for no selling
"unknown_value": "approve", // allow | approve | block when the value can't be computed
"live_requires_approval": true, // every live-money order needs a human
"classes": { "close_all": "allow", "unknown": "approve" }, // override any class
"hide_blocked_tools": true,
"approval_ttl_minutes": 15
}
When an action needs approval, the guard asks you, never the agent.
npx tas-guard approve <id> (or deny <id>), and the agent repeats the call. Each approval works once, for that exact call, within 15 minutes.Tested with Claude Desktop (Windows, October 2026) against an Alpaca paper account: Claude Desktop doesn't show the in-app prompt, so approvals there go through npx tas-guard approve <id>. Claude relays the request id and waits for you.
npx tas-guard pending
npx tas-guard approve 3f9a1c
Get held actions on your phone with Approve and Deny buttons, alerts when an order is blocked, and a kill switch you can hit from anywhere. It uses your own Telegram bot, so nothing goes through our servers.
npx tas-guard telegram-setup --config ~/tas-guard/alpaca.json
It walks you through creating a bot with @BotFather, waits for you to press Start in your bot, then saves the bot token and your chat id into the guard config. Restart your AI app afterwards.
What you get:
wait_seconds) for your tap and then goes through in the same call. If you're slower, the agent is told to retry after you approve.notify_blocked, on by default) and, optionally, when an order is sent to the broker (notify_executed)./status (limits and today's usage), /pending (re-sends anything waiting), /kill, /resume."cli": false so tas-guard approve refuses requests from this guard. That closes the gap where an agent with shell access could run the approve command itself."approvals": {
"cli": false,
"telegram": {
"bot_token": "${TAS_TELEGRAM_BOT_TOKEN}",
"chat_id": "123456789",
"wait_seconds": 45,
"notify_blocked": true,
"notify_executed": false
}
}
Several guards can share one bot. Whichever one is listening handles the tap, and the others pick up the decision from the shared state.
npx tas-guard kill "market looks wrong" # every guard blocks new orders immediately
npx tas-guard resume
Reads, cancels and closing positions keep working while it's on, so you (or the agent) can still flatten.
Every decision is appended to ~/.tas-guard/audit.jsonl: tool, class, arguments (secrets redacted), decision, reason, order value, and whether it executed. Each line includes the hash of the previous line.
npx tas-guard log --tail 20
npx tas-guard verify-log # detects edited or deleted lines
A profile says what each tool of a server can do with money and where its order fields live. The schema is schema/connector-profile.v1.json. Built-in profiles are verified against each server's source code or official docs, with the commit or page recorded:
alpaca-mcp-server: alpacahq/alpaca-mcp-server @ 2a973a0ccxt-mcp-server: ccxt/ccxt mcp/ @ 310bf4brobinhood-trading-mcp: Robinhood's hosted Trading MCP, from the 94-tool table in Robinhood's "Trading with your agent" article (2026-10-21). The server is closed source and Robinhood publishes no argument names, so order values can't be read: every order needs approval, and a symbols_allow list blocks orders.Using a server without a profile? "profile": "auto" classifies tools by name and MCP annotations, and blocks anything it doesn't recognize. Profiles for more brokers are welcome as PRs.
Tested on Windows with the Microsoft Store version of Claude Desktop, October 2026.
upstream.env in alpaca.json), not in Claude Desktop's config. Claude Desktop keeps its own copy of each server's settings, so keys you change in its config file may not reach the server. Keys in the guard file always win. The guard's log shows which key the upstream received (last 4 characters only), e.g. credential ALPACA_API_KEY: ...PBYR (from guard config).cmd /c to start npx in claude_desktop_config.json: "command": "cmd", "args": ["/c", "npx", "-y", "tas-guard", "run", "--config", "C:\\Users\\you\\tas-guard\\alpaca.json"].%LOCALAPPDATA%\Packages\Claude_*\LocalCache\Roaming\Claude\.uv tool install alpaca-mcp-server once and set "command": "alpaca-mcp-server", "args": [] in the guard file. uvx otherwise re-resolves the server on every launch, which adds 10 to 20 seconds.telegram-setup), or approve held actions with npx tas-guard approve <id>.tas-guard approve itself, unless you use phone approvals with "cli": false. Even then, an agent that can edit files in ~/.tas-guard could tamper with state, so run trading agents without shell access to your home folder where you can.symbols_allow to keep limits meaningful.npm test # 36 tests: unit, end to end through a mock broker over real MCP stdio, and the phone flow against a mock Telegram API
MIT licensed. Built by TradingAgentStack, the independent directory of what AI trading tools can do with your money.
Source-derived launch command. Check the maintainer’s required arguments and credentials before running:
npx -y tas-guardMerge this template into ~/Library/Application Support/Claude/claude_desktop_config.json. Keep existing servers. Add any arguments, credentials, and permissions required by the maintainer; this template has not been install-tested.
{
"mcpServers": {
"io-github-intoapex-tas-guard": {
"command": "npx",
"args": [
"-y",
"tas-guard"
]
}
}
}Restart Claude Desktop completely for changes to take effect. Confirm the server appears connected in the client’s tool list, then try a read-only example from its documentation.
Claude Desktop setup referenceTAS Guard works with any MCP-compatible client. Copy the config snippet from the Configuration section above and add it to the file shown for your client, then restart the application.
~/Library/Application Support/Claude/claude_desktop_config.jsonRestart Claude Desktop completely for changes to take effect.~/.cursor/mcp.jsonRestart Cursor for changes to take effect..vscode/mcp.jsonReload VS Code window for changes to take effect.~/.codeium/windsurf/mcp_config.jsonRestart Windsurf for changes to take effect..mcp.jsonSave at the project root, then start Claude Code in that project and review the MCP server approval prompt. Keep real credentials out of shared files.