@cyanheads/exchange-rates-mcp-server 0.1.1
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.
- package/AGENTS.md +392 -0
- package/CLAUDE.md +392 -0
- package/Dockerfile +105 -0
- package/LICENSE +201 -0
- package/README.md +340 -0
- package/changelog/0.1.x/0.1.1.md +32 -0
- package/changelog/template.md +127 -0
- package/dist/config/server-config.d.ts +13 -0
- package/dist/config/server-config.d.ts.map +1 -0
- package/dist/config/server-config.js +27 -0
- package/dist/config/server-config.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-server/prompts/definitions/index.d.ts +6 -0
- package/dist/mcp-server/prompts/definitions/index.d.ts.map +1 -0
- package/dist/mcp-server/prompts/definitions/index.js +6 -0
- package/dist/mcp-server/prompts/definitions/index.js.map +1 -0
- package/dist/mcp-server/resources/definitions/fx-currencies.resource.d.ts +14 -0
- package/dist/mcp-server/resources/definitions/fx-currencies.resource.d.ts.map +1 -0
- package/dist/mcp-server/resources/definitions/fx-currencies.resource.js +43 -0
- package/dist/mcp-server/resources/definitions/fx-currencies.resource.js.map +1 -0
- package/dist/mcp-server/resources/definitions/fx-rates-latest.resource.d.ts +15 -0
- package/dist/mcp-server/resources/definitions/fx-rates-latest.resource.d.ts.map +1 -0
- package/dist/mcp-server/resources/definitions/fx-rates-latest.resource.js +61 -0
- package/dist/mcp-server/resources/definitions/fx-rates-latest.resource.js.map +1 -0
- package/dist/mcp-server/resources/definitions/index.d.ts +7 -0
- package/dist/mcp-server/resources/definitions/index.d.ts.map +1 -0
- package/dist/mcp-server/resources/definitions/index.js +7 -0
- package/dist/mcp-server/resources/definitions/index.js.map +1 -0
- package/dist/mcp-server/tools/definitions/fx-convert-currency.tool.d.ts +33 -0
- package/dist/mcp-server/tools/definitions/fx-convert-currency.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/fx-convert-currency.tool.js +133 -0
- package/dist/mcp-server/tools/definitions/fx-convert-currency.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/fx-dataframe-describe.tool.d.ts +28 -0
- package/dist/mcp-server/tools/definitions/fx-dataframe-describe.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/fx-dataframe-describe.tool.js +105 -0
- package/dist/mcp-server/tools/definitions/fx-dataframe-describe.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/fx-dataframe-query.tool.d.ts +25 -0
- package/dist/mcp-server/tools/definitions/fx-dataframe-query.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/fx-dataframe-query.tool.js +125 -0
- package/dist/mcp-server/tools/definitions/fx-dataframe-query.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/fx-get-rate.tool.d.ts +30 -0
- package/dist/mcp-server/tools/definitions/fx-get-rate.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/fx-get-rate.tool.js +121 -0
- package/dist/mcp-server/tools/definitions/fx-get-rate.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/fx-get-rates.tool.d.ts +28 -0
- package/dist/mcp-server/tools/definitions/fx-get-rates.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/fx-get-rates.tool.js +115 -0
- package/dist/mcp-server/tools/definitions/fx-get-rates.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/fx-get-timeseries.tool.d.ts +41 -0
- package/dist/mcp-server/tools/definitions/fx-get-timeseries.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/fx-get-timeseries.tool.js +229 -0
- package/dist/mcp-server/tools/definitions/fx-get-timeseries.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/fx-list-currencies.tool.d.ts +14 -0
- package/dist/mcp-server/tools/definitions/fx-list-currencies.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/fx-list-currencies.tool.js +47 -0
- package/dist/mcp-server/tools/definitions/fx-list-currencies.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/index.d.ts +12 -0
- package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/index.js +12 -0
- package/dist/mcp-server/tools/definitions/index.js.map +1 -0
- package/dist/services/canvas/canvas-accessor.d.ts +10 -0
- package/dist/services/canvas/canvas-accessor.d.ts.map +1 -0
- package/dist/services/canvas/canvas-accessor.js +12 -0
- package/dist/services/canvas/canvas-accessor.js.map +1 -0
- package/dist/services/frankfurter/frankfurter-service.d.ts +46 -0
- package/dist/services/frankfurter/frankfurter-service.d.ts.map +1 -0
- package/dist/services/frankfurter/frankfurter-service.js +181 -0
- package/dist/services/frankfurter/frankfurter-service.js.map +1 -0
- package/dist/services/frankfurter/types.d.ts +59 -0
- package/dist/services/frankfurter/types.d.ts.map +1 -0
- package/dist/services/frankfurter/types.js +6 -0
- package/dist/services/frankfurter/types.js.map +1 -0
- package/package.json +104 -0
- package/server.json +127 -0
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview fx_dataframe_describe — list tables and columns on a DataCanvas.
|
|
3
|
+
* @module mcp-server/tools/definitions/fx-dataframe-describe.tool
|
|
4
|
+
*/
|
|
5
|
+
import { tool, z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
import { JsonRpcErrorCode, serviceUnavailable } from '@cyanheads/mcp-ts-core/errors';
|
|
7
|
+
import { getCanvas } from '../../../services/canvas/canvas-accessor.js';
|
|
8
|
+
export const fxDataframeDescribe = tool('fx_dataframe_describe', {
|
|
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.',
|
|
11
|
+
annotations: {
|
|
12
|
+
readOnlyHint: true,
|
|
13
|
+
idempotentHint: true,
|
|
14
|
+
openWorldHint: false,
|
|
15
|
+
},
|
|
16
|
+
input: z.object({
|
|
17
|
+
canvas_id: z
|
|
18
|
+
.string()
|
|
19
|
+
.describe('Canvas ID returned by fx_get_timeseries. ' +
|
|
20
|
+
'Re-run fx_get_timeseries to obtain a fresh canvas_id if this one has expired.'),
|
|
21
|
+
}),
|
|
22
|
+
output: z.object({
|
|
23
|
+
canvas_id: z.string().describe('The canvas ID echoed back — use this in fx_dataframe_query.'),
|
|
24
|
+
tables: z
|
|
25
|
+
.array(z
|
|
26
|
+
.object({
|
|
27
|
+
name: z.string().describe('Table or view name.'),
|
|
28
|
+
kind: z.string().describe('Either "table" or "view".'),
|
|
29
|
+
row_count: z.number().describe('Number of rows in this table or view.'),
|
|
30
|
+
columns: z
|
|
31
|
+
.array(z
|
|
32
|
+
.object({
|
|
33
|
+
name: z.string().describe('Column name.'),
|
|
34
|
+
type: z.string().describe('DuckDB column type (e.g. VARCHAR, DOUBLE, BIGINT).'),
|
|
35
|
+
nullable: z.boolean().describe('Whether the column accepts NULL values.'),
|
|
36
|
+
})
|
|
37
|
+
.describe('A single column descriptor.'))
|
|
38
|
+
.describe('Column schema for this table.'),
|
|
39
|
+
})
|
|
40
|
+
.describe('A single table or view staged on the canvas.'))
|
|
41
|
+
.describe('All tables and views currently staged on this canvas.'),
|
|
42
|
+
expires_at: z.string().describe('ISO 8601 timestamp when this canvas will be evicted.'),
|
|
43
|
+
}),
|
|
44
|
+
errors: [
|
|
45
|
+
{
|
|
46
|
+
reason: 'canvas_not_found',
|
|
47
|
+
code: JsonRpcErrorCode.NotFound,
|
|
48
|
+
when: 'canvas_id does not exist or has been evicted.',
|
|
49
|
+
recovery: 'Re-run fx_get_timeseries to obtain a fresh canvas_id.',
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
async handler(input, ctx) {
|
|
53
|
+
const canvas = getCanvas();
|
|
54
|
+
if (!canvas) {
|
|
55
|
+
throw serviceUnavailable('DataCanvas is not enabled. Set CANVAS_PROVIDER_TYPE=duckdb.');
|
|
56
|
+
}
|
|
57
|
+
let instance;
|
|
58
|
+
try {
|
|
59
|
+
instance = await canvas.acquire(input.canvas_id, ctx);
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
const msg = err.message ?? '';
|
|
63
|
+
if (msg.includes('not found') || msg.includes('NotFound')) {
|
|
64
|
+
throw ctx.fail('canvas_not_found', `Canvas "${input.canvas_id}" not found or has expired.`, {
|
|
65
|
+
...ctx.recoveryFor('canvas_not_found'),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
throw err;
|
|
69
|
+
}
|
|
70
|
+
const tableInfos = await instance.describe();
|
|
71
|
+
ctx.log.info('Described canvas', {
|
|
72
|
+
canvasId: input.canvas_id,
|
|
73
|
+
tableCount: tableInfos.length,
|
|
74
|
+
});
|
|
75
|
+
const tables = tableInfos.map((t) => ({
|
|
76
|
+
name: t.name,
|
|
77
|
+
kind: t.kind,
|
|
78
|
+
row_count: t.rowCount,
|
|
79
|
+
columns: t.columns.map((c) => ({
|
|
80
|
+
name: c.name,
|
|
81
|
+
type: c.type,
|
|
82
|
+
nullable: c.nullable ?? false,
|
|
83
|
+
})),
|
|
84
|
+
}));
|
|
85
|
+
return {
|
|
86
|
+
canvas_id: input.canvas_id,
|
|
87
|
+
tables,
|
|
88
|
+
expires_at: instance.expiresAt,
|
|
89
|
+
};
|
|
90
|
+
},
|
|
91
|
+
format: (result) => {
|
|
92
|
+
if (result.tables.length === 0) {
|
|
93
|
+
return [{ type: 'text', text: `Canvas \`${result.canvas_id}\` has no staged tables.` }];
|
|
94
|
+
}
|
|
95
|
+
const lines = [`**Canvas \`${result.canvas_id}\`** — expires ${result.expires_at}\n`];
|
|
96
|
+
for (const t of result.tables) {
|
|
97
|
+
lines.push(`### ${t.name} (${t.kind}, ${t.row_count} rows)`);
|
|
98
|
+
for (const col of t.columns) {
|
|
99
|
+
lines.push(`- \`${col.name}\` ${col.type}${col.nullable ? ' (nullable)' : ''}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return [{ type: 'text', text: lines.join('\n') }];
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
//# sourceMappingURL=fx-dataframe-describe.tool.js.map
|
|
@@ -0,0 +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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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview fx_dataframe_query — run SQL against DataCanvas tables from fx_get_timeseries.
|
|
3
|
+
* @module mcp-server/tools/definitions/fx-dataframe-query.tool
|
|
4
|
+
*/
|
|
5
|
+
import { z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
7
|
+
export declare const fxDataframeQuery: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
|
|
8
|
+
canvas_id: z.ZodString;
|
|
9
|
+
query: z.ZodString;
|
|
10
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
11
|
+
rows: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
12
|
+
row_count: z.ZodNumber;
|
|
13
|
+
canvas_id: z.ZodString;
|
|
14
|
+
}, z.core.$strip>, readonly [{
|
|
15
|
+
readonly reason: "canvas_not_found";
|
|
16
|
+
readonly code: JsonRpcErrorCode.NotFound;
|
|
17
|
+
readonly when: "canvas_id does not exist or has been evicted.";
|
|
18
|
+
readonly recovery: "Re-run fx_get_timeseries to obtain a fresh canvas_id.";
|
|
19
|
+
}, {
|
|
20
|
+
readonly reason: "invalid_query";
|
|
21
|
+
readonly code: JsonRpcErrorCode.InvalidParams;
|
|
22
|
+
readonly when: "SQL is not a SELECT, references unknown tables/columns, or has a syntax error.";
|
|
23
|
+
readonly recovery: "Run fx_dataframe_describe first to verify table and column names.";
|
|
24
|
+
}], undefined>;
|
|
25
|
+
//# sourceMappingURL=fx-dataframe-query.tool.d.ts.map
|
|
@@ -0,0 +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;;;;;;;;;;;;;;;;;cA2I3B,CAAC"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview fx_dataframe_query — run SQL against DataCanvas tables from fx_get_timeseries.
|
|
3
|
+
* @module mcp-server/tools/definitions/fx-dataframe-query.tool
|
|
4
|
+
*/
|
|
5
|
+
import { tool, z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
import { JsonRpcErrorCode, serviceUnavailable } from '@cyanheads/mcp-ts-core/errors';
|
|
7
|
+
import { getCanvas } from '../../../services/canvas/canvas-accessor.js';
|
|
8
|
+
export const fxDataframeQuery = tool('fx_dataframe_query', {
|
|
9
|
+
description: 'Run a read-only SQL SELECT against DataCanvas tables staged by fx_get_timeseries. ' +
|
|
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.',
|
|
12
|
+
annotations: {
|
|
13
|
+
readOnlyHint: true,
|
|
14
|
+
idempotentHint: true,
|
|
15
|
+
openWorldHint: false,
|
|
16
|
+
},
|
|
17
|
+
input: z.object({
|
|
18
|
+
canvas_id: z
|
|
19
|
+
.string()
|
|
20
|
+
.describe('Canvas ID returned by fx_get_timeseries. ' +
|
|
21
|
+
'Re-run fx_get_timeseries to obtain a fresh canvas_id if this one has expired.'),
|
|
22
|
+
query: z
|
|
23
|
+
.string()
|
|
24
|
+
.describe('Read-only SQL SELECT statement. Reference tables by the names returned by fx_dataframe_describe ' +
|
|
25
|
+
'or the table_name field from fx_get_timeseries. ' +
|
|
26
|
+
"Example: SELECT date, rate FROM fx_usd_eur WHERE date > '2024-01-01' ORDER BY date"),
|
|
27
|
+
}),
|
|
28
|
+
output: z.object({
|
|
29
|
+
rows: z
|
|
30
|
+
.array(z
|
|
31
|
+
.record(z.string(), z.unknown())
|
|
32
|
+
.describe('One result row — column-name → value pairs matching the SELECT columns.'))
|
|
33
|
+
.describe('Result rows, capped at the canvas row limit (default 10 000). Each key is a column name from the query.'),
|
|
34
|
+
row_count: z
|
|
35
|
+
.number()
|
|
36
|
+
.describe('Total rows in the full result set before the row cap was applied.'),
|
|
37
|
+
canvas_id: z
|
|
38
|
+
.string()
|
|
39
|
+
.describe('The canvas ID used — pass to a subsequent fx_dataframe_query or fx_dataframe_describe call.'),
|
|
40
|
+
}),
|
|
41
|
+
errors: [
|
|
42
|
+
{
|
|
43
|
+
reason: 'canvas_not_found',
|
|
44
|
+
code: JsonRpcErrorCode.NotFound,
|
|
45
|
+
when: 'canvas_id does not exist or has been evicted.',
|
|
46
|
+
recovery: 'Re-run fx_get_timeseries to obtain a fresh canvas_id.',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
reason: 'invalid_query',
|
|
50
|
+
code: JsonRpcErrorCode.InvalidParams,
|
|
51
|
+
when: 'SQL is not a SELECT, references unknown tables/columns, or has a syntax error.',
|
|
52
|
+
recovery: 'Run fx_dataframe_describe first to verify table and column names.',
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
async handler(input, ctx) {
|
|
56
|
+
const canvas = getCanvas();
|
|
57
|
+
if (!canvas) {
|
|
58
|
+
throw serviceUnavailable('DataCanvas is not enabled. Set CANVAS_PROVIDER_TYPE=duckdb.');
|
|
59
|
+
}
|
|
60
|
+
let instance;
|
|
61
|
+
try {
|
|
62
|
+
instance = await canvas.acquire(input.canvas_id, ctx);
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
const msg = err.message ?? '';
|
|
66
|
+
if (msg.includes('not found') || msg.includes('NotFound')) {
|
|
67
|
+
throw ctx.fail('canvas_not_found', `Canvas "${input.canvas_id}" not found or has expired.`, {
|
|
68
|
+
...ctx.recoveryFor('canvas_not_found'),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
let result;
|
|
74
|
+
try {
|
|
75
|
+
result = await instance.query(input.query, { signal: ctx.signal });
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
const msg = err.message ?? '';
|
|
79
|
+
// ValidationError from the four-layer gate, or DuckDB parse errors
|
|
80
|
+
if (msg.includes('ValidationError') ||
|
|
81
|
+
msg.includes('SELECT') ||
|
|
82
|
+
msg.includes('syntax') ||
|
|
83
|
+
msg.includes('not found') ||
|
|
84
|
+
msg.includes('does not exist') ||
|
|
85
|
+
msg.includes('read_csv') ||
|
|
86
|
+
msg.includes('register_as_clash') ||
|
|
87
|
+
err.code === 'ValidationError') {
|
|
88
|
+
throw ctx.fail('invalid_query', `SQL query rejected: ${msg}`, {
|
|
89
|
+
...ctx.recoveryFor('invalid_query'),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
throw err;
|
|
93
|
+
}
|
|
94
|
+
ctx.log.info('Executed dataframe query', {
|
|
95
|
+
canvasId: input.canvas_id,
|
|
96
|
+
rowCount: result.rowCount,
|
|
97
|
+
});
|
|
98
|
+
return {
|
|
99
|
+
rows: result.rows,
|
|
100
|
+
row_count: result.rowCount,
|
|
101
|
+
canvas_id: input.canvas_id,
|
|
102
|
+
};
|
|
103
|
+
},
|
|
104
|
+
format: (result) => {
|
|
105
|
+
if (result.rows.length === 0) {
|
|
106
|
+
return [{ type: 'text', text: `Query returned 0 rows (canvas \`${result.canvas_id}\`).` }];
|
|
107
|
+
}
|
|
108
|
+
const cols = Object.keys(result.rows[0] ?? {});
|
|
109
|
+
const header = `| ${cols.join(' | ')} |`;
|
|
110
|
+
const sep = `| ${cols.map(() => '---').join(' | ')} |`;
|
|
111
|
+
const rowLines = result.rows
|
|
112
|
+
.slice(0, 50)
|
|
113
|
+
.map((r) => `| ${cols.map((c) => String(r[c] ?? '')).join(' | ')} |`);
|
|
114
|
+
const note = result.row_count > 50
|
|
115
|
+
? `\n*Showing 50 of ${result.row_count} rows*`
|
|
116
|
+
: `\n*${result.row_count} rows*`;
|
|
117
|
+
return [
|
|
118
|
+
{
|
|
119
|
+
type: 'text',
|
|
120
|
+
text: [header, sep, ...rowLines].join('\n') + note + ` · canvas \`${result.canvas_id}\``,
|
|
121
|
+
},
|
|
122
|
+
];
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
//# sourceMappingURL=fx-dataframe-query.tool.js.map
|
|
@@ -0,0 +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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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview fx_get_rate — get the exchange rate for a single currency pair.
|
|
3
|
+
* @module mcp-server/tools/definitions/fx-get-rate.tool
|
|
4
|
+
*/
|
|
5
|
+
import { z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
7
|
+
export declare const fxGetRate: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
|
|
8
|
+
base_currency: z.ZodString;
|
|
9
|
+
quote_currency: z.ZodString;
|
|
10
|
+
date: z.ZodOptional<z.ZodString>;
|
|
11
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
12
|
+
base_currency: z.ZodString;
|
|
13
|
+
quote_currency: z.ZodString;
|
|
14
|
+
rate: z.ZodNumber;
|
|
15
|
+
rate_date: z.ZodString;
|
|
16
|
+
date_snapped: z.ZodBoolean;
|
|
17
|
+
rate_type: z.ZodString;
|
|
18
|
+
source: z.ZodString;
|
|
19
|
+
}, z.core.$strip>, readonly [{
|
|
20
|
+
readonly reason: "unsupported_currency";
|
|
21
|
+
readonly code: JsonRpcErrorCode.InvalidParams;
|
|
22
|
+
readonly when: "base_currency or quote_currency is not in the ECB currency set.";
|
|
23
|
+
readonly recovery: "Call fx_list_currencies to get the list of valid currency codes.";
|
|
24
|
+
}, {
|
|
25
|
+
readonly reason: "date_out_of_range";
|
|
26
|
+
readonly code: JsonRpcErrorCode.InvalidParams;
|
|
27
|
+
readonly when: "date is before 1999-01-04 or in the future.";
|
|
28
|
+
readonly recovery: "ECB data starts 1999-01-04; omit date for the latest available rate.";
|
|
29
|
+
}], undefined>;
|
|
30
|
+
//# sourceMappingURL=fx-get-rate.tool.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview fx_get_rate — get the exchange rate for a single currency pair.
|
|
3
|
+
* @module mcp-server/tools/definitions/fx-get-rate.tool
|
|
4
|
+
*/
|
|
5
|
+
import { tool, z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
7
|
+
import { ECB_START_DATE, getFrankfurterService, } from '../../../services/frankfurter/frankfurter-service.js';
|
|
8
|
+
export const fxGetRate = tool('fx_get_rate', {
|
|
9
|
+
description: 'Get the exchange rate for a currency pair on a given date (default: latest). ' +
|
|
10
|
+
'Returns the rate, the actual rate date (which may differ from the requested date on ' +
|
|
11
|
+
'weekends/holidays — ECB publishes business days only), and source provenance. ' +
|
|
12
|
+
'Cross-rates are triangulated through EUR automatically. ' +
|
|
13
|
+
'Use fx_convert_currency when you want the converted amount; use this tool when you only need the rate number.',
|
|
14
|
+
annotations: {
|
|
15
|
+
readOnlyHint: true,
|
|
16
|
+
idempotentHint: true,
|
|
17
|
+
openWorldHint: true,
|
|
18
|
+
},
|
|
19
|
+
input: z.object({
|
|
20
|
+
base_currency: z
|
|
21
|
+
.string()
|
|
22
|
+
.describe('ISO 4217 base currency code (e.g. USD). Call fx_list_currencies to get valid codes.'),
|
|
23
|
+
quote_currency: z
|
|
24
|
+
.string()
|
|
25
|
+
.describe('ISO 4217 quote currency code (e.g. EUR). The rate is expressed as "how many quote units per 1 base unit".'),
|
|
26
|
+
date: z
|
|
27
|
+
.string()
|
|
28
|
+
.optional()
|
|
29
|
+
.describe('ISO 8601 date (YYYY-MM-DD). Omit for the latest available rate. ' +
|
|
30
|
+
'ECB data starts 1999-01-04. Future dates are not supported.'),
|
|
31
|
+
}),
|
|
32
|
+
output: z.object({
|
|
33
|
+
base_currency: z.string().describe('The base currency code.'),
|
|
34
|
+
quote_currency: z.string().describe('The quote currency code.'),
|
|
35
|
+
rate: z
|
|
36
|
+
.number()
|
|
37
|
+
.describe('Exchange rate: units of quote currency per 1 unit of base currency.'),
|
|
38
|
+
rate_date: z.string().describe('Actual date of the rate returned.'),
|
|
39
|
+
date_snapped: z
|
|
40
|
+
.boolean()
|
|
41
|
+
.describe('True when the API returned a different date than requested — ' +
|
|
42
|
+
'ECB silently snaps weekend/holiday requests to the prior business day.'),
|
|
43
|
+
rate_type: z
|
|
44
|
+
.string()
|
|
45
|
+
.describe('Always "ECB reference (mid-market)" — these are reference rates, not tradeable bid/ask.'),
|
|
46
|
+
source: z.string().describe('Always "ECB via Frankfurter" — the upstream data provider.'),
|
|
47
|
+
}),
|
|
48
|
+
errors: [
|
|
49
|
+
{
|
|
50
|
+
reason: 'unsupported_currency',
|
|
51
|
+
code: JsonRpcErrorCode.InvalidParams,
|
|
52
|
+
when: 'base_currency or quote_currency is not in the ECB currency set.',
|
|
53
|
+
recovery: 'Call fx_list_currencies to get the list of valid currency codes.',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
reason: 'date_out_of_range',
|
|
57
|
+
code: JsonRpcErrorCode.InvalidParams,
|
|
58
|
+
when: 'date is before 1999-01-04 or in the future.',
|
|
59
|
+
recovery: 'ECB data starts 1999-01-04; omit date for the latest available rate.',
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
async handler(input, ctx) {
|
|
63
|
+
const service = getFrankfurterService();
|
|
64
|
+
const date = input.date ?? 'latest';
|
|
65
|
+
if (date !== 'latest') {
|
|
66
|
+
if (date < ECB_START_DATE) {
|
|
67
|
+
throw ctx.fail('date_out_of_range', `Date ${date} is before ECB data start ${ECB_START_DATE}.`, {
|
|
68
|
+
...ctx.recoveryFor('date_out_of_range'),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
72
|
+
if (date > today) {
|
|
73
|
+
throw ctx.fail('date_out_of_range', `Date ${date} is in the future.`, {
|
|
74
|
+
...ctx.recoveryFor('date_out_of_range'),
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
let resolved;
|
|
79
|
+
try {
|
|
80
|
+
resolved = await service.getRate(input.base_currency, input.quote_currency, date);
|
|
81
|
+
}
|
|
82
|
+
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') });
|
|
86
|
+
}
|
|
87
|
+
throw err;
|
|
88
|
+
}
|
|
89
|
+
ctx.log.info('Fetched rate', {
|
|
90
|
+
base: resolved.baseCurrency,
|
|
91
|
+
quote: resolved.quoteCurrency,
|
|
92
|
+
rate: resolved.rate,
|
|
93
|
+
date: resolved.rateDate,
|
|
94
|
+
snapped: resolved.dateSnapped,
|
|
95
|
+
});
|
|
96
|
+
return {
|
|
97
|
+
base_currency: resolved.baseCurrency,
|
|
98
|
+
quote_currency: resolved.quoteCurrency,
|
|
99
|
+
rate: resolved.rate,
|
|
100
|
+
rate_date: resolved.rateDate,
|
|
101
|
+
date_snapped: resolved.dateSnapped,
|
|
102
|
+
rate_type: resolved.rateType,
|
|
103
|
+
source: resolved.source,
|
|
104
|
+
};
|
|
105
|
+
},
|
|
106
|
+
format: (result) => {
|
|
107
|
+
const snapNote = result.date_snapped
|
|
108
|
+
? `\n⚠️ *Requested date snapped to ${result.rate_date} (weekend/holiday — ECB publishes business days only)*`
|
|
109
|
+
: '';
|
|
110
|
+
return [
|
|
111
|
+
{
|
|
112
|
+
type: 'text',
|
|
113
|
+
text: `**${result.base_currency}/${result.quote_currency}** — ${result.rate_date}\n` +
|
|
114
|
+
`Rate: **${result.rate}** (${result.quote_currency} per ${result.base_currency})` +
|
|
115
|
+
snapNote +
|
|
116
|
+
`\n*${result.rate_type} · ${result.source}*`,
|
|
117
|
+
},
|
|
118
|
+
];
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
//# sourceMappingURL=fx-get-rate.tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fx-get-rate.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/fx-get-rate.tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview fx_get_rates — bulk snapshot of all rates for one base currency.
|
|
3
|
+
* @module mcp-server/tools/definitions/fx-get-rates.tool
|
|
4
|
+
*/
|
|
5
|
+
import { z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
7
|
+
export declare const fxGetRates: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
|
|
8
|
+
base_currency: z.ZodString;
|
|
9
|
+
date: z.ZodOptional<z.ZodString>;
|
|
10
|
+
symbols: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
11
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
12
|
+
base_currency: z.ZodString;
|
|
13
|
+
rate_date: z.ZodString;
|
|
14
|
+
rates: z.ZodRecord<z.ZodString, z.ZodNumber>;
|
|
15
|
+
rate_type: z.ZodString;
|
|
16
|
+
source: z.ZodString;
|
|
17
|
+
}, z.core.$strip>, readonly [{
|
|
18
|
+
readonly reason: "unsupported_currency";
|
|
19
|
+
readonly code: JsonRpcErrorCode.InvalidParams;
|
|
20
|
+
readonly when: "base_currency is not in the ECB currency set.";
|
|
21
|
+
readonly recovery: "Call fx_list_currencies to get the list of valid currency codes.";
|
|
22
|
+
}, {
|
|
23
|
+
readonly reason: "date_out_of_range";
|
|
24
|
+
readonly code: JsonRpcErrorCode.InvalidParams;
|
|
25
|
+
readonly when: "date is before 1999-01-04 or in the future.";
|
|
26
|
+
readonly recovery: "ECB data starts 1999-01-04; omit date for the latest available rate.";
|
|
27
|
+
}], undefined>;
|
|
28
|
+
//# sourceMappingURL=fx-get-rates.tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fx-get-rates.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/fx-get-rates.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,UAAU;;;;;;;;;;;;;;;;;;;;cAoIrB,CAAC"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview fx_get_rates — bulk snapshot of all rates for one base currency.
|
|
3
|
+
* @module mcp-server/tools/definitions/fx-get-rates.tool
|
|
4
|
+
*/
|
|
5
|
+
import { tool, z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
7
|
+
import { ECB_START_DATE, getFrankfurterService, } from '../../../services/frankfurter/frankfurter-service.js';
|
|
8
|
+
export const fxGetRates = tool('fx_get_rates', {
|
|
9
|
+
description: 'Get all available exchange rates for one base currency in a single snapshot. ' +
|
|
10
|
+
'Useful for bulk comparison and seeding downstream tools. ' +
|
|
11
|
+
'Returns a map of quote currency → rate plus the snapshot date. ' +
|
|
12
|
+
'Optionally filter to a subset of quote currencies via symbols.',
|
|
13
|
+
annotations: {
|
|
14
|
+
readOnlyHint: true,
|
|
15
|
+
idempotentHint: true,
|
|
16
|
+
openWorldHint: false,
|
|
17
|
+
},
|
|
18
|
+
input: z.object({
|
|
19
|
+
base_currency: z
|
|
20
|
+
.string()
|
|
21
|
+
.describe('ISO 4217 base currency code (e.g. USD). Call fx_list_currencies to get valid codes.'),
|
|
22
|
+
date: z
|
|
23
|
+
.string()
|
|
24
|
+
.optional()
|
|
25
|
+
.describe('ISO 8601 date (YYYY-MM-DD). Omit for the latest available rate. ' +
|
|
26
|
+
'ECB data starts 1999-01-04. Future dates are not supported.'),
|
|
27
|
+
symbols: z
|
|
28
|
+
.array(z.string().describe('ISO 4217 currency code to include in the response.'))
|
|
29
|
+
.optional()
|
|
30
|
+
.describe('Optional list of quote currency codes to filter the response. ' +
|
|
31
|
+
'Omit to return all ~30 supported currencies.'),
|
|
32
|
+
}),
|
|
33
|
+
output: z.object({
|
|
34
|
+
base_currency: z.string().describe('The base currency code.'),
|
|
35
|
+
rate_date: z
|
|
36
|
+
.string()
|
|
37
|
+
.describe('Actual date of the rates. May differ from requested date on weekends/holidays — ' +
|
|
38
|
+
'ECB publishes business days only; the API silently snaps to the prior business day.'),
|
|
39
|
+
rates: z
|
|
40
|
+
.record(z.string(), z.number())
|
|
41
|
+
.describe('Map of quote currency code → exchange rate (units of quote per 1 base).'),
|
|
42
|
+
rate_type: z
|
|
43
|
+
.string()
|
|
44
|
+
.describe('Always "ECB reference (mid-market)" — these are reference rates, not tradeable bid/ask.'),
|
|
45
|
+
source: z.string().describe('Always "ECB via Frankfurter" — the upstream data provider.'),
|
|
46
|
+
}),
|
|
47
|
+
errors: [
|
|
48
|
+
{
|
|
49
|
+
reason: 'unsupported_currency',
|
|
50
|
+
code: JsonRpcErrorCode.InvalidParams,
|
|
51
|
+
when: 'base_currency is not in the ECB currency set.',
|
|
52
|
+
recovery: 'Call fx_list_currencies to get the list of valid currency codes.',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
reason: 'date_out_of_range',
|
|
56
|
+
code: JsonRpcErrorCode.InvalidParams,
|
|
57
|
+
when: 'date is before 1999-01-04 or in the future.',
|
|
58
|
+
recovery: 'ECB data starts 1999-01-04; omit date for the latest available rate.',
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
async handler(input, ctx) {
|
|
62
|
+
const service = getFrankfurterService();
|
|
63
|
+
const date = input.date ?? 'latest';
|
|
64
|
+
if (date !== 'latest') {
|
|
65
|
+
if (date < ECB_START_DATE) {
|
|
66
|
+
throw ctx.fail('date_out_of_range', `Date ${date} is before ECB data start ${ECB_START_DATE}.`, {
|
|
67
|
+
...ctx.recoveryFor('date_out_of_range'),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
71
|
+
if (date > today) {
|
|
72
|
+
throw ctx.fail('date_out_of_range', `Date ${date} is in the future.`, {
|
|
73
|
+
...ctx.recoveryFor('date_out_of_range'),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
let raw;
|
|
78
|
+
try {
|
|
79
|
+
raw = await service.getRates(input.base_currency, date, input.symbols);
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
const msg = err.message ?? '';
|
|
83
|
+
if (msg.includes('not found')) {
|
|
84
|
+
throw ctx.fail('unsupported_currency', `Currency "${input.base_currency}" is not supported by the ECB.`, { ...ctx.recoveryFor('unsupported_currency') });
|
|
85
|
+
}
|
|
86
|
+
throw err;
|
|
87
|
+
}
|
|
88
|
+
ctx.log.info('Fetched rates snapshot', {
|
|
89
|
+
base: input.base_currency,
|
|
90
|
+
date: raw.date,
|
|
91
|
+
count: Object.keys(raw.rates).length,
|
|
92
|
+
});
|
|
93
|
+
return {
|
|
94
|
+
base_currency: raw.base,
|
|
95
|
+
rate_date: raw.date,
|
|
96
|
+
rates: raw.rates,
|
|
97
|
+
rate_type: 'ECB reference (mid-market)',
|
|
98
|
+
source: 'ECB via Frankfurter',
|
|
99
|
+
};
|
|
100
|
+
},
|
|
101
|
+
format: (result) => {
|
|
102
|
+
const rateLines = Object.entries(result.rates)
|
|
103
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
104
|
+
.map(([code, rate]) => `**${code}**: ${rate}`);
|
|
105
|
+
return [
|
|
106
|
+
{
|
|
107
|
+
type: 'text',
|
|
108
|
+
text: `**${result.base_currency} exchange rates** — ${result.rate_date}\n` +
|
|
109
|
+
`*${result.rate_type} · ${result.source}*\n\n` +
|
|
110
|
+
rateLines.join('\n'),
|
|
111
|
+
},
|
|
112
|
+
];
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
//# sourceMappingURL=fx-get-rates.tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fx-get-rates.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/fx-get-rates.tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview fx_get_timeseries — historical daily rates for a currency pair over a date range.
|
|
3
|
+
* @module mcp-server/tools/definitions/fx-get-timeseries.tool
|
|
4
|
+
*/
|
|
5
|
+
import { z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
7
|
+
export declare const fxGetTimeseries: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
|
|
8
|
+
base_currency: z.ZodString;
|
|
9
|
+
quote_currency: z.ZodString;
|
|
10
|
+
start_date: z.ZodString;
|
|
11
|
+
end_date: z.ZodString;
|
|
12
|
+
canvas_id: z.ZodOptional<z.ZodString>;
|
|
13
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
14
|
+
base_currency: z.ZodString;
|
|
15
|
+
quote_currency: z.ZodString;
|
|
16
|
+
start_date: z.ZodString;
|
|
17
|
+
end_date: z.ZodString;
|
|
18
|
+
rates: z.ZodRecord<z.ZodString, z.ZodNumber>;
|
|
19
|
+
rate_count: z.ZodNumber;
|
|
20
|
+
rate_type: z.ZodString;
|
|
21
|
+
source: z.ZodString;
|
|
22
|
+
spilled: z.ZodBoolean;
|
|
23
|
+
canvas_id: z.ZodOptional<z.ZodString>;
|
|
24
|
+
table_name: z.ZodOptional<z.ZodString>;
|
|
25
|
+
}, z.core.$strip>, readonly [{
|
|
26
|
+
readonly reason: "unsupported_currency";
|
|
27
|
+
readonly code: JsonRpcErrorCode.InvalidParams;
|
|
28
|
+
readonly when: "base_currency or quote_currency is not in the ECB currency set.";
|
|
29
|
+
readonly recovery: "Call fx_list_currencies to get the list of valid currency codes.";
|
|
30
|
+
}, {
|
|
31
|
+
readonly reason: "date_out_of_range";
|
|
32
|
+
readonly code: JsonRpcErrorCode.InvalidParams;
|
|
33
|
+
readonly when: "start_date is before 1999-01-04 or end_date is in the future.";
|
|
34
|
+
readonly recovery: "ECB data starts 1999-01-04; omit end_date or use today's date.";
|
|
35
|
+
}, {
|
|
36
|
+
readonly reason: "invalid_range";
|
|
37
|
+
readonly code: JsonRpcErrorCode.InvalidParams;
|
|
38
|
+
readonly when: "start_date is after end_date.";
|
|
39
|
+
readonly recovery: "Ensure start_date is before or equal to end_date.";
|
|
40
|
+
}], undefined>;
|
|
41
|
+
//# sourceMappingURL=fx-get-timeseries.tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fx-get-timeseries.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/fx-get-timeseries.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAEjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AASjE,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAsR1B,CAAC"}
|