Skip to main content
Glama

exchange-rates-mcp-server

Fx Get Timeseries

fx_get_timeseries
Read-onlyIdempotent

Get historical daily Frankfurter v1 rates for a currency pair over an inclusive date range. Rates use same-date EUR legs without intermediate rounding; only days with both currencies appear, and a self-pair is 1 on actual observation dates. Historical membership includes retired currencies. Empty non-publication windows succeed with a notice. summary covers every observation in the requested interval before paging or staging: unweighted arithmetic mean, extrema with earliest-date ties, endpoints and percentage change, without prescribing accounting policy. Inline rates page at 500 observations; continue with next_start_date and the same end_date. Long ranges (>90 days by default) stage when they exceed the 40,000-character preview budget. An explicit canvas_id always stages, including short or empty ranges, and fails if unavailable or unknown. Each registration returns a unique table_name and preserves older tables. spilled=true returns canvas_id and table_name; call fx_dataframe_describe then fx_dataframe_query for SQL. Without DataCanvas, omit canvas_id to page inline.

Input Schema

NameRequiredDescriptionDefault
end_dateYesISO 8601 end date (YYYY-MM-DD). Must be >= start_date. Future dates are not supported.
canvas_idNoExisting canvas ID from a prior call explicitly requesting staging even for short or empty results. Every call creates a uniquely named table. Omit for automatic staging or inline paging.
start_dateYesISO 8601 start date (YYYY-MM-DD). Frankfurter v1 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

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 turned off); or a spilled series (the canvas and table it was staged to, and the tools that read it).
sourceNoAlways "Frankfurter v1" — the upstream data provider.
spilledNoTrue when the complete result was staged on DataCanvas, either explicitly through canvas_id or automatically after exceeding the threshold and preview budget.
summaryNoStatistics over the full clipped requested range before paging or staging; omitted for empty ranges. Continuations summarize their own requested interval.
end_dateNoLast publication date in the requested range. Always inside the requested range — earlier than the requested end when that day had no Frankfurter v1 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 "Daily rate (Frankfurter v1)" — 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 Frankfurter v1 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. Changed12 schema fields changed
    • changedInput schema / properties / canvas_id / description
      Previous value: -"Optional 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."New value: +"Existing canvas ID from a prior call explicitly requesting staging even for short or empty results. Every call creates a uniquely named table. Omit for automatic staging or inline paging."
    • addedInput schema / properties / end_date / pattern
      Added value: +"^\\d{4}-\\d{2}-\\d{2}$"
    • changedInput schema / properties / start_date / description
      Previous value: -"ISO 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."New value: +"ISO 8601 start date (YYYY-MM-DD). Frankfurter v1 data starts 1999-01-04. The actual first data point may be later if start_date falls on a weekend/holiday."
    • addedInput schema / properties / start_date / pattern
      Added value: +"^\\d{4}-\\d{2}-\\d{2}$"
    • changedOutput schema / properties / end_date / description
      Previous 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."New value: +"Last publication date in the requested range. Always inside the requested range — earlier than the requested end when that day had no Frankfurter v1 fix, and equal to it when the series is empty. Later than the last key in rates when truncated is true."
    • changedOutput schema / properties / error / properties / data / properties / reason / description
      Previous value: -"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."New value: +"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 available in the current discovery or served historical data. `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 Frankfurter v1 published no rates across this range. `canvas_not_found`: An explicitly supplied canvas_id does not exist or has expired. `canvas_unavailable`: canvas_id requests staging on a deployment with DataCanvas disabled. Other values are possible when a failure originates below the handler."
    • changedOutput schema / properties / error / properties / data / properties / reason / examples
      Previous value: -[
      -  "invalid_date_format",
      -  "unsupported_currency",
      -  "date_out_of_range",
      -  "invalid_range",
      -  "upstream_no_data"
      -]New value: +[
      +  "invalid_date_format",
      +  "unsupported_currency",
      +  "date_out_of_range",
      +  "invalid_range",
      +  "upstream_no_data",
      +  "canvas_not_found",
      +  "canvas_unavailable"
      +]
    • changedOutput schema / properties / rate_type / description
      Previous value: -"Always \"ECB reference (mid-market)\" — these are reference rates, not tradeable bid/ask."New value: +"Always \"Daily rate (Frankfurter v1)\" — these are reference rates, not tradeable bid/ask."
    • changedOutput schema / properties / source / description
      Previous value: -"Always \"ECB via Frankfurter\" — the upstream data provider."New value: +"Always \"Frankfurter v1\" — the upstream data provider."
    • changedOutput schema / properties / spilled / description
      Previous value: -"True when the range exceeded the staging threshold and the series exceeded the inline preview budget, so the full result was staged on DataCanvas."New value: +"True when the complete result was staged on DataCanvas, either explicitly through canvas_id or automatically after exceeding the threshold and preview budget."
    • changedOutput schema / properties / start_date / description
      Previous 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."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 Frankfurter v1 fix, and equal to it when the series is empty."
    • addedOutput schema / properties / summary
      Added value: +{
      +  "additionalProperties": false,
      +  "description": "Statistics over the full clipped requested range before paging or staging; omitted for empty ranges. Continuations summarize their own requested interval.",
      +  "properties": {
      +    "change_pct": {
      +      "description": "Percentage change from first to last observation.",
      +      "type": "number"
      +    },
      +    "first": {
      +      "additionalProperties": false,
      +      "description": "Chronologically first observation.",
      +      "properties": {
      +        "date": {
      +          "description": "First publication date.",
      +          "type": "string"
      +        },
      +        "rate": {
      +          "description": "First rate.",
      +          "type": "number"
      +        }
      +      },
      +      "required": [
      +        "rate",
      +        "date"
      +      ],
      +      "type": "object"
      +    },
      +    "last": {
      +      "additionalProperties": false,
      +      "description": "Chronologically last observation.",
      +      "properties": {
      +        "date": {
      +          "description": "Last publication date.",
      +          "type": "string"
      +        },
      +        "rate": {
      +          "description": "Last rate.",
      +          "type": "number"
      +        }
      +      },
      +      "required": [
      +        "rate",
      +        "date"
      +      ],
      +      "type": "object"
      +    },
      +    "max": {
      +      "additionalProperties": false,
      +      "description": "Maximum observation.",
      +      "properties": {
      +        "date": {
      +          "description": "Earliest date with the highest rate.",
      +          "type": "string"
      +        },
      +        "rate": {
      +          "description": "Highest rate.",
      +          "type": "number"
      +        }
      +      },
      +      "required": [
      +        "rate",
      +        "date"
      +      ],
      +      "type": "object"
      +    },
      +    "mean": {
      +      "description": "Unweighted arithmetic mean of all daily observations in the requested range; not an accounting-policy prescription.",
      +      "type": "number"
      +    },
      +    "min": {
      +      "additionalProperties": false,
      +      "description": "Minimum observation.",
      +      "properties": {
      +        "date": {
      +          "description": "Earliest date with the lowest rate.",
      +          "type": "string"
      +        },
      +        "rate": {
      +          "description": "Lowest rate.",
      +          "type": "number"
      +        }
      +      },
      +      "required": [
      +        "rate",
      +        "date"
      +      ],
      +      "type": "object"
      +    }
      +  },
      +  "required": [
      +    "mean",
      +    "min",
      +    "max",
      +    "first",
      +    "last",
      +    "change_pct"
      +  ],
      +  "type": "object"
      +}
  2. Changed2 schema fields changed
    • changedOutput schema / properties / notice / description
      Previous 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)."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 turned off); or a spilled series (the canvas and table it was staged to, and the tools that read it)."
    • changedOutput schema / properties / spilled / description
      Previous value: -"True when the full result was staged on the DataCanvas (range exceeded threshold)."New value: +"True when the range exceeded the staging threshold and the series exceeded the inline preview budget, so the full result was staged on DataCanvas."
  3. Changed1 schema field changed
    • addedInput schema / properties / canvas_id / pattern
      Added value: +"^[A-Za-z0-9_-]{10}$"
  4. 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"
      +}
  5. 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"
      -]
  6. 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."
  7. First observed

