finature Docs
Open dashboard

Finature MCP server

Last updated Oct 10, 2026

Overview

Finature runs a remote MCP server so an AI agent can ask which of your cards to use and, with your approval, make a purchase. The agent never sees card numbers and cannot approve its own purchases. Every decision records its inputs and versions, so it can be reproduced later.

Endpoint. /mcp/ on the server that serves this page, Streamable HTTP.

Status. Team preview. Only people with the team access code can connect an agent. Purchases run on Stripe Issuing test mode or the simulator; no real money moves.

The demo wallet uses five real cards and their published earning rules as of October 2026. Merchants, card numbers and offers are fictional. Rates change, so check with the issuer.

Prefer a short walkthrough? See the Quickstart.

Connect a client

Every client needs the endpoint URL and your personal token, sent as Authorization: Bearer <token>. Get the token in the dashboard under Agents, Connect your own agent; see Authentication. The dashboard also shows each snippet below with your token filled in.

Claude Code

bash
claude mcp add --transport http finature /mcp/ --header "Authorization: Bearer YOUR_TOKEN"

Claude Desktop

Claude Desktop reaches remote servers with a header through mcp-remote. Add this to claude_desktop_config.json, then restart Claude Desktop. Custom connectors in claude.ai need OAuth sign-in, which isn't available yet.

json
{
  "mcpServers": {
    "finature": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "/mcp/",
               "--header", "Authorization:${FINATURE_AUTH}"],
      "env": { "FINATURE_AUTH": "Bearer YOUR_TOKEN" }
    }
  }
}

Cursor

Add this to ~/.cursor/mcp.json:

json
{
  "mcpServers": {
    "finature": {
      "url": "/mcp/",
      "headers": { "Authorization": "Bearer YOUR_TOKEN" }
    }
  }
}

VS Code

Add this to .vscode/mcp.json in your workspace:

json
{
  "servers": {
    "finature": {
      "type": "http",
      "url": "/mcp/",
      "headers": { "Authorization": "Bearer YOUR_TOKEN" }
    }
  }
}

Strands Agents (Python)

Pass your token as a bearer header. The agent lists the server's tools and calls them like any other tool.

python
from strands import Agent
from strands.tools.mcp import MCPClient
from mcp.client.streamable_http import streamablehttp_client

finature = MCPClient(lambda: streamablehttp_client(
    "/mcp/",
    headers={"Authorization": f"Bearer {token}"},
))

with finature:
    agent = Agent(tools=finature.list_tools_sync())
    agent("Buy Studio Buds from Demo Electronics. Ask before paying.")

Any other client

Any MCP client that supports Streamable HTTP and custom headers can connect. Point it at the endpoint above and send your token in the Authorization header.

Authentication

Team preview: agents connect with a personal token. A signed-in user who has the team's access code creates one in the dashboard (Agents, Connect your own agent) and names it after the agent. The token is shown once. OAuth sign-in, for claude.ai connectors and clients without custom headers, comes later.

  • A token acts only on the account that created it, lasts 30 days, and can be revoked in the dashboard at any time.
  • Purchases prepared with a token always wait for approval in the dashboard. The server's own agent has a small standing allowance; tokens have none.
  • A token can make up to 30 tool calls a minute.
  • Tokens are issued for this server. Every request validates the token's audience.
  • The client's access token is never forwarded to a payment provider. Finature uses its own separate provider credentials.
  • The user identity comes from the token. No tool accepts a user ID argument, so an agent cannot act for a different user by changing a parameter.
  • Each token is bound to one user. Revoked and expired tokens are refused on the next request.

Scopes

Each token carries scopes. A tool call returns SCOPE_DENIED when the token lacks the scope that the tool needs.

ScopeAllows
wallet:readRead the card summaries and policy summary from wallet_summary. Cards appear as opaque references.
decisions:readRun recommend_payment and read stored decisions with explain_decision. Reads only; nothing is reserved.
purchases:preparePrepare purchase intents, read their status and receipts, and cancel an intent before submission. Preparing reserves limits for a short time and charges nothing.
purchases:executeCall execute_purchase for an intent that is approved or covered by a valid mandate.
catalog:readSearch the demo store with search_catalog and get a server quote with get_quote.
purchases:refundRefund an approved purchase with refund_purchase, once. Refunds return to the original card.
dining:reserveFind restaurants near the user and book or cancel a table (demo reservations).
cards:freezeList the user's Finature cards and freeze one.

