MCP server for official central-bank and tax-authority exchange rates — let AI tools (Claude Code, Cursor, Claude Desktop) fetch published rates from 60+ institutions (ECB, Fed, HMRC, BOJ, ...) via the AllRatesToday API.
This server demonstrates strong naming conventions, comprehensive parameter descriptions, and well-structured tool definitions. All 5 tools follow the verb_noun pattern (list_, get_, compare_). Parameter schemas use Zod with regex validation and descriptive annotations. However, output schemas are not formally documented in the tool definitions, they are inferred from API responses and descriptions. Error handling includes basic categorization (retryable vs user-fixable via API key requirement) but lacks explicit recovery guidance in error responses themselves. The tool descriptions are domain-expert quality (194 - 250 chars average), well above the baseline 194 chars, and include WHEN-to-use guidance ('Use this for OFFICIAL rates...', 'Use this when the user wants one currency pair...'). Descriptions correctly identify mutual dependencies (e.g., 'Give both source and target, or neither'). Tool composition is clean: each tool has exactly one responsibility, and output fields (bank codes, dates, rates) chain properly across tools (list_central_banks outputs codes that feed into get_official_rates and others). Security is sound: API key injected via environment variable, no secrets in parameters, READ_ONLY hints applied. The main gaps are: (1) output schemas not explicitly documented in tool registration, (2) error responses are generic fail() wrappers without actionable guidance, (3) no structured error classification or recovery hints in responses.
Use this when the user wants one currency pair across ALL institutions at once — 'what does each central bank say USD/EUR is?', 'spread between official USD/LKR rates', 'which bank has the highest official rate for X?'. One call returns every covered bank's latest official rate for the pair plus spread stats { min, max, median, spread_bps } over fresh sources (stale quarterly publishers are returned but excluded from stats). Rates a bank doesn't publish directly are cross-computed within that bank's own table only — banks are never mixed. Methodology: https://allratestoday.com/official-rates-methodology/
Use this for a date-by-date series of one bank's OFFICIAL rates — 'ECB USD rate for every day of 2025', 'how did the CBSL official rate move last quarter'. Returns one row per published date: { series: [{ date, rate, rate_type, derived, method }] }. Give either `symbol` (matches either side of the pair vs the bank's home currency) or a source+target pair. Defaults to the last year when from/to omitted. Paid plans only, and BILLED BY VOLUME: one API call per month of history covered (a year-long series costs ~12 calls) — keep ranges as narrow as the question needs. For publication dates without values (free) use get_publication_calendar.
Use this for OFFICIAL rates a specific institution published — 'what is the ECB rate for USD?', 'Bank of Japan official rate on 2026-03-14', 'HMRC rate for invoicing'. These are the fixed rates in force for compliance, tax, customs, and accounting — NOT live market rates. Omit `date` for the newest published table; give `date` (paid plans) for the table in force on that day (weekends/holidays roll back to the last published date — the response's rate_date says which). Omit source/target for the bank's full table { rates: [{ base, quote, type, value }] }; give both for one pair — cross-computed via the bank's own home currency when not directly published, flagged `derived`. For live mid-market rates use the @allratestoday/mcp-server package instead.
Output schemas not formally documented in tool definitions. Tool descriptions explain what fields are returned (e.g., 'returns { banks: [...], disclaimer }', 'returns { series: [{date, rate, ...}] }'), but these are embedded in natural language rather than registered as JSON Schema. LLMs cannot extract the structured schema for planning downstream tool use.
Error responses lack actionable recovery guidance. The fail() function returns raw error messages ('AllRatesToday error (401): Unauthorized') without telling the LLM what to do next. No guidance on retryability, required actions (e.g., 'Sign up for a free API key'), or alternative tools.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 69 | 2025-06-18+ | v2 |
Use this to see WHICH dates a bank actually published a rate table — 'did the ECB publish on 2026-01-01?', 'how often does the US Treasury publish?'. Returns dates only, no rate values, so it is available on every plan (unlike history). Gaps reveal weekends, holidays, and weekly/quarterly publication cadence. Give ?year=YYYY or a from/to range; large ranges are capped and flagged `truncated`.
Call this FIRST when you need a bank code, or when the user asks 'which central banks do you cover?' or 'do you have rates from X?'. Returns { banks: [{ code, name, country, home_ccy, rate_types, latest date, ... }], disclaimer } for 60+ institutions — central banks (ECB, Fed, BOJ, ...) plus tax authorities (HMRC, US Treasury, ...). The `code` field is what every other tool takes as its `bank` parameter. Cheap to call.
Parameter validation occurs inside tool handlers rather than at schema level. The check '(source && !target) || (!source && target)' is implemented as runtime logic; the schema does not declare this mutual exclusivity. LLMs cannot infer the constraint from the schema alone.
Large date ranges in get_official_rate_history incur high API costs ('BILLED BY VOLUME: one API call per month of history covered'). Descriptions warn about this, but the schema enforces no minimum range constraints. An LLM could request 10 years of history in one call, incurring ~120 API calls without realizing the cost impact.
The get_official_rates tool accepts optional date for historical data, but the description does not specify format precision (YYYY-MM-DD only, or does it accept YYYY-MM?). The isoDate regex is strict (^\d{4}-\d{2}-\d{2}$), but this is not mentioned in the parameter description, only in the schema validation.