MCP server
Tuppence runs a remote MCP server at
https://api.tuppence.ai/mcp (streamable HTTP). Add it to Claude or ChatGPT and sign in, with
no key (below), or connect a developer’s agent with an API key
as a bearer token. Either way the assistant gets only the tools its access allows, acting as your
account.
Connect from Claude or ChatGPT
Section titled “Connect from Claude or ChatGPT”To run your business from your own assistant, add Tuppence as a connector with the URL and no key. Tuppence signs you in and asks what the assistant may do; you can take that back at any time.
- In Claude, open Settings → Connectors → Add custom connector. In ChatGPT, turn on developer
mode and create a connector. Enter
https://api.tuppence.ai/mcpand leave any key or client fields empty. - Tuppence opens in your browser. Sign in, pick the account and the mode (test, or live on a live account with a fresh code or Face ID), and untick anything you don’t want the assistant to have. It never gets more than your role allows.
- Back in the assistant, ask: “show me this week’s online orders”, “take the oat latte off sale”.
URL: https://api.tuppence.ai/mcpKey: none (Tuppence signs you in)| Reads and changes | Run straight away, as you, within what you allowed. Every store change is on your change history with its undo. |
| Money, and anything that can’t be undone | Refunds, labels and archiving a product never run on the connection alone. Tuppence answers approval_required, and the change waits in the dashboard’s Store → Assistant → Approvals until someone on your team approves it with a fresh code or Face ID. It then runs once, as them. |
| More access | When a tool needs access you didn’t allow, Tuppence tells the assistant which, and it asks you to connect again with it. |
| On the record | Request logs, the audit log and the change history name the app and the person, for example Claude, for Mo Khan. |
| Taking it back | Settings → Connected apps lists each app with its access and when it last acted. Remove access stops it on its next request. Owners see everyone’s apps; everyone else sees their own. Someone who leaves the team takes their apps’ access with them. |
A developer’s agent, or a client without sign-in, uses a key instead:
1. Make a restricted key
Section titled “1. Make a restricted key”Give an agent a restricted key (rk_…, Developers → API keys → Restricted) with only the
scopes it needs. It sees only the tools those scopes allow: a key without refunds:write has no
create_refund. Add an IP allowlist if the agent runs at a fixed address.
get_card_details needs card_details:read. A key has it only if you add it by name when you
create the key. A secret key never has it.
2. Connect a client
Section titled “2. Connect a client”Clients that support remote MCP take the URL and an Authorization: Bearer <key> header.
Stdio-only clients run @tuppence/mcp, a small local server that forwards to the remote one.
claude mcp add tuppence --env TUPPENCE_API_KEY=rk_test_… -- npx -y @tuppence/mcpmcpServers in the client’s config file:
{ "mcpServers": { "tuppence": { "command": "npx", "args": ["-y", "@tuppence/mcp"], "env": { "TUPPENCE_API_KEY": "rk_test_…" } } }}URL: https://api.tuppence.ai/mcpHeader: Authorization: Bearer rk_test_…Use the TUPPENCE_API_KEY environment variable rather than --api-key, because other processes can
read command lines. TUPPENCE_ACCOUNT=acct_… (or --account) makes every tool act as one
connected account.
3. Call tools
Section titled “3. Call tools”| Rule | |
|---|---|
idempotency_key | Required on every tool that moves money, and on create_booking. Use a unique string per operation and resend it unchanged on a retry, so a retry doesn’t pay twice. Tuppence doesn’t derive one for the agent, because MCP request ids are small per-session numbers that unrelated calls would share. |
| Errors | A refused call returns the API’s error (code, message, param) for the agent to read and correct. |
| Audit | Tool calls appear in your request logs and audit log as made by an agent (by a key), or by the app and its person (signed in). |
| People decide | No tool moves pending money, switches next-day payouts or submits a dispute on its own. It returns an approve_url or approval_id for a person. Tuppence staff decide Direct Debit applications. |
search_docs | Needs no scope. Any key can search these docs, so an agent can look up how to integrate Tuppence as it works. |
| Tool | Does |
|---|---|
create_payment_link, get_payment_link, list_payment_links, update_payment_link, expire_payment_link | Payment links |
create_payment, get_payment, list_payments | Payments. create_payment returns an approval_url for a person to pay |
capture_payment | Captures a held payment (requires_capture, from capture_method: manual), in full or up to amount, before capture_before |
cancel_payment | Cancels a payment that has not succeeded and releases a held payment’s money. Destructive |
charge_saved_card | An off-session charge under a customer’s mandate |
create_refund | Refunds. Marked destructive for the client |
create_customer | Customers |
create_plan, create_subscription, get_subscription, cancel_subscription | Subscriptions: a plan’s price never changes; a subscription needs a saved card; cancelling is at period end unless now: true |
record_usage, get_credit_balance, create_credit_topup | Prepaid credit and metering |
get_direct_debit, apply_for_direct_debit | Direct Debit: status, limits, usage, price, and applying with a reason. Tuppence staff decide; no tool approves, declines or changes limits |
list_payment_method_domains, add_payment_method_domain, remove_payment_method_domain | Domains your checkout runs on, for Apple Pay and Google Pay (wallets on your own domain) |
| Tool | Does |
|---|---|
get_account | The account the key acts as: country, region (gb, us or eu), default_currency |
get_account_pricing | Every price the business pays now, as GET /v1/account/pricing: list or agreed, with dates |
get_balance | The balance and what it is made of |
get_instant_quote | What moving pending money now costs and what arrives, to bank (default) or card_balance. If it can’t, returns reason, message, fix_url and available_from |
move_pending_money | Requests an instant move (destination, amount) but never moves money itself. Returns status: approval_required with the quote and an approve_url (a person approves with a second factor), or status: unavailable with the reason |
set_next_day_payouts | Requests a switch of next-day payouts on or off (enabled, UK only) but never changes the price itself. Returns approval_required with an approve_url for an owner or admin, unavailable with the reason (no message while the offer isn’t open to the business), or unchanged. next_day is the account’s state, as on GET /v1/payout_settings |
| Tool | Does |
|---|---|
create_agent_card, freeze_card | Create a single-use card for one purchase at one merchant (without its number), and freeze it. With a restricted key, create_agent_card works only for the agent the request is signed as, on its default spending policy and within its limits (what a restricted key can do). Over a card limit, returns the error with limit, used, max, resets_at, increase_url |
get_card_details | An active virtual card’s number, CVC and expiry: your business’s own or an agent’s (a signed agent reads only its own). Needs card_details:read and the owner’s permission. See cards at a checkout |
request_approval, get_approval | Ask a person before buying, then check their answer |
get_card_balance, list_card_authorizations, list_card_transactions | What the cards can spend, tried to buy and spent. get_spendable_balance is get_card_balance’s old name, kept for agents that use it |
list_topup_options | Every way to top up the card balance for an amount: whether each is open (and if not, why and where to fix it), its fee, what you pay, when the money would be spendable and what it would wait on. For a bank transfer, the bank details too |
create_topup, list_topups, get_topup | Top up the card balance. method: bank_transfer (UK, free) needs no approval because no money moves: it returns the card balance’s sort code, account number and a reference to send the money with. method: balance (pending money onto the cards now) returns approval_required for a person to make, as move_pending_money does. Until a top-up is available, delay says what it is waiting for and expected_available_at when it should be spendable |
create_topup_link | A hosted page for a person to top up the card balance, by card or bank transfer: answers its url (shown only now). No money moves until they pay. Single use, 24 hours |
get_issuing_limits | The account’s card limits for cards, cardholders and spend: usage and when each resets |
list_card_disputes, get_card_dispute | Card disputes on purchases the cards made |
draft_card_dispute, add_card_dispute_evidence, submit_card_dispute | Prepare a dispute for a person to submit (below) |
| Tool | Does |
|---|---|
list_services, create_service | The services a client can book |
find_availability, create_booking, get_booking, list_bookings, reschedule_booking, cancel_booking | Free slots and bookings. create_booking needs an idempotency_key, and the booking has source: agent. See Bookings for AI agents |
approve_booking, decline_booking | Answer a pending_approval booking. Approving confirms it and tells the client. Declining frees the slot and tells them why |
add_to_waitlist | When nothing is free, the client is offered a held slot as soon as one opens |
| Tool | Does |
|---|---|
get_site, list_site_versions | Your store: the site, its draft pages and its versions |
update_site_page, update_brand_kit, update_navigation | Change the draft. Nothing is live until publish_site |
publish_site, schedule_site, revert_site | Check the draft and make it live (now or at at), or put an earlier version back. A page that can’t go live is named, with its block |
schedule_site_version | Put a previewed version (a proposal’s preview_ref) live at at, exactly as previewed. Nothing is scheduled if the draft has changed since |
list_redirects, list_not_found | The store’s redirects, and paths shoppers asked for that it had no page for (three or more times in a day), each with a suggested target |
create_redirect | Send an old path to a page that exists, with one 301. A person approves it, because customers land on it. Chains flatten to one hop; a loop is refused |
get_store_import, preview_store_import | An import from an old store, and what it would do now: counts per kind, matches, conflicts and what will not move. Neither imports anything or shows the store’s token |
get_domain_switch | The domain switch: the one record to change, the email records it keeps, the TXT records to add first, and its probes. The DNS records stay yours to change |
set_stock_mirror | Turn the stock mirror on or off. A person approves it, because it changes a real store’s stock. On again after off needs the store’s token, given on the Move your store screen |
list_products, create_product, update_product, set_product_available, archive_product | Your catalogue, one for the till and the online store: find products, add one, change its name or price (a change set you can undo), take it off sale everywhere at once, or archive it for good |
update_product_web_fields, publish_product_online, unpublish_product_online | Products online. No price changes here. Publishing is refused, with what is missing, until a product is ready |
list_media, update_media | The media library, and a photo’s description and focal point |
list_collections, create_collection, update_collection, set_collection_sort | Collections of products, and their order. sort: ai ranks them by a score worked out from the last 28 days’ sales, margin, stock and newness |
list_store_orders, get_store_order | Store orders. Cancelling one is a person’s, in the dashboard or the assistant |
list_sales_channels, list_listings, get_listing_issues, channel_report | Sales channels: the channels, each variant’s listing and its problems, and what each channel sold, with its own fees apart |
resync_listing, set_product_channels, fix_listing_issue, pause_channel | Send a listing again, put a product on or off a channel, fix a problem that is a product field (a claim is a person’s to approve), and pause a channel. Connecting and disconnecting are a person’s, in the dashboard |
list_fulfilments, get_fulfilment, get_shipment | Packing and labels: the pick list, a parcel with the carrier’s prices for its label, and its tracking |
buy_label, void_label | Buy the packed parcel’s label at the carrier’s price, or void an unused one. Both need an idempotency_key; a label is bought once per parcel |
mark_shipped | Mark a parcel shipped, with its label or a typed tracking number. The shopper is told it’s on its way |
close_shipping_day, book_carrier_collection | Hand the day’s parcels over: each carrier’s end-of-day manifest (a USPS SCAN form, or the Royal Mail manifest on your own contract), and a carrier collection where the carrier books them through Tuppence (USPS, free) |
get_shipping_profiles, preview_delivery_options, update_shipping_rate | Shipping and delivery: the delivery rates, what a shopper at an address is offered, and changing a rate’s price, which a person approves every time |
list_returns, get_return, approve_return, reject_return | Returns and exchanges, as your returns policy decided them. Approving takes back only what your returns policy allows; an exception and the refund are a person’s |
propose_store_change | A change in the owner’s words (prices, stock, the online store, collections, page blocks, the brand kit, the menu), proposed as a change set for a person to approve. Nothing changes until they do |
set_price | One variant’s price for everyone, at the till and online. A person approves every price. On a drop, the old price shows as the “was” price only if it was the price for 28 days in a row |
purge_store_cache, retry_channel_sync, mark_store_finding | The store-health job’s fixes: refresh every cached page of the store (undone by refreshing again), try a sales channel in error again (a person approves it), and mark a problem it found as fixed |
start_promotion, end_promotion | Switch on or off a promotion the promotions job drafted, by its id: a code for everyone, online, at the discount its margin check chose. No tool sets a discount or picks a customer |
set_stock | How many of a product there are now, written as a stock adjustment of the difference |
get_tax_settings, list_tax_registrations, get_nexus_status | Tax on your online store: the settings, the registrations you hold, and US states near their thresholds |
add_tax_registration | Records a registration you hold. A person approves it, every time |
list_policies, approve_policy_version | Your store’s policies. Approving a draft is a person’s, every time; no tool writes policy words |
get_selling_abroad_checklist | Selling abroad: each destination’s checklist and its fixes |
list_store_threads, get_store_thread | The store inbox: shoppers’ conversations by chat and email, with their messages |
reply_to_store_thread, update_store_thread | Answer a shopper as your store, close a conversation or give it back to the assistant. A person approves every answer |
list_product_reviews, publish_product_review, reply_to_product_review, reject_product_review | Reviews. A rejection names a rule of your review policy, never that a review is negative. A person approves each change |
get_store_email_sender | Where your store’s emails come from, its records and today’s sending limit. Adding or removing a domain is yours, in the dashboard |
list_subscription_offers, create_subscription_offer, update_subscription_offer | Subscribe and save: offer a product on repeat for less, change the saving or hide it. A person approves each change |
get_store_search_insights | Search: what shoppers searched for most, and what found nothing |
list_store_flows, update_store_flow, resume_store_flow | Flows: the emails that go on their own; on or off, their words, back on after a pause. A person approves each change |
list_store_campaigns, get_store_campaign, create_store_campaign, update_store_campaign, schedule_store_campaign, unschedule_store_campaign | Campaigns: one-off emails to the customers who said yes. No tool takes a list of people. A person approves each change |
Every store change is on your change history with its undo. No tool deletes the site or changes its domains, trader details or payment methods.
| Tool | Does |
|---|---|
create_connected_account, get_connected_account, list_connected_accounts | Connect: the businesses your platform onboards and runs |
create_onboarding_link | Hosted onboarding, valid once and for five minutes. It is a credential: give it only to the account’s own person |
create_transfer, list_application_fees, get_connect_balance | Your fees on your accounts’ payments, what Tuppence holds for you, and transfers from it to an account |
search_docs | Searches these docs, section by section. No scope needed |
Act as a connected account
Section titled “Act as a connected account”A platform’s agent acts as one of its connected accounts through any tool’s
account argument (acct_…). It works like the Tuppence-Account header on the REST API, with
the same refusals:
{ "name": "create_booking", "arguments": { "account": "acct_…", "items": [{ "service": "svc_…" }], "starts_at": "…", "idempotency_key": "…" } }| Case | Answer |
|---|---|
| An account the key cannot act as | account_invalid |
account on the platform’s own Connect tools (accounts, links, transfers, fees) | connected_account_not_allowed |
| A Connect tool about an account | It takes connected_account instead |
The Connect tools need the connected_accounts scopes (account_links:write for onboarding links,
transfers:write for transfers, application_fees:read for the fees).
Cards at a checkout
Section titled “Cards at a checkout”An agent’s card works wherever the merchant charges it without asking the cardholder (API and SaaS billing, subscriptions, top-ups) and at most low-value checkouts (in the UK, under £30).
To enter the card at a checkout, the agent needs its number. get_card_details returns an active
virtual card’s number, CVC and expiry (your business’s own, or an agent’s; a request an agent signs
reads only that agent’s cards) to a key made with card_details:read, where the account’s owner
has allowed it. Use a single-use card locked to the merchant, send the details only
to the merchant’s payment form, and never store, print or log them. Every read is audited and sent
as card.details_retrieved. See See a card’s details.
Card disputes
Section titled “Card disputes”An agent prepares a card dispute. A person submits it.
draft_card_disputetakescard_transaction(a capture,ctx_…) andreason, and optionallyexplanation,evidenceandamount. When the agent made the purchase, the evidence is prefilled from its records: the person’s approval, the cart and the mandate.add_card_dispute_evidence(dispute, plusreason,explanation,evidenceoramount) adds what the reason still needs. Files go throughPOST /v1/card_disputes/{id}/filesor the dashboard, not MCP.submit_card_dispute(dispute) checks the evidence and reports anything missing. It then opens an approval and returnsobject: "card_dispute_submission",status: "pending_approval"and theapproval_id(apr_…).- Poll
get_approvalwith that id until a person approves in the dashboard or app. The dispute is submitted in their name. If they decline, the draft is unchanged.
A dispute is submitted once, as the business’s statement to the card network. Submitting the same dispute again returns the same approval.
Card limits
Section titled “Card limits”create_agent_card is subject to the account’s card limits. When a limit
blocks it, the error carries limit, used, max, resets_at and increase_url, and a retry gets
the same answer. Use a card the agent already has, wait until resets_at, or ask a person to request
higher limits. get_issuing_limits shows the numbers up front.
- Agent frameworks: the same tools in OpenAI Agents, LangChain and the Claude Agent SDK.
- HTTP 402: charge agents for your own API.