Skip to main content
Glama

exchange-rates-mcp-server

Server Details

Convert currencies, get FX rates, and query historical ECB exchange rate data.

If you are the author of this connector, you can claim ownership with GitHub, an HTTP challenge, or a DNS record. Claimed connector authors can inspect health checks, view analytics, and manage their listing.
Status
Healthy
Last Tested
Transport
Streamable HTTP · MCP 2025-11-25
URL
Repository
cyanheads/exchange-rates-mcp-server
GitHub Stars
1
Server Listing
@cyanheads/exchange-rates-mcp-server

TDQS

A4.2/5.0

Scored across 7 tools

Disambiguation4/5

Most tools are clearly distinct: fx_convert_currency vs fx_get_rate are explicitly differentiated (amount vs rate number), and fx_get_rates vs fx_get_timeseries differ by snapshot vs range. However, fx_dataframe_describe and fx_dataframe_query are tightly coupled and could confuse agents about which to call first, though their descriptions do clarify the required order.

Naming Consistency4/5

All tools follow a consistent fx_ prefix with verb_noun structure (convert_currency, get_rate, get_rates, get_timeseries, list_currencies). The two dataframe tools break the verb_noun pattern slightly by using noun_verb (dataframe_describe, dataframe_query), but the prefix and overall style remain predictable.

Tool Count5/5

Seven tools is well-scoped for an exchange-rate server: currency listing, single rate, bulk rates, conversion, timeseries, and two data-analysis helpers. Each tool serves a distinct purpose without redundancy.

Completeness4/5

The core exchange-rate domain is well covered: list currencies, get current/historical rates, convert amounts, and retrieve timeseries. The DataCanvas SQL query path adds analytical depth. Minor gaps include no batch conversion across multiple amounts and no direct cross-rate endpoint without EUR triangulation, but these are workable.

Available Tools

7 tools
fx_convert_currencyFx Convert CurrencyA
Read-onlyIdempotent
Inspect

Convert an amount between any two currencies at the latest or a historical rate. Returns the converted amount, the rate used, the actual rate date, and whether the date was snapped from a weekend/holiday to the prior business day. Cross-rates are triangulated through EUR automatically.

ParametersJSON Schema
NameRequiredDescriptionDefault
dateNoISO 8601 date (YYYY-MM-DD) for a historical rate. Omit for the latest available rate. ECB data starts 1999-01-04. Future dates are not supported.
amountYesAmount in the base currency to convert. Must be greater than zero.
base_currencyYesISO 4217 source currency code (e.g. USD). Call fx_list_currencies to get valid codes.
quote_currencyYesISO 4217 target currency code (e.g. EUR). The amount will be expressed in this currency. Call fx_list_currencies to get valid codes.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rateNoExchange rate used: units of quote currency per 1 unit of base currency.
errorNoPresent when the call failed. Absent on success.
sourceNoAlways "ECB via Frankfurter" — the upstream data provider.
rate_dateNoActual date of the rate used for conversion.
rate_typeNoAlways "ECB reference (mid-market)" — these are reference rates, not tradeable bid/ask.
base_amountNoThe input amount in the base currency.
date_snappedNoTrue when the API returned a different date than requested — ECB silently snaps weekend/holiday requests to the prior business day.
quote_amountNoThe converted amount in the quote currency, rounded to 6 decimal places.
base_currencyNoSource currency code.
quote_currencyNoTarget currency code.

TDQS

A4.1/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover the read-only/idempotent safety profile, and the description adds real behavioral context beyond them: the exact return fields, weekend/holiday snapping to the prior business day, and automatic EUR triangulation for cross-rates. That is precisely the kind of non-obvious behavior an agent needs.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tight sentences, front-loaded with the core action, then the return shape, then the triangulation caveat. No filler and nothing repeated from the schema.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With annotations covering safety and an output schema covering the response, the description only needs to supply the surprising behaviors — snapping and triangulation — which it does. Nothing needed to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents date handling, ISO 4217 codes, and the amount constraint; the description adds no new parameter detail. Baseline 3 applies when the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Convert an amount between any two currencies') with the rate-source scope (latest or historical). It is clearly distinct from the rate-fetching siblings (fx_get_rate, fx_get_rates, fx_get_timeseries), though it never names them, so the differentiation is implied rather than explicit.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description signals the two modes (omit date for latest, supply date for historical) but gives no explicit guidance on when to reach for this tool instead of calling fx_get_rate and multiplying, nor any prerequisites or exclusions. Usage is inferable but not stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fx_dataframe_describeFx Dataframe DescribeA
Read-onlyIdempotent
Inspect

List tables and columns staged on a DataCanvas from a prior fx_get_timeseries call. Required first step before fx_dataframe_query — use it to discover table names and column schemas. Requires DataCanvas (CANVAS_PROVIDER_TYPE=duckdb) — without it this tool is not listed at all and fx_get_timeseries returns every range inline.

ParametersJSON Schema
NameRequiredDescriptionDefault
canvas_idYesCanvas ID returned by fx_get_timeseries. Re-run fx_get_timeseries to obtain a fresh canvas_id if this one has expired.

Output Schema

ParametersJSON Schema
NameRequiredDescription
errorNoPresent when the call failed. Absent on success.
tablesNoAll tables and views currently staged on this canvas.
canvas_idNoThe canvas ID echoed back — use this in fx_dataframe_query.
expires_atNoISO 8601 timestamp when this canvas will be evicted.

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and idempotentHint=true, and the description adds non-obvious context: the tool only exists when CANVAS_PROVIDER_TYPE=duckdb, and without it fx_get_timeseries returns every range inline. This goes beyond the structured annotations by explaining environmental dependencies and consequences.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences with no filler: purpose is front-loaded, usage guidance follows, and the prerequisite/environment note is last. Every sentence adds distinct value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-parameter, read-only discovery tool with an output schema and complete annotations, the description covers purpose, when to use it, how to get the parameter, and what happens without the required environment. No important calling context is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, and the canvas_id parameter is already documented as returned by fx_get_timeseries with expiry guidance. The tool description repeats the relationship to fx_get_timeseries but adds no parameter-level detail beyond the schema, so the baseline of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'List tables and columns staged on a DataCanvas from a prior fx_get_timeseries call.' It also distinguishes the tool from fx_dataframe_query by explicitly positioning it as the required discovery step, so an agent can tell when this tool applies.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description states exactly when to use it ('Required first step before fx_dataframe_query') and what to use it for ('discover table names and column schemas'). It also names the prerequisite DataCanvas environment and explains the fallback behavior when the prerequisite is absent, making the selection rules explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fx_dataframe_queryFx Dataframe QueryA
Read-onlyIdempotent
Inspect

