modan v1.0.0
Connect Modan to Claude, Cursor and any MCP client
Modan runs a remote Model Context Protocol server at https://modan.io/api/mcp. Point an MCP client at it with your API key and the model can read current provider-level African FX rates, convert amounts net of fees, benchmark against an independent mid and pull history — the same data as the REST API, one tool call per request of the same daily quota.
Claude Code
One command. Replace the key with yours from Account → API keys.
claude mcp add --transport http modan https://modan.io/api/mcp \ --header "X-API-Key: mdn_live_YOUR_KEY_HERE"
Cursor
Add to .cursor/mcp.json in your project (or the global one), then enable the server under Settings → MCP.
{
"mcpServers": {
"modan": {
"url": "https://modan.io/api/mcp",
"headers": { "X-API-Key": "mdn_live_YOUR_KEY_HERE" }
}
}
}VS Code (GitHub Copilot agent mode)
Add to .vscode/mcp.json.
{
"servers": {
"modan": {
"type": "http",
"url": "https://modan.io/api/mcp",
"headers": { "X-API-Key": "mdn_live_YOUR_KEY_HERE" }
}
}
}Any other MCP client
Codex, Windsurf, Claude Desktop connectors and the rest accept a remote streamable-HTTP server: use the URL and the header below in whatever shape the client's configuration takes.
{
"mcpServers": {
"modan": {
"type": "http",
"url": "https://modan.io/api/mcp",
"headers": { "X-API-Key": "mdn_live_YOUR_KEY_HERE" }
}
}
}ChatGPT connectors cannot send custom headers today; use a Custom GPT Action with openapi.json and API-key auth on the X-API-Key header instead.
The tools
| Tool | Arguments | What it returns |
|---|---|---|
get_rates Get corridor rates | from to provider_type? | Current exchange rates for a corridor (e.g. GBP→NGN): every tracked provider's rate, fee, spread in bps vs the best current rate from the SAME provider_type and rate_type, provider_type, transfer time, last_checked and last_changed timestamps, a stale flag for quotes not checked in 24 hours (never ranked), and the independent mid-market reference (mid_rate + per-provider vs_mid_bps) when available. Pass provider_type (e.g. ["imto"]) for one kind of provider. Real-time on Team keys; as of the top of the hour on free and Individual keys. |
convert Convert an amount across a corridor | from to amount provider_type? | Convert an amount for every provider on a corridor: gross converted value, net-of-fee value in the target currency, and which provider delivers the best net amount to the recipient: best across the provider types returned, and best_by_type within each type (null where none qualifies). Non-executable prices (a central bank's official reference, a parallel print) and stale quotes (not checked in 24 hours) are returned and labelled but never chosen as best. Pass provider_type (e.g. ["imto"]) to compare one kind of provider. |
fetch_rates Fetch mid-market rates for one base against many quotes | base quotes provider_type? | Mid-market reference rates from one base currency to up to 20 quote currencies in a single call (cross-computed through the freshest USD snapshot), plus the best current executable provider rate for pairs that are covered corridors (null when no quote qualifies), among the provider types passed in provider_type or every type when it is omitted. The mid never depends on provider_type. |
get_history Get historical rates | from to days? provider? provider_type? | Historical provider rates for a corridor, oldest first. Returns timestamped observations (t, provider_id, rate) over the requested window, optionally for one provider or for the provider types passed in provider_type. An observation is recorded when a price MOVES: a provider holding a steady price records no new point however often it is confirmed, so an empty window does not mean the provider stopped quoting. Use get_rates for the current quote and its last_checked. |
list_corridors List covered corridors | provider_type? | All currency corridors Modan currently covers, with the number of providers quoting each (checked in the last 24 hours), how many more quotes are stale, and the provider types present (provider_types). Pass provider_type to list only the corridors those types quote, counting only their quotes. |
list_providers List tracked providers | — | Active providers Modan tracks — central banks, commercial banks, non-bank LPs, IMTOs, fintech PSPs, crypto venues, bureaux de change and aggregators — with provider_type, the rate_type they publish, and typical transfer time. |
list_currencies List supported currencies | — | Active currencies with their role (source, target or both). |
Things to ask once it is connected
- “What is the best GBP to NGN rate right now, and how far is it from the mid?”
- “Convert 2,000 USD to KES across every provider and tell me who delivers the most net of fees.”
- “Which providers quote USDT to NGN, and how do their rates compare?”
- “Which money-transfer operator gives the best USD to NGN rate, and how does the best fintech compare?”
- “Show me how LemFi's GBP→NGN rate moved over the last 30 days.”
How to read the answers
- spread_bps is basis points below the best current rate of the same provider type and rate type on the corridor at that moment (0 = best of its kind): an IMTO is measured against IMTOs, a bank against banks. It is null for a stale quote, and it is not a spread against the mid.
- provider_type narrows an answer to kinds of provider. get_rates, convert, fetch_rates, get_history and list_corridors take it as an array of imto, fintech_psp, commercial_bank, central_bank, non_bank_lp, bureau_de_change, crypto_venue and aggregator, for example ["imto"]; leave it out for every type. Kinds of provider are never ranked against each other, so convert returns best_by_type, the best net amount within each type (null where none qualifies), beside best, the best across the types returned. A filtered result echoes the types as provider_types, and list_corridors lists the types quoting each corridor. An unknown type comes back as a tool error naming the valid ones, and uses no quota.
- stale is true when a quote has not been checked for 24 hours before the moment the answer describes. A stale quote is still returned, with its dates, but it is never a corridor's best rate, the best value on convert or a fetch_rates provider_best, and list_corridors counts it in stale_count rather than in providers, as /corridors does. A quote not checked for 7 days is not returned. Commercial and central banks publish on weekdays only, so their quotes age on a weekday clock: Friday's close stays current through the weekend.
- vs_mid_bps is the distance from an independent mid-market reference, refreshed hourly. Stablecoin corridors (USDT, USDC) have no fiat mid, so the field is omitted rather than estimated.
- rate_type says what kind of price it is — official, interbank, retail, p2p or parallel. A central bank's official print is returned and labelled but never chosen as the best rate or the best value on convert, because nobody can transact at it.
- data_freshness and as_of tell the model whether it got hourly or real-time data. Every provider entry carries two clocks: last_checked, when the quote was last confirmed (read this for freshness, and on an hourly key it is never later than as_of), and last_changed, when the price last moved. last_updated is a deprecated alias of last_changed.
Check the connection without a key
initialize, tools/list and ping are open, so a client can discover the tools before you add a key.
curl -X POST https://modan.io/api/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Frequently asked questions
- What is the Modan MCP server?
- A remote Model Context Protocol server at https://modan.io/api/mcp that gives Claude, Cursor, Codex and any MCP client seven tools over Modan's African FX data: current provider rates for a corridor, conversion net of fees, mid-market rates for one base against many quotes, historical observations, and lists of corridors, providers and currencies. It uses streamable HTTP with plain JSON responses and needs no local install.
- Do I need an API key?
- Yes for tool calls: pass a Modan API key in the X-API-Key header (or as a Bearer token). Keys are free on signup — 50 requests a day on the free plan, 250 on Individual, 1,000 on Team — and each tool call uses one request of that daily quota, shared with the REST API. The initialize, tools/list and ping methods work without a key, so a client can connect and discover the tools before you add one.
- Is the data real-time?
- It depends on the plan, and the answer is always stated in the response. Free and Individual keys receive rates as of the top of the current UTC hour (data_freshness: "hourly" with an as_of timestamp); Team keys receive every observation as it lands (data_freshness: "realtime"). How often a provider is checked depends on how its rates are collected (some automatically through the day, some by hand), so read each quote's last_checked rather than assume a cadence; every observation is kept, so history is append-only. A quote that has not been re-checked for 24 hours comes back marked stale and is never named the best rate; one not checked for 7 days is not returned.
- Can the model ask for one kind of provider?
- Yes. get_rates, convert, fetch_rates, get_history and list_corridors take an optional provider_type, such as ["imto"] for money-transfer operators or ["commercial_bank"] for banks' board rates; without it every type is returned. Kinds of provider are never ranked against each other, spreads are measured within a provider type and rate type, and a filtered answer says which types it covers. An unknown type comes back as an error naming the valid ones and uses no quota.
- Can ChatGPT use it?
- ChatGPT's connectors do not send custom headers, so they cannot authenticate to the MCP server today. A Custom GPT Action can: import https://modan.io/openapi.json and choose API-key authentication with the custom header name X-API-Key, and the GPT can call the same data over REST.
Machine-readable: llms.txt · llms-full.txt · openapi.json · registry manifest server.json. Full endpoint reference in the API documentation.