@dangahagan/weather-mcp 1.34.0 → 1.34.1

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
@@ -3,7 +3,7 @@
3
3
  [![npm version](https://badge.fury.io/js/@dangahagan%2Fweather-mcp.svg)](https://www.npmjs.com/package/@dangahagan/weather-mcp)
4
4
  [![MCP Registry](https://img.shields.io/badge/MCP-Registry-blue)](https://registry.modelcontextprotocol.io/v0/servers?search=io.github.dgahagan/weather-mcp)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
- [![Tests](https://img.shields.io/badge/tests-4%2C385%20passing-brightgreen)](./docs/testing/TEST_SUITE_README.md)
6
+ [![Tests](https://img.shields.io/badge/tests-4%2C420%20passing-brightgreen)](./docs/testing/TEST_SUITE_README.md)
7
7
  [![Node](https://img.shields.io/badge/node-%3E%3D18-339933?logo=node.js&logoColor=white)](https://nodejs.org)
8
8
 
9
9
  **Give your AI assistant real weather data — 17 tools, zero API keys, zero signup, zero cost.**
@@ -58,7 +58,7 @@ Choose this one if you want:
58
58
 
59
59
  - **Genuinely free** — every data source is a free public API. No trial that expires, no credit card, no rate-limited "free tier" bait.
60
60
  - **No API keys** — install to first forecast in under a minute. Nothing to configure, nothing to leak into a repo. ([Four optional keys](#optional-api-keys) add extras if you want them; the default configuration needs none.)
61
- - **Fully open source** — MIT licensed, readable TypeScript, 4,385 tests. Audit it, fork it, fix it.
61
+ - **Fully open source** — MIT licensed, readable TypeScript, 4,420 tests. Audit it, fork it, fix it.
62
62
  - **Privacy-respecting** — your queries go directly from your machine to public weather APIs. No middleman server, and no telemetry unless you opt in.
63
63
  - **Breadth** — 17 tools covering weather, safety hazards (lightning, floods, wildfires), marine conditions, air quality, and historical data back to 1940. Most weather MCPs stop at forecasts.
64
64
 
@@ -391,7 +391,7 @@ Being honest about what free public data can and can't do:
391
391
  ```bash
392
392
  npm run build # Compile TypeScript
393
393
  npm run dev # Run in development mode
394
- npm test # Run all 4,385 tests
394
+ npm test # Run all 4,420 tests
395
395
  npm run test:coverage # Coverage report
396
396
  npm run audit # Dependency vulnerability scan
397
397
  ```
@@ -4,6 +4,10 @@
4
4
  */
5
5
  /**
6
6
  * Bounding box for geographic area
7
+ *
8
+ * Latitude is clamped to [-90, 90]. Longitude is an interval on the unwrapped line and is
9
+ * never clamped: `minLon < -180` or `maxLon > 180` means the box crosses the antimeridian.
10
+ * The interval is never wider than 360°.
7
11
  */
8
12
  export interface BoundingBox {
9
13
  minLat: number;
@@ -11,8 +15,18 @@ export interface BoundingBox {
11
15
  maxLat: number;
12
16
  maxLon: number;
13
17
  }
18
+ /**
19
+ * True when `lon` lies in the box's longitude interval, read on the unwrapped line:
20
+ * a box that crosses ±180° admits a longitude that lands inside it after a ±360° shift.
21
+ */
22
+ export declare function lonInBox(lon: number, box: BoundingBox): boolean;
14
23
  /**
15
24
  * Calculate bounding box around a point given a radius in kilometers
25
+ *
26
+ * The longitude interval is `lon ± lonDelta` on the unwrapped line, so a box near ±180°
27
+ * extends past it (see `BoundingBox`). The half-width is capped at 180°: toward the poles
28
+ * `cos(lat) → 0` and `lonDelta` grows without bound, and a 360°-wide interval is already the
29
+ * full circle.
16
30
  * @param lat Latitude
17
31
  * @param lon Longitude
18
32
  * @param radiusKm Radius in kilometers
@@ -25,6 +39,31 @@ export declare function calculateBoundingBox(lat: number, lon: number, radiusKm:
25
39
  * @returns Array of neighbor geohashes (up to 8 neighbors)
26
40
  */
27
41
  export declare function getGeohashNeighbors(hash: string): string[];
42
+ /**
43
+ * Geohash tiles for a search area, with the count the precision budget is charged
44
+ */
45
+ export interface GeohashTileSet {
46
+ /** Every tile admitted, on both sides of the antimeridian when the box crosses it */
47
+ tiles: Set<string>;
48
+ /** How many of `tiles` the budget counts: the start tile, plus every tile whose centre lies
49
+ * inside the box with its longitude interval clamped to [-180, 180] */
50
+ budgeted: number;
51
+ }
52
+ /**
53
+ * Compute geohash tiles that overlap a search area, and the budgeted count
54
+ * Uses breadth-first search to find all geohashes whose centre lies within the bounding box
55
+ *
56
+ * The budgeted count is the start tile plus every tile whose centre lies inside the box
57
+ * with its longitude interval clamped to [-180, 180]: exactly the tiles this function
58
+ * returned before subscriptions crossed the antimeridian. Tiles that enter only through the
59
+ * wrap or the far-side seed are subscribed but not counted.
60
+ * @param lat Center latitude
61
+ * @param lon Center longitude
62
+ * @param radiusKm Search radius in kilometers
63
+ * @param precision Geohash precision (1-12)
64
+ * @returns The tile set and its budgeted count
65
+ */
66
+ export declare function computeGeohashTilesBudgeted(lat: number, lon: number, radiusKm: number, precision: number): GeohashTileSet;
28
67
  /**
29
68
  * Compute geohash tiles that overlap a circular search area
30
69
  * Uses breadth-first search to find all geohashes within the bounding box
@@ -37,13 +76,18 @@ export declare function getGeohashNeighbors(hash: string): string[];
37
76
  export declare function computeGeohashTiles(lat: number, lon: number, radiusKm: number, precision: number): Set<string>;
38
77
  /**
39
78
  * Calculate optimal geohash tiles for MQTT subscription
40
- * Selects the coarsest precision that keeps the tile count at or below maxTiles
79
+ * Selects the finest (highest) precision whose budgeted tile count is at or below maxTiles:
80
+ * precisions run from 1 to 12 and the last one that fits is kept. The budgeted count is the
81
+ * tile set as it was before subscriptions crossed the antimeridian (see
82
+ * computeGeohashTilesBudgeted), so the precision chosen is the same as before, on every input. A box that crosses the
83
+ * antimeridian may return more than maxTiles tiles (observed maximum 2 × maxTiles), because
84
+ * tiles that enter only through the wrap or the far-side seed do not count against the budget.
41
85
  * This balances spatial granularity against subscription overhead
42
86
  *
43
87
  * @param lat Center latitude
44
88
  * @param lon Center longitude
45
89
  * @param radiusKm Search radius in kilometers
46
- * @param maxTiles Maximum number of tiles (default 9, as used by homeassistant-blitzortung)
90
+ * @param maxTiles Maximum budgeted tiles (default 9, as used by homeassistant-blitzortung)
47
91
  * @returns Set of geohash strings to subscribe to
48
92
  */
49
93
  export declare function calculateGeohashSubscriptions(lat: number, lon: number, radiusKm: number, maxTiles?: number): Set<string>;
@@ -1 +1 @@
1
- {"version":3,"file":"geohash.d.ts","sourceRoot":"","sources":["../../src/utils/geohash.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,WAAW,CAW5F;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAmB1D;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,GAAG,CAAC,MAAM,CAAC,CA0Cb;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,6BAA6B,CAC3C,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,QAAQ,GAAE,MAAU,GACnB,GAAG,CAAC,MAAM,CAAC,CAsBb;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAgBT"}
1
+ {"version":3,"file":"geohash.d.ts","sourceRoot":"","sources":["../../src/utils/geohash.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAI/D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,WAAW,CAY5F;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAmB1D;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,qFAAqF;IACrF,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACnB;4EACwE;IACxE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,cAAc,CAoEhB;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,GAAG,CAAC,MAAM,CAAC,CAEb;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,6BAA6B,CAC3C,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,QAAQ,GAAE,MAAU,GACnB,GAAG,CAAC,MAAM,CAAC,CAsBb;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAgBT"}
@@ -3,8 +3,22 @@
3
3
  * Based on the homeassistant-blitzortung implementation
4
4
  */
5
5
  import * as geohash from '../vendor/ngeohash.js';
6
+ /**
7
+ * True when `lon` lies in the box's longitude interval, read on the unwrapped line:
8
+ * a box that crosses ±180° admits a longitude that lands inside it after a ±360° shift.
9
+ */
10
+ export function lonInBox(lon, box) {
11
+ return (lon >= box.minLon && lon <= box.maxLon)
12
+ || (lon + 360 >= box.minLon && lon + 360 <= box.maxLon)
13
+ || (lon - 360 >= box.minLon && lon - 360 <= box.maxLon);
14
+ }
6
15
  /**
7
16
  * Calculate bounding box around a point given a radius in kilometers
17
+ *
18
+ * The longitude interval is `lon ± lonDelta` on the unwrapped line, so a box near ±180°
19
+ * extends past it (see `BoundingBox`). The half-width is capped at 180°: toward the poles
20
+ * `cos(lat) → 0` and `lonDelta` grows without bound, and a 360°-wide interval is already the
21
+ * full circle.
8
22
  * @param lat Latitude
9
23
  * @param lon Longitude
10
24
  * @param radiusKm Radius in kilometers
@@ -14,11 +28,12 @@ export function calculateBoundingBox(lat, lon, radiusKm) {
14
28
  // Earth's circumference at equator: ~40,000 km
15
29
  const latDelta = (radiusKm * 360) / 40000;
16
30
  const lonDelta = latDelta / Math.cos((lat * Math.PI) / 180);
31
+ const d = Math.min(180, lonDelta);
17
32
  return {
18
33
  minLat: Math.max(-90, lat - latDelta),
19
- minLon: Math.max(-180, lon - lonDelta),
34
+ minLon: lon - d,
20
35
  maxLat: Math.min(90, lat + latDelta),
21
- maxLon: Math.min(180, lon + lonDelta)
36
+ maxLon: lon + d
22
37
  };
23
38
  }
24
39
  /**
@@ -40,29 +55,58 @@ export function getGeohashNeighbors(hash) {
40
55
  neighbors.push(geohash.neighbor(hash, [-1, -1])); // SW
41
56
  }
42
57
  catch (error) {
43
- // If neighbor calculation fails, return empty array
44
- // This can happen at edge cases (poles, date line)
58
+ // Defensive guard only: the vendored neighbor clamps latitude, wraps longitude
59
+ // across the antimeridian and does not throw
45
60
  }
46
61
  return neighbors;
47
62
  }
48
63
  /**
49
- * Compute geohash tiles that overlap a circular search area
50
- * Uses breadth-first search to find all geohashes within the bounding box
64
+ * Compute geohash tiles that overlap a search area, and the budgeted count
65
+ * Uses breadth-first search to find all geohashes whose centre lies within the bounding box
66
+ *
67
+ * The budgeted count is the start tile plus every tile whose centre lies inside the box
68
+ * with its longitude interval clamped to [-180, 180]: exactly the tiles this function
69
+ * returned before subscriptions crossed the antimeridian. Tiles that enter only through the
70
+ * wrap or the far-side seed are subscribed but not counted.
51
71
  * @param lat Center latitude
52
72
  * @param lon Center longitude
53
73
  * @param radiusKm Search radius in kilometers
54
74
  * @param precision Geohash precision (1-12)
55
- * @returns Set of geohash strings that overlap the search area
75
+ * @returns The tile set and its budgeted count
56
76
  */
57
- export function computeGeohashTiles(lat, lon, radiusKm, precision) {
77
+ export function computeGeohashTilesBudgeted(lat, lon, radiusKm, precision) {
58
78
  const bbox = calculateBoundingBox(lat, lon, radiusKm);
79
+ const crosses = bbox.minLon < -180 || bbox.maxLon > 180;
80
+ const clampedMinLon = Math.max(-180, bbox.minLon);
81
+ const clampedMaxLon = Math.min(180, bbox.maxLon);
59
82
  const tiles = new Set();
60
83
  const queue = [];
61
84
  const MAX_TILES = 10000; // Safety limit to prevent memory exhaustion
62
- // Start with the center point's geohash
85
+ let budgeted = 0;
86
+ const inClampedBox = (centre) => centre.latitude >= bbox.minLat &&
87
+ centre.latitude <= bbox.maxLat &&
88
+ centre.longitude >= clampedMinLon &&
89
+ centre.longitude <= clampedMaxLon;
90
+ const admit = (hash, counted) => {
91
+ tiles.add(hash);
92
+ queue.push(hash);
93
+ if (counted) {
94
+ budgeted++;
95
+ }
96
+ };
97
+ // Start with the center point's geohash. It is always counted, as it always was
63
98
  const centerHash = geohash.encode(lat, lon, precision);
64
- queue.push(centerHash);
65
- tiles.add(centerHash);
99
+ admit(centerHash, true);
100
+ // A box that crosses the antimeridian also starts from the far side's edge cell at the
101
+ // point's latitude. Like the start cell, that cell always overlaps the box, and the
102
+ // centre test below can reject both for the same reason. encode places 180 in the
103
+ // easternmost cell and -180 in the westernmost, so this is the far edge by construction.
104
+ if (crosses) {
105
+ const farHash = geohash.encode(lat, lon >= 0 ? -180 : 180, precision);
106
+ if (!tiles.has(farHash)) {
107
+ admit(farHash, inClampedBox(geohash.decode(farHash)));
108
+ }
109
+ }
66
110
  // Breadth-first search to find all tiles within bounding box
67
111
  while (queue.length > 0) {
68
112
  const currentHash = queue.shift();
@@ -73,30 +117,45 @@ export function computeGeohashTiles(lat, lon, radiusKm, precision) {
73
117
  }
74
118
  // Safety check: prevent unbounded growth
75
119
  if (tiles.size >= MAX_TILES) {
76
- return tiles;
120
+ return { tiles, budgeted };
77
121
  }
78
122
  // Decode neighbor to check if it's within bounding box
79
123
  const decoded = geohash.decode(neighbor);
80
124
  if (decoded.latitude >= bbox.minLat &&
81
125
  decoded.latitude <= bbox.maxLat &&
82
- decoded.longitude >= bbox.minLon &&
83
- decoded.longitude <= bbox.maxLon) {
84
- tiles.add(neighbor);
85
- queue.push(neighbor);
126
+ lonInBox(decoded.longitude, bbox)) {
127
+ admit(neighbor, inClampedBox(decoded));
86
128
  }
87
129
  }
88
130
  }
89
- return tiles;
131
+ return { tiles, budgeted };
132
+ }
133
+ /**
134
+ * Compute geohash tiles that overlap a circular search area
135
+ * Uses breadth-first search to find all geohashes within the bounding box
136
+ * @param lat Center latitude
137
+ * @param lon Center longitude
138
+ * @param radiusKm Search radius in kilometers
139
+ * @param precision Geohash precision (1-12)
140
+ * @returns Set of geohash strings that overlap the search area
141
+ */
142
+ export function computeGeohashTiles(lat, lon, radiusKm, precision) {
143
+ return computeGeohashTilesBudgeted(lat, lon, radiusKm, precision).tiles;
90
144
  }
91
145
  /**
92
146
  * Calculate optimal geohash tiles for MQTT subscription
93
- * Selects the coarsest precision that keeps the tile count at or below maxTiles
147
+ * Selects the finest (highest) precision whose budgeted tile count is at or below maxTiles:
148
+ * precisions run from 1 to 12 and the last one that fits is kept. The budgeted count is the
149
+ * tile set as it was before subscriptions crossed the antimeridian (see
150
+ * computeGeohashTilesBudgeted), so the precision chosen is the same as before, on every input. A box that crosses the
151
+ * antimeridian may return more than maxTiles tiles (observed maximum 2 × maxTiles), because
152
+ * tiles that enter only through the wrap or the far-side seed do not count against the budget.
94
153
  * This balances spatial granularity against subscription overhead
95
154
  *
96
155
  * @param lat Center latitude
97
156
  * @param lon Center longitude
98
157
  * @param radiusKm Search radius in kilometers
99
- * @param maxTiles Maximum number of tiles (default 9, as used by homeassistant-blitzortung)
158
+ * @param maxTiles Maximum budgeted tiles (default 9, as used by homeassistant-blitzortung)
100
159
  * @returns Set of geohash strings to subscribe to
101
160
  */
102
161
  export function calculateGeohashSubscriptions(lat, lon, radiusKm, maxTiles = 9) {
@@ -104,8 +163,8 @@ export function calculateGeohashSubscriptions(lat, lon, radiusKm, maxTiles = 9)
104
163
  // Iterate through precision levels 1-12
105
164
  // Start with coarse precision and increase until we exceed maxTiles
106
165
  for (let precision = 1; precision <= 12; precision++) {
107
- const tiles = computeGeohashTiles(lat, lon, radiusKm, precision);
108
- if (tiles.size <= maxTiles) {
166
+ const { tiles, budgeted } = computeGeohashTilesBudgeted(lat, lon, radiusKm, precision);
167
+ if (budgeted <= maxTiles) {
109
168
  result = tiles;
110
169
  }
111
170
  else {
@@ -1 +1 @@
1
- {"version":3,"file":"geohash.js","sourceRoot":"","sources":["../../src/utils/geohash.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"geohash.js","sourceRoot":"","sources":["../../src/utils/geohash.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dangahagan/weather-mcp",
3
- "version": "1.34.0",
3
+ "version": "1.34.1",
4
4
  "description": "MCP server giving AI assistants 17 weather tools with zero API keys: global forecasts, current conditions, alerts, air quality, marine conditions, lightning detection, radar imagery, river levels, wildfire tracking, and historical weather back to 1940. Built entirely on free public APIs (NOAA, Open-Meteo, USGS, NIFC).",
5
5
  "mcpName": "io.github.dgahagan/weather-mcp",
6
6
  "main": "dist/index.js",