fpv-airspace 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Toby Goulden
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,306 @@
1
+ # FPV Airspace
2
+
3
+ An MCP server that answers the question existing airspace tools do not: **can I legally take off and fly a drone here, in the UK?**
4
+
5
+ Airspace restriction data is the easy half. A UK pilot actually has to clear three layers: permanent airspace restrictions (aerodrome flight restriction zones, prohibited, restricted and danger areas), temporary restrictions (NOTAMs), and landowner rules (National Trust byelaws, council park byelaws). The practical workaround pilots use is launching from a public right of way, where no landowner permission is needed. This server puts all of that behind twelve tools that take a place name, a postcode or coordinates.
6
+
7
+ Informational only. It is not a substitute for a NATS pre-flight briefing, the CAA Drone Code, or permission from the landowner and any relevant aerodrome.
8
+
9
+ ## What it answers
10
+
11
+ - **Is this point inside a restriction?** `check_location` returns a one-line verdict and every zone containing the point, with vertical limits, activation notes and who to ask. Prison restricted areas (the 400 m zones around every closed prison and young offender institution in England and Wales, an offence to enter without HMPPS permission) are recognised as their own zone type rather than as aerodromes.
12
+ - **What is this aerodrome's zone?** `get_aerodrome_zone` by name or ICAO code, including runway protection zones.
13
+ - **Is there a NOTAM in force?** `check_notams` reads the live NATS UK bulletin, filters by point, radius and date, and never silently drops NOTAMs it cannot place.
14
+ - **What does my route cross?** `check_route` for a list of waypoints or an area, with the distance along the route at which each zone is entered.
15
+ - **Can I take off here?** `check_takeoff_site` lists the nearest public rights of way with distances and the responsible council, the nearest public parking, ground hazards within 1 km (railways, major roads, power lines and pylons, substations, helipads, masts, military land) and places people gather (schools, hospitals, parks), plus National Trust land and known council byelaws at the point.
16
+ - **Where can I park?** `find_parking` lists car parks, laybys and rest areas from OpenStreetMap, nearest first, with fee and access notes.
17
+ - **Can I fly here, now?** `preflight_briefing` combines everything into one GO, CAUTION or NO-GO answer with reasons: airspace verdict, live NOTAMs, the weather window and geomagnetic activity, rights of way, parking and, when you name your drone, its rules. A live source that fails is reported as an outage, never assumed clear.
18
+ - **What is the ground doing?** `check_terrain` profiles ground elevation along a route or around a point against the 120 m rule, which is measured from the surface below the aircraft, and warns when rising ground eats the clearance or falling ground puts a fixed height above the limit.
19
+ - **Where could I take off?** `find_takeoff_spots` scores points on public rights of way, next to parking and on open access land within a radius, excludes anything inside prohibited, restricted, prison or aerodrome zones or on banned land, and returns the best few with reasons; the map shows them numbered.
20
+ - **What can my drone do?** `check_drone_rules` takes a model name or a weight and class mark and answers which open subcategory applies (A1, A2 or A3), the separation from people, whether Flyer and Operator IDs are needed and when Remote ID is required, under the CAA class mark rules in force from 2026. `check_takeoff_site` accepts a `drone` too and adds the same summary to the site report. Both tools also list open access land (context for take-off, never permission) and SSSI or National Park designations (advisory) at the point, and treat Forestry England land as a take-off ban without a permit. Take-off and location reports also name the local authority at the point and any council-wide drone policy recorded for it in the seed list.
21
+ - **Is the weather flyable?** `check_weather` gives an hourly forecast from Open-Meteo with wind and gusts at 10 m, wind at 120 m, rain, visibility, cloud, temperature and daylight, each hour rated good, caution or poor against typical small-drone limits.
22
+ - **Show me.** On the hosted server every location answer carries a map link, and clients that support MCP Apps (Claude web, desktop and mobile) render the map inline: zones coloured by severity, NOTAM circles, footpaths, landowner land and parking.
23
+ - Plus `geocode` to disambiguate place names and `get_data_status` for data provenance and attribution.
24
+
25
+ ## Quick start
26
+
27
+ Requires Node 22.13 or newer (the server uses Node's built-in SQLite, so there is nothing native to compile). The package is a single bundled file with no dependencies, so `npx` starts it in a few seconds.
28
+
29
+ ```bash
30
+ npx -y fpv-airspace
31
+ ```
32
+
33
+ ### Claude Desktop
34
+
35
+ Add to `claude_desktop_config.json` (Settings > Developer > Edit Config):
36
+
37
+ ```json
38
+ {
39
+ "mcpServers": {
40
+ "fpv-airspace": {
41
+ "command": "npx",
42
+ "args": ["-y", "fpv-airspace"],
43
+ "env": {
44
+ "OS_NAMES_API_KEY": ""
45
+ }
46
+ }
47
+ }
48
+ }
49
+ ```
50
+
51
+ `OS_NAMES_API_KEY` is optional. Without it, geocoding uses OpenStreetMap's Nominatim. With a free key from the [OS Data Hub](https://osdatahub.os.uk/), Ordnance Survey place names are tried first.
52
+
53
+ On first use the server downloads the current airspace data pack (about 49 MB compressed, 115 MB on disk: 1,050 restriction zones, 520,391 rights of way, 1,694 landowner polygons) into `~/.cache/fpv-airspace` and reports progress on stderr. NOTAM and geocoding tools work while it downloads. The pack is refreshed automatically when a new AIRAC cycle is published.
54
+
55
+ ### Claude Desktop extension (.mcpb)
56
+
57
+ Each release also ships `fpv-airspace.mcpb`. Download it from the [releases page](https://github.com/noodlemctwoodle/fpv-airspace/releases), double-click it, and Claude Desktop installs the server with its bundled Node runtime and a settings panel for the optional OS Names key.
58
+
59
+ ### Claude Code
60
+
61
+ ```bash
62
+ claude mcp add fpv-airspace -- npx -y fpv-airspace
63
+ ```
64
+
65
+ ### Remote connector (Claude web, mobile and voice)
66
+
67
+ The same server runs as a Cloudflare Worker at **`https://fpv-airspace.fetchlabs.co.uk/mcp`**, which is what Claude's mobile app and voice mode can reach: they cannot run local servers, only remote connectors. Add it on claude.ai under Settings > Connectors > Add custom connector with that URL, and it becomes available on every surface, including voice conversations. Every tool accepts `format: "brief"`, which returns two or three spoken-friendly sentences instead of the full report.
68
+
69
+ Hosting your own copy:
70
+
71
+ ```bash
72
+ npx wrangler login
73
+ ```
74
+
75
+ ```bash
76
+ npm run worker:setup
77
+ ```
78
+
79
+ The setup script creates the D1 database and KV namespace, writes their ids into `wrangler.toml`, and loads the latest national pack into D1. Then `npm run worker:deploy` prints the URL. With the repository variable `CLOUDFLARE_DEPLOY=true` and the `CLOUDFLARE_API_TOKEN` / `CLOUDFLARE_ACCOUNT_ID` secrets set, GitHub Actions deploys on every push to `main` and reloads D1 whenever a new data pack is built.
80
+
81
+ ### Hosted (streamable HTTP on your own server)
82
+
83
+ ```bash
84
+ npx -y fpv-airspace --transport http --port 8080
85
+ # or
86
+ docker build -t fpv-airspace . && docker run -p 8080:8080 -v drone-data:/data fpv-airspace
87
+ ```
88
+
89
+ `POST /mcp` speaks the MCP streamable HTTP transport (stateless), `GET /healthz` reports pack and NOTAM cache state.
90
+
91
+ ## Tools
92
+
93
+ Every tool accepts `format: "text"` (default, a plain-text report), `format: "json"` (the same data as structured JSON) or `format: "brief"` (two or three sentences written to be read aloud, for voice). Tools that take a location accept either `place` (name, postcode or landmark) or `lat` and `lon`. When a place name matches several places, the tool returns the candidates instead of guessing.
94
+
95
+ ### `check_location`
96
+
97
+ Permanent airspace restrictions and landowner rules at a point.
98
+
99
+ | Argument | Type | Notes |
100
+ |---|---|---|
101
+ | `place` / `lat`+`lon` | string / numbers | One form only |
102
+ | `include_above_120m` | boolean | Also list zones whose lower limit is above 400 ft |
103
+
104
+ Example prompt: *"Can I fly my drone at Tyndale Monument?"*
105
+
106
+ ```
107
+ No permanent airspace restriction at this point.
108
+ Note: "the layby below Tyndale Monument" was not found; results are for "Tyndale Monument".
109
+
110
+ Location: Tyndale Monument, North Nibley, Gloucestershire (51.68390, -2.40540) via nominatim
111
+ ...
112
+ Attribution: Airspace restrictions: UK AIP ENR 5.1 UAS Flight Restrictions dataset © NATS Limited ...; Geocoding © OpenStreetMap contributors (ODbL), via Nominatim
113
+ ```
114
+
115
+ ### `get_aerodrome_zone`
116
+
117
+ | Argument | Type | Notes |
118
+ |---|---|---|
119
+ | `aerodrome` | string | Name or ICAO code, e.g. `Bristol` or `EGGD` |
120
+ | `include_geojson` | boolean | Include zone geometry |
121
+
122
+ Example prompt: *"Show me the Gatwick FRZ."*
123
+
124
+ ### `check_notams`
125
+
126
+ | Argument | Type | Notes |
127
+ |---|---|---|
128
+ | `place` / `lat`+`lon` | | |
129
+ | `radius_km` | 0–50 | Also list NOTAMs whose circle comes within this distance. 0 = covering only |
130
+ | `date` | ISO 8601 | Defaults to now; the bulletin covers the next 7 days |
131
+ | `max_results` | 1–50 | |
132
+
133
+ Example prompt: *"Any NOTAMs over Durdle Door this Saturday?"*
134
+
135
+ ### `check_route`
136
+
137
+ | Argument | Type | Notes |
138
+ |---|---|---|
139
+ | `waypoints` | array of `[lon, lat]` or place names | 2–50 points; routes over 500 km are rejected |
140
+ | `area` | `{ bbox: [w, s, e, n] }` or GeoJSON Polygon | Instead of waypoints |
141
+ | `include_above_120m` | boolean | |
142
+
143
+ Example prompt: *"Check a route from Clevedon to Portishead along the coast."*
144
+
145
+ ### `check_takeoff_site`
146
+
147
+ | Argument | Type | Notes |
148
+ |---|---|---|
149
+ | `place` / `lat`+`lon` | | |
150
+ | `max_paths` | 1–20 | Rights of way to list (default 5) |
151
+ | `search_radius_m` | 50–5000 | Default 1000 |
152
+
153
+ Example prompt: *"Where's the nearest footpath I could take off from near Corfe Castle?"*
154
+
155
+ ```
156
+ Take-off restricted by landowner rule.
157
+ No permanent airspace restriction at this point.
158
+ Landowner rule: Corfe Castle (National Trust (always open land)) - National Trust byelaws prohibit taking off or landing unmanned aircraft on Trust land without the Trust's permission.
159
+ Nearest public right of way: 120 m away (footpath, Dorset).
160
+ ...
161
+ ```
162
+
163
+ ### `find_parking`
164
+
165
+ | Argument | Type | Notes |
166
+ |---|---|---|
167
+ | `place` / `lat`+`lon` | | |
168
+ | `max_results` | 1–20 | Default 5 |
169
+ | `search_radius_m` | 100–10000 | Default 2000 |
170
+ | `include_private` | boolean | Also list private, customers-only and permit parking |
171
+
172
+ Example prompt: *"Where can I park near Durdle Door?"*
173
+
174
+ ### `check_weather`
175
+
176
+ | Argument | Type | Notes |
177
+ |---|---|---|
178
+ | `place` / `lat`+`lon` | | |
179
+ | `date` | ISO 8601 | A bare date reports daylight hours; a date-time starts there. Defaults to now |
180
+ | `hours` | 1–24 | Hours to report from the start (default 6) |
181
+
182
+ Example prompt: *"Is it flyable at Ilkley Moor on Saturday afternoon?"*
183
+
184
+ Ratings are advisory: caution from 8 m/s, poor from 10.7 m/s sustained or 12 m/s gusts, any rain, visibility under 1.5 km, and a caution for freezing temperatures, low cloud or strong wind at 120 m.
185
+
186
+ The report also carries the NOAA planetary K-index (geomagnetic activity), which affects GPS accuracy and compass behaviour: quiet, unsettled, active (Kp 4) or storm (Kp 5 and above). It does not change the weather rating; the briefing tool treats a storm as caution.
187
+
188
+ ### `preflight_briefing`
189
+
190
+ One report for a take-off point and time. `place` / `lat`+`lon`, `date` (default now), `hours` (window length, default 3), optional `drone` and `a2_certificate`, `frz_permission` when an aerodrome has already agreed the flight, `notam_radius_km` (default 10), `max_paths`. The status is deterministic: prohibited, prison or restricted airspace, an FRZ without permission, or a landowner ban is NO-GO; an FRZ with permission, a covering NOTAM, a danger area, poor weather, a geomagnetic storm, a physical ground hazard within 200 m, or any live source that could not be read is CAUTION; otherwise GO. Notes (marginal weather, nearby or unlocated NOTAMs, a school, hospital or park within 150 m, no right of way nearby, A3 separation) never change the status. The JSON carries each sub-result, the reasons and an `outages` list.
191
+
192
+ ### `check_terrain`
193
+
194
+ Ground elevation against the 120 m rule. Give `waypoints` (2 to 50) for a profile, or a point with `radius_m` (default 500) for the ground around it; `flight_height_m` (default 120) is the planned height above take-off and `step_m` the sample spacing. Reports the highest and lowest ground relative to the take-off point and warns when the ground rises within 30 m of the flight height (caution) or above it (poor), or falls far enough that the flight would be more than 120 m above the surface. Elevations come from Copernicus GLO-90 via Open-Meteo at about 90 m resolution, so cliffs and buildings are not resolved.
195
+
196
+ ### `find_takeoff_spots`
197
+
198
+ Candidate spots come from the nearest rights of way (the closest point of each plus samples every 250 m), public parking and open access land within `search_radius_m` (default 3 km), de-duplicated on a 50 m grid and capped at 60. Each is checked against the airspace and landowner layers: prohibited, restricted, prison and aerodrome zones and take-off bans exclude it; a covering NOTAM, a danger area, a landowner rule or a ground hazard within 200 m lower the score; being on a right of way, having parking within 300 m and open access land raise it; distance from the centre costs a little. Scores are integers with a reason per term, so the ordering is deterministic. `max_results` (default 3), optional `drone` and `a2_certificate` (a school, hospital, park or similar within 150 m costs a little, and a lot for A3 pilots). The JSON lists the spots and the excluded candidates grouped by reason. A right of way is a right to pass, not to stop and fly.
199
+
200
+ ### `check_drone_rules`
201
+
202
+ Which UK open category rules apply to a consumer drone. Give `model` (looked up in the curated catalogue in `src/services/drones/catalogue.ts`: DJI, Autel, Potensic, HoverAir and Parrot models with take-off weight, EU C-class and UK class marks) or `weight_g` with an optional `class_mark` (C0 to C4, UK0 to UK4, or none). Set `a2_certificate` if the pilot holds an A2 CofC and `date` to see the rules on a future date. The answer gives the subcategory, overflight and separation rules, registration (Flyer ID and Operator ID, 100 g threshold from 2026), Remote ID dates (UK1 to UK3 from 2026, camera aircraft of 100 g or more otherwise from 2028) and the transition under which EU C-class labels count as UK classes until the end of 2027. Rules and dates live in `src/services/drones/rules.ts` with the CAA pages they were taken from; the catalogue is community-maintained like the byelaw list, and an unconfirmed class mark is left null so the aircraft is treated as legacy.
203
+
204
+ ### Maps
205
+
206
+ The hosted server serves `GET /map?lat=&lon=[&radius=][&route=lon,lat;lon,lat]` as a standalone Leaflet map, `GET /api/view` as the JSON behind it, and `GET /api/wind?bbox=w,s,e,n&z=` for the wind field (one Open-Meteo request per view, snapped to a fixed lattice of at most 64 points and cached per point). It also publishes an MCP App resource (`ui://fpv-airspace/map`) attached to `check_location`, `check_takeoff_site`, `check_route` and `find_parking`, so hosts that support MCP Apps show the map inline with the answer; `preflight_briefing`, `check_terrain` and `find_takeoff_spots` carry it too, the last with its spots numbered on the map. Claude web, desktop and mobile render it; Claude Code shows the text only.
207
+
208
+ The layers panel (top left) offers Map or Satellite base layers and a checkbox for every overlay, grouped into Airspace (each zone class and NOTAMs), On the ground (rights of way, landowner land, parking, route) and Weather. Prohibited and restricted areas and aerodrome FRZs are always drawn and cannot be switched off. Satellite is Esri World Imagery with a place-name overlay; add `basemap=satellite` to the `/map` URL to open in that view. Weather has three toggles: Conditions now (a badge with the Open-Meteo flyability rating for the coming hour, including the wind at 120 m), Wind flow (animated streamlines over the visible map, as on a forecast chart, coloured by the advisory thresholds; a still frame when the browser prefers reduced motion) and Rain radar (the latest RainViewer frame, coarse at about 600 m per pixel on the free tier). `weather=0` on `/api/view` skips the forecast. Base layer, overlay and panel choices are remembered per browser.
209
+
210
+ The search box (top left) takes a place name, a postcode or `lat, lon` and calls `GET /api/geocode?q=`, which runs the same resolver as the tools: a confident match moves the map, an ambiguous name shows the candidates to pick from, and nothing is ever guessed. The page URL updates as you search, so it can be shared.
211
+
212
+ The location card has a Your drone picker fed by `GET /api/drones` (the catalogue with each model's rules summary and a drawn silhouette). Choosing a model makes it the location marker and the key swatch, and shows its subcategory and overflight rule in the card. Add `drone=<catalogue id>` to the `/map` URL to preselect one; `check_takeoff_site` called with a `drone` does this for the MCP App. The silhouettes are original drawings, one per family (palm, mini, air, mavic, fpv, phantom), because manufacturer photographs are copyrighted.
213
+
214
+ Map tiles © OpenStreetMap contributors; imagery © Esri, Maxar, Earthstar Geographics, and the GIS User Community; rain radar © RainViewer; weather © Open-Meteo.com (CC BY 4.0).
215
+
216
+ ### `geocode`
217
+
218
+ | Argument | Type | Notes |
219
+ |---|---|---|
220
+ | `query` | string | |
221
+ | `limit` | 1–10 | |
222
+
223
+ ### `get_data_status`
224
+
225
+ No arguments. Reports pack tag, AIRAC effective dates, per-source fetch dates and licences, NOTAM cache age, geocoding providers and every attribution string.
226
+
227
+ ## Data sources and attribution
228
+
229
+ | Layer | Source | Refresh | Licence |
230
+ |---|---|---|---|
231
+ | Flight restrictions | [NATS UK AIP ENR 5.1 UAS Flight Restrictions dataset](https://nats-uk.ead-it.com/cms-nats/opencms/en/uas-restriction-zones/) (AIXM 5.1, cross-checked against the NATS KML) | Every AIRAC cycle (28 days) | © NATS Limited; redistribution terms unconfirmed, see [licences/NATS.md](licences/NATS.md) |
232
+ | NOTAMs | [NATS AIS contingency PIB](https://www.nats.aero/do-it-online/pre-flight-information-bulletins/) (all UK NOTAMs in force or within 7 days) | Hourly upstream, 30 min cache | Informational; obtain an official briefing |
233
+ | Rights of way | Council open data aggregated by [rowmaps.com](https://www.rowmaps.com/) (143 authorities, England and Wales) | Weekly | Open Government Licence v3 per council, OS attribution, see [licences/rowmaps.md](licences/rowmaps.md) |
234
+ | National Trust land | [National Trust Open Data](https://open-data-national-trust.hub.arcgis.com/) Always Open and Limited Access | When edited | OGL v3 / CC-BY |
235
+ | Council byelaws | [data/byelaws/seed.yaml](data/byelaws/seed.yaml) in this repository | Manual | MIT; incomplete by nature |
236
+ | Elevation | [Open-Meteo Elevation API](https://open-meteo.com/en/docs/elevation-api), Copernicus GLO-90 DEM | Live, cached 30 days per 100 m cell | CC BY 4.0 (Open-Meteo), Copernicus data licence |
237
+ | Geomagnetic activity | [NOAA SWPC planetary K-index](https://services.swpc.noaa.gov/products/noaa-planetary-k-index.json) | Live, cached 15 min | US Government, public domain |
238
+ | Drone rules | [CAA class marks](https://www.caa.co.uk/drones/getting-started-with-drones-and-model-aircraft/class-marks/) and the [Drone Code](https://register-drones.caa.co.uk/drone-code), summarised in `src/services/drones/rules.ts`; drone catalogue curated from manufacturer specifications | With the code | Crown copyright, OGL v3; the CAA pages are authoritative |
239
+ | Open access land | [Natural England CRoW Access Layer](https://naturalengland-defra.opendata.arcgis.com/datasets/Defra::crow-act-2000-access-layer/about), [NRW open country and common land](https://datamap.gov.wales/) | With the pack | OGL v3, see [licences/natural-england.md](licences/natural-england.md) and [licences/natural-resources-wales.md](licences/natural-resources-wales.md) |
240
+ | SSSI and National Parks | Natural England and Natural Resources Wales designation boundaries | With the pack | OGL v3, advisory only |
241
+ | Forestry England land | [Forestry England Legal Boundary](https://data-forestry.opendata.arcgis.com/) | With the pack | OGL v3 with acknowledgement, see [licences/forestry-england.md](licences/forestry-england.md); byelaws need a permit for drones |
242
+ | Scottish core paths | [Core Paths - Scotland, Improvement Service Spatial Hub](https://data.spatialhub.scot/dataset/core_paths-is) (needs a free account key, `SPATIALHUB_AUTHKEY`) | Weekly | OGL v3 per council, see [licences/improvement-service-core-paths.md](licences/improvement-service-core-paths.md) |
243
+ | Local authorities | [ONS Local Authority Districts (May 2026) BSC](https://geoportal.statistics.gov.uk/) | With the pack | OGL v3, see [licences/ONS.md](licences/ONS.md) |
244
+ | Ground hazards | OpenStreetMap via the Geofabrik extract: railways, motorways and trunk roads, power lines and minor lines, pylons, substations, generators, helipads, masts, military land, schools, nurseries, hospitals, fire and fuel stations, parks, cemeteries | Weekly | ODbL, see [licences/openstreetmap-ODbL.md](licences/openstreetmap-ODbL.md); advisory only |
245
+ | Parking and laybys | OpenStreetMap via the [Geofabrik Great Britain extract](https://download.geofabrik.de/europe/great-britain.html) (`amenity=parking`, `highway=rest_area`) | Weekly | ODbL |
246
+ | Country boundaries | ONS Countries (December 2024) BUC | Yearly | OGL v3 |
247
+ | Geocoding | postcodes.io, OS Names API (optional), Nominatim | Live, cached 30 days | OGL v3; ODbL |
248
+ | Weather | [Open-Meteo](https://open-meteo.com/) forecast API | Live, cached 15 min | CC BY 4.0 |
249
+
250
+ Every response ends with an `Attribution:` line listing only the sources actually used, including the per-council attribution that the OGL requires for rights-of-way data.
251
+
252
+ The permanent layers are assembled into a data pack by [`.github/workflows/build-pack.yml`](.github/workflows/build-pack.yml), published as a GitHub Release tagged `pack-<AIRAC date>-<run>`, and verified by sha256 on download. See [CLAUDE.md](CLAUDE.md) for the pipeline layout.
253
+
254
+ ## Configuration
255
+
256
+ | Variable | Default | Purpose |
257
+ |---|---|---|
258
+ | `OS_NAMES_API_KEY` | unset | Enables the OS Names geocoder |
259
+ | `OS_NAMES_URL` | `https://api.os.uk/search/names/v1/find` | |
260
+ | `OPEN_METEO_ELEVATION_URL` | `https://api.open-meteo.com/v1/elevation` | |
261
+ | `ELEVATION_CACHE_TTL_SECONDS` | `2592000` | Terrain does not change |
262
+ | `NOAA_KP_URL` | `https://services.swpc.noaa.gov/products/noaa-planetary-k-index.json` | |
263
+ | `SPACE_WEATHER_CACHE_TTL_SECONDS` | `900` | |
264
+ | `OSM_PBF_URL`, `NE_CROW_URL`, `NE_SSSI_URL`, `NE_NATIONAL_PARKS_URL`, `NRW_WFS_URL`, `FE_LEGAL_BOUNDARY_URL`, `ONS_LAD_URL`, `SPATIALHUB_WFS_URL` | (upstream defaults) | Build only: override a source endpoint when a publisher moves it; the `NRW_*_TYPENAME` and `SPATIALHUB_TYPENAME` variables do the same for WFS layer names |
265
+ | `SPATIALHUB_AUTHKEY` | (unset) | Build only: Spatial Hub account key for Scottish core paths; without it the pack has none |
266
+ | `NOMINATIM_URL` | `https://nominatim.openstreetmap.org/search` | Self-host to lift the 1 req/s limit |
267
+ | `POSTCODES_IO_URL` | `https://api.postcodes.io` | |
268
+ | `NOTAM_PIB_URL` | `https://pibs.nats.co.uk/operational/pibs/PIB.xml` | |
269
+ | `NOTAM_CACHE_TTL_SECONDS` | `1800` | |
270
+ | `OPEN_METEO_URL` | `https://api.open-meteo.com/v1/forecast` | |
271
+ | `WEATHER_CACHE_TTL_SECONDS` | `900` | |
272
+ | `GEOCODE_CACHE_TTL_SECONDS` | `2592000` | 30 days |
273
+ | `HTTP_TIMEOUT_MS` | `8000` | Live calls |
274
+ | `FPV_AIRSPACE_CACHE_DIR` | `~/.cache/fpv-airspace` | Pack and caches (`DRONE_AIRSPACE_CACHE_DIR` and an existing `~/.cache/uk-drone-airspace-mcp` are still honoured) |
275
+ | `PACK_MANIFEST_URL` | latest GitHub release manifest | Point at a mirror or `file://` |
276
+ | `PACK_PATH` | unset | Use a local pack and skip downloads |
277
+ | `PACK_UPDATE_CHECK` | `true` | Background check for a newer pack |
278
+ | `PACK_STALE_HOURS` | `24` | How often to check |
279
+ | `LOG_LEVEL` | `info` | `debug`, `info`, `warn`, `error`, `silent` |
280
+ | `MCP_TRANSPORT` / `--transport` | `stdio` | `stdio` or `http` |
281
+ | `PORT` / `--port` | `8080` | HTTP transport |
282
+
283
+ ## Caveats
284
+
285
+ - Rights-of-way data is an interpretation of each council's Definitive Map, not the Definitive Map itself, and covers England and Wales only. Scotland has no definitive map (access rights apply instead) and Northern Ireland has very few recorded rights of way; the tools say so.
286
+ - The landowner rule layer holds National Trust land and a hand-curated list of council byelaws. Absence of a rule never means take-off is permitted.
287
+ - Only zones reaching below 400 ft (120 m) count towards a verdict; higher zones are listed on request.
288
+ - NOTAMs are read from the NATS contingency bulletin, which may lag the live system by up to an hour.
289
+ - ICAO codes for aerodromes come from a curated table covering major and well-known UK aerodromes; lookups by name always work.
290
+
291
+ ## Development
292
+
293
+ ```bash
294
+ npm install
295
+ npm test # offline vitest suite
296
+ npm run lint && npm run typecheck
297
+ npm run build && npx @modelcontextprotocol/inspector node dist/index.js
298
+ npm run pack:build -- --region south-west --tag pack-dev # build a small real pack (network)
299
+ PACK_PATH=build/pack/pack-dev.sqlite node dist/index.js
300
+ ```
301
+
302
+ Releases: tag `vX.Y.Z` to publish to npm and attach the `.mcpb` bundle. The data pack has its own release cadence driven by `build-pack.yml`.
303
+
304
+ ## Licence
305
+
306
+ MIT. Data sources carry their own licences; see the [licences](licences/) folder.