@pipeworx/mcp-rba 0.1.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/LICENSE +21 -0
- package/README.md +55 -0
- package/package.json +20 -0
- package/server.json +18 -0
- package/src/index.ts +257 -0
- package/tsconfig.json +14 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Pipeworx
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# mcp-rba
|
|
2
|
+
|
|
3
|
+
RBA MCP — Reserve Bank of Australia statistics (free, no auth).
|
|
4
|
+
|
|
5
|
+
Part of [Pipeworx](https://pipeworx.io) — an MCP gateway connecting AI agents to 884+ live data sources.
|
|
6
|
+
|
|
7
|
+
## Tools
|
|
8
|
+
|
|
9
|
+
| Tool | Description |
|
|
10
|
+
|------|-------------|
|
|
11
|
+
|
|
12
|
+
## Quick Start
|
|
13
|
+
|
|
14
|
+
Add to your MCP client (Claude Desktop, Cursor, Windsurf, etc.):
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"mcpServers": {
|
|
19
|
+
"rba": {
|
|
20
|
+
"url": "https://gateway.pipeworx.io/rba/mcp"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Or connect to the full Pipeworx gateway for access to all 884+ data sources:
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"mcpServers": {
|
|
31
|
+
"pipeworx": {
|
|
32
|
+
"url": "https://gateway.pipeworx.io/mcp"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Using with ask_pipeworx
|
|
39
|
+
|
|
40
|
+
Instead of calling tools directly, you can ask questions in plain English:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
ask_pipeworx({ question: "your question about Rba data" })
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The gateway picks the right tool and fills the arguments automatically.
|
|
47
|
+
|
|
48
|
+
## More
|
|
49
|
+
|
|
50
|
+
- [All tools and guides](https://github.com/pipeworx-io/examples)
|
|
51
|
+
- [pipeworx.io](https://pipeworx.io)
|
|
52
|
+
|
|
53
|
+
## License
|
|
54
|
+
|
|
55
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pipeworx/mcp-rba",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "RBA MCP — Reserve Bank of Australia statistics (free, no auth).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.ts",
|
|
7
|
+
"types": "src/index.ts",
|
|
8
|
+
"keywords": ["mcp", "mcp-server", "model-context-protocol", "pipeworx", "rba"],
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/pipeworx-io/mcp-rba"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"typecheck": "tsc --noEmit"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"typescript": "^5.7.0"
|
|
19
|
+
}
|
|
20
|
+
}
|
package/server.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
+
"name": "io.github.pipeworx-io/rba",
|
|
4
|
+
"title": "Rba",
|
|
5
|
+
"description": "RBA MCP — Reserve Bank of Australia statistics (free, no auth).",
|
|
6
|
+
"version": "0.1.0",
|
|
7
|
+
"websiteUrl": "https://pipeworx.io/packs/rba",
|
|
8
|
+
"repository": {
|
|
9
|
+
"url": "https://github.com/pipeworx-io/mcp-rba",
|
|
10
|
+
"source": "github"
|
|
11
|
+
},
|
|
12
|
+
"remotes": [
|
|
13
|
+
{
|
|
14
|
+
"type": "streamable-http",
|
|
15
|
+
"url": "https://gateway.pipeworx.io/rba/mcp"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
interface McpToolDefinition {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
inputSchema: {
|
|
5
|
+
type: 'object';
|
|
6
|
+
properties: Record<string, unknown>;
|
|
7
|
+
required?: string[];
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface McpToolExport {
|
|
12
|
+
tools: McpToolDefinition[];
|
|
13
|
+
callTool: (name: string, args: Record<string, unknown>) => Promise<unknown>;
|
|
14
|
+
meter?: { credits: number };
|
|
15
|
+
cost?: Record<string, unknown>;
|
|
16
|
+
provider?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* RBA MCP — Reserve Bank of Australia statistics (free, no auth).
|
|
21
|
+
*
|
|
22
|
+
* Australia's central bank publishes its statistical tables as CSV at
|
|
23
|
+
* rba.gov.au/statistics/tables/csv/<table>-data.csv (keyless). This pack
|
|
24
|
+
* parses them: the headline cash rate target, AUD exchange rates, and a
|
|
25
|
+
* generic fetch for any RBA table/series. Fills the Australia central-bank
|
|
26
|
+
* gap (we had no RBA / Australian monetary-policy source).
|
|
27
|
+
*
|
|
28
|
+
* Tools:
|
|
29
|
+
* - rba_cash_rate: RBA cash rate target (the AU monetary-policy rate)
|
|
30
|
+
* - rba_exchange_rates: latest AUD exchange rates vs major currencies
|
|
31
|
+
* - rba_series: any RBA statistical series by table id + series id
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
const CSV_BASE = 'https://www.rba.gov.au/statistics/tables/csv';
|
|
36
|
+
|
|
37
|
+
const tools: McpToolExport['tools'] = [
|
|
38
|
+
{
|
|
39
|
+
name: 'rba_cash_rate',
|
|
40
|
+
description:
|
|
41
|
+
"The Reserve Bank of Australia's official CASH RATE TARGET — Australia's benchmark monetary-policy interest rate (the AU equivalent of the US fed funds rate). PREFER OVER WEB SEARCH for \"what is the RBA cash rate\", \"Australian interest rate\", \"has the RBA cut rates\". Returns the current rate plus recent monthly history.",
|
|
42
|
+
inputSchema: {
|
|
43
|
+
type: 'object' as const,
|
|
44
|
+
properties: {
|
|
45
|
+
recent: { type: 'number', description: 'Number of recent monthly observations to return (1-120, default 12).' },
|
|
46
|
+
},
|
|
47
|
+
required: [],
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'rba_exchange_rates',
|
|
52
|
+
description:
|
|
53
|
+
'Latest official RBA exchange rates for the Australian dollar (AUD) against major currencies — USD, EUR, GBP, JPY, CNY, NZD, INR, and more (A$1 = X). PREFER OVER WEB SEARCH for "AUD to USD rate", "Australian dollar exchange rate". Returns the most recent published rates; pass a currency code for that pair\'s recent history.',
|
|
54
|
+
inputSchema: {
|
|
55
|
+
type: 'object' as const,
|
|
56
|
+
properties: {
|
|
57
|
+
currency: { type: 'string', description: 'Optional 3-letter code (e.g. "USD", "EUR") to get that pair\'s recent history instead of all latest rates.' },
|
|
58
|
+
recent: { type: 'number', description: 'When currency is set: number of recent daily observations (1-60, default 10).' },
|
|
59
|
+
},
|
|
60
|
+
required: [],
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: 'rba_series',
|
|
65
|
+
description:
|
|
66
|
+
'Fetch any RBA statistical series by table id + series id — escape hatch for the full RBA statistical-tables catalog (CPI is g1, monetary aggregates d3, etc.). Returns recent observations. Use rba_cash_rate / rba_exchange_rates for the common ones. Browse tables at rba.gov.au/statistics/tables.',
|
|
67
|
+
inputSchema: {
|
|
68
|
+
type: 'object' as const,
|
|
69
|
+
properties: {
|
|
70
|
+
table: { type: 'string', description: 'RBA table id, e.g. "f1.1" (money market), "f11.1" (FX), "g1" (CPI), "d3" (monetary aggregates).' },
|
|
71
|
+
series_id: { type: 'string', description: 'RBA series id within the table, e.g. "FIRMMCRT" (cash rate target), "FXRUSD" (A$/USD).' },
|
|
72
|
+
recent: { type: 'number', description: 'Recent observations to return (1-120, default 12).' },
|
|
73
|
+
},
|
|
74
|
+
required: ['table', 'series_id'],
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
// ── CSV parsing ──────────────────────────────────────────────────────
|
|
80
|
+
// RBA tables share a fixed shape: a title line, then labelled header rows
|
|
81
|
+
// (Title, Description, Frequency, Type, Units, Source, Publication date,
|
|
82
|
+
// Series ID), then data rows keyed by DD/MM/YYYY. Columns 1..N are series.
|
|
83
|
+
|
|
84
|
+
interface RbaSeries {
|
|
85
|
+
series_id: string;
|
|
86
|
+
name: string;
|
|
87
|
+
units: string;
|
|
88
|
+
col: number;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function splitCsvLine(line: string): string[] {
|
|
92
|
+
const out: string[] = [];
|
|
93
|
+
let cur = '';
|
|
94
|
+
let q = false;
|
|
95
|
+
for (let i = 0; i < line.length; i++) {
|
|
96
|
+
const c = line[i];
|
|
97
|
+
if (q) {
|
|
98
|
+
if (c === '"' && line[i + 1] === '"') { cur += '"'; i++; }
|
|
99
|
+
else if (c === '"') q = false;
|
|
100
|
+
else cur += c;
|
|
101
|
+
} else if (c === '"') q = true;
|
|
102
|
+
else if (c === ',') { out.push(cur); cur = ''; }
|
|
103
|
+
else cur += c;
|
|
104
|
+
}
|
|
105
|
+
out.push(cur);
|
|
106
|
+
return out;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// RBA tables use two date formats: DD/MM/YYYY (e.g. f1.1) and DD-Mon-YYYY
|
|
110
|
+
// (e.g. f11.1 exchange rates). Recognize and normalize both to YYYY-MM-DD.
|
|
111
|
+
const MONTHS: Record<string, string> = {
|
|
112
|
+
jan: '01', feb: '02', mar: '03', apr: '04', may: '05', jun: '06',
|
|
113
|
+
jul: '07', aug: '08', sep: '09', oct: '10', nov: '11', dec: '12',
|
|
114
|
+
};
|
|
115
|
+
const DATE_RE = /^(\d{2})[/-]([0-9]{2}|[A-Za-z]{3})[/-](\d{4})$/;
|
|
116
|
+
|
|
117
|
+
function isDateCell(s: string): boolean {
|
|
118
|
+
return DATE_RE.test(s.trim());
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function toIso(d: string): string {
|
|
122
|
+
const m = DATE_RE.exec(d.trim());
|
|
123
|
+
if (!m) return d.trim();
|
|
124
|
+
const mon = /^\d{2}$/.test(m[2]) ? m[2] : (MONTHS[m[2].toLowerCase()] ?? m[2]);
|
|
125
|
+
return `${m[3]}-${mon}-${m[1]}`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
interface ParsedTable {
|
|
129
|
+
table_title: string;
|
|
130
|
+
series: RbaSeries[];
|
|
131
|
+
rows: { date: string; values: (number | null)[] }[]; // values[col-1]
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function fetchTable(tableId: string): Promise<ParsedTable> {
|
|
135
|
+
const id = tableId.trim().toLowerCase().replace(/[^a-z0-9.]/g, '');
|
|
136
|
+
const res = await fetch(`${CSV_BASE}/${id}-data.csv`, { headers: { 'User-Agent': 'Pipeworx/1.0 (pipeworx.io)' } });
|
|
137
|
+
if (!res.ok) throw new Error(`RBA table "${tableId}" not found (HTTP ${res.status}). Check the id at rba.gov.au/statistics/tables.`);
|
|
138
|
+
const text = (await res.text()).replace(/^/, '');
|
|
139
|
+
const lines = text.split(/\r?\n/);
|
|
140
|
+
|
|
141
|
+
let titleRow: string[] = [], unitsRow: string[] = [], idRow: string[] = [];
|
|
142
|
+
let tableTitle = '';
|
|
143
|
+
let dataStart = -1;
|
|
144
|
+
for (let i = 0; i < lines.length; i++) {
|
|
145
|
+
const cells = splitCsvLine(lines[i]);
|
|
146
|
+
const head = cells[0]?.trim();
|
|
147
|
+
if (i === 0) tableTitle = head ?? '';
|
|
148
|
+
if (head === 'Title') titleRow = cells;
|
|
149
|
+
else if (head === 'Units') unitsRow = cells;
|
|
150
|
+
else if (head === 'Series ID') { idRow = cells; dataStart = i + 1; }
|
|
151
|
+
else if (dataStart >= 0 && isDateCell(head ?? '')) { dataStart = i; break; }
|
|
152
|
+
}
|
|
153
|
+
if (idRow.length === 0) throw new Error(`RBA table "${tableId}" has an unexpected format (no Series ID row).`);
|
|
154
|
+
|
|
155
|
+
const series: RbaSeries[] = [];
|
|
156
|
+
for (let c = 1; c < idRow.length; c++) {
|
|
157
|
+
const sid = idRow[c]?.trim();
|
|
158
|
+
if (sid) series.push({ series_id: sid, name: (titleRow[c] ?? '').trim(), units: (unitsRow[c] ?? '').trim(), col: c });
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const rows: ParsedTable['rows'] = [];
|
|
162
|
+
for (let i = dataStart; i < lines.length; i++) {
|
|
163
|
+
const cells = splitCsvLine(lines[i]);
|
|
164
|
+
if (!isDateCell(cells[0]?.trim() ?? '')) continue;
|
|
165
|
+
const values = series.map((s) => {
|
|
166
|
+
const v = cells[s.col]?.trim();
|
|
167
|
+
if (v == null || v === '') return null;
|
|
168
|
+
const n = Number(v);
|
|
169
|
+
return Number.isFinite(n) ? n : null;
|
|
170
|
+
});
|
|
171
|
+
rows.push({ date: toIso(cells[0].trim()), values });
|
|
172
|
+
}
|
|
173
|
+
return { table_title: tableTitle, series, rows };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Recent observations for one series within a parsed table (skip blank values).
|
|
177
|
+
function seriesObservations(t: ParsedTable, seriesId: string, recent: number) {
|
|
178
|
+
const idx = t.series.findIndex((s) => s.series_id.toUpperCase() === seriesId.toUpperCase());
|
|
179
|
+
if (idx === -1) return null;
|
|
180
|
+
const s = t.series[idx];
|
|
181
|
+
const obs = t.rows
|
|
182
|
+
.map((r) => ({ date: r.date, value: r.values[idx] }))
|
|
183
|
+
.filter((o) => o.value !== null);
|
|
184
|
+
return { series: s, observations: obs.slice(-Math.max(1, recent)) };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// ── Tool implementations ─────────────────────────────────────────────
|
|
188
|
+
|
|
189
|
+
async function cashRate(recent?: number) {
|
|
190
|
+
const n = Math.min(120, Math.max(1, recent ?? 12));
|
|
191
|
+
const t = await fetchTable('f1.1');
|
|
192
|
+
const r = seriesObservations(t, 'FIRMMCRT', n);
|
|
193
|
+
if (!r) throw new Error('RBA cash rate series (FIRMMCRT) not found in table f1.1.');
|
|
194
|
+
const latest = r.observations[r.observations.length - 1] ?? null;
|
|
195
|
+
return {
|
|
196
|
+
series_id: 'FIRMMCRT',
|
|
197
|
+
name: r.series.name,
|
|
198
|
+
units: r.series.units,
|
|
199
|
+
current_rate: latest ? latest.value : null,
|
|
200
|
+
as_of: latest ? latest.date : null,
|
|
201
|
+
history: r.observations,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async function exchangeRates(currency?: string, recent?: number) {
|
|
206
|
+
const t = await fetchTable('f11.1');
|
|
207
|
+
if (currency && currency.trim()) {
|
|
208
|
+
const code = currency.trim().toUpperCase();
|
|
209
|
+
// Map common code -> RBA series id (FXR + code-ish); fall back to matching the name "A$1=CODE".
|
|
210
|
+
const match = t.series.find((s) => s.name.toUpperCase().endsWith(`=${code}`) || s.units.toUpperCase() === code);
|
|
211
|
+
if (!match) return { error: 'currency_not_found', message: `No AUD/${code} series in RBA f11.1. Try USD, EUR, GBP, JPY, CNY, NZD, INR, etc.` };
|
|
212
|
+
const r = seriesObservations(t, match.series_id, Math.min(60, Math.max(1, recent ?? 10)))!;
|
|
213
|
+
return { pair: `AUD/${code}`, series_id: match.series_id, units: match.units, observations: r.observations };
|
|
214
|
+
}
|
|
215
|
+
// Latest rate for every currency: take the last row with a value per series.
|
|
216
|
+
const rates: Record<string, { value: number; date: string }> = {};
|
|
217
|
+
for (let i = 0; i < t.series.length; i++) {
|
|
218
|
+
const s = t.series[i];
|
|
219
|
+
for (let r = t.rows.length - 1; r >= 0; r--) {
|
|
220
|
+
const v = t.rows[r].values[i];
|
|
221
|
+
if (v !== null) { rates[s.units || s.name] = { value: v, date: t.rows[r].date }; break; }
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return { base: 'AUD', note: 'A$1 = value (i.e. how many units of each currency one Australian dollar buys; TWI/SDR are indices).', latest_rates: rates };
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async function genericSeries(table: string, seriesId: string, recent?: number) {
|
|
228
|
+
const n = Math.min(120, Math.max(1, recent ?? 12));
|
|
229
|
+
const t = await fetchTable(table);
|
|
230
|
+
const r = seriesObservations(t, seriesId, n);
|
|
231
|
+
if (!r) return { error: 'series_not_found', message: `Series "${seriesId}" not in RBA table "${table}". Available: ${t.series.slice(0, 25).map((s) => s.series_id).join(', ')}${t.series.length > 25 ? ' …' : ''}` };
|
|
232
|
+
return {
|
|
233
|
+
table,
|
|
234
|
+
table_title: t.table_title,
|
|
235
|
+
series_id: r.series.series_id,
|
|
236
|
+
name: r.series.name,
|
|
237
|
+
units: r.series.units,
|
|
238
|
+
observations: r.observations,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// ── Router ───────────────────────────────────────────────────────────
|
|
243
|
+
|
|
244
|
+
async function callTool(name: string, args: Record<string, unknown>): Promise<unknown> {
|
|
245
|
+
switch (name) {
|
|
246
|
+
case 'rba_cash_rate':
|
|
247
|
+
return cashRate(args.recent as number | undefined);
|
|
248
|
+
case 'rba_exchange_rates':
|
|
249
|
+
return exchangeRates(args.currency as string | undefined, args.recent as number | undefined);
|
|
250
|
+
case 'rba_series':
|
|
251
|
+
return genericSeries(args.table as string, args.series_id as string, args.recent as number | undefined);
|
|
252
|
+
default:
|
|
253
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export default { tools, callTool, meter: { credits: 1 } } satisfies McpToolExport;
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"outDir": "dist",
|
|
10
|
+
"rootDir": "src",
|
|
11
|
+
"declaration": true
|
|
12
|
+
},
|
|
13
|
+
"include": ["src"]
|
|
14
|
+
}
|