Run a read-only SQL SELECT against DataCanvas tables staged by fx_get_timeseries. Supports aggregations, GROUP BY, window functions, and JOINs across multiple registered tables. Run fx_dataframe_describe first to discover table names and column schemas. Requires DataCanvas (CANVAS_PROVIDER_TYPE=duckdb) — without it this tool is not listed at all and fx_get_timeseries returns every range inline.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYesRead-only SQL SELECT statement. Reference tables by the names returned by fx_dataframe_describe or the table_name field from fx_get_timeseries. Example: SELECT date, rate FROM fx_usd_eur WHERE date > '2024-01-01' ORDER BY date
canvas_idYesCanvas ID returned by fx_get_timeseries. Re-run fx_get_timeseries to obtain a fresh canvas_id if this one has expired.
row_limitNoMost rows to return (1–10000, default 150). When the query produces more, truncated is true — page with ORDER BY <column> LIMIT <n> OFFSET <m> in the SQL, or aggregate, rather than raising this toward the maximum.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rowsNoResult rows, at most row_limit (default 150). Each key is a column name from the query.
errorNoPresent when the call failed. Absent on success.
noticeNoPresent when truncated is true: how many rows came back and the ORDER BY … LIMIT … OFFSET query shape that fetches the next page.
canvas_idNoThe canvas ID used — pass to a subsequent fx_dataframe_query or fx_dataframe_describe call.
row_countNoRows returned — always the length of rows. When truncated is true this equals row_limit, not the full result size.
truncatedNoTrue when the query produced more rows than row_limit and rows holds only the first row_limit of them. Fetch the rest with ORDER BY <column> LIMIT <n> OFFSET <m> — ORDER BY is required for deterministic paging — or aggregate to shrink the result.

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Aligns with annotations: 'read-only SQL SELECT' matches readOnlyHint=true and idempotentHint=true, with no contradiction. The description adds genuinely useful context beyond annotations by disclosing that without DataCanvas the tool is not listed at all and fx_get_timeseries degrades to inline ranges — valuable behavioral information the agent couldn't get from the schema or annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, each earning its place: core purpose first, then workflow, then environment prerequisite. The SQL feature list (aggregations, GROUP BY, window functions, JOINs) is packed efficiently. No filler or redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return-value details are handled elsewhere. The description covers supported SQL constructs, the required discovery step, the environment prerequisite, and the degradation behavior — nothing an agent needs to invoke it correctly is missing. The row_limit parameter's pagination advice in the schema completes the picture.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% so all three parameters are already documented, giving a baseline of 3. The description adds modest value by tying the query parameter to the discoverable table names from fx_dataframe_describe, but the heavy lifting is done by the schema's thorough parameter descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: 'Run a read-only SQL SELECT against DataCanvas tables.' The tool is clearly distinguishable from its siblings (fx_get_rate, fx_get_rates, fx_get_timeseries) because it offers arbitrary SQL with aggregations, window functions, and JOINs rather than fixed lookups. The listed SQL capabilities make its purpose unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit sequencing guidance: 'Run fx_dataframe_describe first to discover table names and column schemas.' It also names the staging prerequisite (fx_get_timeseries) and the environment requirement (DataCanvas). It doesn't explicitly say when to prefer a simpler sibling like fx_get_rate, but the context for when to call it is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fx_get_rateFx Get RateA
Read-onlyIdempotent
Inspect

Get the exchange rate for a currency pair on a given date (default: latest). Returns the rate, the actual rate date (which may differ from the requested date on weekends/holidays — ECB publishes business days only), and source provenance. Cross-rates are triangulated through EUR automatically. A same-currency pair returns a rate of 1, dated to the same publication day any other pair would report for that date. Use fx_convert_currency when you want the converted amount; use this tool when you only need the rate number.

ParametersJSON Schema
NameRequiredDescriptionDefault
dateNoISO 8601 date (YYYY-MM-DD). Omit for the latest available rate. ECB data starts 1999-01-04. Future dates are not supported.
base_currencyYesISO 4217 base currency code (e.g. USD). Call fx_list_currencies to get valid codes.
quote_currencyYesISO 4217 quote currency code (e.g. EUR). The rate is expressed as "how many quote units per 1 base unit". Call fx_list_currencies to get valid codes.

Output Schema

ParametersJSON Schema
NameRequiredDescription
rateNoExchange rate: units of quote currency per 1 unit of base currency.
errorNoPresent when the call failed. Absent on success.
sourceNoAlways "ECB via Frankfurter" — the upstream data provider.
rate_dateNoActual date of the rate returned.
rate_typeNoAlways "ECB reference (mid-market)" — these are reference rates, not tradeable bid/ask.
date_snappedNoTrue when the API returned a different date than requested — ECB silently snaps weekend/holiday requests to the prior business day.
base_currencyNoThe base currency code.
quote_currencyNoThe quote currency code.

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover safety (readOnly, idempotent, openWorld), and the description adds substantial behavioral context beyond them: the returned rate date may differ from the requested date because ECB publishes business days only, cross-rates are triangulated through EUR, same-currency pairs return 1 dated to the normal publication day, and provenance is included. These are non-obvious behaviors an agent could not infer from annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads purpose and return shape before edge cases, and every sentence carries information. It is slightly dense, running four clauses long with the same-currency edge case, which is useful but secondary to tool selection.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists so return values need no elaboration, yet the description still covers the holiday/weekend date fallback, triangulation, the same-currency edge case, and sibling routing. Nothing an agent needs to call this correctly and interpret the result is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3, but the description adds semantics the schema does not: the default for an omitted date is the latest available rate, and rates for pairs without a direct quote are triangulated via EUR rather than failing. It does not restate the ISO 4217 directionality, which the schema already covers.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource (get the exchange rate for a currency pair on a given date) with an explicit default, and distinguishes itself from fx_convert_currency by output type (rate number vs converted amount). An agent can differentiate it from the conversion sibling without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly routes the agent: 'Use fx_convert_currency when you want the converted amount; use this tool when you only need the rate number,' and points to fx_list_currencies for valid codes via the schema. It does not, however, resolve the closest-looking sibling, fx_get_rates, so the single-vs-multiple choice is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fx_get_ratesFx Get RatesA
Read-onlyIdempotent
Inspect