purchases:execute lets the agent call execute_purchase. It does not approve anything by itself. Execution still needs an approval record from the trusted app, or a valid mandate.

Approvals and mandates

By default, every purchase an agent prepares needs your approval. You give it in the Finature app, which is the trusted surface. An approval cannot be passed as a tool argument, and an agent cannot approve its own purchase. The app shows the merchant, item, final total, delivery, selected card and estimated reward. Nothing is charged until you approve.

A mandate is an optional standing permission that you create in the app. A purchase that falls inside a valid mandate can proceed without a separate approval. Each mandate is bound to one user and one client, and you can revoke it at any time.

Mandate fields

FieldMeaning
Merchant allowlistThe merchants the mandate covers. Any other merchant needs approval.
CategoryThe purchase categories the mandate covers.
Per-purchase amount and currencyThe largest single purchase allowed, in one currency.
Daily and total spendLimits across all purchases under the mandate, per day and overall.
ExpiryThe date and time after which the mandate no longer applies.
Permitted cardsThe set of cards the mandate may use.
Recurring purchasesAllowed or not allowed. Recurring purchases run only when this is set to allowed.
Bound user and clientThe user and the client the mandate applies to.

Revocation takes effect before any later submission. It does not reverse a payment that was already submitted. That payment is reconciled and, where the rail supports it, cancelled.

What an approval binds

An approval covers one exact purchase. It binds:

  • the merchant
  • the quote and cart hash
  • the exact amount and currency
  • the selected card
  • the delivery terms
  • the policy version
  • the expiry. An intent expires two minutes after it is prepared, or earlier if the merchant quote expires first. The approval expires with the intent.

If the price or the card changes, the approval no longer applies. The agent must prepare a new intent, and you must approve that one. Finature never changes the card or the price after approval.

Tools

The server exposes twenty tools; your client lists them after it connects. The ones below with a section of their own are the purchase loop. Each is a thin wrapper over the same service operations the REST API uses, so both paths share permission checks, error codes and idempotency rules. Tools take no user ID. Unknown tools and unknown fields are rejected, not treated as alternative instructions.

ToolScopeSide effect
wallet_summarywallet:readRead only
search_catalogcatalog:readRead only
get_quotecatalog:readRead only
recommend_paymentdecisions:readRead only
explain_decisiondecisions:readRead only
prepare_purchasepurchases:prepareReserves limits
get_purchase_statuspurchases:prepareRead only
execute_purchasepurchases:executeCharges once
cancel_purchasepurchases:prepareCancels
get_receiptpurchases:prepareRead only
list_open_orderspurchases:prepareRead only
list_purchasespurchases:prepareRead only
refund_purchasepurchases:refundStarts refund
list_finature_cardscards:freezeRead only
freeze_finature_cardcards:freezeFreezes card
find_restaurantsdining:reserveRead only
reserve_tabledining:reserveBooks (demo)
list_reservationsdining:reserveRead only
cancel_reservationdining:reserveCancels
search_card_databasewallet:readRead only

wallet_summary

Returns the cards the user can pay with, as opaque references, and the current policy version. Read only.

Takes no arguments. The user comes from the token.

The response lists each card with its reference, nickname, whether it is the default, and its status. Card numbers never appear.

Request
{
  "name": "wallet_summary",
  "arguments": {}
}
Response
{
  "cards": [
    { "card_ref": "card_amex_gold", "nickname": "Amex Gold", "is_default": false, "status": "ACTIVE" },
    { "card_ref": "card_savor", "nickname": "Capital One Savor", "is_default": false, "status": "PAUSED" },
    { "card_ref": "card_bce", "nickname": "Amex Blue Cash Everyday", "is_default": false, "status": "ACTIVE" },
    { "card_ref": "card_quicksilver", "nickname": "Capital One Quicksilver", "is_default": true, "status": "ACTIVE" },
    { "card_ref": "card_chrome", "nickname": "Discover it Chrome", "is_default": false, "status": "ACTIVE" }
  ],
  "policy_version": 3
}

recommend_payment

Ranks the user's cards for a verified checkout quote and returns the best one with its estimated value. Read only. It reserves no spending capacity, and its result is not permission to pay.

NameTypeRequiredDescription
quote_idstringYesA checkout quote from the merchant, verified by the server.
decision_idstringNoLinks this evaluation to an earlier decision.

The response is a decision. The full object is described in Decision object.

