@cyanheads/exchange-rates-mcp-server 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/AGENTS.md +53 -33
  2. package/CLAUDE.md +53 -33
  3. package/Dockerfile +9 -6
  4. package/LICENSE +1 -1
  5. package/README.md +24 -15
  6. package/changelog/0.1.x/0.1.5.md +29 -0
  7. package/changelog/0.2.x/0.2.0.md +41 -0
  8. package/changelog/template.md +5 -3
  9. package/dist/index.js +18 -4
  10. package/dist/index.js.map +1 -1
  11. package/dist/mcp-server/resources/definitions/fx-rates-latest.resource.d.ts.map +1 -1
  12. package/dist/mcp-server/resources/definitions/fx-rates-latest.resource.js +4 -3
  13. package/dist/mcp-server/resources/definitions/fx-rates-latest.resource.js.map +1 -1
  14. package/dist/mcp-server/tools/definitions/fx-convert-currency.tool.d.ts +10 -0
  15. package/dist/mcp-server/tools/definitions/fx-convert-currency.tool.d.ts.map +1 -1
  16. package/dist/mcp-server/tools/definitions/fx-convert-currency.tool.js +29 -5
  17. package/dist/mcp-server/tools/definitions/fx-convert-currency.tool.js.map +1 -1
  18. package/dist/mcp-server/tools/definitions/fx-dataframe-describe.tool.d.ts.map +1 -1
  19. package/dist/mcp-server/tools/definitions/fx-dataframe-describe.tool.js +7 -4
  20. package/dist/mcp-server/tools/definitions/fx-dataframe-describe.tool.js.map +1 -1
  21. package/dist/mcp-server/tools/definitions/fx-dataframe-query.tool.d.ts +6 -1
  22. package/dist/mcp-server/tools/definitions/fx-dataframe-query.tool.d.ts.map +1 -1
  23. package/dist/mcp-server/tools/definitions/fx-dataframe-query.tool.js +30 -10
  24. package/dist/mcp-server/tools/definitions/fx-dataframe-query.tool.js.map +1 -1
  25. package/dist/mcp-server/tools/definitions/fx-get-rate.tool.d.ts +10 -0
  26. package/dist/mcp-server/tools/definitions/fx-get-rate.tool.d.ts.map +1 -1
  27. package/dist/mcp-server/tools/definitions/fx-get-rate.tool.js +29 -5
  28. package/dist/mcp-server/tools/definitions/fx-get-rate.tool.js.map +1 -1
  29. package/dist/mcp-server/tools/definitions/fx-get-rates.tool.d.ts +11 -1
  30. package/dist/mcp-server/tools/definitions/fx-get-rates.tool.d.ts.map +1 -1
  31. package/dist/mcp-server/tools/definitions/fx-get-rates.tool.js +33 -7
  32. package/dist/mcp-server/tools/definitions/fx-get-rates.tool.js.map +1 -1
  33. package/dist/mcp-server/tools/definitions/fx-get-timeseries.tool.d.ts +13 -1
  34. package/dist/mcp-server/tools/definitions/fx-get-timeseries.tool.d.ts.map +1 -1
  35. package/dist/mcp-server/tools/definitions/fx-get-timeseries.tool.js +93 -44
  36. package/dist/mcp-server/tools/definitions/fx-get-timeseries.tool.js.map +1 -1
  37. package/dist/services/canvas/canvas-accessor.d.ts.map +1 -1
  38. package/dist/services/frankfurter/errors.d.ts +42 -0
  39. package/dist/services/frankfurter/errors.d.ts.map +1 -0
  40. package/dist/services/frankfurter/errors.js +64 -0
  41. package/dist/services/frankfurter/errors.js.map +1 -0
  42. package/dist/services/frankfurter/frankfurter-service.d.ts +42 -8
  43. package/dist/services/frankfurter/frankfurter-service.d.ts.map +1 -1
  44. package/dist/services/frankfurter/frankfurter-service.js +146 -67
  45. package/dist/services/frankfurter/frankfurter-service.js.map +1 -1
  46. package/dist/services/frankfurter/types.d.ts +14 -0
  47. package/dist/services/frankfurter/types.d.ts.map +1 -1
  48. package/package.json +12 -11
  49. package/server.json +5 -5
