Skip to main content
Glama

exchange-rates-mcp-server

Fx Get Timeseries

fx_get_timeseries
Read-onlyIdempotent

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.

Input Schema

TableJSON 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

TableJSON Schema
NameRequiredDescriptionDefault
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.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed1 schema field changed
    • addedInput schema / properties / canvas_id / pattern
      Added value: +"^[A-Za-z0-9_-]{10}$"
  2. Changed10 schema 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. Changed6 schema 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"
      -]
  4. Changed5 schema 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."
  5. First observed

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.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.