Gravv MCP server
Connect an AI assistant to Gravv with the @gravv-infra/mcp server: install it, pick toolsets, and know the confirm, live-money and sandbox-only gates.
The Gravv MCP server connects an AI assistant to the Gravv API through the Model Context Protocol. The assistant can search these docs, then onboard customers, run KYC, open accounts, add payees, move money, issue cards and exchange currency with your own API key. It runs on your machine as a stdio process, so it works with Claude, Cursor, VS Code and any other MCP client.
This page describes version 0.4.0 of the npm package @gravv-infra/mcp. Gravv published earlier releases as @gravvfi/mcp. Install @gravv-infra/mcp.
Example. You ask your assistant to "pay our US supplier 1,250 USD by ACH". It searches the docs and learns that a new payee must reach active before a transfer can succeed. It adds the payee with createExternalAccount, reads it with getExternalAccount until it is active, and calls createTransfer. That first call does not run. It returns a preview: "Transfer 1250.00 USD from … to external_account … Once submitted this cannot be reversed from the API." The transfer runs only when you agree and the assistant calls again with confirm: true.
Before you start
- Node.js 20 or later (
node --version). - A sandbox API key that starts with
grvSec_sandbox_. See Authentication. The key decides the environment, because sandbox and live share one host,https://api.gravv.xyz. - Keep the key out of files you commit. Where your client can read it from an environment variable or ask for it, use that instead of pasting it into a config file.
Without a key, the server still starts in docs-only mode.
To run the same calls yourself from a terminal, use the Gravv CLI. It uses the same error fixes and the same list of calls that move money as this server.
Install
Every client runs the same command: npx -y @gravv-infra/mcp, with GRAVV_API_KEY in its environment. Replace <SANDBOX_API_KEY> with your key.
Claude Code
claude mcp add gravv --env GRAVV_API_KEY=<SANDBOX_API_KEY> -- npx -y @gravv-infra/mcp
VS Code and GitHub Copilot
VS Code uses servers, not mcpServers, and needs type. Put this in .vscode/mcp.json to share it with your team, or run MCP: Open User Configuration for your own:
{
"servers": {
"gravv": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@gravv-infra/mcp"],
"env": { "GRAVV_API_KEY": "<SANDBOX_API_KEY>" }
}
}
}
Codex CLI
codex mcp add gravv --env GRAVV_API_KEY=<SANDBOX_API_KEY> -- npx -y @gravv-infra/mcp
Or in ~/.codex/config.toml:
[mcp_servers.gravv]
command = "npx"
args = ["-y", "@gravv-infra/mcp"]
[mcp_servers.gravv.env]
GRAVV_API_KEY = "<SANDBOX_API_KEY>"
Claude Desktop, Cursor, Windsurf, Cline and Zed
These use the mcpServers shape:
{
"mcpServers": {
"gravv": {
"command": "npx",
"args": ["-y", "@gravv-infra/mcp"],
"env": { "GRAVV_API_KEY": "<SANDBOX_API_KEY>" }
}
}
}
| Client | Where the configuration goes |
|---|---|
| Claude Desktop | claude_desktop_config.json (open Settings, then Developer, then Edit Config) |
| Cursor | ~/.cursor/mcp.json, or .cursor/mcp.json in a project |
| Windsurf | ~/.codeium/windsurf/mcp_config.json |
| Cline | The MCP Servers panel, or cline_mcp_settings.json |
| Zed | settings.json, under context_servers |
Any other client
Point it at a stdio process:
command: npx
args: ["-y", "@gravv-infra/mcp"]
env: GRAVV_API_KEY=<SANDBOX_API_KEY>
The server negotiates MCP protocol version 2025-06-18 and still serves clients that speak only 2024-11-05.
Check it works
Ask your assistant "What Gravv accounts do I have?". It should call listAccountsV2 and answer with your sandbox data. If you have no accounts yet, ask it to search the Gravv docs for how to open one. The server also writes one line to stderr when it starts, with the number of tools and prompts and the environment.
How to use it
Ask in plain words, as you would ask a teammate. The assistant picks the tools, reads the docs it needs and shows you what it did.
| You ask | What the assistant does |
|---|---|
| "How do I pay a Kenyan mobile money wallet?" | Searches these docs with searchGravvDocs and answers with the steps and links |
| "Onboard a test customer called Ada Lovelace" | Calls createCustomer, then startCustomerKyc, and gives you the KYC link |
| "Which accounts can a Nigerian business open?" | Calls getAccountCapabilities with NG and business |
| "Send 50 USD to payee ext_123" | Checks the payee is active, then shows a preview from createTransfer. It sends only after you agree |
| "Why is transfer trx_123 stuck?" | Reads the transaction and its webhooks, then tells you the next step |
| "Write a Python function that creates a payee" | Reads the payee docs and writes the code with the right fields |
For a whole task, pick one of the prompts instead. A prompt tells the assistant what to ask you first and which tools to call in order.
To keep an assistant from changing anything, start the server with --read-only. It can then only read.
Configuration
| Variable | Default | What it does |
|---|---|---|
GRAVV_API_KEY |
none | Your secret key. Its prefix selects the environment. Leave it out for docs-only mode |
GRAVV_ALLOW_LIVE_WRITES |
not set | true lets money-moving tools run on a live key. See A live key needs a second signal |
GRAVV_TOOLSETS |
the default set | A comma-separated list of toolsets, or all. Same as --toolsets |
GRAVV_RATE_PER_MINUTE |
60 |
The server's own request rate, with bursts of 15. The API allows 600 a minute per business (Rate limits) |
GRAVV_BASE_URL |
https://api.gravv.xyz |
The API host. Another host, or plain http, starts the server with a warning, because the server sends your key there. Same as --base-url |
GRAVV_DOCS_ORIGIN |
https://gravv-docs.syntext.dev |
Where the documentation tools read these docs. Same as --docs-origin |
GRAVV_DOWNLOAD_DIR |
gravv-mcp-downloads in the system temp folder |
Where the server saves downloaded files. Same as --download-dir |
The command-line options are --toolsets=<list>, --read-only, --base-url=<url>, --docs-origin=<url>, --download-dir=<dir> and --help. --read-only has no variable.
- A toolset name that does not exist stops the server with the list of names.
- Version 0.3.2 removed
approvalsandpayment-links, and version 0.4.0 removedaccount-applications. The server ignores these names and gives a warning. - Version 0.4.0 merged
featuresintocards. Forfeatures, the server loadscardsinstead and gives a warning. - A
--docs-originthat is not anhttporhttpsaddress, or that carries a query, a fragment or credentials, stops the server with the reason. - The server reads the key from the environment and sends it only to the API host. The server never writes it to disk, logs it or puts it in a result.
Toolsets
There are 83 API tools in 12 toolsets. Every toolset loads by default. That is 83 tools, or 82 on a live key, because a live key hides the sandbox tool. The two documentation tools are always loaded.
npx -y @gravv-infra/mcp --toolsets=customers,accounts,cards
npx -y @gravv-infra/mcp --toolsets=all
| Toolset | Default | Tools |
|---|---|---|
customers |
Yes | createCustomer, getCustomer, listCustomers, updateCustomer |
kyc |
Yes | startCustomerKyc, startCustomerKycS2S, uploadCustomerKycDocument, getCustomerKycDocuments, downloadCustomerKycDocument, getCustomerKycStatus |
accounts |
Yes | getAccountCapabilities, getAccountRequirements, createAccountV2, getAccountV2, listAccountsV2, getAccountApplicationV2, listAccountApplicationsV2, updateAccountApplicationV2, and createAccount, getAccount, listAccounts for your business's one card account only |
external-accounts |
Yes | createExternalAccount, getExternalAccount, listExternalAccounts, updateExternalAccount, verifyExternalAccount, listExternalAccountInstitutions |
transfers |
Yes | createTransfer, uploadTransferDocument, resumeTransfer, getCustomerLimits, getRemitterLimits, getTransferRates, listTransferSupportedCurrencies, listTransferSupportedCountries, listTransferSupportedCountriesForAddress |
transactions |
Yes | listTransactions, getTransaction, getTransactionsVolume, exportTransactions |
cards |
Yes | createCard, getCard, listCards, getCardBalance, updateCardStatus, withdrawFromCard, createCardApplication, getCardApplication, listCardApplications, startCardholderKyc, startCardholderKycS2S, uploadCardholderKycDocument, listFeatures, checkFeatureEligibility, activateFeature |
wallets |
Yes | createWallet, getWallet, listWallets |
fx |
Yes | getFxQuote, listFxRates, listFxCurrencyPairs, createFxOrder, getFxOrder, listFxOrders, cancelFxOrder, listFxPendingApprovals, listSettlementInstructions, createSettlementInstruction, getSettlementInstruction, updateSettlementInstruction, deleteSettlementInstruction |
collections |
Yes | createCardPaymentIntent, chargeSavedCard, listSavedCards, getSavedCard, deleteSavedCard, createCollection, getCollection |
webhooks |
Yes | getWebhookHistory, getWebhookEventDetail, getWebhookCallHistory, retryWebhookEvent |
sandbox |
Yes, sandbox keys only | simulateDeposit |
- Card KYC (
startCardholderKycand its pair) verifies a cardholder for card issuing. It is separate from a customer's own KYC inkyc. After card KYC,activateFeaturewithfeature_idvirtual_cardsissues a card at once in sandbox. In live it submits a card application, and you issue the card withcreateCardonce Gravv approves it. See Cards. If the assistant calls an old name,startRiskKyc,startRiskKycS2SoruploadRiskDocument, the server tells it the new name to use. createCollectiontakes bank transfers only. A card payment goes throughcreateCardPaymentIntent, which returns the hosted payment link, the intent id and the card token that later charges need.- Each tool's description lists its prerequisites, such as "the customer's KYC must be completed", and the assistant reads them before it calls.
Safety gates
Sandbox and live share one host, so nothing in a request shows which environment it reaches. The server uses the gates below to keep the two apart.
Money-moving tools take two calls
createTransfer, resumeTransfer, withdrawFromCard, createFxOrder, cancelFxOrder, createCollection, createCardPaymentIntent and chargeSavedCard return a preview on the first call. The preview has status confirmation_required, says what the call will do, and shows the request it will send. Nothing reaches the API. The call runs only when the assistant calls again with the same arguments plus confirm: true, after you agree.
resumeTransferis gated because resuming a paused transfer releases the payout.cancelFxOrderis gated because you cannot undo a cancel, and Gravv does not return money it already debited.createCardPaymentIntentis gated because, with a savedcard_token, it charges the card at once. It asks even when it only makes a link.
A live key needs a second signal
On a grvSec_live_ key, the server refuses money-moving tools, even with confirm: true, unless you started it with GRAVV_ALLOW_LIVE_WRITES=true. The server treats a key with any other prefix as live. Reads and other writes work on a live key without it. On a live key the preview also carries a warning that confirming moves real money.
Tools that cannot be undone take two calls too
deleteSavedCard, deleteSettlementInstruction and activateFeature return a preview first and run only with confirm: true. activateFeature can issue a card, and you cannot switch off a feature it activates. These tools move no money, so they do not need GRAVV_ALLOW_LIVE_WRITES.
The deposit simulator works only in sandbox
simulateDeposit (POST /v2/accounts/simulate/deposit) appears only with a key that starts grvSec_sandbox_. On any other key, the server hides it and refuses a call to it by name before it sends any request. The server does not offer the card and collection simulators.
Read-only mode
--read-only turns off every tool that is not a GET, for reporting. For live, a safe setup is one read-only server entry on the live key, and all writes on a sandbox key.
Files stay out of the conversation
Upload tools take a local file_path. The server reads the file, checks its type from its first bytes (JPEG, PNG or PDF) and its size, and sends it, so the file never passes through the assistant. The server refuses a file over the tool's limit before it sends anything. The limits are:
| Tool | Largest file |
|---|---|
uploadCustomerKycDocument |
10 MB |
uploadCardholderKycDocument |
5 MB |
uploadTransferDocument |
700 KB |
createAccountV2, updateAccountApplicationV2 |
2 MB each |
In sandbox, keep the files of one call under 500 KB in total. See Upload sizes. downloadCustomerKycDocument saves the image to a file readable only by you, in a folder only you can open. It returns the file's path, type and size, not the image.
Cardholder data is never returned
The card number, CVV and PIN routes are not tools under any setting, and the server redacts any field named like card data from results. Show card details with the client-side decryption flow.
Prompts
A prompt is a complete task that you pick in your client, often from / or a prompt menu. Each prompt does these things:
- It says what the assistant must ask you before any call.
- It gives the tool order for each path, with its confirm steps and how to recover.
- It names the docs page to read.
- It ends with the rules every task shares. Never invent a value, confirm before money moves, and know when to resend with the same
idempotency_key.
An optional argument narrows a prompt to one path.
| Prompt | For | Optional argument |
|---|---|---|
open-account |
A bank account or your business's card account; an application the bank is waiting on; deposit details | path: v2, card-account, manage |
onboard-customer |
Create and verify a person or a business; resubmit; read KYC documents | path: person-hosted, person-s2s, business, resubmit, documents |
send-payout |
ACH, wire, RTP, SWIFT, SEPA, Faster Payments, or between own accounts | rail: ach, wire, rtp, swift, sepa, fps, book |
send-remittance |
A bank account or mobile wallet abroad | country: NG, GH, KE, RW, ZM, UG, CG, CI, BJ, MX, BR, CO, IN, HK, or xof |
send-crypto |
USDC or USDT to a wallet; USDT to USDC between own wallets | path: same-network, cross-network, convert |
convert-currency |
Quote and place an FX order; settlement instructions; follow or cancel | path: quote-and-order, settlement-instruction, manage |
issue-card |
Verify a cardholder and issue a card; fund, limit, freeze, close | path: hosted-kyc, own-kyc, fund, limit, freeze-or-block, close, another-card |
collect-payment |
Stablecoin deposits, bank deposits, bank transfers, card checkout, saved cards | method: stablecoin, bank-deposit, bank-transfer, card, saved-card |
reconcile |
Transactions against your books, one transfer's webhooks, fees, missed events | path: transactions, trace, fees, webhooks |
fix-stuck-transfer |
A transfer that is pending, paused, under review or failed | transaction_id |
Prompts use the same route labels as these docs (Available, Not yet available, Not supported). A prompt says when it needs a tool the session has not loaded, such as simulateDeposit on a live key, and when there is no API key.
Documentation tools and docs-only mode
Two tools search and read every page of these docs:
searchGravvDocstakes aquery, an optionallimit(default 6) and an optional docssection.getGravvDocPagetakes a page slug or its full URL.
Both tools read the site's /llms.txt and /llms-full.txt and refresh them every hour. When they cannot reach the site, they keep answering from the last copy and add a note. Search matches words, not meaning. If it finds nothing, use the words these docs use, such as "transfer" or "payee".
Without GRAVV_API_KEY, the server runs in docs-only mode. The two documentation tools and the ten prompts work, and an API tool replies that it needs a key. You can use this mode to learn the API before you have credentials.
Results and errors
- Every result carries
environment, which issandbox,liveorunknown.unknownmeans the server does not recognize the key prefix, and the server treats that key as live. A write also carries theidempotency_keythat the server sent. - A result that is HTTP 200 but not done, such as a paused transfer, a declined card payment or a refused card KYC file, carries
next_stepahead ofdata. - An error carries
status, the API'scodewhen there is one, and ahintthat says what to do instead. - After a timeout, a network error or a
5xx, the result saysoutcome_unknown: true, which means the call may have run. First check whether it ran, with the tool the hint names. If nothing happened, call again with the same arguments plus thatidempotency_key. After any other4xx, call again without it, because the API keeps a4xxresponse for 24 hours under its key. See Idempotent requests. ignored_argumentsnames the arguments the tool does not take. The server did not send them, so a misspelled filter shows up here.- The server cuts a result over 100,000 characters. A list keeps fewer items and says to ask for a smaller page.
Going live
- Run the whole flow in sandbox first. Sandbox and live hold separate data, so an id from one does not exist in the other.
- Replace
GRAVV_API_KEYwith your live key. The host does not change. - Reads and other writes work at once. Money movement stays blocked until you also set
GRAVV_ALLOW_LIVE_WRITES=true:
{
"mcpServers": {
"gravv": {
"command": "npx",
"args": ["-y", "@gravv-infra/mcp"],
"env": {
"GRAVV_API_KEY": "<LIVE_API_KEY>",
"GRAVV_ALLOW_LIVE_WRITES": "true"
}
}
}
}
What it cannot do
| Asked for | Answer | Instead |
|---|---|---|
| Card number, CVV or PIN | Not available over MCP, under any setting | The client-side decryption flow |
| Approving or rejecting a transfer, payee or FX order | Not available. Those routes are Dashboard-only | The Gravv Dashboard. FX orders placed with an API key need no approval |
| Card or collection simulators | Not offered yet | simulateDeposit funds a sandbox account |
| Payment links | Retired | createCardPaymentIntent for a hosted card payment |
| Freezing, unfreezing or closing an account | Not available | Contact support |
Troubleshooting
| What you see | Why | Fix |
|---|---|---|
| Only two tools load | GRAVV_API_KEY is not set, so the server is in docs-only mode |
Set the key and restart |
401 on every call |
The API refused the key | Copy the whole current key |
404 for an id you know |
The id is from the other environment | Check environment in the result |
| "exists but its toolset is not loaded" | The tool's toolset is off | Restart with --toolsets=all, or name the toolset |
confirmation_required instead of a result |
The tool moves money or cannot be undone | Review the preview, then call again with confirm: true |
| "moves money and this is a LIVE key" | GRAVV_ALLOW_LIVE_WRITES is not set |
Set it only if you mean live payments |
| "is a sandbox-only test tool" | You called simulateDeposit with a key that is not a sandbox key |
Use a grvSec_sandbox_ key |
A warning about a grvPub_ key |
Publishable keys cannot call the API | Use a secret key |
422 |
You reused an idempotency_key with other arguments, or the API refused a transfer for a limit, a block or missing details |
Read code and the hint |
Repeated 429 |
Another process uses the same Gravv account | Wait, then try again |
413 on an upload |
The file is over the tool's limit, or over 500 KB in sandbox | Send a smaller file |