@@ -0,0 +1,29 @@
1
+ ---
2
+ summary: "Adopt @cyanheads/mcp-ts-core ^0.10.9; DataCanvas describe() filter and SQL-gate fixes reach fx_dataframe_* tools; new check-dependency-specifiers devcheck step + plugin-manifest packaging checks"
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 0.1.5 — 2026-06-20
8
+
9
+ Framework maintenance pass: `@cyanheads/mcp-ts-core` `^0.10.6` → `^0.10.9` plus dependency refresh.
10
+
11
+ ## Added
12
+
13
+ - **`check-dependency-specifiers` devcheck step** — vendored `scripts/check-dependency-specifiers.ts`, registered in the devcheck run (flag `--no-dep-specifiers`); hard-fails on floating specifiers (`latest`, `*`, pre-release dist-tags) in `package.json`'s dependency sections and `bun.lock`'s `workspaces` map. Catches a `bun update --latest` writing a literal `latest` dist-tag into the lock that would re-resolve past the manifest range on the next install. ([#246](https://github.com/cyanheads/mcp-ts-core/issues/246))
14
+ - **Plugin-manifest packaging checks** — `devcheck.config.json` gains `packaging.pluginManifests: true`, enabling `lint:packaging` to validate `.claude-plugin/plugin.json` and `.codex-plugin/plugin.json`/`mcp.json`: non-empty descriptions, unscoped machine name in display fields, and the full scoped package name in the `npx -y` install arg. ([#240](https://github.com/cyanheads/mcp-ts-core/issues/240))
15
+
16
+ ## Changed
17
+
18
+ - **DataCanvas describe() / SQL gate** — `fx_dataframe_describe` and `fx_dataframe_query` pick up two framework fixes: a `DuckdbProvider.describe({ tableName })` binder error on filtered table lookups is resolved ([#235](https://github.com/cyanheads/mcp-ts-core/issues/235)), and a `SELECT`-shaped query that fails to prepare (mistyped column, unknown function) now throws `ValidationError` with `reason: 'invalid_sql'` and the DuckDB binder detail, instead of the misleading `non_select_statement` ([#236](https://github.com/cyanheads/mcp-ts-core/issues/236)).
19
+ - **Fresh-scaffold devcheck guards** — `scripts/devcheck.ts`, `scripts/build-changelog.ts`, `scripts/check-framework-antipatterns.ts`, and `scripts/check-skill-versions.ts` re-synced with the framework's git-repo and worktree-deletion guards. ([#242](https://github.com/cyanheads/mcp-ts-core/issues/242), [#243](https://github.com/cyanheads/mcp-ts-core/issues/243), [#237](https://github.com/cyanheads/mcp-ts-core/issues/237))
20
+ - **Vendored skills** — 14 `skills/<name>/SKILL.md` re-synced to the installed framework version (`add-tool`, `api-auth`, `api-canvas`, `api-config`, `api-context`, `api-errors`, `api-services`, `api-telemetry`, `field-test`, `git-wrapup`, `orchestrations`, `polish-docs-meta`, `report-issue-local`, `tool-defs-analysis`).
21
+ - **`frankfurter-service.ts`** — outbound `User-Agent` string synced to the current version.
22
+
23
+ ### Dependencies
24
+
25
+ - `@cyanheads/mcp-ts-core` ^0.10.6 → ^0.10.9
26
+ - `@duckdb/node-api` ^1.5.3-r.3 → ^1.5.4-r.1
27
+ - `@biomejs/biome` ^2.4.16 → ^2.5.0
28
+ - `@types/node` ^25.9.3 → ^26.0.0
29
+ - `vitest` ^4.1.8 → ^4.1.9
@@ -0,0 +1,41 @@
1
+ ---
2
+ summary: "fx_convert_currency.amount rejects non-positive values; fx_get_timeseries clips to the requested range and handles same-currency pairs; DataCanvas tools gated off when unconfigured; mcp-ts-core ^0.11.0"
3
+ breaking: true
4
+ security: false
5
+ ---
6
+
7
+ # 0.2.0 — 2026-07-31
8
+
9
+ ## Added
10
+
11
+ - **`fx_get_timeseries` `notice` enrichment** — a success-path `ctx.enrich.notice()` explains an empty `rates` map (the range covers only non-publication days) or a long range that stayed inline because `CANVAS_PROVIDER_TYPE` isn't set, so a legitimate empty/inline result doesn't read as a failure.
12
+ - **`disabledTool()` gating** — `fx_dataframe_describe` and `fx_dataframe_query` are skipped from `tools/list` when DataCanvas isn't configured; they stay visible as disabled cards (hinting `CANVAS_PROVIDER_TYPE=duckdb`) on the HTML landing page and server manifest. ([#7](https://github.com/cyanheads/exchange-rates-mcp-server/issues/7))
13
+ - **Structured Frankfurter service errors** — `src/services/frankfurter/errors.ts` stamps `invalid_date_format`, `unsupported_currency`, and `upstream_no_data` reasons onto `McpError.data`; tool handlers read them via `failureOf()` instead of matching on error-message prose.
14
+ - **`fx_dataframe_query` `missing_table` error contract** — a query against a table not staged on the canvas (or evicted by TTL) now surfaces its own reason instead of falling through to `invalid_query`.
15
+
16
+ ## Changed
17
+
18
+ - **`fx_convert_currency.amount` rejects non-positive values** — schema gains `.positive()`; zero and negative amounts are now rejected at parse time instead of silently converting. **Breaking**: a client that relied on converting a zero or negative amount must change its input. ([#5](https://github.com/cyanheads/exchange-rates-mcp-server/issues/5))
19
+ - **Date validation runs before range/currency checks** — `fx_get_rate`, `fx_get_rates`, `fx_get_timeseries`, and `fx_convert_currency` validate `YYYY-MM-DD` shape and calendar validity (new `isIsoDate()`) before any lexicographic range comparison or upstream call, surfacing a new `invalid_date_format` reason instead of misclassifying a malformed date as `unsupported_currency` or `invalid_range`. ([#4](https://github.com/cyanheads/exchange-rates-mcp-server/issues/4))
20
+ - **Same-currency identity handling extended** — `fx_get_rate` (already identity-aware) now dates its `rate: 1` result to the ECB's real publication day for that currency, via a proxy quote, instead of always `today`; `fx_get_timeseries` and `fx_get_rates` gain the same identity path, returning `rate: 1` per publication day or for a base-in-`symbols` request instead of surfacing Frankfurter's upstream 422. ([#6](https://github.com/cyanheads/exchange-rates-mcp-server/issues/6))
21
+ - **`fx_get_rates` misattributed `unsupported_currency`** — an invalid entry in `symbols` now names the offending currency instead of always blaming `base_currency`. ([#8](https://github.com/cyanheads/exchange-rates-mcp-server/issues/8))
22
+ - **`fx_dataframe_query` cancelled queries** — an aborted query (`ctx.signal`) is no longer misclassified as `invalid_query`; it now propagates as the framework's own Timeout instead of matching the SQL-gate's error-message check.
23
+ - **Currency codes checked against a live, day-cached ECB set** (`FrankfurterService.currencies()`) before any request, replacing the old regex format guard — an unsupported code is now named specifically instead of surfacing a generic upstream 404.
24
+ - **Template-lag adoption** — `bunfig.toml` (Socket install scanner + a 3-day `minimumReleaseAge` supply-chain guard, framework excluded), `Dockerfile` (`--ignore-scripts`, `--omit=dev` on the OTel install layer, BuildKit cache mounts, pinned `oven/bun:1.3.14` base images), `.gitignore` (`data/` → `/data/`, root-scoped only), `devcheck.config.json` (scanner added to the depcheck ignore list; `outdated.allowlist` cleared), `.env.example` (documents `CANVAS_TEMP_PATH`), new `.gitattributes`, `.github/SECURITY.md`, `.github/FUNDING.yml`; 19 framework skill files re-synced to the installed `mcp-ts-core` version.
25
+
26
+ ## Fixed
27
+
28
+ - **`fx_get_timeseries` no longer returns dates outside the requested range** — Frankfurter's snap-to-prior-business-day on a weekend/holiday-opening range is now clipped: rows outside `[start_date, end_date]` are dropped, and a range covering only non-publication days returns an empty `rates` map with `rate_count: 0` plus an explanatory `notice` instead of a rate from before the requested window. ([#3](https://github.com/cyanheads/exchange-rates-mcp-server/issues/3))
29
+ - **`getTimeSeries()` no longer fabricates a `0` rate** for a day the quoted currency is missing from the upstream response — the row is dropped instead of reading as a real (and wrong) ECB rate.
30
+
31
+ ### Dependencies
32
+
33
+ - `@cyanheads/mcp-ts-core` ^0.10.9 → ^0.11.0
34
+ - `@duckdb/node-api` ^1.5.4-r.1 → ^1.5.5-r.1 (0.11.0 raised this optional peer's floor)
35
+ - `typescript` ^6.0.3 → ^7.0.2
36
+ - `@biomejs/biome` ^2.5.0 → 2.5.5 (pinned)
37
+ - `@types/node` ^26.0.0 → 26.1.1 (pinned)
38
+ - `tsc-alias` ^1.8.17 → ^1.9.1
39
+ - `ignore` ^7.0.5 → ^7.0.6
40
+ - `vitest` ^4.1.9 → ^4.1.10
41
+ - new dev dependency: `@socketsecurity/bun-security-scanner` ^1.1.2
@@ -15,9 +15,11 @@ summary: ""
15
15
  # usage. Flagged as `Breaking` in the rollup.
16
16
  breaking: false
17
17
 
18
- # Set `true` if this release contains any security fix. Pairs with the
19
- # `## Security` section below. Flagged as `Security` in the rollup so
20
- # users can triage upgrade urgency at a glance.
18
+ # Set `true` ONLY for a security fix in THIS project's own source code — a
19
+ # vulnerability or hardening in code you ship. A dependency or transitive CVE
20
+ # bump is routine maintenance, NOT a security release: record it under
21
+ # `## Dependencies` (with the advisory ID) and leave this `false`. When true,
22
+ # pairs with the `## Security` section below and flags `Security` in the rollup.
21
23
  security: false
22
24
 
23
25
  # Optional free-form notes for maintenance agents processing this release.
package/dist/index.js CHANGED
@@ -3,10 +3,23 @@
3
3
  * @fileoverview exchange-rates-mcp-server MCP server entry point.
4
4
  * @module index
5
5
  */
6
- import { createApp } from '@cyanheads/mcp-ts-core';
6
+ import { createApp, disabledTool } from '@cyanheads/mcp-ts-core';
7
+ import { config } from '@cyanheads/mcp-ts-core/config';
7
8
  import { fxCurrenciesResource, fxRatesLatestResource, } from './mcp-server/resources/definitions/index.js';
8
9
  import { fxConvertCurrency, fxDataframeDescribe, fxDataframeQuery, fxGetRate, fxGetRates, fxGetTimeseries, fxListCurrencies, } from './mcp-server/tools/definitions/index.js';
9
10
  import { setCanvas } from './services/canvas/canvas-accessor.js';
11
+ /**
12
+ * The dataframe tools are useless without a canvas to read — every call fails
13
+ * with "DataCanvas is not enabled", which is a worse client experience than not
14
+ * advertising them at all. Gate on the synchronously-parsed global config, not
15
+ * on `core.canvas` from `setup()`: this `tools` array is evaluated as part of
16
+ * the `createApp()` argument, so it is fully built before `setup()` ever runs.
17
+ */
18
+ const canvasEnabled = config.canvas.providerType !== 'none';
19
+ const canvasGate = {
20
+ hint: 'CANVAS_PROVIDER_TYPE=duckdb',
21
+ reason: 'DataCanvas is not configured here, so fx_get_timeseries never stages a table to query.',
22
+ };
10
23
  await createApp({
11
24
  name: 'exchange-rates-mcp-server',
12
25
  title: 'exchange-rates-mcp-server',
@@ -16,8 +29,8 @@ await createApp({
16
29
  fxGetRate,
17
30
  fxConvertCurrency,
18
31
  fxGetTimeseries,
19
- fxDataframeDescribe,
20
- fxDataframeQuery,
32
+ canvasEnabled ? fxDataframeDescribe : disabledTool(fxDataframeDescribe, canvasGate),
33
+ canvasEnabled ? fxDataframeQuery : disabledTool(fxDataframeQuery, canvasGate),
21
34
  ],
22
35
  resources: [fxCurrenciesResource, fxRatesLatestResource],
23
36
  prompts: [],
@@ -25,7 +38,8 @@ await createApp({
25
38
  '- Rates are mid-market ECB reference rates — not tradeable bid/ask.\n' +
26
39
  '- Use fx_list_currencies first to disambiguate "dollars" (USD/AUD/CAD/HKD/SGD).\n' +
27
40
  '- Cross-rates (e.g. USD→JPY) are triangulated through EUR automatically.\n' +
28
- '- Long time-series (>90 days) spill to DataCanvas; use fx_dataframe_query for SQL.',
41
+ '- Long time-series (>90 days) spill to DataCanvas when it is enabled (CANVAS_PROVIDER_TYPE=duckdb);\n' +
42
+ ' use fx_dataframe_query for SQL. Without it, long ranges return inline and the dataframe tools are not listed.',
29
43
  setup(core) {
30
44
  setCanvas(core.canvas);
31
45
  },
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EACL,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,6CAA6C,CAAC;AACrD,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,SAAS,EACT,UAAU,EACV,eAAe,EACf,gBAAgB,GACjB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,sCAAsC,CAAC;AAEjE,MAAM,SAAS,CAAC;IACd,IAAI,EAAE,2BAA2B;IACjC,KAAK,EAAE,2BAA2B;IAClC,KAAK,EAAE;QACL,gBAAgB;QAChB,UAAU;QACV,SAAS;QACT,iBAAiB;QACjB,eAAe;QACf,mBAAmB;QACnB,gBAAgB;KACjB;IACD,SAAS,EAAE,CAAC,oBAAoB,EAAE,qBAAqB,CAAC;IACxD,OAAO,EAAE,EAAE;IAEX,YAAY,EACV,4FAA4F;QAC5F,uEAAuE;QACvE,mFAAmF;QACnF,4EAA4E;QAC5E,oFAAoF;IAEtF,KAAK,CAAC,IAAI;QACR,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACzB,CAAC;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AACvD,OAAO,EACL,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,6CAA6C,CAAC;AACrD,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,SAAS,EACT,UAAU,EACV,eAAe,EACf,gBAAgB,GACjB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,sCAAsC,CAAC;AAEjE;;;;;;GAMG;AACH,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,YAAY,KAAK,MAAM,CAAC;AAC5D,MAAM,UAAU,GAAG;IACjB,IAAI,EAAE,6BAA6B;IACnC,MAAM,EAAE,wFAAwF;CACxF,CAAC;AAEX,MAAM,SAAS,CAAC;IACd,IAAI,EAAE,2BAA2B;IACjC,KAAK,EAAE,2BAA2B;IAClC,KAAK,EAAE;QACL,gBAAgB;QAChB,UAAU;QACV,SAAS;QACT,iBAAiB;QACjB,eAAe;QACf,aAAa,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,YAAY,CAAC,mBAAmB,EAAE,UAAU,CAAC;QACnF,aAAa,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,YAAY,CAAC,gBAAgB,EAAE,UAAU,CAAC;KAC9E;IACD,SAAS,EAAE,CAAC,oBAAoB,EAAE,qBAAqB,CAAC;IACxD,OAAO,EAAE,EAAE;IAEX,YAAY,EACV,4FAA4F;QAC5F,uEAAuE;QACvE,mFAAmF;QACnF,4EAA4E;QAC5E,uGAAuG;QACvG,iHAAiH;IAEnH,KAAK,CAAC,IAAI;QACR,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACzB,CAAC;CACF,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"fx-rates-latest.resource.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/fx-rates-latest.resource.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAY,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAIrD,eAAO,MAAM,qBAAqB;;;;;;;;6BA+DhC,CAAC"}
1
+ {"version":3,"file":"fx-rates-latest.resource.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/fx-rates-latest.resource.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAY,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAKrD,eAAO,MAAM,qBAAqB;;;;;;;;6BA+DhC,CAAC"}
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { resource, z } from '@cyanheads/mcp-ts-core';
6
6
  import { validationError } from '@cyanheads/mcp-ts-core/errors';
7
+ import { failureOf } from '../../../services/frankfurter/errors.js';
7
8
  import { getFrankfurterService } from '../../../services/frankfurter/frankfurter-service.js';
8
9
  export const fxRatesLatestResource = resource('fx://rates/latest/{base}', {
9
10
  name: 'fx-rates-latest',
@@ -31,9 +32,9 @@ export const fxRatesLatestResource = resource('fx://rates/latest/{base}', {
31
32
  raw = await service.getRates(params.base, 'latest');
32
33
  }
33
34
  catch (err) {
34
- const msg = err.message ?? '';
35
- if (msg.includes('not found')) {
36
- throw validationError(`Currency "${params.base}" is not supported by the ECB. Call fx_list_currencies to get valid codes.`, { base: params.base }, { cause: err });
35
+ const failure = failureOf(err);
36
+ if (failure?.reason === 'unsupported_currency') {
37
+ throw validationError(`${err.message} Call fx_list_currencies to get valid codes.`, { base: params.base, field: failure.field, reason: failure.reason }, { cause: err });
37
38
  }
38
39
  throw err;
39
40
  }
@@ -1 +1 @@
1
- {"version":3,"file":"fx-rates-latest.resource.js","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/fx-rates-latest.resource.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fx-rates-latest.resource.js","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/fx-rates-latest.resource.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,+CAA+C,CAAC;AAEtF,MAAM,CAAC,MAAM,qBAAqB,GAAG,QAAQ,CAAC,0BAA0B,EAAE;IACxE,IAAI,EAAE,iBAAiB;IACvB,WAAW,EACT,sEAAsE;QACtE,iFAAiF;IACnF,QAAQ,EAAE,kBAAkB;IAC5B,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,mDAAmD,CAAC;KAC/E,CAAC;IACF,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,yBAAyB,CAAC;QAC7D,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,2BAA2B,CAAC;QAC3D,KAAK,EAAE,CAAC;aACL,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;aAC9B,QAAQ,CACP,0FAA0F,CAC3F;QACH,SAAS,EAAE,CAAC;aACT,MAAM,EAAE;aACR,QAAQ,CACP,yFAAyF,CAC1F;QACH,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,4DAA4D,CAAC;KAC1F,CAAC;IAEF,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG;QACvB,MAAM,OAAO,GAAG,qBAAqB,EAAE,CAAC;QACxC,IAAI,GAAiD,CAAC;QACtD,IAAI,CAAC;YACH,GAAG,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACtD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;YAC/B,IAAI,OAAO,EAAE,MAAM,KAAK,sBAAsB,EAAE,CAAC;gBAC/C,MAAM,eAAe,CACnB,GAAI,GAAa,CAAC,OAAO,8CAA8C,EACvE,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,EACnE,EAAE,KAAK,EAAE,GAAG,EAAE,CACf,CAAC;YACJ,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;QACD,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,+BAA+B,EAAE;YAC5C,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,MAAM;SACrC,CAAC,CAAC;QACH,OAAO;YACL,aAAa,EAAE,GAAG,CAAC,IAAI;YACvB,SAAS,EAAE,GAAG,CAAC,IAAI;YACnB,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,SAAS,EAAE,4BAA4B;YACvC,MAAM,EAAE,qBAAqB;SAC9B,CAAC;IACJ,CAAC;IAED,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QACX,SAAS,EAAE;YACT,EAAE,GAAG,EAAE,uBAAuB,EAAE,IAAI,EAAE,kBAAkB,EAAE,QAAQ,EAAE,kBAAkB,EAAE;YACxF,EAAE,GAAG,EAAE,uBAAuB,EAAE,IAAI,EAAE,kBAAkB,EAAE,QAAQ,EAAE,kBAAkB,EAAE;YACxF,EAAE,GAAG,EAAE,uBAAuB,EAAE,IAAI,EAAE,kBAAkB,EAAE,QAAQ,EAAE,kBAAkB,EAAE;YACxF,EAAE,GAAG,EAAE,uBAAuB,EAAE,IAAI,EAAE,kBAAkB,EAAE,QAAQ,EAAE,kBAAkB,EAAE;SACzF;KACF,CAAC;CACH,CAAC,CAAC"}
@@ -20,6 +20,11 @@ export declare const fxConvertCurrency: import("@cyanheads/mcp-ts-core").ToolDef
20
20
  rate_type: z.ZodString;
21
21
  source: z.ZodString;
22
22
  }, z.core.$strip>, readonly [{
23
+ readonly reason: "invalid_date_format";
24
+ readonly code: JsonRpcErrorCode.ValidationError;
25
+ readonly when: "date is not a real calendar date written as YYYY-MM-DD.";
26
+ readonly recovery: "Pass an ISO 8601 calendar date in YYYY-MM-DD form, for example 2024-06-01.";
27
+ }, {
23
28
  readonly reason: "unsupported_currency";
24
29
  readonly code: JsonRpcErrorCode.ValidationError;
25
30
  readonly when: "base_currency or quote_currency is not in the ECB currency set.";
@@ -29,5 +34,10 @@ export declare const fxConvertCurrency: import("@cyanheads/mcp-ts-core").ToolDef
29
34
  readonly code: JsonRpcErrorCode.ValidationError;
30
35
  readonly when: "date is before 1999-01-04 or in the future.";
31
36
  readonly recovery: "ECB data starts 1999-01-04; omit date for the latest available rate.";
37
+ }, {
38
+ readonly reason: "upstream_no_data";
39
+ readonly code: JsonRpcErrorCode.NotFound;
40
+ readonly when: "Both currencies are supported but the ECB published no rate for this date.";
41
+ readonly recovery: "Try a more recent date — ECB history starts later for some currencies.";
32
42
  }], undefined>;
33
43
  //# sourceMappingURL=fx-convert-currency.tool.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fx-convert-currency.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/fx-convert-currency.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAOjE,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;cAsJ5B,CAAC"}
1
+ {"version":3,"file":"fx-convert-currency.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/fx-convert-currency.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AASjE,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAmL5B,CAAC"}
@@ -4,7 +4,8 @@
4
4
  */
5
5
  import { tool, z } from '@cyanheads/mcp-ts-core';
6
6
  import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
7
- import { ECB_START_DATE, getFrankfurterService, } from '../../../services/frankfurter/frankfurter-service.js';
7
+ import { failureOf } from '../../../services/frankfurter/errors.js';
8
+ import { ECB_START_DATE, getFrankfurterService, isIsoDate, } from '../../../services/frankfurter/frankfurter-service.js';
8
9
  export const fxConvertCurrency = tool('fx_convert_currency', {
9
10
  description: 'Convert an amount between any two currencies at the latest or a historical rate. ' +
10
11
  'Returns the converted amount, the rate used, the actual rate date, and whether the ' +
@@ -24,7 +25,8 @@ export const fxConvertCurrency = tool('fx_convert_currency', {
24
25
  .describe('ISO 4217 target currency code (e.g. EUR). The amount will be expressed in this currency.'),
25
26
  amount: z
26
27
  .number()
27
- .describe('Amount in the base currency to convert. Must be a positive number.'),
28
+ .positive()
29
+ .describe('Amount in the base currency to convert. Must be greater than zero.'),
28
30
  date: z
29
31
  .string()
30
32
  .optional()
@@ -52,6 +54,12 @@ export const fxConvertCurrency = tool('fx_convert_currency', {
52
54
  source: z.string().describe('Always "ECB via Frankfurter" — the upstream data provider.'),
53
55
  }),
54
56
  errors: [
57
+ {
58
+ reason: 'invalid_date_format',
59
+ code: JsonRpcErrorCode.ValidationError,
60
+ when: 'date is not a real calendar date written as YYYY-MM-DD.',
61
+ recovery: 'Pass an ISO 8601 calendar date in YYYY-MM-DD form, for example 2024-06-01.',
62
+ },
55
63
  {
56
64
  reason: 'unsupported_currency',
57
65
  code: JsonRpcErrorCode.ValidationError,
@@ -64,11 +72,21 @@ export const fxConvertCurrency = tool('fx_convert_currency', {
64
72
  when: 'date is before 1999-01-04 or in the future.',
65
73
  recovery: 'ECB data starts 1999-01-04; omit date for the latest available rate.',
66
74
  },
75
+ {
76
+ reason: 'upstream_no_data',
77
+ code: JsonRpcErrorCode.NotFound,
78
+ when: 'Both currencies are supported but the ECB published no rate for this date.',
79
+ recovery: 'Try a more recent date — ECB history starts later for some currencies.',
80
+ },
67
81
  ],
68
82
  async handler(input, ctx) {
69
83
  const service = getFrankfurterService();
70
84
  const date = input.date ?? 'latest';
71
85
  if (date !== 'latest') {
86
+ /** Format first — the range comparisons below are lexicographic and would misread a malformed date. */
87
+ if (!isIsoDate(date)) {
88
+ throw ctx.fail('invalid_date_format', `date "${date}" is not a valid YYYY-MM-DD calendar date.`, { ...ctx.recoveryFor('invalid_date_format'), field: 'date' });
89
+ }
72
90
  if (date < ECB_START_DATE) {
73
91
  throw ctx.fail('date_out_of_range', `Date ${date} is before ECB data start ${ECB_START_DATE}.`, {
74
92
  ...ctx.recoveryFor('date_out_of_range'),
@@ -86,9 +104,15 @@ export const fxConvertCurrency = tool('fx_convert_currency', {
86
104
  resolved = await service.getRate(input.base_currency, input.quote_currency, date);
87
105
  }
88
106
  catch (err) {
89
- const msg = err.message ?? '';
90
- if (msg.includes('not found')) {
91
- throw ctx.fail('unsupported_currency', `Currency "${input.base_currency}" or "${input.quote_currency}" is not supported by the ECB.`, { ...ctx.recoveryFor('unsupported_currency') });
107
+ const failure = failureOf(err);
108
+ if (failure?.reason === 'unsupported_currency') {
109
+ throw ctx.fail('unsupported_currency', err.message, {
110
+ ...ctx.recoveryFor('unsupported_currency'),
111
+ field: failure.field,
112
+ });
113
+ }
114
+ if (failure?.reason === 'upstream_no_data') {
115
+ throw ctx.fail('upstream_no_data', `The ECB published no ${input.base_currency}/${input.quote_currency} rate for ${date === 'latest' ? 'the latest business day' : date}.`, { ...ctx.recoveryFor('upstream_no_data') });
92
116
  }
93
117
  throw err;
94
118
  }
@@ -1 +1 @@
1
- {"version":3,"file":"fx-convert-currency.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/fx-convert-currency.tool.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fx-convert-currency.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/fx-convert-currency.tool.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"fx-dataframe-describe.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/fx-dataframe-describe.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAEjD,OAAO,EAAE,gBAAgB,EAAsB,MAAM,+BAA+B,CAAC;AAGrF,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;cAkH9B,CAAC"}
1
+ {"version":3,"file":"fx-dataframe-describe.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/fx-dataframe-describe.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAEjD,OAAO,EAAE,gBAAgB,EAAgC,MAAM,+BAA+B,CAAC;AAG/F,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;cAsH9B,CAAC"}
@@ -3,11 +3,13 @@
3
3
  * @module mcp-server/tools/definitions/fx-dataframe-describe.tool
4
4
  */
5
5
  import { tool, z } from '@cyanheads/mcp-ts-core';
6
- import { JsonRpcErrorCode, serviceUnavailable } from '@cyanheads/mcp-ts-core/errors';
6
+ import { JsonRpcErrorCode, McpError, serviceUnavailable } from '@cyanheads/mcp-ts-core/errors';
7
7
  import { getCanvas } from '../../../services/canvas/canvas-accessor.js';
8
8
  export const fxDataframeDescribe = tool('fx_dataframe_describe', {
9
9
  description: 'List tables and columns staged on a DataCanvas from a prior fx_get_timeseries call. ' +
10
- 'Required first step before fx_dataframe_query — use it to discover table names and column schemas.',
10
+ 'Required first step before fx_dataframe_query — use it to discover table names and column schemas. ' +
11
+ 'Requires DataCanvas (CANVAS_PROVIDER_TYPE=duckdb) — without it this tool is not listed at all ' +
12
+ 'and fx_get_timeseries returns every range inline.',
11
13
  annotations: {
12
14
  readOnlyHint: true,
13
15
  idempotentHint: true,
@@ -59,8 +61,9 @@ export const fxDataframeDescribe = tool('fx_dataframe_describe', {
59
61
  instance = await canvas.acquire(input.canvas_id, ctx);
60
62
  }
61
63
  catch (err) {
62
- const msg = err.message ?? '';
63
- if (msg.includes('not found') || msg.includes('NotFound')) {
64
+ /** The canvas layer stamps a contract `reason` on its own throws — read that, don't match prose. */
65
+ const reason = err instanceof McpError ? err.data?.reason : undefined;
66
+ if (reason === 'canvas_not_found') {
64
67
  throw ctx.fail('canvas_not_found', `Canvas "${input.canvas_id}" not found or has expired.`, {
65
68
  ...ctx.recoveryFor('canvas_not_found'),
66
69
  });
@@ -1 +1 @@
1
- {"version":3,"file":"fx-dataframe-describe.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/fx-dataframe-describe.tool.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fx-dataframe-describe.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/fx-dataframe-describe.tool.ts"],"names":[],"mappings":"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"}
@@ -17,10 +17,15 @@ export declare const fxDataframeQuery: import("@cyanheads/mcp-ts-core").ToolDefi
17
17
  readonly code: JsonRpcErrorCode.NotFound;
18
18
  readonly when: "canvas_id does not exist or has been evicted.";
19
19
  readonly recovery: "Re-run fx_get_timeseries to obtain a fresh canvas_id.";
20
+ }, {
21
+ readonly reason: "missing_table";
22
+ readonly code: JsonRpcErrorCode.NotFound;
23
+ readonly when: "The SQL references a table that is not staged on this canvas, or whose TTL expired.";
24
+ readonly recovery: "Call fx_dataframe_describe to list the staged tables, or re-run fx_get_timeseries.";
20
25
  }, {
21
26
  readonly reason: "invalid_query";
22
27
  readonly code: JsonRpcErrorCode.ValidationError;
23
- readonly when: "SQL is not a SELECT, references unknown tables/columns, or has a syntax error.";
28
+ readonly when: "SQL is not a SELECT, references unknown columns, or has a syntax error.";
24
29
  readonly recovery: "Run fx_dataframe_describe first to verify table and column names.";
25
30
  }], undefined>;
26
31
  //# sourceMappingURL=fx-dataframe-query.tool.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fx-dataframe-query.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/fx-dataframe-query.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAEjD,OAAO,EAAE,gBAAgB,EAAsB,MAAM,+BAA+B,CAAC;AAGrF,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;cA+J3B,CAAC"}
1
+ {"version":3,"file":"fx-dataframe-query.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/fx-dataframe-query.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAEjD,OAAO,EAAE,gBAAgB,EAAgC,MAAM,+BAA+B,CAAC;AAG/F,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;cAyL3B,CAAC"}
@@ -3,12 +3,14 @@
3
3
  * @module mcp-server/tools/definitions/fx-dataframe-query.tool
4
4
  */
5
5
  import { tool, z } from '@cyanheads/mcp-ts-core';
6
- import { JsonRpcErrorCode, serviceUnavailable } from '@cyanheads/mcp-ts-core/errors';
6
+ import { JsonRpcErrorCode, McpError, serviceUnavailable } from '@cyanheads/mcp-ts-core/errors';
7
7
  import { getCanvas } from '../../../services/canvas/canvas-accessor.js';
8
8
  export const fxDataframeQuery = tool('fx_dataframe_query', {
9
9
  description: 'Run a read-only SQL SELECT against DataCanvas tables staged by fx_get_timeseries. ' +
10
10
  'Supports aggregations, GROUP BY, window functions, and JOINs across multiple registered tables. ' +
11
- 'Run fx_dataframe_describe first to discover table names and column schemas.',
11
+ 'Run fx_dataframe_describe first to discover table names and column schemas. ' +
12
+ 'Requires DataCanvas (CANVAS_PROVIDER_TYPE=duckdb) — without it this tool is not listed at all ' +
13
+ 'and fx_get_timeseries returns every range inline.',
12
14
  annotations: {
13
15
  readOnlyHint: true,
14
16
  idempotentHint: true,
@@ -50,10 +52,16 @@ export const fxDataframeQuery = tool('fx_dataframe_query', {
50
52
  when: 'canvas_id does not exist or has been evicted.',
51
53
  recovery: 'Re-run fx_get_timeseries to obtain a fresh canvas_id.',
52
54
  },
55
+ {
56
+ reason: 'missing_table',
57
+ code: JsonRpcErrorCode.NotFound,
58
+ when: 'The SQL references a table that is not staged on this canvas, or whose TTL expired.',
59
+ recovery: 'Call fx_dataframe_describe to list the staged tables, or re-run fx_get_timeseries.',
60
+ },
53
61
  {
54
62
  reason: 'invalid_query',
55
63
  code: JsonRpcErrorCode.ValidationError,
56
- when: 'SQL is not a SELECT, references unknown tables/columns, or has a syntax error.',
64
+ when: 'SQL is not a SELECT, references unknown columns, or has a syntax error.',
57
65
  recovery: 'Run fx_dataframe_describe first to verify table and column names.',
58
66
  },
59
67
  ],
@@ -62,13 +70,14 @@ export const fxDataframeQuery = tool('fx_dataframe_query', {
62
70
  if (!canvas) {
63
71
  throw serviceUnavailable('DataCanvas is not enabled. Set CANVAS_PROVIDER_TYPE=duckdb.');
64
72
  }
73
+ /** The canvas layer stamps a contract `reason` on its own throws — read that, don't match prose. */
74
+ const reasonOf = (err) => err instanceof McpError ? err.data?.reason : undefined;
65
75
  let instance;
66
76
  try {
67
77
  instance = await canvas.acquire(input.canvas_id, ctx);
68
78
  }
69
79
  catch (err) {
70
- const msg = err.message ?? '';
71
- if (msg.includes('not found') || msg.includes('NotFound')) {
80
+ if (reasonOf(err) === 'canvas_not_found') {
72
81
  throw ctx.fail('canvas_not_found', `Canvas "${input.canvas_id}" not found or has expired.`, {
73
82
  ...ctx.recoveryFor('canvas_not_found'),
74
83
  });
@@ -80,15 +89,26 @@ export const fxDataframeQuery = tool('fx_dataframe_query', {
80
89
  result = await instance.query(input.query, { signal: ctx.signal });
81
90
  }
82
91
  catch (err) {
92
+ const reason = reasonOf(err);
93
+ if (reason === 'canvas_not_found') {
94
+ throw ctx.fail('canvas_not_found', `Canvas "${input.canvas_id}" not found or has expired.`, {
95
+ ...ctx.recoveryFor('canvas_not_found'),
96
+ });
97
+ }
98
+ if (reason === 'missing_table') {
99
+ throw ctx.fail('missing_table', err.message, {
100
+ ...ctx.recoveryFor('missing_table'),
101
+ });
102
+ }
103
+ /** An aborted query is a Timeout, not a rejected statement — don't blame the caller's SQL for it. */
104
+ if (reason === 'cancelled')
105
+ throw err;
83
106
  const msg = err.message ?? '';
84
- // ValidationError from the four-layer gate, or DuckDB parse errors
85
- if (msg.includes('ValidationError') ||
107
+ // Remaining SQL-gate rejections: non-SELECT statements, binder and syntax errors.
108
+ if (reason !== undefined ||
86
109
  msg.includes('SELECT') ||
87
110
  msg.includes('syntax') ||
88
- msg.includes('not found') ||
89
111
  msg.includes('does not exist') ||
90
- msg.includes('read_csv') ||
91
- msg.includes('register_as_clash') ||
92
112
  err.code === 'ValidationError') {
93
113
  throw ctx.fail('invalid_query', `SQL query rejected: ${msg}`, {
94
114
  ...ctx.recoveryFor('invalid_query'),
@@ -1 +1 @@
1
- {"version":3,"file":"fx-dataframe-query.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/fx-dataframe-query.tool.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fx-dataframe-query.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/fx-dataframe-query.tool.ts"],"names":[],"mappings":"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"}
@@ -17,6 +17,11 @@ export declare const fxGetRate: import("@cyanheads/mcp-ts-core").ToolDefinition<
17
17
  rate_type: z.ZodString;
18
18
  source: z.ZodString;
19
19
  }, z.core.$strip>, readonly [{
20
+ readonly reason: "invalid_date_format";
21
+ readonly code: JsonRpcErrorCode.ValidationError;
22
+ readonly when: "date is not a real calendar date written as YYYY-MM-DD.";
23
+ readonly recovery: "Pass an ISO 8601 calendar date in YYYY-MM-DD form, for example 2024-06-01.";
24
+ }, {
20
25
  readonly reason: "unsupported_currency";
21
26
  readonly code: JsonRpcErrorCode.ValidationError;
22
27
  readonly when: "base_currency or quote_currency is not in the ECB currency set.";
@@ -26,5 +31,10 @@ export declare const fxGetRate: import("@cyanheads/mcp-ts-core").ToolDefinition<
26
31
  readonly code: JsonRpcErrorCode.ValidationError;
27
32
  readonly when: "date is before 1999-01-04 or in the future.";
28
33
  readonly recovery: "ECB data starts 1999-01-04; omit date for the latest available rate.";
34
+ }, {
35
+ readonly reason: "upstream_no_data";
36
+ readonly code: JsonRpcErrorCode.NotFound;
37
+ readonly when: "Both currencies are supported but the ECB published no rate for this date.";
38
+ readonly recovery: "Try a more recent date — ECB history starts later for some currencies.";
29
39
  }], undefined>;
30
40
  //# sourceMappingURL=fx-get-rate.tool.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fx-get-rate.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/fx-get-rate.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAOjE,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;cA0IpB,CAAC"}
1
+ {"version":3,"file":"fx-get-rate.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/fx-get-rate.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AASjE,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAuKpB,CAAC"}
@@ -4,12 +4,14 @@
4
4
  */
5
5
  import { tool, z } from '@cyanheads/mcp-ts-core';
6
6
  import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
7
- import { ECB_START_DATE, getFrankfurterService, } from '../../../services/frankfurter/frankfurter-service.js';
7
+ import { failureOf } from '../../../services/frankfurter/errors.js';
8
+ import { ECB_START_DATE, getFrankfurterService, isIsoDate, } from '../../../services/frankfurter/frankfurter-service.js';
8
9
  export const fxGetRate = tool('fx_get_rate', {
9
10
  description: 'Get the exchange rate for a currency pair on a given date (default: latest). ' +
10
11
  'Returns the rate, the actual rate date (which may differ from the requested date on ' +
11
12
  'weekends/holidays — ECB publishes business days only), and source provenance. ' +
12
- 'Cross-rates are triangulated through EUR automatically. ' +
13
+ 'Cross-rates are triangulated through EUR automatically. A same-currency pair returns a rate of 1, ' +
14
+ 'dated to the same publication day any other pair would report for that date. ' +
13
15
  'Use fx_convert_currency when you want the converted amount; use this tool when you only need the rate number.',
14
16
  annotations: {
15
17
  readOnlyHint: true,
@@ -46,6 +48,12 @@ export const fxGetRate = tool('fx_get_rate', {
46
48
  source: z.string().describe('Always "ECB via Frankfurter" — the upstream data provider.'),
47
49
  }),
48
50
  errors: [
51
+ {
52
+ reason: 'invalid_date_format',
53
+ code: JsonRpcErrorCode.ValidationError,
54
+ when: 'date is not a real calendar date written as YYYY-MM-DD.',
55
+ recovery: 'Pass an ISO 8601 calendar date in YYYY-MM-DD form, for example 2024-06-01.',
56
+ },
49
57
  {
50
58
  reason: 'unsupported_currency',
51
59
  code: JsonRpcErrorCode.ValidationError,
@@ -58,11 +66,21 @@ export const fxGetRate = tool('fx_get_rate', {
58
66
  when: 'date is before 1999-01-04 or in the future.',
59
67
  recovery: 'ECB data starts 1999-01-04; omit date for the latest available rate.',
60
68
  },
69
+ {
70
+ reason: 'upstream_no_data',
71
+ code: JsonRpcErrorCode.NotFound,
72
+ when: 'Both currencies are supported but the ECB published no rate for this date.',
73
+ recovery: 'Try a more recent date — ECB history starts later for some currencies.',
74
+ },
61
75
  ],
62
76
  async handler(input, ctx) {
63
77
  const service = getFrankfurterService();
64
78
  const date = input.date ?? 'latest';
65
79
  if (date !== 'latest') {
80
+ /** Format first — the range comparisons below are lexicographic and would misread a malformed date. */
81
+ if (!isIsoDate(date)) {
82
+ throw ctx.fail('invalid_date_format', `date "${date}" is not a valid YYYY-MM-DD calendar date.`, { ...ctx.recoveryFor('invalid_date_format'), field: 'date' });
83
+ }
66
84
  if (date < ECB_START_DATE) {
67
85
  throw ctx.fail('date_out_of_range', `Date ${date} is before ECB data start ${ECB_START_DATE}.`, {
68
86
  ...ctx.recoveryFor('date_out_of_range'),
@@ -80,9 +98,15 @@ export const fxGetRate = tool('fx_get_rate', {
80
98
  resolved = await service.getRate(input.base_currency, input.quote_currency, date);
81
99
  }
82
100
  catch (err) {
83
- const msg = err.message ?? '';
84
- if (msg.includes('not found')) {
85
- throw ctx.fail('unsupported_currency', `Currency pair "${input.base_currency}/${input.quote_currency}" not available from ECB.`, { ...ctx.recoveryFor('unsupported_currency') });
101
+ const failure = failureOf(err);
102
+ if (failure?.reason === 'unsupported_currency') {
103
+ throw ctx.fail('unsupported_currency', err.message, {
104
+ ...ctx.recoveryFor('unsupported_currency'),
105
+ field: failure.field,
106
+ });
107
+ }
108
+ if (failure?.reason === 'upstream_no_data') {
109
+ throw ctx.fail('upstream_no_data', `The ECB published no ${input.base_currency}/${input.quote_currency} rate for ${date === 'latest' ? 'the latest business day' : date}.`, { ...ctx.recoveryFor('upstream_no_data') });
86
110
  }
87
111
  throw err;
88
112
  }