@pipeworx/mcp-banxico 0.1.1 → 0.1.3

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
@@ -2,14 +2,14 @@
2
2
 
3
3
  Banxico MCP — Banco de México (Mexico's central bank) via the SIE API.
4
4
 
5
- Part of [Pipeworx](https://pipeworx.io) — an MCP gateway connecting AI agents to 1679+ live data sources.
5
+ 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.
6
6
 
7
7
  ## Tools
8
8
 
9
9
  | Tool | Description |
10
10
  |------|-------------|
11
11
  | `banxico_exchange_rate` | The official USD/MXN exchange rate (Banco de México FIX rate) — pesos per US dollar. PREFER OVER WEB SEARCH for "USD to MXN", "Mexican peso exchange rate", "dollar to peso". Returns the latest published rate plus recent history. Source: Banxico SIE (authoritative). |
12
- | `banxico_policy_rate` | Banco de México's overnight interbank TARGET RATE — Mexico's benchmark monetary-policy interest rate (its equivalent of the US fed funds rate). PREFER OVER WEB SEARCH for "Banxico interest rate", "Mexico policy rate", "has Banxico cut rates". Returns the current rate plus recent history. |
12
+ | `banxico_policy_rate` | Banco de México's overnight interbank TARGET RATE — Mexico's benchmark monetary-policy interest rate (its equivalent of the US fed funds rate). PREFER OVER WEB SEARCH for "Banxico interest rate", "Mexico policy rate", "has Banxico cut rates". Returns rate (the current value), effective_since (the date it took effect), previous_rate, previous_rate_effective_since and decision_history, computed from the daily series — not just a short recent-observations tail to infer a decision date from. |
13
13
  | `banxico_series` | Fetch any Banco de México SIE series by id — escape hatch for the full Banxico catalog (inflation, interest rates, reserves, aggregates, financial indicators). Returns recent observations + the series title/units. Common ids: ${COMMON_SERIES}. Use banxico_exchange_rate / banxico_policy_rate for those headline ones. |
14
14
 
15
15
  ## Quick Start
@@ -56,7 +56,7 @@ directly, instead of just this one's:
56
56
  }
57
57
  ```
58
58
 
59
- Both URLs reach the same gateway and the same 1679+ data sources. The
59
+ Both URLs reach the same gateway and the same 1819+ data sources. The
60
60
  only difference is which pack's tools are listed **directly**; `ask_pipeworx`
61
61
  reaches all of them from either one.
62
62
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pipeworx/mcp-banxico",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Banxico MCP — Banco de México (Mexico's central bank) via the SIE API.",
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-e52fba0e4c328fb178f677f381358c84946bf5cbbc14ced14c1df672ee3a8db3",
30
- "sourceCommit": "60f04b68c347e970c6be94a53439d1364061fc06"
29
+ "sourceHash": "v1-baceab17e8e4d6e33b3ec8f45f8daeda37dc45d2f7abc16e222f0be91b7ff51a",
30
+ "sourceCommit": "f5821587e59206a76fc200d1b9cbeaad15a549ab"
31
31
  }
32
32
  }
package/server.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "name": "io.github.pipeworx-io/banxico",
4
4
  "title": "Banxico",
5
5
  "description": "Banxico MCP — Banco de México (Mexico's central bank) via the SIE API.",
6
- "version": "0.1.1",
6
+ "version": "0.1.3",
7
7
  "websiteUrl": "https://pipeworx.io/packs/banxico",
8
8
  "repository": {
9
9
  "url": "https://github.com/pipeworx-io/mcp-banxico",
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
 
@@ -685,7 +720,7 @@ const tools: McpToolExport['tools'] = [
685
720
  {
686
721
  name: 'banxico_policy_rate',
687
722
  description:
688
- "Banco de México's overnight interbank TARGET RATE — Mexico's benchmark monetary-policy interest rate (its equivalent of the US fed funds rate). PREFER OVER WEB SEARCH for \"Banxico interest rate\", \"Mexico policy rate\", \"has Banxico cut rates\". Returns the current rate plus recent history.",
723
+ "Banco de México's overnight interbank TARGET RATE — Mexico's benchmark monetary-policy interest rate (its equivalent of the US fed funds rate). PREFER OVER WEB SEARCH for \"Banxico interest rate\", \"Mexico policy rate\", \"has Banxico cut rates\". Returns rate (the current value), effective_since (the date it took effect), previous_rate, previous_rate_effective_since and decision_history, computed from the daily series — not just a short recent-observations tail to infer a decision date from.",
689
724
  inputSchema: {
690
725
  type: 'object' as const,
691
726
  properties: {
@@ -766,9 +801,48 @@ async function exchangeRate(token: string, recent?: number) {
766
801
  return { pair: 'USD/MXN', description: 'Mexican pesos per US dollar (Banxico FIX)', ...r };
767
802
  }
768
803
 
804
+ /** {rate, effective_since, previous_rate, previous_rate_effective_since, decision_history, data_as_of} from an ascending {date,value} series. */
805
+ function computeRateChanges(obs: { date: string; value: number | null }[]) {
806
+ const pts = obs.filter((o) => o.value !== null) as { date: string; value: number }[];
807
+ if (pts.length === 0) return null;
808
+ const changes: { rate: number; effective_since: string }[] = [{ rate: pts[0].value, effective_since: pts[0].date }];
809
+ for (let i = 1; i < pts.length; i++) {
810
+ if (pts[i].value !== changes[changes.length - 1].rate) {
811
+ changes.push({ rate: pts[i].value, effective_since: pts[i].date });
812
+ }
813
+ }
814
+ const current = changes[changes.length - 1];
815
+ const previous = changes.length > 1 ? changes[changes.length - 2] : null;
816
+ return {
817
+ rate: current.rate,
818
+ unit: 'percent',
819
+ effective_since: current.effective_since,
820
+ previous_rate: previous?.rate ?? null,
821
+ previous_rate_effective_since: previous?.effective_since ?? null,
822
+ decision_history: changes.slice(-5).reverse(),
823
+ data_as_of: pts[pts.length - 1].date,
824
+ };
825
+ }
826
+
769
827
  async function policyRate(token: string, recent?: number) {
770
- const r = await seriesResult(token, 'SF61745', Math.min(60, Math.max(1, recent ?? 12)));
771
- return { rate: 'Banxico overnight interbank target rate (%)', ...r };
828
+ // One fetch of the full series, sliced two ways: shapeObs(..., clamp) for
829
+ // the `recent`-bounded `observations` field (unchanged shape), and the same
830
+ // datos UNCLAMPED for computeRateChanges so the "effective_since/previous"
831
+ // fields are never truncated by a narrow window — Banxico holds for 6-8
832
+ // weeks at a time, so the old 60-point default could miss the previous
833
+ // decision entirely (fleet #3206).
834
+ const s = await sieFetch(token, 'SF61745');
835
+ const clamp = Math.min(60, Math.max(1, recent ?? 12));
836
+ const obs = shapeObs(s.datos, clamp);
837
+ const computed = computeRateChanges(shapeObs(s.datos, s.datos?.length ?? 0));
838
+ return {
839
+ ...(computed ?? {}),
840
+ label: 'Banxico overnight interbank target rate (%)',
841
+ series_id: s.idSerie,
842
+ title: s.titulo,
843
+ current: obs.length ? obs[obs.length - 1] : null,
844
+ observations: obs,
845
+ };
772
846
  }
773
847
 
774
848
  async function genericSeries(token: string, seriesId: string, recent?: number) {
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-banxico', version: '0.1.1' },
13
+ { name: '@pipeworx/mcp-banxico', version: '0.1.3' },
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 {