@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 CHANGED
@@ -1,4 +1,6 @@
1
- # Realtime Exchange Rate MCP Server
1
+ # AllRatesToday MCP Server
2
+
3
+ [![Powered by AllRatesToday](https://img.shields.io/badge/Powered%20by-AllRatesToday-orange.svg)](https://allratestoday.com)
2
4
 
3
5
  [![npm version](https://img.shields.io/npm/v/@allratestoday/mcp-server.svg?style=flat-square)](https://www.npmjs.com/package/@allratestoday/mcp-server)
4
6
  [![npm downloads](https://img.shields.io/npm/dm/@allratestoday/mcp-server.svg?style=flat-square)](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`. Rates are served by [AllRatesToday](https://allratestoday.com); a free key is enough for development and personal use.
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 the key is missing, the server prints clear registration instructions on stderr and exits with code 1.
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 an API key.
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" | Plan request limit hit | Wait until next month or upgrade plan |
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 | Upstream server-side issue | `HTTP 5xx — <upstream message>` |
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 the upstream API — never the LLM's conversation context, sheet contents, or anything else.
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 upstream API. It's never logged or transmitted elsewhere.
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 an initial upstream cache miss. Subsequent calls are fast (<200ms typically).
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 point to your own instance.
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/realtime-exchange-rate-mcp.git
400
- cd realtime-exchange-rate-mcp
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/realtime-exchange-rate-mcp](https://github.com/cahthuranag/realtime-exchange-rate-mcp). Before opening a PR:
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/realtime-exchange-rate-mcp/releases) for the full list. Recent highlights:
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
- - **Bug reports**: [github.com/cahthuranag/realtime-exchange-rate-mcp/issues](https://github.com/cahthuranag/realtime-exchange-rate-mcp/issues)
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': 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 msg = (body && typeof body === 'object' && 'error' in body && typeof body.error === 'string'
58
+ const upstream = body && typeof body === 'object' && 'error' in body && typeof body.error === 'string'
47
59
  ? body.error
48
- : `HTTP ${res.status}`);
49
- throw new AllRatesTodayError(msg, res.status, body);
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 { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
- import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
4
+ import { z } from 'zod';
5
5
  import { AllRatesTodayClient, AllRatesTodayError } from './client.js';
6
- const CCY = {
7
- type: 'string',
8
- description: 'ISO 4217 currency code (e.g. USD, EUR, GBP).',
9
- minLength: 3,
10
- maxLength: 3,
11
- };
12
- const tools = [
13
- {
14
- name: 'get_exchange_rate',
15
- description: 'Get the current mid-market exchange rate between two currencies. Returns a single rate number. Requires API key (ALLRATES_API_KEY).',
16
- inputSchema: {
17
- type: 'object',
18
- additionalProperties: false,
19
- properties: {
20
- source: CCY,
21
- target: CCY,
22
- },
23
- required: ['source', 'target'],
24
- },
25
- },
26
- {
27
- name: 'get_historical_rates',
28
- description: 'Get historical exchange-rate data points for a currency pair over a period. Periods: 1d (hourly), 7d (daily), 30d (daily), 1y (weekly). Requires API key (ALLRATES_API_KEY).',
29
- inputSchema: {
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
- ' This MCP server requires an API key.',
36
+ ' AllRatesToday MCP server requires an API key.',
87
37
  '',
88
- ' 1. Get a free key at https://allratestoday.com/register',
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 Server({ name: 'realtime-exchange-rate-mcp', version: '0.3.2' }, { capabilities: { tools: {} } });
106
- server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
107
- server.setRequestHandler(CallToolRequestSchema, async (req) => {
108
- const { name, arguments: args = {} } = req.params;
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
- switch (name) {
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
- const message = err instanceof AllRatesTodayError
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;
@@ -0,0 +1,3 @@
1
+ import { createRequire } from 'node:module';
2
+ const require = createRequire(import.meta.url);
3
+ export const VERSION = require('../package.json').version;
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@allratestoday/mcp-server",
3
- "version": "0.3.3",
4
- "mcpName": "io.github.cahthuranag/realtime-exchange-rate-mcp",
5
- "description": "MCP server that lets AI coding tools (Claude Code, Cursor, Claude Desktop) fetch real-time and historical currency exchange rates.",
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://github.com/cahthuranag/realtime-exchange-rate-mcp",
17
+ "homepage": "https://allratestoday.com",
17
18
  "repository": {
18
19
  "type": "git",
19
- "url": "git+https://github.com/cahthuranag/realtime-exchange-rate-mcp.git"
20
+ "url": "git+https://github.com/cahthuranag/mcp-server.git"
20
21
  },
21
- "bugs": "https://github.com/cahthuranag/realtime-exchange-rate-mcp/issues",
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.4",
44
+ "@modelcontextprotocol/sdk": "^1.29.0",
43
45
  "zod": "^3.23.8"
44
46
  },
45
47
  "devDependencies": {