Get all available exchange rates for one base currency in a single snapshot. Useful for bulk comparison and seeding downstream tools. Returns a map of quote currency → rate, the actual snapshot date, and whether a historical date was snapped from a weekend/holiday to the prior business day. Optionally filter to a subset of quote currencies via symbols. Listing the base currency itself in symbols is accepted and returns a rate of 1 for it.

ParametersJSON Schema
NameRequiredDescriptionDefault
dateNoISO 8601 date (YYYY-MM-DD). Omit for the latest available rate. ECB data starts 1999-01-04. Future dates are not supported.
symbolsNoOptional list of quote currency codes to filter the response. When provided, must contain at least one currency code — omit the field entirely, not an empty array, to return all ~30 supported currencies (the base is not among them). Including base_currency here is valid — it comes back with a rate of 1.
base_currencyYesISO 4217 base currency code (e.g. USD). Call fx_list_currencies to get valid codes.

Output Schema

ParametersJSON Schema
NameRequiredDescription
errorNoPresent when the call failed. Absent on success.
ratesNoMap of quote currency code → exchange rate (units of quote per 1 base).
sourceNoAlways "ECB via Frankfurter" — the upstream data provider.
rate_dateNoActual date of the rates. May differ from requested date on weekends/holidays — ECB publishes business days only; the API silently snaps to the prior business day.
rate_typeNoAlways "ECB reference (mid-market)" — these are reference rates, not tradeable bid/ask.
date_snappedNoTrue when the API returned a different date than requested — ECB silently snaps weekend/holiday requests to the prior business day. Always false when date is omitted.
base_currencyNoThe base currency code.

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover the safety profile (readOnly, idempotent, openWorld), and the description goes further by disclosing non-obvious behavior: weekend/holiday dates are snapped to the prior business day, symbols is a filter not a union, and a base listed in symbols returns 1.0. It still omits caching, rate-limit, or staleness disclosure, so it is strong but not exhaustive.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four compact sentences, front-loaded with the core purpose, then return shape, then the optional filter, then the edge case. No filler and no repetition of the name or title.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Though an output schema exists (so return values need not be explained here), the description still conveys the shape of the map, the effective snapshot date, and the business-day snapping rule, plus filter and edge-case semantics. For a 3-parameter read tool, an agent has everything needed to call it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% – every parameter is documented in the schema, including the ISO date format, the ECB 1999-01-04 start, and the empty-array prohibition. The description only restates the symbols edge case (base currency → rate of 1), so it adds little beyond the schema; baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource with clear scope: 'Get all available exchange rates for one base currency in a single snapshot.' The 'all... bulk comparison' framing implicitly separates it from the singular fx_get_rate and from fx_get_timeseries, but no sibling is named, so the differentiation is left to inference.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

'Useful for bulk comparison and seeding downstream tools' gives a hint of when this is the right choice, but there is no explicit when-not guidance and no named alternative (fx_get_rate for a single pair, fx_get_timeseries for history). Usage is implied rather than stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fx_get_timeseriesFx Get TimeseriesA
Read-onlyIdempotent
Inspect

Get historical daily exchange rates for a currency pair over a date range. ECB publishes on business days only — weekends and holidays produce no entry, and no date outside the requested range is ever returned, so a range covering only non-publication days comes back with an empty rates map and a notice explaining why. A same-currency pair returns a rate of 1 on each publication day in the range. Inline results are returned as a date→rate map paged at 500 publication days: rate_count is always the total for the requested range, and when a page is cut short the response carries truncated=true and next_start_date — call again with start_date set to next_start_date and the same end_date for the next page. When DataCanvas is enabled (CANVAS_PROVIDER_TYPE=duckdb) long ranges (>90 days by default) spill to it instead: the response carries spilled=true, a canvas_id, and a table_name — call fx_dataframe_describe to inspect the staged table, then fx_dataframe_query to run SQL against it. Without DataCanvas long ranges are paged inline (spilled=false) and the notice says so.

ParametersJSON Schema
NameRequiredDescriptionDefault
end_dateYesISO 8601 end date (YYYY-MM-DD). Must be >= start_date. Future dates are not supported.
canvas_idNoOptional canvas ID from a prior call. Omit on the first call to start a fresh canvas; pass the returned canvas_id to append tables to an existing canvas.
start_dateYesISO 8601 start date (YYYY-MM-DD). ECB data starts 1999-01-04. The actual first data point may be later if start_date falls on a weekend/holiday.
base_currencyYesISO 4217 base currency code (e.g. USD). Call fx_list_currencies to get valid codes.
quote_currencyYesISO 4217 quote currency code (e.g. EUR). Call fx_list_currencies to get valid codes.

Output Schema

