Files
sure/docs/hosting/mcp.md
T
Brandon ce92b36351 feat(bills): assistant and MCP tools for bills (#3203)
* feat(bills): assistant and MCP tools for bills

Last of three chunks carved out of #3083, stacked on the UI bundle. Exposes
bills to the builtin assistant and to MCP clients. Everything here is gated
behind preview features, so the tools are absent from tools/list until a user
opts in.

Seven tools:

- get_bills, get_bill_details and get_paycheck_plan for reads
- get_bill_audit, a deterministic review that surfaces likely duplicates, price
  changes, trials about to convert, upcoming renewals and long-overdue bills
- create_bill, update_bill and record_bill_payment for writes

Shared argument parsing, permission checks and error shapes live in
BillsSupport, so every tool answers with the same {error, hint} contract the
existing tools use, and a bad argument never aborts the turn.

The write tools mutate financial records on a model's say-so, so they refuse
rather than guess: a payment cannot exceed what its cycle still owes, a repeated
settle will not quietly close next month, an unrecognized frequency is an error
instead of a silent monthly default, and non-finite or negative amounts are
rejected before they reach the database.

The read tools say what they filtered. An empty result names the statuses that
do hold matches, the paycheck plan discloses the unconfirmed series it excluded
from spending headroom, and history and price-change windows report their real
totals rather than letting a caller sum a truncated list.

A not-found no longer returns the scoped relation's SQL, which handed any MCP
client the access-control schema for the cost of a guessed id.

The in-page AI helpers are not here. Smart fill and smart configuration are
buttons on the bills pages, so they ship with the UI bundle along with the
provider-side suggester they call.

Suite 7,854 runs, 0 failures. Rubocop clean, eager loading verified.

* Address the ready-review round

* Reject an out-of-range audit lookback out loud

* Speak the cycle remainder guard through the allocator locale
2026-09-02 07:06:13 +02:00

440 lines
16 KiB
Markdown

# MCP Server for External AI Assistants
Sure includes a Model Context Protocol (MCP) server endpoint that allows external AI assistants like Claude.ai, Claude Desktop, GPT agents, or custom AI clients to query and act on your financial data.
## What is MCP?
[Model Context Protocol](https://modelcontextprotocol.io/) is a JSON-RPC 2.0 protocol that enables AI assistants to access structured data and tools from external applications. Instead of copying and pasting financial data into a chat window, your AI assistant can directly query Sure's data through a secure API.
This is useful when:
- You want to use an external AI assistant (Claude, GPT, custom agents) to analyze your Sure financial data
- You prefer to keep your LLM provider separate from Sure
- You're building custom AI agents that need access to financial tools
## Authentication Modes
Sure supports two ways to authenticate MCP clients:
### 1. OAuth 2.0 / dynamic client registration (recommended)
This is the best option for Claude.ai and other MCP clients that support OAuth. Sure exposes:
- `/.well-known/oauth-protected-resource`
- `/.well-known/oauth-authorization-server`
- `POST /register` for dynamic client registration
These endpoints let compatible MCP clients register a public OAuth client, redirect you back to Sure for sign-in, and receive a bearer token with the `read_write` scope.
### 2. Static bearer token via environment variables
This is the simpler fallback for custom agents, scripts, and deployments where you want to pin the MCP server to a specific Sure user.
Set these environment variables:
| Variable | Description | Example |
|----------|-------------|---------|
| `MCP_API_TOKEN` | Bearer token for authentication | `your-secret-token-here` |
| `MCP_USER_EMAIL` | Email of the Sure user whose data the assistant can access | `user@example.com` |
Both variables are required for the legacy token flow. OAuth clients using the
MCP discovery and dynamic registration endpoints do not need these variables.
### Generating a secure token
Generate a random token for `MCP_API_TOKEN`:
```bash
# macOS/Linux
openssl rand -base64 32
# Or use any secure password generator
```
### Choosing the user for static-token auth
The `MCP_USER_EMAIL` must match an existing Sure user's email address. The AI assistant will have access to all financial data for that user's family.
> [!CAUTION]
> The AI assistant can call the MCP tools available to the specified user. This includes reading financial data and write-capable tools such as statement import, goal/category/tag changes, transaction updates, and budget updates. Only set this for users you trust with your AI provider.
## Configuration
### Docker Compose
Add the environment variables to your `compose.yml`:
```yaml
x-rails-env: &rails_env
MCP_API_TOKEN: your-secret-token-here
MCP_USER_EMAIL: user@example.com
```
Both `web` and `worker` services inherit this configuration.
### Kubernetes (Helm)
Add the variables to your `values.yaml` or set them via Secrets:
```yaml
env:
MCP_API_TOKEN: your-secret-token-here
MCP_USER_EMAIL: user@example.com
```
Or create a Secret and reference it:
```yaml
envFrom:
- secretRef:
name: sure-mcp-credentials
```
## Protocol Details
The MCP endpoint is available at:
```
POST /mcp
```
### Authentication
MCP supports OAuth authorization-code flow for clients such as Claude Code.
Clients should discover the protected-resource metadata, register dynamically,
request the advertised `read_write` scope, and send the resulting access token
as a Bearer token. Dynamically registered clients are assigned this scope so
their tokens can authenticate to MCP.
For self-hosted deployments or clients without OAuth support, requests may use
the legacy `MCP_API_TOKEN` as a Bearer token:
```
Authorization: Bearer <token>
```
That token can come from either:
- an OAuth authorization flow handled by the MCP client, or
- the static `MCP_API_TOKEN` environment variable described above.
### Supported Methods
Sure implements the following JSON-RPC 2.0 methods:
| Method | Description |
|--------|-------------|
| `initialize` | Protocol handshake, returns server info and capabilities |
| `tools/list` | Lists available financial tools with schemas |
| `tools/call` | Executes a tool with provided arguments |
### Available Tools
The MCP endpoint exposes the same tool registry used by Sure's built-in assistant. Clients should treat `tools/list` as the source of truth.
At the time of writing, `tools/list` includes:
| Tool | Description |
|------|-------------|
| `get_transactions` | Search transactions with filters (exact names or ids), sorting by date or absolute amount, and pagination |
| `get_recurring_transactions` | Detected and manual recurring transactions (subscriptions, bills, salaries) with expected dates and per-currency totals |
| `get_accounts` | Accounts with ids and current balances; pass `include_balance_series: true` for a period-bounded history series |
| `get_holdings` | Query investment holdings |
| `get_balance_sheet` | Net worth, assets and liabilities with a configurable history period and interval |
| `get_income_statement` | Income and expenses for a period, with optional monthly series, prior-period comparison and account filtering |
| `get_budget` | Budget summary for a month, with optional prior months |
| `get_merchants` | Merchants with the ids `update_transaction` accepts and the exact names `get_transactions` filters on |
| `get_tags` | Tags with pagination |
| `get_categories` | Categories with hierarchy and pagination |
| `create_goal` | Create a savings goal linked to depository accounts |
| `create_tag` / `update_tag` | Manage tags |
| `create_category` / `update_category` | Manage categories |
| `update_transaction` | Edit a transaction's metadata (name, notes, category, merchant, tags) |
| `update_budget` | Update budget allocations for a month |
| `import_bank_statement` | Import bank statement data |
| `search_family_files` | Search documents uploaded through the import flow. Note this is the vector-store document index, not the Statement Vault — statements archived via `upload_account_statement` are not searchable through it |
### Preview Tools
These additional tools appear only when the MCP user has opted into preview
features (Settings → Preferences). Until then they are absent from `tools/list`,
and calling one by name returns an "Unknown tool" error. The Statement Vault
tools additionally require the user to be an admin or member, matching the
permissions enforced in the web UI.
| Tool | Description |
|------|-------------|
| `upload_account_statement` | Store a statement document (PDF/CSV/XLSX) in the Statement Vault; deduplicates by SHA-256 |
| `list_account_statements` | List vault documents with their SHA-256, period, linked account and review status |
| `get_account_statement` | One statement's details and its reconciliation checks against the ledger — present only once someone has entered the statement's opening/closing balances in the web UI, since nothing extracts them from the document. Does not return the file: stored documents are served only to a signed-in browser session |
| `get_statement_coverage` | Month-by-month statement coverage for an account: `covered`, `missing`, `mismatched`, `ambiguous`, `duplicate`, `not_expected`, each with a reconciliation status |
| `record_valuation` | Record an account's value on a date, with a required source citation |
| `get_valuations` | List recorded valuations newest first, including the citation stored in each entry's notes; the read pair for `record_valuation` |
| `get_insights` | Read the proactive insights feed (spending anomalies, cash-flow warnings, subscription audits and more) without marking anything read |
| `get_bills` | List bills, subscriptions and other recurring obligations with each one's current payment state |
| `get_bill_details` | One bill's full configuration, open occurrences, payment history, price-change history and cost analytics |
| `get_paycheck_plan` | Income plan sliced into pay periods: what is due before the next payday, what stays reserved for later bills, what is safe to spend |
| `get_bill_audit` | Deterministic bills review: possible duplicates, price changes, trials about to convert, upcoming renewals, long-overdue bills and undeclared recurring patterns |
| `create_bill` | Create a bill, subscription, installment plan or income schedule |
| `update_bill` | Update one bill's configuration; amount changes apply from today forward |
| `record_bill_payment` | Record a partial payment against a bill's open occurrence, or settle it in full |
Because tool calls never pass through the Bills pages' controllers, the bills
tools re-check the family's recurring-transactions feature gate (Settings →
Recurring transactions) and the MCP user's per-account access on every call.
With the feature disabled they return an error result instead of data, bills
tied to accounts the user cannot see are never returned, and the write tools
refuse series on accounts shared with the user read-only.
They exist for agents that maintain a document-backed record of a family's
wealth over time. See
[Wealth history with an external agent harness](../llm-guides/wealth-agent-harness.md).
## Example Requests
### Initialize
Handshake to verify protocol version and capabilities:
```bash
curl -X POST https://your-sure-instance/mcp \
-H "Authorization: Bearer your-secret-token" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize"
}'
```
Response:
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-03-26",
"capabilities": {
"tools": {}
},
"serverInfo": {
"name": "sure",
"version": "1.0"
}
}
}
```
### List Tools
Get available tools with their schemas:
```bash
curl -X POST https://your-sure-instance/mcp \
-H "Authorization: Bearer your-secret-token" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list"
}'
```
Response includes tool names, descriptions, and JSON schemas for parameters.
### OAuth discovery
MCP clients that support OAuth can discover Sure's metadata automatically:
```bash
curl https://your-sure-instance/.well-known/oauth-protected-resource
curl https://your-sure-instance/.well-known/oauth-authorization-server
```
The authorization-server metadata includes:
- `authorization_endpoint`: `https://your-sure-instance/oauth/authorize`
- `token_endpoint`: `https://your-sure-instance/oauth/token`
- `registration_endpoint`: `https://your-sure-instance/register`
- `scopes_supported`: `["read_write"]`
### Call a Tool
Execute a tool to get transactions:
```bash
curl -X POST https://your-sure-instance/mcp \
-H "Authorization: Bearer your-secret-token" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "get_transactions",
"arguments": {
"start_date": "2024-01-01",
"end_date": "2024-01-31"
}
}
}'
```
Response:
```json
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "[{\"id\":\"...\",\"amount\":-45.99,\"date\":\"2024-01-15\",\"name\":\"Coffee Shop\"}]"
}
]
}
}
```
## Security Considerations
### Transient Session Isolation
The MCP controller creates a **transient session** for each request. This prevents session state leaks that could expose other users' data if the Sure instance is using impersonation features.
Each MCP request:
1. Authenticates the token
2. Loads the authorized Sure user
3. Creates a temporary session scoped to that user
4. Executes the tool call
5. Discards the session
This ensures the AI assistant can only access data for the intended user.
### Pipelock Security Scanning
For production deployments, we recommend using [Pipelock](https://github.com/luckyPipewrench/pipelock) to scan MCP traffic for security threats.
Pipelock provides:
- **DLP scanning**: Detects secrets being exfiltrated through tool calls
- **Prompt injection detection**: Identifies attempts to manipulate the AI
- **Tool poisoning detection**: Prevents malicious tool call sequences
- **Policy enforcement**: Block or warn on suspicious patterns
- **Signed receipts**: Produces verifiable evidence for mediated MCP decisions when the flight recorder is configured with storage and a signing key
See the [Pipelock documentation](pipelock.md) and the example configuration in `compose.example.ai.yml` for setup instructions.
### Network Security
The `/mcp` endpoint is exposed on the same port as the web UI (default 3000). For hardened deployments:
**Docker Compose:**
- The MCP endpoint is protected by the `MCP_API_TOKEN` but is reachable on port 3000
- For additional security, use Pipelock's MCP reverse proxy (port 8889) which adds scanning
- See `compose.example.ai.yml` for a Pipelock configuration
**Kubernetes:**
- Use NetworkPolicies to restrict access to the MCP endpoint
- Route external agents through Pipelock's MCP reverse proxy
- See the [Helm chart documentation](../../charts/sure/README.md) for Pipelock ingress setup
## Production Deployment
For a production-ready setup with security scanning:
1. **Download the example configuration:**
```bash
curl -o compose.ai.yml https://raw.githubusercontent.com/we-promise/sure/main/compose.example.ai.yml
curl -o pipelock.example.yaml https://raw.githubusercontent.com/we-promise/sure/main/pipelock.example.yaml
```
2. **Set your MCP credentials in `.env`:**
```bash
MCP_API_TOKEN=your-secret-token
MCP_USER_EMAIL=user@example.com
```
3. **Start the stack:**
```bash
docker compose -f compose.ai.yml up -d
```
4. **Connect your AI assistant to the Pipelock MCP proxy:**
```
http://your-server:8889
```
The Pipelock proxy (port 8889) scans all MCP traffic before forwarding to Sure's `/mcp` endpoint.
## Connecting AI Assistants
### Claude.ai
Sure's Settings UI is already geared toward Claude.ai OAuth integrations:
1. Open **Settings -> Integrations** in Claude.ai
2. Click **Add integration**
3. Paste your Sure MCP URL
4. Claude redirects you to Sure to sign in and authorize access
If you are using Pipelock, use the reverse-proxy URL on port `8889`. Otherwise use the app URL ending in `/mcp`.
### Claude Desktop
If your Claude Desktop build expects a raw MCP endpoint instead of an OAuth integration flow, point it at:
- `http://your-server:8889` when using Pipelock, or
- `http://your-server:3000/mcp` for direct access
Use either the client's OAuth support or a bearer token, depending on what that build supports.
### Custom Agents
Any AI agent that supports JSON-RPC 2.0 can connect to the MCP endpoint. The agent should:
1. Send a POST request to `/mcp`
2. Include the `Authorization: Bearer <token>` header
3. Use the JSON-RPC 2.0 format for requests
4. Handle the protocol methods: `initialize`, `tools/list`, `tools/call`
## Troubleshooting
### "unauthorized" error
**Symptom:** Requests return HTTP 401 with "unauthorized"
**Fix:** Verify one of these is true:
- The OAuth flow completed successfully and the client is sending the issued bearer token
- The static token matches `MCP_API_TOKEN`
- If you are using the static-token flow, `MCP_USER_EMAIL` matches an existing Sure user
### Static token works, but the user still gets rejected
**Symptom:** Requests return HTTP 401 even though the bearer token matches `MCP_API_TOKEN`
**Fix:** The `MCP_USER_EMAIL` probably does not match an existing user. Check that:
- The email is correct
- The user exists in the database
- There are no typos or extra spaces
### Pipelock connection refused
**Symptom:** AI assistant cannot connect to Pipelock's MCP proxy (port 8889)
**Fix:**
1. Verify Pipelock is running: `docker compose ps pipelock`
2. Check Pipelock health: `docker compose exec pipelock /pipelock healthcheck --addr 127.0.0.1:8888`
3. Verify the port is exposed in your `compose.yml`
## See Also
- [External AI Assistant Configuration](ai.md#external-ai-assistant) - Configure Sure's chat to use an external agent
- [Pipelock Security Proxy](pipelock.md) - Set up security scanning for MCP traffic
- [Model Context Protocol Specification](https://modelcontextprotocol.io/) - Official MCP documentation