TDQS

A4.2/5.0
Behavior5/5

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

Goes well beyond the readOnly/idempotent annotations with substantive semantics: same-date EUR legs without intermediate rounding, only days with both currencies returned, self-pair equals 1, retired currencies included, empty non-publication windows succeed with a notice, and the 40,000-character preview budget triggering staging. It also discloses failure behavior for an unknown canvas_id and that each registration creates a unique, preserved table_name.

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

Conciseness3/5

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

The essential scoping and paging facts are front-loaded in the first two sentences, which is good. However, the remainder is a dense wall of run-on clauses mixing edge cases, staging rules, and follow-up tool names, and lines like 'without prescribing accounting policy' are noise that slows an agent scanning for the call decision.

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 tool with a published output schema and full annotation coverage, this description covers the remaining burden: paging continuation, staging thresholds, spilled=true payload shape, and the required fx_dataframe_describe/fx_dataframe_query follow-up. An agent has everything needed to call it correctly in either mode.

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 coverage is 100%, so the baseline is 3, but the description adds real meaning on top: canvas_id always forces staging even for empty ranges and fails if unavailable, and end_date is reused unchanged when continuing with next_start_date. The summary and paging semantics further clarify what the parameters produce.

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, resource, and scope: historical daily Frankfurter v1 rates for a currency pair over an inclusive date range. That scope is concrete enough to distinguish it from fx_get_rate/fx_convert_currency by inference, but no sibling is named explicitly, so the agent has to reason about which tool is the right one.

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?

Gives clear operating context: page inline at 500 observations with next_start_date, let long ranges stage automatically, pass canvas_id only when staging is desired, and omit it entirely if no DataCanvas is available. It does not mention when to prefer fx_get_rate or fx_convert_currency instead, so the basic tool-selection decision is left implicit.

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.