mirror of
https://github.com/we-promise/sure.git
synced 2026-09-07 15:44:21 +00:00
* docs: explain self-hosted onboarding modes * docs: refresh MCP and external AI docs * docs: correct MCP auth and tool accuracy * docs: address MCP review comments
426 lines
14 KiB
Markdown
426 lines
14 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` | Retrieve transaction history with filtering |
|
|
| `get_accounts` | Get account information and balances |
|
|
| `get_holdings` | Query investment holdings |
|
|
| `get_balance_sheet` | Current financial position (assets, liabilities, net worth) |
|
|
| `get_income_statement` | Income and expenses over a period |
|
|
| `get_budget` | Budget status and budget category breakdowns |
|
|
| `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 |
|
|
| `create_goal` | Create a savings goal linked to depository accounts |
|
|
| `get_tags` | List family tags |
|
|
| `create_tag` | Create a family tag |
|
|
| `update_tag` | Update a family tag |
|
|
| `get_categories` | List family categories |
|
|
| `create_category` | Create a family category |
|
|
| `update_category` | Update a family category |
|
|
| `update_transaction` | Update an existing transaction |
|
|
| `update_budget` | Update a budget category allocation |
|
|
|
|
These are the same tools used by Sure's built-in AI assistant.
|
|
|
|
### 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 |
|
|
|
|
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
|