@allratestoday/mcp-server 0.3.3 → 0.4.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.
- package/README.md +38 -23
- package/dist/client.js +21 -9
- package/dist/index.js +132 -108
- package/dist/version.d.ts +1 -0
- package/dist/version.js +3 -0
- package/package.json +10 -8
package/README.md
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
#
|
|
1
|
+
# AllRatesToday MCP Server
|
|
2
|
+
|
|
3
|
+
[](https://allratestoday.com)
|
|
2
4
|
|
|
3
5
|
[](https://www.npmjs.com/package/@allratestoday/mcp-server)
|
|
4
6
|
[](https://www.npmjs.com/package/@allratestoday/mcp-server)
|
|
@@ -9,7 +11,7 @@ English | [简体中文](./README-zh-CN.md)
|
|
|
9
11
|
|
|
10
12
|
> Give your AI coding assistant a live window into the foreign-exchange market.
|
|
11
13
|
|
|
12
|
-
A Model Context Protocol server that lets **Claude Code**, **Cursor**, **Claude Desktop**, **Windsurf**, and any other MCP-compatible client fetch real-time currency rates, historical data, and multi-currency lookups.
|
|
14
|
+
A Model Context Protocol server that lets **Claude Code**, **Cursor**, **Claude Desktop**, **Windsurf**, and any other MCP-compatible client fetch real-time currency rates, historical data, and multi-currency lookups from the [AllRatesToday API](https://allratestoday.com).
|
|
13
15
|
|
|
14
16
|
After installation, your assistant can answer questions like:
|
|
15
17
|
|
|
@@ -35,6 +37,7 @@ After installation, your assistant can answer questions like:
|
|
|
35
37
|
- [Verify it works](#verify-it-works)
|
|
36
38
|
- [Tools reference](#tools-reference)
|
|
37
39
|
- [Environment variables](#environment-variables)
|
|
40
|
+
- [Plans and limits](#plans-and-limits)
|
|
38
41
|
- [Troubleshooting](#troubleshooting)
|
|
39
42
|
- [Error reference](#error-reference)
|
|
40
43
|
- [FAQ](#faq)
|
|
@@ -51,6 +54,7 @@ After installation, your assistant can answer questions like:
|
|
|
51
54
|
|---|---|
|
|
52
55
|
| **Currencies** | 150+ ISO 4217 codes, all major and most exotics |
|
|
53
56
|
| **Update frequency** | Mid-market rates refresh every ~60 seconds |
|
|
57
|
+
| **Data source** | Tier-1 financial data providers (Reuters / Refinitiv-class) |
|
|
54
58
|
| **Historical depth** | Up to 1 year via `1d` / `7d` / `30d` / `1y` granularity |
|
|
55
59
|
| **Tools exposed** | 4 — `get_exchange_rate`, `get_historical_rates`, `get_rates_authenticated`, `list_currencies` |
|
|
56
60
|
| **Transport** | stdio (subprocess), MCP 1.x compatible |
|
|
@@ -60,14 +64,14 @@ After installation, your assistant can answer questions like:
|
|
|
60
64
|
|
|
61
65
|
## Get an API key (required)
|
|
62
66
|
|
|
63
|
-
The server **will not start** without a valid `ALLRATES_API_KEY`.
|
|
67
|
+
The server **will not start** without a valid `ALLRATES_API_KEY`. A free key is enough for development and personal use — **no credit card required**.
|
|
64
68
|
|
|
65
|
-
1. Register at [allratestoday.com/register](https://allratestoday.com/register)
|
|
69
|
+
1. Register at [allratestoday.com/register](https://allratestoday.com/register) — 30 seconds
|
|
66
70
|
2. Verify your email
|
|
67
71
|
3. Copy your key from the dashboard (format: `art_live_xxxxx`)
|
|
68
72
|
4. Use it as `ALLRATES_API_KEY` in the configs below
|
|
69
73
|
|
|
70
|
-
If
|
|
74
|
+
If you forget, the server prints clear registration instructions on stderr and exits with code 1.
|
|
71
75
|
|
|
72
76
|
---
|
|
73
77
|
|
|
@@ -203,7 +207,7 @@ After configuring your client, test in this order:
|
|
|
203
207
|
|
|
204
208
|
## Tools reference
|
|
205
209
|
|
|
206
|
-
All four tools require
|
|
210
|
+
All four tools require `ALLRATES_API_KEY`.
|
|
207
211
|
|
|
208
212
|
### `get_exchange_rate`
|
|
209
213
|
|
|
@@ -332,13 +336,19 @@ You set these in your MCP client's config (in the `env` block) — not in your s
|
|
|
332
336
|
|
|
333
337
|
---
|
|
334
338
|
|
|
339
|
+
## Plans
|
|
340
|
+
|
|
341
|
+
A free tier and paid plans are available. See [allratestoday.com/pricing](https://allratestoday.com/pricing) for current quotas. All plans include the same currency coverage and historical depth — only the request quotas differ.
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
335
345
|
## Troubleshooting
|
|
336
346
|
|
|
337
347
|
| Symptom | Likely cause | Fix |
|
|
338
348
|
|---|---|---|
|
|
339
349
|
| Client shows "MCP server failed to start" or red dot | `ALLRATES_API_KEY` not set or invalid | Verify the key in your client config; check it matches the dashboard |
|
|
340
|
-
| Tools show but every call returns "Invalid API key" | Key is malformed (missing prefix, truncated, or revoked) | Copy a fresh key from the dashboard |
|
|
341
|
-
| Tools return "API quota exceeded" |
|
|
350
|
+
| Tools show but every call returns "Invalid AllRatesToday API key" | Key is malformed (missing prefix, truncated, or revoked) | Copy a fresh key from the dashboard |
|
|
351
|
+
| Tools return "AllRatesToday API quota exceeded" | Free-tier monthly limit hit | Wait until next month or upgrade plan |
|
|
342
352
|
| Historical tool returns "Bad request" | Invalid period or unknown currency code | Period must be `1d`/`7d`/`30d`/`1y`; codes must be 3 letters |
|
|
343
353
|
| Server starts but tools never appear in client | Client didn't reload after config change | Fully quit (not just close) and reopen the client |
|
|
344
354
|
| `npx` runs but hangs forever | The server is waiting for an MCP client to connect — this is normal when run from a shell | Don't run from a shell; let your MCP client launch it |
|
|
@@ -363,9 +373,9 @@ The server maps API errors to clear, actionable messages.
|
|
|
363
373
|
|---|---|---|
|
|
364
374
|
| 200 | Success | (rate returned) |
|
|
365
375
|
| 400 | Bad request — usually unknown currency code | `Bad request — possibly an unknown currency code` |
|
|
366
|
-
| 401 | Invalid or missing API key | `Invalid API key` |
|
|
367
|
-
| 429 | Quota exceeded | `API quota exceeded` |
|
|
368
|
-
| 5xx |
|
|
376
|
+
| 401 | Invalid or missing API key | `Invalid AllRatesToday API key` |
|
|
377
|
+
| 429 | Quota exceeded | `AllRatesToday API quota exceeded` |
|
|
378
|
+
| 5xx | Server-side issue at allratestoday.com | `HTTP 5xx — <upstream message>` |
|
|
369
379
|
|
|
370
380
|
The LLM will surface these messages in its response, so a user prompt that hits a 429 results in the assistant saying *"the API quota has been exceeded — please try again next month or upgrade your plan."*
|
|
371
381
|
|
|
@@ -373,20 +383,23 @@ The LLM will surface these messages in its response, so a user prompt that hits
|
|
|
373
383
|
|
|
374
384
|
## FAQ
|
|
375
385
|
|
|
386
|
+
**Is the free plan really enough for normal use?**
|
|
387
|
+
Yes for personal/dev use. The free tier covers a few daily questions. Heavy interactive use, multiple chat sessions per day, or running the server in production should consider the paid tiers.
|
|
388
|
+
|
|
376
389
|
**Do you store my conversation or query data?**
|
|
377
|
-
No. Only your API key and the request parameters (source, target, period, time) are sent to
|
|
390
|
+
No. Only your API key and the request parameters (source, target, period, time) are sent to allratestoday.com — never the LLM's conversation context, sheet contents, or anything else.
|
|
378
391
|
|
|
379
392
|
**What happens to my API key?**
|
|
380
|
-
It's only sent as a `Bearer` token in the `Authorization` header on requests to the
|
|
393
|
+
It's only sent as a `Bearer` token in the `Authorization` header on requests to the AllRatesToday API. It's never logged or transmitted elsewhere.
|
|
381
394
|
|
|
382
395
|
**Why is my historical request slow on first call?**
|
|
383
|
-
Cold-start of `npx` (first run downloads the package) plus
|
|
396
|
+
Cold-start of `npx` (first run downloads the package) plus the initial AllRatesToday cache miss. Subsequent calls are fast (<200ms typically).
|
|
384
397
|
|
|
385
398
|
**Can I run this without npm/Node?**
|
|
386
399
|
Not currently — Node ≥18 is required. We've considered a standalone binary; if that matters to you, open an issue.
|
|
387
400
|
|
|
388
401
|
**Is there a self-hosted option?**
|
|
389
|
-
Yes, set `ALLRATES_BASE_URL` to
|
|
402
|
+
Yes, set `ALLRATES_BASE_URL` to your own AllRatesToday instance. Contact support@allratestoday.com for self-hosted licensing.
|
|
390
403
|
|
|
391
404
|
**Does this work with ChatGPT?**
|
|
392
405
|
The Anthropic MCP standard works with any MCP-compatible client. ChatGPT Desktop has experimental MCP support; check OpenAI's docs for current status.
|
|
@@ -396,8 +409,8 @@ The Anthropic MCP standard works with any MCP-compatible client. ChatGPT Desktop
|
|
|
396
409
|
## Development
|
|
397
410
|
|
|
398
411
|
```bash
|
|
399
|
-
git clone https://github.com/cahthuranag/
|
|
400
|
-
cd
|
|
412
|
+
git clone https://github.com/cahthuranag/mcp-server.git
|
|
413
|
+
cd mcp-server
|
|
401
414
|
npm install
|
|
402
415
|
npm run build
|
|
403
416
|
ALLRATES_API_KEY=art_live_xxxxx node dist/index.js
|
|
@@ -411,7 +424,7 @@ To watch and rebuild on changes during development:
|
|
|
411
424
|
npm run dev
|
|
412
425
|
```
|
|
413
426
|
|
|
414
|
-
To test against a local instance:
|
|
427
|
+
To test against a local AllRatesToday instance:
|
|
415
428
|
|
|
416
429
|
```bash
|
|
417
430
|
ALLRATES_BASE_URL=http://localhost:8080/api ALLRATES_API_KEY=test_key node dist/index.js
|
|
@@ -422,7 +435,7 @@ ALLRATES_BASE_URL=http://localhost:8080/api ALLRATES_API_KEY=test_key node dist/
|
|
|
422
435
|
```
|
|
423
436
|
src/
|
|
424
437
|
├── index.ts # MCP server, tool registration, request handlers
|
|
425
|
-
└── client.ts # HTTP client + error mapping
|
|
438
|
+
└── client.ts # HTTP client for AllRatesToday API + error mapping
|
|
426
439
|
dist/ # Compiled JS (gitignored)
|
|
427
440
|
server.json # MCP registry manifest
|
|
428
441
|
package.json # npm metadata, dependencies, scripts
|
|
@@ -430,10 +443,10 @@ package.json # npm metadata, dependencies, scripts
|
|
|
430
443
|
|
|
431
444
|
### Contributing
|
|
432
445
|
|
|
433
|
-
Issues and PRs welcome at [github.com/cahthuranag/
|
|
446
|
+
Issues and PRs welcome at [github.com/cahthuranag/mcp-server](https://github.com/cahthuranag/mcp-server). Before opening a PR:
|
|
434
447
|
|
|
435
448
|
1. `npm run build` should succeed with no errors
|
|
436
|
-
2. Test against a real API key (set in `ALLRATES_API_KEY`)
|
|
449
|
+
2. Test against a real AllRatesToday API key (set in `ALLRATES_API_KEY`)
|
|
437
450
|
3. Update tool descriptions in `src/index.ts` if you change tool behavior
|
|
438
451
|
4. Update this README's "Tools reference" section if you add or rename a tool
|
|
439
452
|
|
|
@@ -441,7 +454,7 @@ Issues and PRs welcome at [github.com/cahthuranag/realtime-exchange-rate-mcp](ht
|
|
|
441
454
|
|
|
442
455
|
## Changelog
|
|
443
456
|
|
|
444
|
-
See [GitHub Releases](https://github.com/cahthuranag/
|
|
457
|
+
See [GitHub Releases](https://github.com/cahthuranag/mcp-server/releases) for the full list. Recent highlights:
|
|
445
458
|
|
|
446
459
|
- **0.3.x** — API key required for all tools; fail-fast at startup with clear error
|
|
447
460
|
- **0.2.x** — Removed news tool, required auth on `get_historical_rates`
|
|
@@ -451,8 +464,10 @@ See [GitHub Releases](https://github.com/cahthuranag/realtime-exchange-rate-mcp/
|
|
|
451
464
|
|
|
452
465
|
## Support
|
|
453
466
|
|
|
454
|
-
- **
|
|
467
|
+
- **API issues**: [support@allratestoday.com](mailto:support@allratestoday.com)
|
|
468
|
+
- **Bug reports**: [github.com/cahthuranag/mcp-server/issues](https://github.com/cahthuranag/mcp-server/issues)
|
|
455
469
|
- **MCP questions**: [modelcontextprotocol.io](https://modelcontextprotocol.io) — protocol docs
|
|
470
|
+
- **Status / uptime**: [allratestoday.com](https://allratestoday.com) (status page in development)
|
|
456
471
|
|
|
457
472
|
---
|
|
458
473
|
|
package/dist/client.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { VERSION } from './version.js';
|
|
1
2
|
const DEFAULT_BASE_URL = 'https://allratestoday.com/api';
|
|
2
|
-
const USER_AGENT = `allratestoday-mcp/0.3.1`;
|
|
3
3
|
export class AllRatesTodayError extends Error {
|
|
4
4
|
status;
|
|
5
5
|
body;
|
|
@@ -10,6 +10,18 @@ export class AllRatesTodayError extends Error {
|
|
|
10
10
|
this.name = 'AllRatesTodayError';
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
|
+
function errorMessage(status, upstream) {
|
|
14
|
+
switch (status) {
|
|
15
|
+
case 400:
|
|
16
|
+
return upstream ?? 'Bad request — possibly an unknown currency code';
|
|
17
|
+
case 401:
|
|
18
|
+
return 'Invalid AllRatesToday API key';
|
|
19
|
+
case 429:
|
|
20
|
+
return 'AllRatesToday API quota exceeded';
|
|
21
|
+
default:
|
|
22
|
+
return upstream ? `HTTP ${status} — ${upstream}` : `HTTP ${status}`;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
13
25
|
export class AllRatesTodayClient {
|
|
14
26
|
apiKey;
|
|
15
27
|
baseUrl;
|
|
@@ -25,14 +37,14 @@ export class AllRatesTodayClient {
|
|
|
25
37
|
if (value !== undefined && value !== '')
|
|
26
38
|
url.searchParams.set(key, value);
|
|
27
39
|
}
|
|
40
|
+
if (!this.apiKey) {
|
|
41
|
+
throw new AllRatesTodayError('AllRatesToday API key is required. Sign up free at https://allratestoday.com/register to get a key, then set ALLRATES_API_KEY in your MCP config.');
|
|
42
|
+
}
|
|
28
43
|
const headers = {
|
|
29
44
|
'Accept': 'application/json',
|
|
30
|
-
'User-Agent':
|
|
45
|
+
'User-Agent': `allratestoday-mcp/${VERSION}`,
|
|
46
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
31
47
|
};
|
|
32
|
-
if (!this.apiKey) {
|
|
33
|
-
throw new AllRatesTodayError('API key is required. Get one at https://allratestoday.com/register, then set ALLRATES_API_KEY in your MCP config.');
|
|
34
|
-
}
|
|
35
|
-
headers['Authorization'] = `Bearer ${this.apiKey}`;
|
|
36
48
|
const res = await this.fetchImpl(url.toString(), { method: 'GET', headers });
|
|
37
49
|
const text = await res.text();
|
|
38
50
|
let body;
|
|
@@ -43,10 +55,10 @@ export class AllRatesTodayClient {
|
|
|
43
55
|
body = text;
|
|
44
56
|
}
|
|
45
57
|
if (!res.ok) {
|
|
46
|
-
const
|
|
58
|
+
const upstream = body && typeof body === 'object' && 'error' in body && typeof body.error === 'string'
|
|
47
59
|
? body.error
|
|
48
|
-
:
|
|
49
|
-
throw new AllRatesTodayError(
|
|
60
|
+
: undefined;
|
|
61
|
+
throw new AllRatesTodayError(errorMessage(res.status, upstream), res.status, body);
|
|
50
62
|
}
|
|
51
63
|
return body;
|
|
52
64
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,91 +1,41 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
-
import {
|
|
4
|
+
import { z } from 'zod';
|
|
5
5
|
import { AllRatesTodayClient, AllRatesTodayError } from './client.js';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
type: 'object',
|
|
31
|
-
additionalProperties: false,
|
|
32
|
-
properties: {
|
|
33
|
-
source: CCY,
|
|
34
|
-
target: CCY,
|
|
35
|
-
period: {
|
|
36
|
-
type: 'string',
|
|
37
|
-
enum: ['1d', '7d', '30d', '1y'],
|
|
38
|
-
default: '7d',
|
|
39
|
-
description: 'Time period to fetch history for.',
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
required: ['source', 'target'],
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
name: 'get_rates_authenticated',
|
|
47
|
-
description: 'Get rates with higher limits and multi-target support. Requires API key (ALLRATES_API_KEY). Supports comma-separated targets like "EUR,GBP,JPY".',
|
|
48
|
-
inputSchema: {
|
|
49
|
-
type: 'object',
|
|
50
|
-
additionalProperties: false,
|
|
51
|
-
properties: {
|
|
52
|
-
source: CCY,
|
|
53
|
-
target: {
|
|
54
|
-
type: 'string',
|
|
55
|
-
description: 'One or more target codes, comma-separated.',
|
|
56
|
-
},
|
|
57
|
-
time: {
|
|
58
|
-
type: 'string',
|
|
59
|
-
format: 'date-time',
|
|
60
|
-
description: 'Optional historical ISO 8601 timestamp.',
|
|
61
|
-
},
|
|
62
|
-
group: {
|
|
63
|
-
type: 'string',
|
|
64
|
-
enum: ['hour', 'day', 'week', 'month'],
|
|
65
|
-
description: 'Optional grouping window.',
|
|
66
|
-
},
|
|
67
|
-
},
|
|
68
|
-
required: ['source', 'target'],
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
name: 'list_currencies',
|
|
73
|
-
description: 'List all supported currencies with code, name, and symbol. Requires API key (ALLRATES_API_KEY). Cached 24h upstream.',
|
|
74
|
-
inputSchema: { type: 'object', additionalProperties: false, properties: {} },
|
|
75
|
-
},
|
|
76
|
-
];
|
|
77
|
-
function text(s) {
|
|
78
|
-
const out = typeof s === 'string' ? s : JSON.stringify(s, null, 2);
|
|
79
|
-
return { content: [{ type: 'text', text: out }] };
|
|
6
|
+
import { VERSION } from './version.js';
|
|
7
|
+
const CCY_DESC = "ISO 4217 currency code, 3 letters, case-insensitive (e.g. 'USD', 'EUR', 'GBP', 'JPY'). For a source/target pair, the returned rate is how much 1 unit of source is worth in target. Fiat only — no crypto, no commodities. Call list_currencies if unsure whether a code is supported.";
|
|
8
|
+
const ccy = z
|
|
9
|
+
.string()
|
|
10
|
+
.regex(/^[A-Za-z]{3}$/, 'must be a 3-letter ISO 4217 currency code')
|
|
11
|
+
.describe(CCY_DESC);
|
|
12
|
+
const ccyList = z
|
|
13
|
+
.string()
|
|
14
|
+
.regex(/^[A-Za-z]{3}(,[A-Za-z]{3})*$/, 'must be one or more 3-letter ISO 4217 codes, comma-separated, no spaces')
|
|
15
|
+
.describe("One or more ISO 4217 codes, comma-separated, no spaces, case-insensitive. Examples: 'EUR' (single) or 'EUR,GBP,JPY' (multi). Each target becomes a separate row in the response.");
|
|
16
|
+
const READ_ONLY = { readOnlyHint: true, openWorldHint: true };
|
|
17
|
+
function ok(structured) {
|
|
18
|
+
return {
|
|
19
|
+
content: [{ type: 'text', text: JSON.stringify(structured, null, 2) }],
|
|
20
|
+
structuredContent: structured,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function fail(err) {
|
|
24
|
+
const message = err instanceof AllRatesTodayError
|
|
25
|
+
? `AllRatesToday error${err.status ? ` (${err.status})` : ''}: ${err.message}`
|
|
26
|
+
: err instanceof Error
|
|
27
|
+
? err.message
|
|
28
|
+
: String(err);
|
|
29
|
+
return { content: [{ type: 'text', text: message }], isError: true };
|
|
80
30
|
}
|
|
81
31
|
async function main() {
|
|
82
32
|
const apiKey = process.env.ALLRATES_API_KEY;
|
|
83
33
|
if (!apiKey) {
|
|
84
34
|
console.error([
|
|
85
35
|
'',
|
|
86
|
-
'
|
|
36
|
+
' AllRatesToday MCP server requires an API key.',
|
|
87
37
|
'',
|
|
88
|
-
' 1.
|
|
38
|
+
' 1. Sign up free at https://allratestoday.com/register (free tier — no card required)',
|
|
89
39
|
' 2. Copy your API key from the dashboard',
|
|
90
40
|
' 3. Set ALLRATES_API_KEY in your MCP client config:',
|
|
91
41
|
'',
|
|
@@ -102,45 +52,119 @@ async function main() {
|
|
|
102
52
|
apiKey,
|
|
103
53
|
baseUrl: process.env.ALLRATES_BASE_URL,
|
|
104
54
|
});
|
|
105
|
-
const server = new
|
|
106
|
-
server.
|
|
107
|
-
|
|
108
|
-
|
|
55
|
+
const server = new McpServer({ name: 'allratestoday-mcp', version: VERSION });
|
|
56
|
+
server.registerTool('get_exchange_rate', {
|
|
57
|
+
title: 'Get live exchange rate',
|
|
58
|
+
description: "Use this when the user asks 'what is X in Y?', 'convert X to Y', 'current rate of EUR/USD', or any single fiat-to-fiat live exchange rate question. Returns the latest mid-market rate as a JSON object like { rate: 0.9234, source } meaning 1 source = 0.9234 target. Does NOT support cryptocurrencies, commodities, or arithmetic on amounts. For multiple targets in one call use get_rates_authenticated; for a past date or fixed lookback (7d/30d/1y) use get_historical_rates.",
|
|
59
|
+
inputSchema: { source: ccy, target: ccy },
|
|
60
|
+
outputSchema: {
|
|
61
|
+
rate: z.number().describe('How much 1 unit of source is worth in target'),
|
|
62
|
+
source: z.string().describe('Upstream data provider the rate came from'),
|
|
63
|
+
},
|
|
64
|
+
annotations: READ_ONLY,
|
|
65
|
+
}, async ({ source, target }) => {
|
|
66
|
+
try {
|
|
67
|
+
return ok(await client.getRate(source.toUpperCase(), target.toUpperCase()));
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
return fail(err);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
server.registerTool('get_historical_rates', {
|
|
74
|
+
title: 'Get historical rate time-series',
|
|
75
|
+
description: "Use this for fixed-window time-series questions like 'how has EUR/USD moved this week', 'show me the last month of GBP/JPY', or 'chart 1-year history of AUD/USD'. Returns { source, target, period, data: [{ date, rate, timestamp }, ...] } — sampling is fixed per period (1d=hourly, 7d/30d=daily, 1y=weekly) and the window always ends NOW. For a specific past datetime use get_rates_authenticated with `time`. For a single live rate use get_exchange_rate.",
|
|
76
|
+
inputSchema: {
|
|
77
|
+
source: ccy,
|
|
78
|
+
target: ccy,
|
|
79
|
+
period: z
|
|
80
|
+
.enum(['1d', '7d', '30d', '1y'])
|
|
81
|
+
.default('7d')
|
|
82
|
+
.describe("Lookback window ending NOW. '1d' returns ~24 hourly points, '7d' returns 7 daily points, '30d' returns 30 daily points, '1y' returns ~52 weekly points. Defaults to '7d' if omitted."),
|
|
83
|
+
},
|
|
84
|
+
outputSchema: {
|
|
85
|
+
source: z.string(),
|
|
86
|
+
target: z.string(),
|
|
87
|
+
period: z.string(),
|
|
88
|
+
source_api: z.string().optional().describe('Upstream data provider'),
|
|
89
|
+
data: z.array(z.object({
|
|
90
|
+
date: z.string(),
|
|
91
|
+
rate: z.number(),
|
|
92
|
+
timestamp: z.number(),
|
|
93
|
+
})),
|
|
94
|
+
},
|
|
95
|
+
annotations: READ_ONLY,
|
|
96
|
+
}, async ({ source, target, period }) => {
|
|
97
|
+
try {
|
|
98
|
+
return ok(await client.getHistoricalRates(source.toUpperCase(), target.toUpperCase(), period));
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
return fail(err);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
server.registerTool('get_rates_authenticated', {
|
|
105
|
+
title: 'Get multi-target or point-in-time rates',
|
|
106
|
+
description: "Use this for (a) one source against multiple targets in a single call ('USD vs EUR, GBP, JPY'), or (b) the rate at a specific past datetime ('EUR/USD at 2025-03-14T12:00Z'), optionally bucketed by hour/day/week/month. Returns { rates: [{ rate, source, target, time }, ...] } — one row per target × time bucket. For a single live pair use get_exchange_rate. For fixed lookback windows (1d/7d/30d/1y ending now) use get_historical_rates.",
|
|
107
|
+
inputSchema: {
|
|
108
|
+
source: ccy,
|
|
109
|
+
target: ccyList,
|
|
110
|
+
time: z
|
|
111
|
+
.string()
|
|
112
|
+
.datetime({ offset: true })
|
|
113
|
+
.optional()
|
|
114
|
+
.describe("Single point-in-time ISO 8601 UTC timestamp (e.g. '2025-03-14T12:00:00Z'). Omit for the latest rate."),
|
|
115
|
+
group: z
|
|
116
|
+
.enum(['hour', 'day', 'week', 'month'])
|
|
117
|
+
.optional()
|
|
118
|
+
.describe("Aggregation bucket: 'hour', 'day', 'week', or 'month'. Useful when time is omitted to return rolling averages. Omit for raw single-point output."),
|
|
119
|
+
},
|
|
120
|
+
outputSchema: {
|
|
121
|
+
rates: z.array(z.object({
|
|
122
|
+
rate: z.number(),
|
|
123
|
+
source: z.string(),
|
|
124
|
+
target: z.string(),
|
|
125
|
+
time: z.string(),
|
|
126
|
+
})),
|
|
127
|
+
},
|
|
128
|
+
annotations: READ_ONLY,
|
|
129
|
+
}, async ({ source, target, time, group }) => {
|
|
130
|
+
try {
|
|
131
|
+
const rates = await client.getAuthenticatedRates({
|
|
132
|
+
source: source.toUpperCase(),
|
|
133
|
+
target: target.toUpperCase(),
|
|
134
|
+
time,
|
|
135
|
+
group,
|
|
136
|
+
});
|
|
137
|
+
return ok({ rates });
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
return fail(err);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
server.registerTool('list_currencies', {
|
|
144
|
+
title: 'List supported currencies',
|
|
145
|
+
description: "Call this BEFORE other tools when you are unsure whether a currency code is supported, or when the user asks 'what currencies do you support?', 'is X a valid currency?', or 'what is the symbol for X?'. Returns { currencies: [{ code: 'USD', name: 'US Dollar', symbol: '$' }, ...], count } covering 150+ ISO 4217 fiat currencies. Does NOT include cryptocurrencies. Cheap to call (cached 24h upstream) — use it to validate user input and prevent downstream errors in get_exchange_rate / get_rates_authenticated / get_historical_rates.",
|
|
146
|
+
inputSchema: {},
|
|
147
|
+
outputSchema: {
|
|
148
|
+
currencies: z.array(z.object({
|
|
149
|
+
code: z.string(),
|
|
150
|
+
name: z.string(),
|
|
151
|
+
symbol: z.string(),
|
|
152
|
+
})),
|
|
153
|
+
count: z.number(),
|
|
154
|
+
},
|
|
155
|
+
annotations: READ_ONLY,
|
|
156
|
+
}, async () => {
|
|
109
157
|
try {
|
|
110
|
-
|
|
111
|
-
case 'get_exchange_rate': {
|
|
112
|
-
const { source, target } = args;
|
|
113
|
-
return text(await client.getRate(source, target));
|
|
114
|
-
}
|
|
115
|
-
case 'get_historical_rates': {
|
|
116
|
-
const { source, target, period = '7d' } = args;
|
|
117
|
-
return text(await client.getHistoricalRates(source, target, period));
|
|
118
|
-
}
|
|
119
|
-
case 'get_rates_authenticated': {
|
|
120
|
-
return text(await client.getAuthenticatedRates(args));
|
|
121
|
-
}
|
|
122
|
-
case 'list_currencies': {
|
|
123
|
-
return text(await client.listSymbols());
|
|
124
|
-
}
|
|
125
|
-
default:
|
|
126
|
-
return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true };
|
|
127
|
-
}
|
|
158
|
+
return ok(await client.listSymbols());
|
|
128
159
|
}
|
|
129
160
|
catch (err) {
|
|
130
|
-
|
|
131
|
-
? `AllRatesToday error${err.status ? ` (${err.status})` : ''}: ${err.message}`
|
|
132
|
-
: err instanceof Error
|
|
133
|
-
? err.message
|
|
134
|
-
: String(err);
|
|
135
|
-
return { content: [{ type: 'text', text: message }], isError: true };
|
|
161
|
+
return fail(err);
|
|
136
162
|
}
|
|
137
163
|
});
|
|
138
164
|
const transport = new StdioServerTransport();
|
|
139
165
|
await server.connect(transport);
|
|
140
|
-
// Keep process alive; stdio transport handles shutdown.
|
|
141
166
|
}
|
|
142
167
|
main().catch((err) => {
|
|
143
|
-
// eslint-disable-next-line no-console
|
|
144
168
|
console.error('Fatal:', err);
|
|
145
169
|
process.exit(1);
|
|
146
170
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const VERSION: string;
|
package/dist/version.js
ADDED
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@allratestoday/mcp-server",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"mcpName": "io.github.cahthuranag/
|
|
5
|
-
"description": "MCP server
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"mcpName": "io.github.cahthuranag/mcp-server",
|
|
5
|
+
"description": "MCP server for AllRatesToday — let AI coding tools (Claude Code, Cursor, Claude Desktop) fetch real-time and historical currency exchange rates.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"mcp",
|
|
8
8
|
"model-context-protocol",
|
|
@@ -10,15 +10,16 @@
|
|
|
10
10
|
"cursor",
|
|
11
11
|
"currency",
|
|
12
12
|
"exchange-rate",
|
|
13
|
-
"forex"
|
|
13
|
+
"forex",
|
|
14
|
+
"allratestoday"
|
|
14
15
|
],
|
|
15
16
|
"license": "MIT",
|
|
16
|
-
"homepage": "https://
|
|
17
|
+
"homepage": "https://allratestoday.com",
|
|
17
18
|
"repository": {
|
|
18
19
|
"type": "git",
|
|
19
|
-
"url": "git+https://github.com/cahthuranag/
|
|
20
|
+
"url": "git+https://github.com/cahthuranag/mcp-server.git"
|
|
20
21
|
},
|
|
21
|
-
"bugs": "https://github.com/cahthuranag/
|
|
22
|
+
"bugs": "https://github.com/cahthuranag/mcp-server/issues",
|
|
22
23
|
"type": "module",
|
|
23
24
|
"main": "dist/index.js",
|
|
24
25
|
"bin": {
|
|
@@ -34,12 +35,13 @@
|
|
|
34
35
|
},
|
|
35
36
|
"scripts": {
|
|
36
37
|
"build": "tsc",
|
|
38
|
+
"test": "node --test test/*.test.mjs",
|
|
37
39
|
"prepublishOnly": "npm run build",
|
|
38
40
|
"start": "node dist/index.js",
|
|
39
41
|
"dev": "tsc --watch"
|
|
40
42
|
},
|
|
41
43
|
"dependencies": {
|
|
42
|
-
"@modelcontextprotocol/sdk": "^1.0
|
|
44
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
43
45
|
"zod": "^3.23.8"
|
|
44
46
|
},
|
|
45
47
|
"devDependencies": {
|