@pipeworx/mcp-rba 0.2.0 → 0.2.2

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
@@ -5,7 +5,7 @@ and any of the 71 published statistical tables (interest rates, bond yields,
5
5
  CPI, monetary aggregates, and more), scraped keyless from RBA's own CSV
6
6
  endpoints.
7
7
 
8
- Part of [Pipeworx](https://pipeworx.io) — an MCP gateway connecting AI agents to 1679+ live data sources.
8
+ Part of [Pipeworx](https://pipeworx.io) — an MCP gateway connecting AI agents to 1819+ live data sources. This is an independent, unofficial integration — not affiliated with, endorsed by, or published by the upstream provider.
9
9
 
10
10
  ## Tools
11
11
 
@@ -110,7 +110,7 @@ directly, instead of just this one's:
110
110
  }
111
111
  ```
112
112
 
113
- Both URLs reach the same gateway and the same 1679+ data sources. The
113
+ Both URLs reach the same gateway and the same 1819+ data sources. The
114
114
  only difference is which pack's tools are listed **directly**; `ask_pipeworx`
115
115
  reaches all of them from either one.
116
116
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pipeworx/mcp-rba",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "RBA MCP — Reserve Bank of Australia statistics (free, no auth).",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -26,7 +26,7 @@
26
26
  "@cloudflare/workers-types": "^4.20260405.1"
27
27
  },
28
28
  "pipeworx": {
29
- "sourceHash": "v1-2efb161ccc6a2b5c47fc6f337d5cbc2cf50a5b2815c24fcea83803f4418e9d49",
30
- "sourceCommit": "00c836ddeaecbdd581dcfb0fb1842efea104006e"
29
+ "sourceHash": "v1-9027e1b23bb9afae10c3d5e5bb0ca300add8a80ccef5528fe6f07682ec0fa03d",
30
+ "sourceCommit": "f5821587e59206a76fc200d1b9cbeaad15a549ab"
31
31
  }
32
32
  }
package/server.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "name": "io.github.pipeworx-io/rba",
4
4
  "title": "Rba",
5
5
  "description": "RBA MCP — Reserve Bank of Australia statistics (free, no auth).",
6
- "version": "0.2.0",
6
+ "version": "0.2.2",
7
7
  "websiteUrl": "https://pipeworx.io/packs/rba",
8
8
  "repository": {
9
9
  "url": "https://github.com/pipeworx-io/mcp-rba",
package/src/index.ts CHANGED
@@ -452,7 +452,42 @@ async function fetchWithTimeout(
452
452
  ),
453
453
  );
454
454
  }
455
- throw err;
455
+ // Fleet #2382. Everything that isn't a timeout/abort here is a genuine
456
+ // NETWORK-LEVEL failure — DNS resolution, connection refused, TLS handshake,
457
+ // Cloudflare's own "Network connection lost." — meaning `fetch()` itself
458
+ // threw and no HTTP response of any kind was ever received. Until this fix
459
+ // that raw exception was rethrown VERBATIM: a bare `TypeError: fetch failed`
460
+ // (or the Workers-runtime equivalent) names no upstream, carries no class
461
+ // token, and reads exactly like a defect in OUR code — because it says
462
+ // nothing about the call at all. It landed in `error`, the tier that means
463
+ // "Pipeworx has a defect", for every one of the (at the time of writing)
464
+ // ~470 packs that call this helper directly with no wrapper of their own.
465
+ //
466
+ // `dexscreener` hit this independently (fleet #1579) and fixed it with a
467
+ // bespoke per-pack try/catch around `fetchWithTimeout`. That fix is correct
468
+ // but only covers one pack; every other caller of this shared helper still
469
+ // leaked the raw exception. Moving the same fix HERE — the one place that
470
+ // already carries the timeout case — covers every pack that uses
471
+ // `fetchWithTimeout` without a wrapper, for free, and without widening
472
+ // `classifyToolError`'s regex list: the fix is giving the message a proper
473
+ // `upstream_down:` token at the point the two facts (no response was ever
474
+ // received, and which host we were trying to reach) are actually in hand,
475
+ // not teaching the classifier to guess from prose after the fact.
476
+ //
477
+ // Safe on the same grounds as the timeout branch above: no argument a
478
+ // caller passes can make `fetch()` itself throw a connection-level error,
479
+ // so this is always an availability failure, never a caller mistake. Same
480
+ // `markInternalOrigin` treatment — an origin we run that never answered is
481
+ // still ours, not a third party's outage.
482
+ const raw = err instanceof Error ? err.message : String(err);
483
+ throw new Error(
484
+ markInternalOrigin(
485
+ `upstream_down: could not reach ${name} at all (${raw.slice(0, 160)}). ` +
486
+ `No request reached ${name}, so this says NOTHING about whether the arguments you passed ` +
487
+ 'are valid — do not re-check them on the strength of this error. Retry shortly.',
488
+ url,
489
+ ),
490
+ );
456
491
  }
457
492
  }
458
493
 
@@ -752,7 +787,7 @@ const tools: McpToolExport['tools'] = [
752
787
  {
753
788
  name: 'rba_cash_rate',
754
789
  description:
755
- "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.",
790
+ "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, the exact date it took effect, the previous rate and when that one took effect, plus recent decision history (table A2, one row per actual decision) — not just a monthly-average series to infer a decision date from. Also returns the F1.1 monthly-average history as before.",
756
791
  inputSchema: {
757
792
  type: 'object' as const,
758
793
  properties: {
@@ -929,17 +964,52 @@ function seriesObservations(t: ParsedTable, seriesId: string, recent: number) {
929
964
 
930
965
  async function cashRate(recent?: number) {
931
966
  const n = Math.min(120, Math.max(1, recent ?? 12));
932
- const t = await fetchTable('f1.1');
967
+ const [t, changes] = await Promise.all([fetchTable('f1.1'), fetchTable('a2')]);
933
968
  const r = seriesObservations(t, 'FIRMMCRT', n);
934
969
  if (!r) throw new Error('RBA cash rate series (FIRMMCRT) not found in table f1.1.');
935
970
  const latest = r.observations[r.observations.length - 1] ?? null;
971
+ // FIRMMCRT (table F1.1) is a MONTHLY AVERAGE, not a point-in-time value — a
972
+ // mid-month decision blends into that month's figure, so it cannot answer
973
+ // "effective since" exactly (e.g. a 30-Sep decision barely moves the
974
+ // September average). Table A2 "Changes in Monetary Policy and Administered
975
+ // Rates" is "As announced" — one row per actual decision, with its exact
976
+ // date and the new target — so the computed fields come from there instead
977
+ // (fleet #3206).
978
+ const decisions = seriesObservations(changes, 'ARBAMPCNCRT', 999999);
979
+ const computed = decisions && decisions.observations.length > 0 ? computeFromDecisions(decisions.observations) : null;
936
980
  return {
981
+ ...(computed ?? {}),
937
982
  series_id: 'FIRMMCRT',
938
983
  name: r.series.name,
939
984
  units: r.series.units,
940
985
  current_rate: latest ? latest.value : null,
941
986
  as_of: latest ? latest.date : null,
942
987
  history: r.observations,
988
+ note:
989
+ 'rate/effective_since/previous_rate/decision_history are computed from table A2 ("Changes in Monetary ' +
990
+ 'Policy and Administered Rates" — one row per actual RBA decision, with its exact date). current_rate/' +
991
+ 'as_of/history remain the F1.1 monthly-average series as before, which smooths over the exact decision ' +
992
+ 'date and should not be used to answer "when did it last change".',
993
+ };
994
+ }
995
+
996
+ /** {rate, effective_since, previous_rate, previous_rate_effective_since, decision_history, data_as_of} from ascending {date,value} decision rows (already one row per change — no de-duplication needed). */
997
+ function computeFromDecisions(obs: { date: string; value: number | null }[]) {
998
+ const pts = obs.filter((o) => o.value !== null) as { date: string; value: number }[];
999
+ if (pts.length === 0) return null;
1000
+ const current = pts[pts.length - 1];
1001
+ const previous = pts.length > 1 ? pts[pts.length - 2] : null;
1002
+ return {
1003
+ rate: current.value,
1004
+ unit: 'percent',
1005
+ effective_since: current.date,
1006
+ previous_rate: previous?.value ?? null,
1007
+ previous_rate_effective_since: previous?.date ?? null,
1008
+ decision_history: pts
1009
+ .slice(-5)
1010
+ .reverse()
1011
+ .map((p) => ({ rate: p.value, effective_since: p.date })),
1012
+ data_as_of: current.date,
943
1013
  };
944
1014
  }
945
1015
 
package/src/server.ts CHANGED
@@ -8,8 +8,9 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
8
8
  import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
9
9
  import pack from './index.js';
10
10
 
11
+
11
12
  const server = new Server(
12
- { name: '@pipeworx/mcp-rba', version: '0.2.0' },
13
+ { name: '@pipeworx/mcp-rba', version: '0.2.2' },
13
14
  { capabilities: { tools: {} } },
14
15
  );
15
16
 
@@ -24,7 +25,9 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
24
25
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
25
26
  const { name, arguments: args } = request.params;
26
27
  try {
27
- const result = await pack.callTool(name, (args ?? {}) as Record<string, unknown>);
28
+ const callArgs = { ...((args ?? {}) as Record<string, unknown>) };
29
+
30
+ const result = await pack.callTool(name, callArgs);
28
31
  return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
29
32
  } catch (err) {
30
33
  return {