ParametersJSON Schema
NameRequiredDescription
errorNoPresent when the call failed. Absent on success.
ratesNoDate → rate map in date order, publication days inside the requested range only. Holds every publication day when truncated is false; otherwise the first 500 of an inline page (continue with next_start_date) or the preview of a spilled series (the full series is on the canvas). Empty when the range contains no publication day at all.
noticeNoExplains a result that would otherwise look broken or incomplete: an empty series (no publication day in the range); an inline page cut short (the start_date to continue from, plus why a long range was not staged when DataCanvas is not configured); or a spilled series (the canvas and table it was staged to, and the tools that read it).
sourceNoAlways "ECB via Frankfurter" — the upstream data provider.
spilledNoTrue when the full result was staged on the DataCanvas (range exceeded threshold).
end_dateNoLast publication date in the requested range. Always inside the requested range — earlier than the requested end when that day had no ECB fix, and equal to it when the series is empty. Later than the last key in rates when truncated is true.
canvas_idNoCanvas ID — present when spilled is true. Pass to fx_dataframe_describe to inspect the staged table, then to fx_dataframe_query to run SQL.
rate_typeNoAlways "ECB reference (mid-market)" — these are reference rates, not tradeable bid/ask.
truncatedNoTrue when rates holds only part of the series: an inline page (next_start_date continues it) or a spilled preview (spilled is true). False when rates holds every publication day in the requested range.
rate_countNoTotal publication days in the requested range (the start_date through end_date passed on this call) — not the number of entries in rates, which is smaller when truncated is true. On a continuation call it counts the days from that start_date onward.
start_dateNoFirst publication date in the requested range, and the first key in rates. Always inside the requested range — later than the requested start when that day had no ECB fix, and equal to it when the series is empty.
table_nameNoCanvas table name — present when spilled is true. Use it as the FROM target in fx_dataframe_query SQL; fx_dataframe_describe lists its columns.
base_currencyNoBase currency code.
quote_currencyNoQuote currency code.
next_start_dateNoPresent only when an inline page was cut short: the first publication date not in rates. Pass it as start_date with the same end_date to get the next page. Absent on the final page.

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare readOnlyHint, openWorldHint, and idempotentHint. The description adds substantial behavioral context: ECB business-day-only publication, empty results and notice for non-publication ranges, same-currency rate of 1, paging with truncated and next_start_date, default spill threshold, and spilled/canvas_id/table_name behavior. This is far beyond what annotations provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is dense but every sentence addresses a distinct operational concern: calendar behavior, same-currency behavior, pagination mechanics, DataCanvas spill routing, and fallback behavior. It is front-loaded with the core purpose and has no filler or redundancies.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Covers all major behaviors an agent needs to call this tool correctly: publication-day constraints, empty-result semantics, paging continuation, spill routing, canvas_id semantics, and default thresholds. An output schema exists, so return-value details do not need to be repeated in the description. No significant operational detail is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Input schema has 100% coverage, so baseline is 3. The description adds extra semantic value by explaining date behavior (business days only, no out-of-range dates, first data point may be later) and by specifying how to continue pagination through start_date=next_start_date with the same end_date, plus canvas_id reuse semantics. This goes beyond the schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('get'), a clear resource ('historical daily exchange rates'), and scope ('currency pair over a date range'). This distinguishes it from sibling tools like fx_get_rate and fx_get_rates, which are clearly not time-series tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit procedural guidance for paging (set start_date to next_start_date, keep same end_date) and for DataCanvas spill (call fx_dataframe_describe then fx_dataframe_query). It does not explicitly contrast with fx_get_rate or fx_get_rates or state when to choose them, but the described workflow is strong and actionable.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

fx_list_currenciesFx List CurrenciesA
Read-onlyIdempotent
Inspect