Request
{
  "name": "recommend_payment",
  "arguments": { "quote_id": "q_7Hk2" }
}
Response
{
  "decision_id": "dec_9f31",
  "selected_card_ref": "card_bce",
  "amount_minor": 16499,
  "currency": "USD",
  "estimated_reward_minor": 1495,
  "estimated_fee_minor": 0,
  "estimated_net_value_minor": 1495,
  "alternative": { "card_ref": "card_quicksilver", "estimated_net_value_minor": 247 },
  "reason_codes": ["CATEGORY_BONUS", "OFFER_APPLIED"],
  "is_payment_authorization": false
}

explain_decision

Returns a stored decision and the reasons behind it. Read only.

NameTypeRequiredDescription
decision_idstringYesA decision owned by the same user and client.

A decision that belongs to another user is denied, and its contents are not returned.

Request
{
  "name": "explain_decision",
  "arguments": { "decision_id": "dec_9f31" }
}
Response
{
  "decision_id": "dec_9f31",
  "selected_card_ref": "card_bce",
  "reason_codes": ["CATEGORY_BONUS", "OFFER_APPLIED"],
  "explanation": "Blue Cash Everyday earns 3% on U.S. online retail, and your activated $10 Amex Offer at Demo Electronics adds to it. Capital One Quicksilver would earn $2.47.",
  "catalog_version": "demo-1",
  "policy_version": 3,
  "engine_version": "1.0.0"
}

prepare_purchase

Creates a purchase intent from a verified quote, using the decision that chose the card. It reserves limits for the intent's short lifetime and charges nothing. The status is AWAITING_APPROVAL, or APPROVED when a valid mandate already covers the purchase.

NameTypeRequiredDescription
quote_idstringYesA checkout quote, verified by the server.
decision_idstringNoA decision from recommend_payment. If omitted, the server evaluates the quote itself. Either way the intent binds the selected card and amount.

The response includes expires_at. After that time the intent cannot be approved or executed, and a new one must be prepared. The app shows the purchase to the user. The tool never returns an approval.

Request
{
  "name": "prepare_purchase",
  "arguments": {
    "quote_id": "q_7Hk2",
    "decision_id": "dec_9f31"
  }
}
Response
{
  "intent_id": "pi_4Ld8",
  "status": "AWAITING_APPROVAL",
  "merchant": "Demo Electronics",
  "total_minor": 16499,
  "currency": "USD",
  "selected_card_ref": "card_bce",
  "rail": "simulator",
  "expires_at": "2026-10-10T18:02:00Z"
}

get_purchase_status

Returns the authoritative state of an intent, its payment attempt and its order. The three states are reported separately. Read only. Poll this tool after PAYMENT_PENDING or SUBMISSION_UNKNOWN.

NameTypeRequiredDescription
intent_idstringYesThe intent to check.
Request
{
  "name": "get_purchase_status",
  "arguments": { "intent_id": "pi_4Ld8" }
}
Response
{
  "intent_id": "pi_4Ld8",
  "intent_status": "COMPLETED",
  "payment_status": "CAPTURED",
  "order_status": "ACCEPTED",
  "payment_attempts": 1,
  "rail": "simulator"
}

execute_purchase

Executes an approved intent. It checks authority, then makes one payment attempt and returns the resulting status. This is the only tool that charges a card. Before submitting, the server rechecks the policy, amount, card status, budgets, reservations and approval, and any change stops the call with an error and no charge.

NameTypeRequiredDescription
intent_idstringYesThe approved intent to execute.
idempotency_keystringYesUnique per operation. Reuse the same key when retrying the same request. See Idempotency and retries.

The status is COMPLETED, PROCESSING or REQUIRES_ACTION, or the call returns an error code from Errors.

Request
{
  "name": "execute_purchase",
  "arguments": {
    "intent_id": "pi_4Ld8",
    "idempotency_key": "k_6f0c2a"
  }
}
Response
{
  "intent_id": "pi_4Ld8",
  "status": "COMPLETED",
  "payment_status": "CAPTURED",
  "order_id": "ord_5521",
  "order_status": "ACCEPTED",
  "selected_card_ref": "card_bce",
  "total_minor": 16499,
  "currency": "USD",
  "rail": "simulator"
}

cancel_purchase

Cancels an intent. Before submission, the cancellation is complete and nothing is charged. After submission, the server requests the rail's void where the rail supports one. A void cannot be promised, so check get_purchase_status for the outcome.

NameTypeRequiredDescription
intent_idstringYesThe intent to cancel.
idempotency_keystringYesUnique per operation. Reuse it when retrying the same cancellation.
Request
{
  "name": "cancel_purchase",
  "arguments": {
    "intent_id": "pi_4Ld8",
    "idempotency_key": "k_c41d09"
  }
}
Response
{
  "intent_id": "pi_4Ld8",
  "status": "CANCELLED",
  "rail": "simulator"
}

