MCP server
The Spend7 MCP server exposes the spend-control layer as tools, so an agent about to spend money can ask “should I?” as a tool call rather than through bespoke HTTP glue.
Two modes
Local, no key
Pass a context object and risk_check scores on the machine, against the ledger in the call. Nothing leaves, nothing is recorded. The other local tools, rail explanations, the scoring policy, the schema, work the same way.
Against your account
Set SPEND7_API_KEY and the account tools work: risk checks use your stored caps and history, decisions are recorded, and on a paid plan the cross-account graph runs.
A tool that needs the account and has no key says so. It never falls back to a local guess dressed up as an answer: the two modes answer different questions, and every response says which one you got.
Run it
git clone https://github.com/maxbeech/spend7
cd spend7 && npm install
npm run mcp # stdio, ready for a client to connectThen point a client at it:
{
"mcpServers": {
"spend7": {
"command": "npx",
"args": ["-y", "tsx", "/path/to/spend7/mcp/server.ts"],
"env": {
"SPEND7_API_KEY": "sp7_live_…",
"SPEND7_API_URL": "https://spend7.com"
}
}
}
}Tools
| risk_check | Score a payment and get allow / flag / deny before the money moves. Local when you pass a context; hosted (stored caps, history, graph) when SPEND7_API_KEY is set. |
| get_spend_limits | Read the caps and merchant lists in force, so an agent can reason about headroom before planning a purchase. Needs SPEND7_API_KEY. |
| list_transactions | Read the decision log. Filter by agent, merchant, decision or rail, and find the paymentId for a payment you want to dispute. Needs SPEND7_API_KEY. |
| explain_rail | What a rail lets Spend7 verify and how it fails. Call it when deciding which fields to populate. Local, no key. |
| get_scoring_policy | Thresholds, per-signal ceilings, windows, category priors, and the ruleset version and hash. Local, no key. |
| describe_payment_intent_schema | The exact JSON schema of a payment intent, so a call is built rather than guessed. Local, no key. |
An example call
// risk_check with an inline ledger: no API key, nothing recorded
{
"intent": {
"agentId": "shopping-agent",
"amountMinor": 90000,
"currency": "USD",
"merchantId": "giftcards.example",
"category": "gift_cards",
"rail": "other"
},
"context": {
"limits": [
{ "scope": "agent", "agentId": "shopping-agent", "window": "daily",
"capMinor": 50000, "currency": "USD" }
],
"history": []
}
}That one comes back deny: $900 against a $500 daily cap is a decisive breach, and gift cards are a category an autonomous agent rarely has a legitimate reason to buy unattended.
Making the check unskippable
An MCP tool is something the model may choose to call. For payments that is often not enough: a model that can decline to check can spend without checking. Where it matters, wrap your payment tool so the check happens in the function body, and use the MCP tools for what they are good at: letting the model reason about budget, headroom and what a rail can verify.