List all supported ISO 4217 currency codes with their full names. Call this before converting to disambiguate "dollars" (USD vs AUD vs CAD vs HKD vs SGD) or to validate a user-supplied currency code. Covers the ~30 ECB reference currencies.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
countNoTotal number of supported currencies.
errorNoPresent when the call failed. Absent on success.
sourceNoAlways "ECB via Frankfurter" — the upstream data provider.
currenciesNoAll supported currencies, sorted alphabetically by code.

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, openWorldHint, and idempotentHint. The description adds useful context about scope ('~30 ECB reference currencies') and the disambiguation purpose, which is beyond the annotations. No contradictions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, no filler. The main purpose is front-loaded, followed by usage context and scope. Every sentence earns its place, and the description is appropriately sized.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool has no parameters and an output schema exists, the description covers all needed context: what it returns (codes and names), when to use it (disambiguation/validation), and its scope (ECB currencies). Nothing essential is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With zero parameters, the schema provides no meaningful info to describe, so the baseline is 4. The description doesn't need to add parameter details and correctly stays silent on parameters.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states the exact action and resource: 'List all supported ISO 4217 currency codes with their full names.' It also distinguishes itself from conversion tools by explaining its disambiguation role, making it clear this is a lookup/list tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit when-to-use guidance: 'Call this before converting... or to validate a user-supplied currency code.' It does not name specific alternatives or exclusions, but the context is clear that this is for listing/validation prior to conversion, which is sufficient.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 3 tool updates
    • Changedfx_dataframe_describe1 field changed
      • addedInput schema / properties / canvas_id / pattern
        Added value: +"^[A-Za-z0-9_-]{10}$"
    • Changedfx_dataframe_query1 field changed
      • addedInput schema / properties / canvas_id / pattern
        Added value: +"^[A-Za-z0-9_-]{10}$"
    • Changedfx_get_timeseries1 field changed
      • addedInput schema / properties / canvas_id / pattern
        Added value: +"^[A-Za-z0-9_-]{10}$"
  2. 2 tool updates
    • Changedfx_dataframe_query5 fields changed
      • addedInput schema / properties / row_limit
        Added value: +{
        +  "default": 150,
        +  "description": "Most rows to return (1–10000, default 150). When the query produces more, truncated is true — page with ORDER BY <column> LIMIT <n> OFFSET <m> in the SQL, or aggregate, rather than raising this toward the maximum.",
        +  "maximum": 10000,
        +  "minimum": 1,
        +  "type": "integer"
        +}
      • addedOutput schema / properties / notice
        Added value: +{
        +  "description": "Present when truncated is true: how many rows came back and the ORDER BY … LIMIT … OFFSET query shape that fetches the next page.",
        +  "type": "string"
        +}
      • changedOutput schema / properties / row_count / description
        Previous value: -"Rows returned. Equals the materialized row count; when truncated is true this is the row cap, not the full result size. Narrow the SELECT (add WHERE/LIMIT or aggregate) to see all rows."New value: +"Rows returned — always the length of rows. When truncated is true this equals row_limit, not the full result size."
      • changedOutput schema / properties / rows / description
        Previous value: -"Result rows, capped at the canvas row limit (default 10 000). Each key is a column name from the query."New value: +"Result rows, at most row_limit (default 150). Each key is a column name from the query."
      • changedOutput schema / properties / truncated / description
        Previous value: -"True when the query produced more rows than the canvas row cap and the result was capped. Refine the query to materialize the complete result."New value: +"True when the query produced more rows than row_limit and rows holds only the first row_limit of them. Fetch the rest with ORDER BY <column> LIMIT <n> OFFSET <m> — ORDER BY is required for deterministic paging — or aggregate to shrink the result."
    • Changedfx_get_timeseries10 fields changed
      • changedOutput schema / anyOf
        Previous value: -[
        -  {
        -    "not": {
        -      "required": [
        -        "error"
        -      ]
        -    },
        -    "required": [
        -      "base_currency",
        -      "quote_currency",
        -      "start_date",
        -      "end_date",
        -      "rates",
        -      "rate_count",
        -      "rate_type",
        -      "source",
        -      "spilled"
        -    ]
        -  },
        -  {
        -    "required": [
        -      "error"
        -    ]
        -  }
        -]New value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "base_currency",
        +      "quote_currency",
        +      "start_date",
        +      "end_date",
        +      "rates",
        +      "rate_count",
        +      "truncated",
        +      "rate_type",
        +      "source",
        +      "spilled"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • changedOutput schema / properties / canvas_id / description
        Previous value: -"Canvas ID — present when spilled is true. Pass to fx_dataframe_query."New value: +"Canvas ID — present when spilled is true. Pass to fx_dataframe_describe to inspect the staged table, then to fx_dataframe_query to run SQL."
      • changedOutput schema / properties / end_date / description
        Previous value: -"Last date in the returned series. Always inside the requested range — earlier than the requested end when that day had no ECB fix, and equal to it when the series is empty."New value: +"Last publication date in the requested range. Always inside the requested range — earlier than the requested end when that day had no ECB fix, and equal to it when the series is empty. Later than the last key in rates when truncated is true."
      • addedOutput schema / properties / next_start_date
        Added value: +{
        +  "description": "Present only when an inline page was cut short: the first publication date not in rates. Pass it as start_date with the same end_date to get the next page. Absent on the final page.",
        +  "type": "string"
        +}
      • changedOutput schema / properties / notice / description
        Previous value: -"Explains a result that would otherwise look broken — an empty series, or a long range that stayed inline because DataCanvas is not configured."New value: +"Explains a result that would otherwise look broken or incomplete: an empty series (no publication day in the range); an inline page cut short (the start_date to continue from, plus why a long range was not staged when DataCanvas is not configured); or a spilled series (the canvas and table it was staged to, and the tools that read it)."
      • changedOutput schema / properties / rate_count / description
        Previous value: -"Total number of data points (publication days) inside the requested range."New value: +"Total publication days in the requested range (the start_date through end_date passed on this call) — not the number of entries in rates, which is smaller when truncated is true. On a continuation call it counts the days from that start_date onward."
      • changedOutput schema / properties / rates / description
        Previous value: -"Date → rate map for the inline result. Publication days inside the requested range only. Truncated to a preview when the result was spilled to canvas; empty when the range contains no publication day at all."New value: +"Date → rate map in date order, publication days inside the requested range only. Holds every publication day when truncated is false; otherwise the first 500 of an inline page (continue with next_start_date) or the preview of a spilled series (the full series is on the canvas). Empty when the range contains no publication day at all."
      • changedOutput schema / properties / start_date / description
        Previous value: -"First date in the returned series. Always inside the requested range — later than the requested start when that day had no ECB fix, and equal to it when the series is empty."New value: +"First publication date in the requested range, and the first key in rates. Always inside the requested range — later than the requested start when that day had no ECB fix, and equal to it when the series is empty."
      • changedOutput schema / properties / table_name / description
        Previous value: -"Canvas table name — present when spilled is true. Use in fx_dataframe_query SQL."New value: +"Canvas table name — present when spilled is true. Use it as the FROM target in fx_dataframe_query SQL; fx_dataframe_describe lists its columns."
      • addedOutput schema / properties / truncated
        Added value: +{
        +  "description": "True when rates holds only part of the series: an inline page (next_start_date continues it) or a spilled preview (spilled is true). False when rates holds every publication day in the requested range.",
        +  "type": "boolean"
        +}
  3. 3 tool updates
    • Changedfx_convert_currency1 field changed
      • changedInput schema / properties / quote_currency / description
        Previous value: -"ISO 4217 target currency code (e.g. EUR). The amount will be expressed in this currency."New value: +"ISO 4217 target currency code (e.g. EUR). The amount will be expressed in this currency. Call fx_list_currencies to get valid codes."
    • Changedfx_get_rate1 field changed
      • changedInput schema / properties / quote_currency / description
        Previous value: -"ISO 4217 quote currency code (e.g. EUR). The rate is expressed as \"how many quote units per 1 base unit\"."New value: +"ISO 4217 quote currency code (e.g. EUR). The rate is expressed as \"how many quote units per 1 base unit\". Call fx_list_currencies to get valid codes."
    • Changedfx_get_rates4 fields changed
      • changedInput schema / properties / symbols / description
        Previous value: -"Optional list of quote currency codes to filter the response. Omit to return all ~30 supported currencies (the base is not among them). Including base_currency here is valid — it comes back with a rate of 1."New value: +"Optional list of quote currency codes to filter the response. When provided, must contain at least one currency code — omit the field entirely, not an empty array, to return all ~30 supported currencies (the base is not among them). Including base_currency here is valid — it comes back with a rate of 1."
      • addedInput schema / properties / symbols / minItems
        Added value: +1
      • changedOutput schema / anyOf
        Previous value: -[
        -  {
        -    "not": {
        -      "required": [
        -        "error"
        -      ]
        -    },
        -    "required": [
        -      "base_currency",
        -      "rate_date",
        -      "rates",
        -      "rate_type",
        -      "source"
        -    ]
        -  },
        -  {
        -    "required": [
        -      "error"
        -    ]
        -  }
        -]New value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "base_currency",
        +      "rate_date",
        +      "rates",
        +      "date_snapped",
        +      "rate_type",
        +      "source"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • addedOutput schema / properties / date_snapped
        Added value: +{
        +  "description": "True when the API returned a different date than requested — ECB silently snaps weekend/holiday requests to the prior business day. Always false when date is omitted.",
        +  "type": "boolean"
        +}
  4. 7 tool updates
    • Changedfx_convert_currency6 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedInput schema / additionalProperties
        Added value: +false
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedOutput schema / anyOf
        Added value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "base_currency",
        +      "quote_currency",
        +      "base_amount",
        +      "quote_amount",
        +      "rate",
        +      "rate_date",
        +      "date_snapped",
        +      "rate_type",
        +      "source"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • addedOutput schema / properties / error
        Added value: +{
        +  "additionalProperties": {},
        +  "description": "Present when the call failed. Absent on success.",
        +  "properties": {
        +    "code": {
        +      "description": "JSON-RPC error code for this failure.",
        +      "maximum": 9007199254740991,
        +      "minimum": -9007199254740991,
        +      "type": "integer"
        +    },
        +    "data": {
        +      "additionalProperties": {},
        +      "properties": {
        +        "reason": {
        +          "description": "Machine-readable failure mode. Declared by this tool: `invalid_date_format`: date is not a real calendar date written as YYYY-MM-DD. `unsupported_currency`: base_currency or quote_currency is not in the ECB currency set. `date_out_of_range`: date is before 1999-01-04 or in the future. `upstream_no_data`: Both currencies are supported but the ECB published no rate for this date. Other values are possible when a failure originates below the handler.",
        +          "examples": [
        +            "invalid_date_format",
        +            "unsupported_currency",
        +            "date_out_of_range",
        +            "upstream_no_data"
        +          ],
        +          "type": "string"
        +        },
        +        "recovery": {
        +          "additionalProperties": {},
        +          "description": "Actionable next step for the caller.",
        +          "properties": {
        +            "hint": {
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "hint"
        +          ],
        +          "type": "object"
        +        },
        +        "retryable": {
        +          "description": "Whether retrying may succeed.",
        +          "type": "boolean"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable description of what went wrong.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "code",
        +    "message"
        +  ],
        +  "type": "object"
        +}
      • removedOutput schema / required
        Removed value: -[
        -  "base_currency",
        -  "quote_currency",
        -  "base_amount",
        -  "quote_amount",
        -  "rate",
        -  "rate_date",
        -  "date_snapped",
        -  "rate_type",
        -  "source"
        -]
    • Changedfx_dataframe_describe6 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedInput schema / additionalProperties
        Added value: +false
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedOutput schema / anyOf
        Added value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "canvas_id",
        +      "tables",
        +      "expires_at"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • addedOutput schema / properties / error
        Added value: +{
        +  "additionalProperties": {},
        +  "description": "Present when the call failed. Absent on success.",
        +  "properties": {
        +    "code": {
        +      "description": "JSON-RPC error code for this failure.",
        +      "maximum": 9007199254740991,
        +      "minimum": -9007199254740991,
        +      "type": "integer"
        +    },
        +    "data": {
        +      "additionalProperties": {},
        +      "properties": {
        +        "reason": {
        +          "description": "Machine-readable failure mode. Declared by this tool: `canvas_not_found`: canvas_id does not exist or has been evicted. Other values are possible when a failure originates below the handler.",
        +          "examples": [
        +            "canvas_not_found"
        +          ],
        +          "type": "string"
        +        },
        +        "recovery": {
        +          "additionalProperties": {},
        +          "description": "Actionable next step for the caller.",
        +          "properties": {
        +            "hint": {
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "hint"
        +          ],
        +          "type": "object"
        +        },
        +        "retryable": {
        +          "description": "Whether retrying may succeed.",
        +          "type": "boolean"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable description of what went wrong.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "code",
        +    "message"
        +  ],
        +  "type": "object"
        +}
      • removedOutput schema / required
        Removed value: -[
        -  "canvas_id",
        -  "tables",
        -  "expires_at"
        -]
    • Changedfx_dataframe_query6 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedInput schema / additionalProperties
        Added value: +false
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedOutput schema / anyOf
        Added value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "rows",
        +      "row_count",
        +      "truncated",
        +      "canvas_id"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • addedOutput schema / properties / error
        Added value: +{
        +  "additionalProperties": {},
        +  "description": "Present when the call failed. Absent on success.",
        +  "properties": {
        +    "code": {
        +      "description": "JSON-RPC error code for this failure.",
        +      "maximum": 9007199254740991,
        +      "minimum": -9007199254740991,
        +      "type": "integer"
        +    },
        +    "data": {
        +      "additionalProperties": {},
        +      "properties": {
        +        "reason": {
        +          "description": "Machine-readable failure mode. Declared by this tool: `canvas_not_found`: canvas_id does not exist or has been evicted. `missing_table`: The SQL references a table that is not staged on this canvas, or whose TTL expired. `invalid_query`: SQL is not a SELECT, references unknown columns, or has a syntax error. Other values are possible when a failure originates below the handler.",
        +          "examples": [
        +            "canvas_not_found",
        +            "missing_table",
        +            "invalid_query"
        +          ],
        +          "type": "string"
        +        },
        +        "recovery": {
        +          "additionalProperties": {},
        +          "description": "Actionable next step for the caller.",
        +          "properties": {
        +            "hint": {
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "hint"
        +          ],
        +          "type": "object"
        +        },
        +        "retryable": {
        +          "description": "Whether retrying may succeed.",
        +          "type": "boolean"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable description of what went wrong.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "code",
        +    "message"
        +  ],
        +  "type": "object"
        +}
      • removedOutput schema / required
        Removed value: -[
        -  "rows",
        -  "row_count",
        -  "truncated",
        -  "canvas_id"
        -]
    • Changedfx_get_rate6 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedInput schema / additionalProperties
        Added value: +false
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedOutput schema / anyOf
        Added value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "base_currency",
        +      "quote_currency",
        +      "rate",
        +      "rate_date",
        +      "date_snapped",
        +      "rate_type",
        +      "source"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • addedOutput schema / properties / error
        Added value: +{
        +  "additionalProperties": {},
        +  "description": "Present when the call failed. Absent on success.",
        +  "properties": {
        +    "code": {
        +      "description": "JSON-RPC error code for this failure.",
        +      "maximum": 9007199254740991,
        +      "minimum": -9007199254740991,
        +      "type": "integer"
        +    },
        +    "data": {
        +      "additionalProperties": {},
        +      "properties": {
        +        "reason": {
        +          "description": "Machine-readable failure mode. Declared by this tool: `invalid_date_format`: date is not a real calendar date written as YYYY-MM-DD. `unsupported_currency`: base_currency or quote_currency is not in the ECB currency set. `date_out_of_range`: date is before 1999-01-04 or in the future. `upstream_no_data`: Both currencies are supported but the ECB published no rate for this date. Other values are possible when a failure originates below the handler.",
        +          "examples": [
        +            "invalid_date_format",
        +            "unsupported_currency",
        +            "date_out_of_range",
        +            "upstream_no_data"
        +          ],
        +          "type": "string"
        +        },
        +        "recovery": {
        +          "additionalProperties": {},
        +          "description": "Actionable next step for the caller.",
        +          "properties": {
        +            "hint": {
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "hint"
        +          ],
        +          "type": "object"
        +        },
        +        "retryable": {
        +          "description": "Whether retrying may succeed.",
        +          "type": "boolean"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable description of what went wrong.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "code",
        +    "message"
        +  ],
        +  "type": "object"
        +}
      • removedOutput schema / required
        Removed value: -[
        -  "base_currency",
        -  "quote_currency",
        -  "rate",
        -  "rate_date",
        -  "date_snapped",
        -  "rate_type",
        -  "source"
        -]
    • Changedfx_get_rates6 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedInput schema / additionalProperties
        Added value: +false
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedOutput schema / anyOf
        Added value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "base_currency",
        +      "rate_date",
        +      "rates",
        +      "rate_type",
        +      "source"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • addedOutput schema / properties / error
        Added value: +{
        +  "additionalProperties": {},
        +  "description": "Present when the call failed. Absent on success.",
        +  "properties": {
        +    "code": {
        +      "description": "JSON-RPC error code for this failure.",
        +      "maximum": 9007199254740991,
        +      "minimum": -9007199254740991,
        +      "type": "integer"
        +    },
        +    "data": {
        +      "additionalProperties": {},
        +      "properties": {
        +        "reason": {
        +          "description": "Machine-readable failure mode. Declared by this tool: `invalid_date_format`: date is not a real calendar date written as YYYY-MM-DD. `unsupported_currency`: base_currency or an entry in symbols is not in the ECB currency set. `date_out_of_range`: date is before 1999-01-04 or in the future. `upstream_no_data`: Every requested currency is supported but the ECB published no rates for this date. Other values are possible when a failure originates below the handler.",
        +          "examples": [
        +            "invalid_date_format",
        +            "unsupported_currency",
        +            "date_out_of_range",
        +            "upstream_no_data"
        +          ],
        +          "type": "string"
        +        },
        +        "recovery": {
        +          "additionalProperties": {},
        +          "description": "Actionable next step for the caller.",
        +          "properties": {
        +            "hint": {
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "hint"
        +          ],
        +          "type": "object"
        +        },
        +        "retryable": {
        +          "description": "Whether retrying may succeed.",
        +          "type": "boolean"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable description of what went wrong.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "code",
        +    "message"
        +  ],
        +  "type": "object"
        +}
      • removedOutput schema / required
        Removed value: -[
        -  "base_currency",
        -  "rate_date",
        -  "rates",
        -  "rate_type",
        -  "source"
        -]
    • Changedfx_get_timeseries6 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedInput schema / additionalProperties
        Added value: +false
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedOutput schema / anyOf
        Added value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "base_currency",
        +      "quote_currency",
        +      "start_date",
        +      "end_date",
        +      "rates",
        +      "rate_count",
        +      "rate_type",
        +      "source",
        +      "spilled"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • addedOutput schema / properties / error
        Added value: +{
        +  "additionalProperties": {},
        +  "description": "Present when the call failed. Absent on success.",
        +  "properties": {
        +    "code": {
        +      "description": "JSON-RPC error code for this failure.",
        +      "maximum": 9007199254740991,
        +      "minimum": -9007199254740991,
        +      "type": "integer"
        +    },
        +    "data": {
        +      "additionalProperties": {},
        +      "properties": {
        +        "reason": {
        +          "description": "Machine-readable failure mode. Declared by this tool: `invalid_date_format`: start_date or end_date is not a real calendar date written as YYYY-MM-DD. `unsupported_currency`: base_currency or quote_currency is not in the ECB currency set. `date_out_of_range`: start_date is before 1999-01-04 or end_date is in the future. `invalid_range`: start_date is after end_date. `upstream_no_data`: Both currencies are supported but the ECB published no rates across this range. Other values are possible when a failure originates below the handler.",
        +          "examples": [
        +            "invalid_date_format",
        +            "unsupported_currency",
        +            "date_out_of_range",
        +            "invalid_range",
        +            "upstream_no_data"
        +          ],
        +          "type": "string"
        +        },
        +        "recovery": {
        +          "additionalProperties": {},
        +          "description": "Actionable next step for the caller.",
        +          "properties": {
        +            "hint": {
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "hint"
        +          ],
        +          "type": "object"
        +        },
        +        "retryable": {
        +          "description": "Whether retrying may succeed.",
        +          "type": "boolean"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable description of what went wrong.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "code",
        +    "message"
        +  ],
        +  "type": "object"
        +}
      • removedOutput schema / required
        Removed value: -[
        -  "base_currency",
        -  "quote_currency",
        -  "start_date",
        -  "end_date",
        -  "rates",
        -  "rate_count",
        -  "rate_type",
        -  "source",
        -  "spilled"
        -]
    • Changedfx_list_currencies6 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedInput schema / additionalProperties
        Added value: +false
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • addedOutput schema / anyOf
        Added value: +[
        +  {
        +    "not": {
        +      "required": [
        +        "error"
        +      ]
        +    },
        +    "required": [
        +      "currencies",
        +      "count",
        +      "source"
        +    ]
        +  },
        +  {
        +    "required": [
        +      "error"
        +    ]
        +  }
        +]
      • addedOutput schema / properties / error
        Added value: +{
        +  "additionalProperties": {},
        +  "description": "Present when the call failed. Absent on success.",
        +  "properties": {
        +    "code": {
        +      "description": "JSON-RPC error code for this failure.",
        +      "maximum": 9007199254740991,
        +      "minimum": -9007199254740991,
        +      "type": "integer"
        +    },
        +    "data": {
        +      "additionalProperties": {},
        +      "properties": {
        +        "reason": {
        +          "description": "Machine-readable failure mode.",
        +          "type": "string"
        +        },
        +        "recovery": {
        +          "additionalProperties": {},
        +          "description": "Actionable next step for the caller.",
        +          "properties": {
        +            "hint": {
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "hint"
        +          ],
        +          "type": "object"
        +        },
        +        "retryable": {
        +          "description": "Whether retrying may succeed.",
        +          "type": "boolean"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    "message": {
        +      "description": "Human-readable description of what went wrong.",
        +      "type": "string"
        +    }
        +  },
        +  "required": [
        +    "code",
        +    "message"
        +  ],
        +  "type": "object"
        +}
      • removedOutput schema / required
        Removed value: -[
        -  "currencies",
        -  "count",
        -  "source"
        -]
  5. 3 tool updates
    • Changedfx_convert_currency2 fields changed
      • changedInput schema / properties / amount / description
        Previous value: -"Amount in the base currency to convert. Must be a positive number."New value: +"Amount in the base currency to convert. Must be greater than zero."
      • addedInput schema / properties / amount / exclusiveMinimum
        Added value: +0
    • Changedfx_get_rates1 field changed
      • changedInput schema / properties / symbols / description
        Previous value: -"Optional list of quote currency codes to filter the response. Omit to return all ~30 supported currencies."New value: +"Optional list of quote currency codes to filter the response. Omit to return all ~30 supported currencies (the base is not among them). Including base_currency here is valid — it comes back with a rate of 1."
    • Changedfx_get_timeseries5 fields changed
      • changedOutput schema / properties / end_date / description
        Previous value: -"Actual last date in the returned series (may differ from requested if that date had no ECB fix)."New value: +"Last date in the returned series. Always inside the requested range — earlier than the requested end when that day had no ECB fix, and equal to it when the series is empty."
      • addedOutput schema / properties / notice
        Added value: +{
        +  "description": "Explains a result that would otherwise look broken — an empty series, or a long range that stayed inline because DataCanvas is not configured.",
        +  "type": "string"
        +}
      • changedOutput schema / properties / rate_count / description
        Previous value: -"Total number of data points (business days) in the range."New value: +"Total number of data points (publication days) inside the requested range."
      • changedOutput schema / properties / rates / description
        Previous value: -"Date → rate map for the inline result. Business days only. Empty when the result was spilled to canvas."New value: +"Date → rate map for the inline result. Publication days inside the requested range only. Truncated to a preview when the result was spilled to canvas; empty when the range contains no publication day at all."
      • changedOutput schema / properties / start_date / description
        Previous value: -"Actual first date in the returned series (may differ from requested if that date had no ECB fix)."New value: +"First date in the returned series. Always inside the requested range — later than the requested start when that day had no ECB fix, and equal to it when the series is empty."
  6. 1 tool update
    • Changedfx_dataframe_query3 fields changed
      • changedOutput schema / properties / row_count / description
        Previous value: -"Total rows in the full result set before the row cap was applied."New value: +"Rows returned. Equals the materialized row count; when truncated is true this is the row cap, not the full result size. Narrow the SELECT (add WHERE/LIMIT or aggregate) to see all rows."
      • addedOutput schema / properties / truncated
        Added value: +{
        +  "description": "True when the query produced more rows than the canvas row cap and the result was capped. Refine the query to materialize the complete result.",
        +  "type": "boolean"
        +}
      • changedOutput schema / required
        Previous value: -[
        -  "rows",
        -  "row_count",
        -  "canvas_id"
        -]New value: +[
        +  "rows",
        +  "row_count",
        +  "truncated",
        +  "canvas_id"
        +]
  7. 7 tool updates
    • First observedfx_convert_currency
    • First observedfx_dataframe_describe
    • First observedfx_dataframe_query
    • First observedfx_get_rate
    • First observedfx_get_rates
    • First observedfx_get_timeseries
    • First observedfx_list_currencies

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    Enables currency conversion and exchange rate lookups using real ECB reference rates, free and without an API key. Supports natural language queries like 'convert 100 USD to EUR' or 'what's the exchange rate between GBP and JPY?'.
    2
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Provides access to currency exchange rates and conversion tools using the Frankfurter API, including latest rates, historical data, and time series from sources like the European Central Bank.
    5
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    Provides real-time and historical foreign exchange rates for 31+ currencies, enabling currency conversion, historical rate lookups, and time series analysis using data from the Frankfurter API.
    -
  • A
    license
    Not graded
    quality
    B
    maintenance
    Provides live and historical foreign-exchange rates from the European Central Bank via Frankfurter API, enabling currency conversion, rate lookup, and trend analysis without API keys.
    4 npm
    MIT
Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.