Releases: kitfunso/luminus
v0.6.1 — Scotland coverage polish
v0.6.1 Release Notes
Released: 2026-04-21
Polish patch for 0.6.0 Scotland coverage. Two semantic fixes surfaced by live end-to-end smoke across nine GB coordinates.
What's fixed
Scottish flood_zone no longer falsely "unknown"
Before 0.6.1, a Scottish coordinate with no flood risk returned flood_zone: "unknown", planning_risk: "unknown" when the Environment Agency service errored on Scottish geometries (which it does, because EA is England-only). The SEPA service correctly returned zero matches but the aggregated verdict treated the EA errors as "we don't know."
Fix: when SEPA is queried successfully with zero errors AND zero matches, that is a confident "clear" signal, even if EA errored for coverage reasons. flood_zone now returns "1" and planning_risk returns "low" in this case.
Verified live at Glasgow River Clyde adjacent (55.8642, -4.2518) and River Ness at Inverness (57.4778, -4.2247) — both now resolve cleanly to "1" / "low" instead of the misleading "unknown".
A secondary fix: the "all queries failed" throw path now only fires when both EA and SEPA have actually failed. Previously it threw when EA failed even though SEPA had returned a successful empty response.
classification_basis honestly labels LCA data
Before 0.6.1, a Scottish LCA result surfaced with classification_basis: "post_1988" or "provisional", which misleadingly implied a Natural England Agricultural Land Classification survey. These basis values belong to English ALC.
Fix: two new ClassificationBasis values introduced:
"lca_detailed"— James Hutton 1:50k LCA (improved agricultural land, partial Scotland cover)"lca_broad"— James Hutton 1:250k LCA (national Scotland cover)
The buildExplanation string was extended accordingly and now reads, e.g., "James Hutton LCA (1:50k detailed) classifies this site as 3.1, equivalent to Best and Most Versatile agricultural land."
Verified live at Edinburgh (55.9533, -3.1883): classification_basis: "lca_detailed" with effective_grade: "Built-up area" and bmv_status: "unknown" (LCCODE 888).
Live smoke verification
Nine coordinates tested end-to-end through the Python SDK against real NESO / NatureScot / SEPA / James Hutton / Natural England / Environment Agency services:
| Site | Result |
|---|---|
| Arthur's Seat (Scottish) | 2 SSSIs via NatureScot, has_hard_constraint: true |
| Glasgow Clyde adjacent | flood_zone: "1" (was "unknown" in 0.6.0) |
| River Ness at Inverness | flood_zone: "1" (was "unknown" in 0.6.0) |
| Edinburgh (LCA built-up) | basis: "lca_detailed" (was "post_1988" in 0.6.0) |
| Carse of Gowrie, Shetland Lerwick | Empty LCA (real upstream data gap — 50k is partial, 250k has gaps) |
| Kelso, Scottish Borders | River Tweed SSSI + SAC via NatureScot |
| Berwick (English border) | 8 constraints from BOTH natural-england + nature-scot, union'd correctly |
| Cambridge (English control) | basis: "provisional", no Scottish sources — English behaviour unchanged |
What is NOT changed
- No API shape changes beyond the two new
ClassificationBasisenum values.post_1988,provisional,nonestill exist. - English-only queries behave identically.
- No new upstreams, no new tools, no new dependencies.
- Python SDK remains at
0.5.0— the new enum values flow through dynamic MCP binding without SDK changes.
Known upstream data gaps (not bugs)
The James Hutton 1:50k LCA covers only improved agricultural land patches, approximately one third of Scotland's area. The 1:250k LCA covers the whole country but has some no-data gaps (e.g., Shetland). When both return empty, classification_basis is "none" and bmv_status is "unknown" — this is honest coverage reporting, not a bug.
Verification
- 400 JS tests passed across 39 test files + 2 env-gated integration tests
- 33 Python tests passed (unchanged)
- TypeScript build clean
npm audit0 vulnerabilities
Versions
luminus-mcp@0.6.1luminus-py==0.5.0(unchanged)
v0.6.0 — Scotland coverage for screening tools
v0.6.0 Release Notes
Released: 2026-04-21
Scotland coverage — the three main GB screening tools now return meaningful results for Scottish coordinates, using NatureScot, SEPA, and James Hutton Institute public data. Before 0.6.0, a Scottish site would silently return empty English results with a "coverage: England only" caveat. Now it returns the right Scottish data alongside.
Minor release. Non-breaking — existing English queries behave identically. Scottish coordinates now get real answers. Border-region coordinates get both English and Scottish results union'd.
What's new
NatureScot protected areas
New library module src/lib/nature-scot.ts queries the NatureScot Spatial Data Hub (services1.arcgis.com/LM9GyVFsughzHdbO) for six Scottish designation layers: SSSI, SAC, SPA, Ramsar, NNR, MPA. Integrated into get_land_constraints. Designations from both Natural England and NatureScot merge into a single constraints array with source: "nature-scot" on Scottish features.
SEPA Flood Risk Management Maps
New library module src/lib/sepa-flood.ts queries SEPA's public map service (map.sepa.org.uk/server/rest/services/Open) for nine flood-extent services — the cross product of river / coastal / surface-water sources with high / medium / low likelihoods (1-in-10 / 1-in-200 / 1-in-1000 year return periods). Integrated into get_flood_risk.
Features:
- Runtime layer-ID discovery (SEPA services don't uniformly use layer 0; the module caches the right layer per service)
- Approximate mapping: SEPA Medium → EA Zone 3, SEPA Low → EA Zone 2
- Raw SEPA matches returned under a new
sepa_matchesfield for callers who want the full detail - Parallel query via
Promise.allSettled— per-service failures degrade gracefully
James Hutton Institute LCA
New library module src/lib/james-hutton-lca.ts queries the James Hutton Institute ArcGIS server (druid.hutton.ac.uk) for Scottish Land Capability for Agriculture. Detailed-first (1:50k, ~1/3 Scotland coverage, improved agricultural land only), falls back to broad (1:250k, national coverage). Integrated into get_agricultural_land.
Features:
- LCCODE normalisation: handles both numeric and decimal class labels (
31→3.1, etc.) - Non-agricultural codes (888 = built-up) map to
is_agricultural: falseandbmv_status: "unknown" - BMV mapping: LCA classes 1, 2, 3.1 → BMV; 3.2 and coarser → non-BMV
- Output surfaces under a new
scotland_lcafield onget_agricultural_land, preserving the existing Englishpost_1988/provisionalfields untouched
Scotland bounding-box helper
src/lib/scotland-bbox.ts exports a coarse bbox check (isScottishCoord(lat, lon)) used by the three tools to gate Scottish-upstream queries. The box is intentionally generous, erring toward "query both" for border coordinates. Dedupe + empty-result handling in each upstream makes border queries clean.
Source metadata + health checks
Three new entries in verify_gis_sources:
nature-scot— live health-check against NatureScot's SSSI servicesepa-flood-map— health-check against SEPA's/layers?f=jsonjames-hutton-lca— health-check against the Hutton 50k service (20-second timeout, longer than Esri-hosted services)
Total GIS source coverage now 24 (was 21 at 0.5.1).
Non-breaking additions
additional_sources?: GisSourceMetadata[]— optional field onget_land_constraints/get_flood_risk/get_agricultural_landresults, populated when Scottish upstreams contributed datasepa_matches?: SepaFloodMatch[]— raw SEPA flood-extent hits onget_flood_riskscotland_lca?: LcaResult— full LCA structured result onget_agricultural_land
What is NOT changed
- Existing English-only queries behave identically. Same shape, same
source_metadata, same caveats. - No changes to
get_grid_connection_intelligence,get_site_connection_report,get_gate2_readiness_check, or any other tool. - Python SDK API surface unchanged — the new fields flow through dynamic MCP binding without SDK changes.
Out of scope for 0.6.0
- Wales — Natural Resources Wales (NRW) has equivalent data but a separate tranche's worth of integration work.
- Northern Ireland — no integrated upstreams today.
- National Scenic Areas (NSA) — the Scottish AONB-equivalent. Not in this release; add in a later tranche.
- EU TYNDP integration and NGED internal signals — remain out of scope per the connection-intelligence pitch primer (see
docs/outreach/gb-grid-pitch-primer.md).
Verification
- 398 JS tests passed across 39 test files
- 2 env-gated live integration tests still pass (
LUMINUS_RUN_INTEGRATION=1) - 33 Python tests passed (unchanged)
- TypeScript build clean
npm audit0 vulnerabilities
Versions
luminus-mcp@0.6.0luminus-py==0.5.0(unchanged — no SDK-level changes)
v0.5.1 — Critical GSP lookup fix + polish
v0.5.1 Release Notes
Released: 2026-04-21
Critical fix for a GB GSP lookup bug that shipped silently in 0.5.0, plus post-0.5.0 polish: Python SDK parity for the two new 0.5.0 composite tools, a connection-intelligence Jupyter notebook, a GB bounding-box guard on get_site_connection_report, and surfacing of previously-silent polygon-path degradations.
This is a patch release. Anyone using get_grid_connection_intelligence, get_site_connection_report, or shortlist_bess_sites should upgrade immediately.
The critical fix
lookupGspRegion() in src/lib/neso-gsp.ts returned WALP_1 (Walpole, Norfolk) for every GB coordinate in 0.5.0. The cause: pointOnSegment() treated the closed-ring degenerate zero-length segment (GeoJSON rings always close by repeating the first vertex as the last) as containing every point. pointInRing short-circuited to true on the closing edge before ray-casting ran, and the first feature in iteration order (WALP_1 early in the file) always won.
Fix: early-return false from pointOnSegment when squaredLength < 1e-18 so degenerate segments are treated as points, not segments.
Downstream impact of the fix:
| Site | 0.5.0 (wrong) | 0.5.1 (correct) |
|---|---|---|
| Berkswell (52.39, -1.64) | WALP_1 @ 129.86 km | BESW_1 @ 1.83 km |
| Canary Wharf (51.505, -0.019) | WALP_1 @ 136.78 km | WHAM_1 @ 2.15 km |
| Birmingham (52.486, -1.89) | WALP_1 @ 143.59 km | NECE_1 @ 3.49 km |
| Manchester (53.477, -2.23) | WALP_1 @ 182.29 km | STAL_1 @ 12.72 km |
Affected tools now return the correct nearest GSP, TEC queue data for the right GSP, and DNO headroom for the right area.
Why mocked unit tests missed it. All existing tests fed BASE_GRID_RESULT with nearest_gsp: { gsp_name: "BERKSWELL", ... } directly. No test exercised the real lookupGspRegion() against live NESO data.
Why it ships now. A new env-gated live integration test at src/lib/neso-gsp.integration.test.ts hits real NESO data and asserts Berkswell resolves to a BESW* GSP within 5 km and Canary Wharf resolves to a non-Walpole London GSP within 5 km. Run with LUMINUS_RUN_INTEGRATION=1.
Silent polygon degradations now surface
Two latent silent-failure paths are now visible:
fetchGspBoundaries()previously swallowed all polygon ZIP fetch/parse errors with a barecatch { return []; }. It now returns a structured outcome with a warning message on failure.findContainedRecord()previously returned a silentnullwhen a polygon contained the query point but itsGSPsproperty codes did not resolve to any CSV record. It now returns a warning naming the mismatched codes.
Both warnings flow into GspLookupResult.warnings and are surfaced by get_grid_connection_intelligence and get_nged_connection_signal in their confidence_notes, so future NESO format changes stop being invisible.
Polish work included
Python SDK parity (luminus-py@0.5.0, bumped in-source, not yet published to PyPI):
Luminus.get_site_connection_report_snapshot(**arguments)andLuminus.get_gate2_readiness_check_snapshot(**arguments)- Typed snapshot dataclasses:
SiteConnectionReportSnapshot,Gate2ReadinessCheckSnapshot,CanonicalConnectionEntrySnapshot,Gate2ReadinessProject,Gate2ReadinessRuleResult,Gate2ReadinessSummary - New Jupyter notebook
python/examples/connection_intelligence_workflow.ipynb
GB bounding-box guard on get_site_connection_report:
The tool now rejects coordinates outside a generous GB bounding box (49.5 to 61.0 lat, -8.5 to 2.0 lon) with a clear error pointing EU callers to screen_site. Previously the tool silently returned a mostly-empty "GB" report for EU coordinates.
compare_sites transparency:
The disclaimer and heuristics_used now explicitly state that the scoring weights are judgment-based and have not been calibrated against realised project outcomes. No weight changes.
Expanded verify_gis_sources coverage:
Added health check for neso-gsp-boundaries — validates that the NESO GSP dataset still publishes a ZIP resource with the region-boundary GeoJSON. The runtime hardcodes the current ZIP URL by filename (embedding a rotation date), so future rotations that drop the resource now surface here instead of silently falling back to nearest-point CSV. Total coverage now 21 sources.
Verification
- 375 JS tests passed across 36 test files + 2 env-gated integration tests
- 33 Python tests passed
- TypeScript build clean
npm audit0 vulnerabilities- Live end-to-end smoke against real NESO confirms post-fix correctness
Versions
luminus-mcp@0.5.1luminus-py==0.5.0(bumped in source for the new typed helpers; published to PyPI separately)
v0.5.0 — GB connection-intelligence tranche
v0.5.0 Release Notes
Released: 2026-04-21
GB connection-intelligence tranche. This release adds a canonical schema for GB connection rows from heterogeneous public sources, a composite site-screening tool that combines grid + land + flood + agricultural signals into a single structured memo, and a transparent, rules-based Gate 2 readiness checklist backed by live public references.
This is a minor release. Nothing previously shipped changes behaviour.
What's in the box
Canonical GB connections schema
New module src/lib/gb-connections/ with:
schema.ts—CanonicalConnectionEntrytype plusConnectionSource,ConnectionLifecycleStage, andCapacityKindenums.normalise.ts— three structural normalisers mapping upstream rows into the canonical shape:normaliseTecRow(NESO TEC register)normaliseNgedQueueRow(NGED public per-GSP queue)normaliseDnoHeadroomRow(SSEN / NPG / UKPN / SPEN / ENWL headroom)
Normalisers are deliberately structural: they do not invent capacity, status, or identifiers that the upstream row does not publish. source_row_id is derived from the upstream row's own identifiers; lifecycle_stage is text-matched against the upstream status field, with "unknown" when the upstream does not publish a lifecycle column.
get_site_connection_report
GB-only composite tool. Calls get_grid_connection_intelligence, get_land_constraints, get_flood_risk, and get_agricultural_land in parallel via Promise.allSettled, so partial upstream failures degrade the report rather than failing it. Returns:
summary— markdown memo with coordinates, nearest GSP, DNO headroom, TEC queue context, hard constraints, flood zone, and ALC grade.structured— normalised nearest GSP, top TEC entries asCanonicalConnectionEntry[], canonical headroom entry, and boolean flags for protected-area, flood, and ALC constraints.traffic_lights—queue,headroom,land_constraintseach asgreen | amber | red | unknown, driven only by published upstream signals (DNO generation RAG, literal-zero TEC+NGED counts, Natural England hard-designation categories).traffic_light_thresholds— the inline rules used, so the reader can see exactly how each colour was derived.confidence_notes,source_metadata,disclaimer.
No invented thresholds, no proprietary scoring, no prediction.
get_gate2_readiness_check
Rules-based checklist against publicly documented NESO Gate 2 entry criteria. Ten rules across planning, land rights, technology, capacity, connection point, grid reference, energisation window, and strategic alignment. Each rule returns one of pass | warn | fail | not_applicable with a human-readable reason and its own reference_url pointing at the live public source (NESO connections reform, queue management, Clean Power 2030 Action Plan).
Explicit non-goal: this is not a Gate 2 likelihood model, probability, or score. The disclaimer states this explicitly.
connections profile
New profile in src/lib/profiles.ts grouping the seven GB connection-intelligence tools for lower context-window cost when only connection work is needed:
npx luminus-mcp --profile connections
Deliberate non-goals
This tranche explicitly does NOT add:
- A predictive Gate 2 likelihood model, score, or probability.
- A GB-wide "queue oracle" or ML overlay on the public data.
- A workflow SaaS, hosted layer, persistence, or auth.
- A demand-centre siting or portfolio ranker.
The rationale is honest evidence: we do not have the training data to justify a predictive Gate 2 model, and shipping one would burn credibility with the network operators we depend on for upstream data.
Verification
- 368 JS tests passed across 36 test files
- TypeScript build passed
npm auditshows only pre-existing transitive issues, unchanged from 0.4.3
Versions
luminus-mcp@0.5.0luminus-py==0.4.1(unchanged)
v0.4.3
Luminus MCP v0.2.0
Luminus MCP v0.2.0
Luminus MCP 0.2.0 is the first release where the project clearly becomes more than a plain European power-data MCP.
This release adds a real GIS screening layer for PV and BESS workflows, tightens backend reliability, introduces context-saving MCP profiles, and starts a notebook-friendly Python SDK for analysts who want DataFrames instead of raw MCP plumbing.
Highlights
1. GIS site screening for PV and BESS
New and expanded GIS capabilities now cover:
- terrain analysis
- grid proximity
- GB transmission connection queue access
- GB grid-connection intelligence
- land constraints
- EU land-cover screening
- England agricultural-land screening
- England flood-risk screening
- composite site screening
- multi-site comparison and ranking
- screening-level site revenue estimation
This gives Luminus a practical site-screening workflow for GB and a reduced but still useful EU mode.
2. Profiles to cut context-window cost
Luminus now supports focused MCP profiles such as:
tradergridregionalbessweathergis
The point is simple: users and agents should not need to load the full tool surface when they only need one slice of it.
3. Backend hardening
This release includes a full backend review and a bunch of real fixes, including:
- runtime auth consistency for key-gated tools
- better cache-key stability
- full-window pagination for Fingrid
- corrected hydro chronology handling
- safer Overpass rate-limit behaviour
- corrected GIS math and screening bugs
- clearer CLI profile handling
4. Python SDK preview
A first notebook-first Python SDK now exists under python/.
Current shape:
- starts
luminus-mcpover stdio - exposes a Python
Luminusclient - supports dynamic tool binding for the live MCP surface
- returns result objects with
to_pandas() - adds
to_geojson()andto_geodataframe()for geospatial notebook work - includes quickstart and GIS/BESS screening examples
Removed
- EMBER was removed from the live tool surface and docs because the public API path is no longer reliable enough to present as a working integration
Verification
- 238 JS tests passed
- Python SDK tests passed
- TypeScript build passed
- npm audit reported 0 vulnerabilities
Published artifacts
- npm:
luminus-mcp@0.2.0 - git tag:
v0.2.0
Upgrade
npm install -g luminus-mcp@0.2.0v0.1.0 - 22 MCP Tools for European Energy Data
First release of Luminus: 22 MCP tools covering the complete EU/UK electricity market stack.
Install: \
pm install luminus-mcp\\
Tools: Generation mix, day-ahead prices, cross-border flows, carbon intensity, gas storage, LNG terminals, weather forecasts, US gas data, UK carbon/demand/frequency, balancing prices, renewable/demand forecasts, power plant database, capacity auctions, outages, net positions, transfer capacities, hydro reservoirs, solar irradiance, EU grid frequency, transmission lines.
Data sources: ENTSO-E, GIE AGSI+/ALSI, Elexon BMRS, Carbon Intensity API, Open-Meteo, EIA, PVGIS, JAO, Open Power System Data, OpenStreetMap, mainsfrequency.com.
All free. No paid APIs.