Overview
Finstracker speaks Model Context Protocol at POST /mcp. Any MCP-compatible client (Claude Desktop, Claude Web custom connectors, agent SDKs, the mcp CLI) can connect with your Finstracker bearer token and call nine tools that read or write your wallet data.
The transport is stateless Streamable HTTP (MCP spec 2025-03-26): one JSON-RPC request per POST, no session IDs, no long-lived SSE streams. This is the simplest profile and is friendly to serverless hosts.
Quickstart (Claude Desktop)
- Create or log into your Finstracker account at finstracker.com/signup.
- Get a bearer token. From a terminal:
Copy thecurl -X POST https://finstracker.com/api/auth/login \ -H 'Content-Type: application/json' \ -d '{"username":"YOUR_USERNAME","password":"YOUR_PASSWORD"}'data.tokenfield from the response. - Open your Claude Desktop config (
~/Library/Application Support/Claude/claude_desktop_config.jsonon macOS) and add:{ "mcpServers": { "finstracker": { "transport": "http", "url": "https://finstracker.com/mcp", "headers": { "Authorization": "Bearer <paste-jwt-here>" } } } } - Restart Claude Desktop. Ask: "What cards do I have on Finstracker?" Claude should call
list_cardsand respond with your wallet.
Authentication
The MCP server reuses the same JWT bearer auth as the REST API. Same token, same login endpoint, no OAuth flow to wire up.
| Step | Where | Notes |
|---|---|---|
| Mint a token | POST /api/auth/login | Returns data.token (JWT). Valid 7 days. |
| Attach to MCP | Authorization: Bearer <jwt> on every POST /mcp | Sent automatically by your MCP client. |
| Refresh | Re-run login when the token expires | The server returns isError on the next call so your client knows. |
One tool, get_catalog_card, is public and works without a token, since the cards catalog is non-PII reference data.
Endpoint & transport
| Property | Value |
|---|---|
| URL | https://finstracker.com/mcp |
| Methods | POST (and OPTIONS for CORS). GET and DELETE return 405. |
| Content-Type | application/json (JSON-RPC 2.0) |
| Accept | application/json, text/event-stream (MCP spec requirement) |
| Session ID | None. Server runs in stateless mode |
| SDK version | Implements MCP 2025-03-26 |
Tools
Every tool returns a content array with a single text block containing pretty-printed JSON. Errors set isError: true and put a human message in the same text block.
list_cards auth
Returns every credit card the authenticated user is tracking, with balances, point value, annual fee, and sign-up bonus progress.
Input: none.
Output:
{
"count": 9,
"cards": [
{
"id": "...",
"cardKey": "chase-sapphire-preferred",
"displayName": "Sapphire Preferred",
"issuer": "Chase",
"rewardType": "points",
"currentPoints": 84210,
"currentCashBack": 0,
"pointValueCents": 1.25,
"annualFee": 95,
"signupBonus": { "spendRequired": 4000, "currentSpend": 4200, "bonusAmount": 75000, "bonusUnit": "points", "completed": true, "applied": true }
}
]
}
list_perks auth
Returns recurring statement credits (monthly Uber Cash, annual hotel credits, etc.) with deadlines and used status.
| Parameter | Type | Default | Notes |
|---|---|---|---|
scope | "open" | "used" | "all" | "open" | Filters by claim status. |
cardId | string | — | Restrict to one card. |
year | int 2000–2100 | — | Restrict to a calendar year. |
limit | int 1–500 | 100 | Max rows returned. |
list_offers auth
Returns merchant offers (Amex Offers, Chase Offers, BoA Deals) attached to your cards.
| Parameter | Type | Default | Notes |
|---|---|---|---|
category | enum | — | One of dining, travel, streaming, retail, grocery, gas, entertainment, other. |
merchant | string ≤80 | — | Case-insensitive substring match. |
includeUsed | bool | false | Include offers you've already claimed. |
includeExpired | bool | false | Include offers past their expiration. |
limit | int 1–500 | 100 |
list_transactions auth
Returns recent earn/redeem transactions, newest first.
| Parameter | Type | Default | Notes |
|---|---|---|---|
cardId | string | — | Restrict to one card. |
type | "earn" | "redeem" | — | |
unit | "points" | "cashback" | — | |
limit | int 1–500 | 50 | |
before | ISO datetime | — | Cursor pagination: return only items older than this. |
get_billing_status auth
Returns the user's subscription state: trialing, active, past_due, or expired, plus the plan, trial end, and any scheduled cancel-at-period-end.
Input: none.
get_catalog_card public
Looks up catalog metadata for a credit card by cardKey. Public, no auth required.
| Parameter | Type | Default | Notes |
|---|---|---|---|
cardKey | string ≤80 | — | e.g. chase-sapphire-preferred. Omit to get the full catalog. |
recommend_card_for_merchant auth
Returns the top three cards from the user's wallet to use at a given merchant. Active merchant offers win first; otherwise the highest point-value card wins.
| Parameter | Type | Default | Notes |
|---|---|---|---|
merchant | string 1–120 | required | Merchant name. Case-insensitive substring match against your offers. |
category | enum (same as list_offers) | — | Helps tie-break when no offer matches. |
amountCents | int 0–10,000,000 | — | Pass the purchase amount for accurate %-back math. |
log_transaction auth write
Records an earn or redeem transaction on a card you own. The card's balance is updated atomically; the unit must match the card's reward type. Gated by the trial paywall: calls fail with a clear error after the trial expires unless you have an active subscription.
| Parameter | Type | Notes |
|---|---|---|
cardId | string | Required. |
type | "earn" | "redeem" | Required. |
unit | "points" | "cashback" | Required; must match card. |
amount | number > 0 | Must be an integer for points cards. |
note | string ≤200 | Optional free-text label. |
mark_perk_used auth write
Toggles whether a perk has been claimed. used: false undoes a claim and clears usedAt.
| Parameter | Type | Default | Notes |
|---|---|---|---|
perkId | string | required | |
used | bool | true |
Errors
Two error layers, matching the MCP spec:
- JSON-RPC protocol errors (bad method, malformed request) come back as an
errorenvelope with a numeric code. - Tool errors (unauthorized, validation failed, paywall) come back as a normal result with
isError: trueand a human message incontent[0].text. This is what an LLM client will surface to the user.
Common tool errors:
| Message | Cause |
|---|---|
| Unauthorized. Attach Authorization: Bearer <jwt>… | No bearer token, expired token, or invalid token. |
| Session expired. Please sign in again. | Token valid but user no longer exists (e.g. account deleted). |
| Trial ended. Please choose a plan to continue. | Trial expired and no active subscription. Read tools still work; write tools and recommendations gated. |
| Card not found. | The cardId doesn't belong to you (or doesn't exist). |
| Insufficient points/cashback to redeem… | log_transaction would push the balance below zero. |
Rate limits
No hard rate limits today. The endpoint runs on Netlify Functions and is bounded by their per-request timeout (~26 s). LLM clients that call tools in tight loops may want to add their own client-side throttling. A built-in token bucket is on the roadmap.
Troubleshooting
- "Server didn't respond" in Claude Desktop. Make sure the JSON config lives under
mcpServers, the URL has no trailing slash, and thetransportis"http". Restart Claude after editing. - Every tool returns "Unauthorized". Your token is missing, malformed, or expired. Re-mint via
POST /api/auth/login. - 406 Not Acceptable. Your client isn't sending
Accept: application/json, text/event-stream. MCP-spec clients do this automatically; rawcurlneeds the header set explicitly. - Recommendations are empty. You have no cards. Add one at finstracker.com/app first.
Versioning
The server reports its version as finstracker@0.1.0 during MCP initialize. Tool input schemas may evolve while the API is in beta. Breaking changes will be called out in the changelog and bumped in the version string.