Sentinel
An MCP proxy that enforces declarative YAML spend policies for AI agents using Agentcard, providing attribution and anomaly detection.
README
Sentinel
A spend-policy and observability layer for Agentcard — declarative YAML policies, an enforcing MCP proxy, and an attribution/anomaly dashboard for AI agents that hold real (or sandbox) money.
The problem
Agentcard issues single-use virtual Visa cards to AI agents, and its own writing describes exactly how that should be governed:
"One card per task. Each task is a separate financial segment with its own card, its own budget, and its own blast radius." … "A task that should cost $12 gets a $15 card. Not a $100 card 'to be safe.'" — Financial Zero Trust for AI Agents
"Agent expense systems need to attribute spend to tasks, workflows, and agent identities." … "Set velocity thresholds based on your historical patterns and alert when they are exceeded." — AI Agent Expense Management
The product today ships numeric limits: a budget, a per-transaction cap, and a human-approval threshold. There is no merchant allowlist/denylist, no velocity limit, no time-window rule, no per-agent policy scoping, and no attribution or anomaly layer — teams are told to build those themselves. Sentinel is that missing layer, built the way the zero-trust post says it should work:
- Policy engine — per-agent YAML rules (merchant allow/deny with wildcards, card velocity,
allowed hours with timezones, per-card caps, daily budgets, approval thresholds) evaluated
before any card exists, returning
ALLOW/DENY/NEEDS_APPROVALwith a machine-readable reason chain of every rule checked, in order. - MCP proxy — a stdio MCP server that wraps Agentcard's MCP server. Every tool passes
through unchanged except
create_cardandbuy_checkout, which are policy-checked first. Every decision — including the denials Agentcard never sees — lands in a SQLite ledger with agent id, task id, and the full rule trace. - Observability — a webhook receiver joins Agentcard transaction events to the ledger, so every settled charge maps to agent → task → policy decision. One dashboard page: spend by agent and task, budget burn-down vs policy, the denied-spend log, and anomaly flags (daily-spend z-score, first-seen merchant, off-hours attempts).
60-second quickstart
git clone <this repo> sentinel && cd sentinel
./demo.sh # creates a venv, seeds two agents, replays a scripted day,
# and serves the dashboard at http://127.0.0.1:8787/
No Agentcard account or credentials needed — the demo runs against a built-in mock that
implements Agentcard's documented API shapes (see docs/NOTES.md). Then try the killer
feature, a dry run that touches nothing:
.venv/bin/sentinel simulate --policy policies/example.yaml \
--agent research-bot --amount 1800 --merchant OPENAI \
--at "2026-07-15T23:30 America/Los_Angeles"
✓ merchant-deny merchant 'OPENAI' matches no deny pattern
✓ merchant-allow merchant 'OPENAI' matches allow pattern 'OPENAI'
✗ hours local time 23:30 PDT is outside allowed window 06:00-22:00
...
Verdict: DENY
Exit codes: 0 ALLOW · 1 NEEDS_APPROVAL · 2 DENY. Add --json for machines,
--state '{"spent_today_cents": 9000}'-style files for what-ifs, or --db sentinel.db
to evaluate against real ledger history.
Architecture
flowchart LR
subgraph agent side
A[AI agent / MCP client]
end
subgraph Sentinel
P[MCP proxy<br/>sentinel proxy]
E[Policy engine<br/>policies/*.yaml]
L[(SQLite ledger<br/>decisions + transactions)]
O[Observe server<br/>webhooks + dashboard]
end
U[Agentcard MCP server<br/>mcp.agentcard.sh<br/>or built-in mock]
W[Agentcard webhooks<br/>transaction.*]
A -- "create_card / buy_checkout<br/>(+ agent_id, task_id)" --> P
A -- "all other tools" --> P
P -- "evaluate" --> E
P -- "ALLOW → forward<br/>(sentinel args stripped)" --> U
P -- "every decision,<br/>incl. denials" --> L
W -- "signed events" --> O
O -- "join to decisions" --> L
O -- "dashboard :8787" --> A
Running it for real
-
Point the proxy at Agentcard (use a sandbox
sk_test_-provisioned OAuth token; never start with live money):cp .env.example .env # fill in AGENTCARD_MCP_URL + AGENTCARD_TOKEN sentinel proxy --policy policies/example.yaml --db sentinel.dbRegister that command as the MCP server in your agent's config instead of Agentcard's — e.g. in
claude_desktop_config.json/.mcp.json:{ "mcpServers": { "agentcard": { "command": "/path/to/.venv/bin/sentinel", "args": ["proxy", "--policy", "policies/example.yaml", "--db", "sentinel.db"], "env": { "AGENTCARD_MCP_URL": "https://mcp.agentcard.sh/mcp", "AGENTCARD_TOKEN": "…", "SENTINEL_AGENT_ID": "research-bot" } } } }Leave
AGENTCARD_MCP_URL/AGENTCARD_TOKENunset and the proxy uses the clearly-labeled mock — useful for CI and local development. -
Run the observe server and register its URL as a webhook endpoint in the Agentcard dashboard (subscribe to
transaction.*):AGENTCARD_WEBHOOK_SECRET=whsec_… sentinel observe --policy policies/example.yaml --db sentinel.db
The proxy adds three optional arguments to the intercepted tools: agent_id and task_id
(attribution, threaded into the ledger and joined to settled transactions) and
merchant_hint on create_card — Agentcard's create_card has no merchant parameter
(cards are open-loop until first charge), so the hint is the only pre-spend merchant control;
it is checked against policy and stripped before forwarding. A NEEDS_APPROVAL verdict
returns a hold; a human retries the call with sentinel_approved: true to release it.
sentinel_approved can never override a DENY.
Policy reference
defaults: # applied to agents not listed (omit = unknown agents denied)
max_per_card_cents: 1000
agents:
research-bot:
max_per_card_cents: 2500 # hard cap per card / checkout
daily_budget_cents: 10000 # forwarded spend per local calendar day
velocity: { max_cards: 5, per: 1h } # trailing-window card-creation limit (s/m/h/d)
merchants:
allow: ["OPENAI", "ANTHROPIC", "AWS"] # if present, everything else is denied
deny: ["*GAMBLING*", "*CRYPTO*"] # deny always wins over allow
hours: { allow: "06:00-22:00", tz: "America/Los_Angeles" } # overnight windows OK
require_approval_over_cents: 1500 # NEEDS_APPROVAL above this
Rule precedence (first deny wins; the full chain is always reported):
merchant-deny → merchant-allow → hours → velocity → max-per-card → daily-budget → approval-threshold. Merchant patterns are case-insensitive; */? are shell-style
wildcards against the whole descriptor, and plain patterns match the descriptor prefix or
any token prefix (OPENAI matches OPENAI *CHATGPT SUBSCR and PAYPAL *OPENAI).
Tests
.venv/bin/pytest --cov=sentinel.policy # engine is 96%+ covered
143 tests cover rule precedence, wildcard matching, timezone/DST/overnight-window edges, velocity window boundaries, ledger joins and dedupe, webhook signatures against known HMAC vectors, and an end-to-end run of the proxy over a real in-memory MCP session.
What this doesn't do (honest edition)
- It is not a network-level control. Sentinel gates what flows through its proxy. An agent holding raw Agentcard credentials — or a card number already minted — can spend without Sentinel ever knowing. Pair it with Agentcard's own budget/limit settings as the backstop; Sentinel is the fine-grained layer, not the outer wall.
- Merchant rules on
create_cardare advisory-by-construction. Agentcard's API takes no merchant at card-creation time, somerchant_hintis only as truthful as the agent supplying it. The hint is still valuable (it's checked, logged, and auditable against the settled descriptor later), andbuy_checkoutmerchant checks are real. Post-hoc, webhook descriptors expose any mismatch on the dashboard. - Merchant matching is string matching.
AWSwill not match a descriptor that readsAMAZON WEB SERVICES; card-network descriptors are messy. Write patterns against descriptors you've actually seen (the dashboard shows raw ones). - Approvals are honor-system at the MCP boundary.
sentinel_approved: trueis meant to be attached by a human-in-the-loop client, not the agent itself. If your agent framework can't guarantee that, treat NEEDS_APPROVAL as DENY. - Anomaly detection is deliberately simple (z-score with ≥3 days history, first-seen merchant, off-hours). A constant-spend history (σ=0) yields no z-flag; day one yields no flags at all. It's a tripwire, not a fraud model.
- The live-API path is untested against production. Card/transaction operations are
MCP-only (no public REST spec), and exercising the real server requires interactive OAuth
onboarding — so CI runs against
MockAgentcardClient, which mirrors the documented shapes indocs/NOTES.md. The policy engine, ledger, CLI, webhook receiver, and dashboard are fully real regardless of upstream. - Single-process ledger. SQLite (WAL) is plenty for one proxy + one observe server; it is not a multi-region audit store.
License
MIT — see LICENSE.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。