get_receipt

Returns the confirmed order and payment details for an intent: the selected card, the exact total, the approval status, and both the estimated and the observed reward. Read only. The observed reward is null until it has been observed.

NameTypeRequiredDescription
intent_idstringYesThe intent whose receipt to return.
Request
{
  "name": "get_receipt",
  "arguments": { "intent_id": "pi_4Ld8" }
}
Response
{
  "intent_id": "pi_4Ld8",
  "order_id": "ord_5521",
  "merchant": "Demo Electronics",
  "selected_card_ref": "card_bce",
  "total_minor": 16499,
  "currency": "USD",
  "approval_status": "APPROVED",
  "estimated_reward_minor": 1495,
  "observed_reward_minor": null,
  "rail": "simulator"
}

refund_purchase

Refunds an approved purchase in full, back to the card it was paid with. Call it only when the user asked for that refund. Safe to repeat: a purchase is refunded at most once. Find the purchase with list_purchases.

NameTypeRequiredDescription
purchase_idstringYesThe purchase to refund, from list_purchases.
Request
{
  "name": "refund_purchase",
  "arguments": { "purchase_id": "pur_7f3a91c2" }
}

Purchase lifecycle

Three state machines are kept apart: the intent (the purchase and your approval), the payment attempt (the charge on the card), and the order (the merchant's acceptance). Each has its own states, and each is reported separately.

states
Intent:

PREPARED -> AWAITING_APPROVAL -> APPROVED -> EXECUTING -> COMPLETED

PREPARED -> APPROVED  (valid existing mandate)

PREPARED / AWAITING_APPROVAL / APPROVED -> EXPIRED or CANCELLED

EXECUTING -> REQUIRES_ACTION or PROCESSING or FAILED

Payment attempt:
CREATED -> SUBMITTING -> AUTHORIZED -> CAPTURED
SUBMITTING -> SUBMISSION_UNKNOWN -> reconciled rail state
SUBMITTING -> DECLINED
AUTHORIZED -> VOIDED or PARTIALLY_CAPTURED or CAPTURED
CAPTURED -> PARTIALLY_REFUNDED -> REFUNDED

Order:
QUOTED -> SUBMITTING -> ACCEPTED -> FULFILLED
SUBMITTING -> UNKNOWN or REJECTED

COMPLETED means the payment was captured and the merchant accepted the order. Shipping can still be pending after that. A payment can succeed while the order fails, and the server shows both facts so they can be reconciled.

Unknown states are first-class. SUBMISSION_UNKNOWN and UNKNOWN are not shown as success or failure until the rail or merchant confirms what happened.

Idempotency and retries

execute_purchase and cancel_purchase take an idempotency_key. The server stores each key with a hash of its request, scoped to the user, client and operation.

  • Same key, same payload: the server returns the original operation. Nothing new starts.
  • Same key, different payload: the call fails with IDEMPOTENCY_CONFLICT. Reuse the original payload with that key, or start a new intent with a new key.
  • One execution attempt per intent: changing the key does not create a second charge for an intent that has already been executed.
  • No double reservation: execution uses the limits reserved at preparation instead of reserving them again.

After SUBMISSION_UNKNOWN or a timeout, call get_purchase_status and wait for the rail's answer. Never retry an ambiguous charge on a different card. If a merchant may have accepted an order before a timeout, reconcile that order before submitting again.

For read tools, follow the retry guidance in the error response. For anything that changes state, reconcile first, then decide whether a retry is needed.

Errors

Errors return a stable code, a safe message, and any structured fields that explain the failure. The table gives the action the caller should take for each code.

CodeWhat the caller should do
AUTH_REQUIREDReauthenticate or request the permitted scope.
SCOPE_DENIEDReauthenticate or request the permitted scope.
QUOTE_EXPIREDObtain a new quote, then prepare again.
POLICY_CHANGEDObtain a new quote, then prepare again under the current policy.
APPROVAL_REQUIREDOpen the trusted approval surface in the app and wait for the user.
NO_ELIGIBLE_CARDExplain which cards were excluded and why. Do not make a charge.
BUDGET_EXCEEDEDStop, or ask the user for an explicit policy change.
USER_ACTION_REQUIREDThe user completes provider authentication outside the agent.
PAYMENT_PENDINGPoll get_purchase_status. Do not retry on a new card.
SUBMISSION_UNKNOWNPoll get_purchase_status. Do not retry on a new card.
PAYMENT_DECLINEDFollow the decline type and the configured fallback policy. A new card needs a new intent and approval.
IDEMPOTENCY_CONFLICTReuse the original payload with its key, or start a new intent with a new key.
PROVIDER_UNAVAILABLEFor reads, follow the retry guidance. For changes, reconcile before any retry.
RATE_LIMITEDFor reads, follow the retry guidance. For changes, reconcile before any retry.

An example error body:

json
{
  "error": {
    "code": "BUDGET_EXCEEDED",
    "message": "This purchase is over the $200.00 per-purchase limit set for this agent.",
    "limit_minor": 20000,
    "requested_minor": 24900
  }
}

Errors never include stack traces or credentials. Messages are safe to show to the user.

Decision object

Every choice of card is returned as a decision. This is the shape returned by recommend_payment and explain_decision:

json
{
   "decision_id": "dec_042",
   "mode": "simulator",
   "selected_card_ref": "card_chrome",
   "amount_minor": 6000,
   "currency": "USD",
   "estimated_reward_minor": 120,
   "estimated_fee_minor": 0,
   "estimated_net_value_minor": 120,
   "alternative": {
       "card_ref": "card_bce",
       "estimated_net_value_minor": 100
   },
   "reason_codes": ["CATEGORY_CAP_NEAR_EXHAUSTION"],
   "category_provenance": "fixture_confirmed",
   "catalog_version": "demo-1",
   "policy_version": 3,
   "wallet_snapshot_id": "snapshot_17",
   "engine_version": "1.0.0",
   "is_payment_authorization": false
}
FieldMeaning
decision_idIdentifier of this decision. Read it back with explain_decision.
modeThe rail the decision was made for, such as simulator.
selected_card_refOpaque reference to the card to use, or NO_ELIGIBLE_CARD.
amount_minorThe purchase amount in integer minor units. For USD, 6000 is $60.00.
currencyThe ISO 4217 code that gives amount_minor its unit.
estimated_reward_minorEstimated reward for the selected card, in minor units. An estimate, not a guarantee.
estimated_fee_minorEstimated fees for the selected card.
estimated_net_value_minorEstimated reward minus estimated fees.
alternativeThe next-best eligible card and its estimated net value.
reason_codesMachine-readable reasons for the choice.
category_provenanceWhere the purchase category came from.
catalog_versionThe offer catalog version used.
policy_versionThe version of the user's policy used.
wallet_snapshot_idThe snapshot of the card set that was evaluated.
engine_versionThe version of the decision engine that produced the result.
is_payment_authorizationAlways false for a decision. A recommendation is never permission to pay.

Amounts are integer minor units with an explicit ISO currency. Do not round or convert them. A stored decision also keeps the ranked eligible cards, the rejected cards with reasons, and any uncertainty flags.

Rails and test mode

A rail is the path a payment takes. Every purchase response includes a rail field, so you can see which path handled it.

RailMoneyAvailability
simulatorFictional money.Default for every purchase. Models pending, partial and failed outcomes, so agents must handle them.
provider test modeTest money through a payment provider's test environment.Only when enabled for your account. Uses the provider's published test cards and test credentials.
liveReal money.Not available.

Security

  • Card numbers stay with the payment partner. The partner tokenizes card details. Raw card numbers never reach Finature's application database, the model, MCP outputs or logs. Cards are referred to by opaque references.
  • Page and tool text is data. Text from web pages and tool results is never treated as instructions. A page that says a purchase is pre-approved changes nothing. Approval comes only from the trusted app.
  • The server enforces every rule. A client that skips its own agent logic, or a model that produces malicious arguments, still meets the same checks, including spending limits, approval and revocation.
  • HTTPS only. The MCP endpoint, the app and provider webhooks all require HTTPS.
  • Provider credentials stay server-side. They are separate from your access token, limited in scope, and never returned to a client.

Changelog

Oct 11, 2026 · Team preview

  • Connect your own agent with a personal token from the dashboard. Team access code required.
  • Twenty tools, adding the store catalog, open orders, Finature card freeze and restaurant booking. refund_purchase replaces request_refund.
  • Purchases run on Stripe Issuing test mode or the simulator.

Oct 10, 2026 · Preview

  • Nine tools for wallet, decisions, purchases, receipts and refunds.
  • Simulator rail, with fictional money.
  • Approval required for every agent purchase by default.
  • Mandates in preview.