Skip to main content
Glama
aaronjbrown

rf-los-mcp

by aaronjbrown

rf-los-mcp

A FastMCP (stdio) server that computes RF line of sight over locally cached FABDEM V1-2 terrain (30 m bare-earth elevation). It is meant to be combined with a repeaterbook MCP server: take repeater lat/lon and output frequency (MHz) from repeaterbook, then ask this server whether you can reach the repeater from a point or along a route.

Setup

python3 -m venv .venv && .venv/bin/pip install -e .

Register with Claude Code (run this yourself; it is not done by the install):

claude mcp add rf-los -- /absolute/path/to/.venv/bin/rf-los-mcp

Related MCP server: open-elevation

Tools

Tool

Purpose

rf_los_check

LOS between observer A and target B, with Fresnel clearance when a frequency is given

rf_los_route

Coverage of one or more targets along a route (waypoints, a GPX file or a GeoJSON file)

terrain_elevation

Ground elevation (m AMSL) for up to 1000 points

terrain_profile

Elevation profile along a path (max 20,000 samples), optional earth-curvature adjustment

dem_cache_status

List cached terrain tiles and the cache directory

dem_prefetch

Download tiles for a bounding box or centre+radius so you can work offline

Defaults: observer 1.5 m AGL, target 25 m AGL (heights are AGL unless *_height_ref="amsl"), k-factor 4/3, "clear" means 0.6 x first Fresnel zone is unobstructed, LOS sample spacing 30 m, route spacing 200 m, target snap radius 1000 m, route max range 100 km. Units are metres, kilometres (route distances only), MHz and decimal degrees.

There is no default frequency: without frequency_mhz the check is geometric only and the result says so. Snapping moves the targets (repeater coordinates are often imprecise) to nearby higher ground, never the observer. For out-and-back routes pass the outbound leg only. Route waypoints and GPX or GeoJSON points are geometry, resampled every sample_spacing_m along the route; only named waypoints plus the start and end get per-waypoint verdicts, so name the stops you care about (GPX track points are treated as unnamed). A route call is limited to 50 targets, 10,000 line-of-sight checks (samples x targets) and about 5 million terrain samples in total, including target snapping.

Other limits: rf_los_check paths and rf_los_route's max_range_km are at most 300 km. Any one call (other than dem_prefetch) downloads at most 16 new terrain tiles (about 200 MB), and dem_prefetch takes at most 100 tiles (about 1.2 GB) per call. These limits stop a mistaken or manipulated agent request from filling the disk or hammering the FABDEM server.

geojson_path takes a .geojson/.json file (for example an OpenStreetMap export). It must pass the official GeoJSON JSON Schema (RFC 7946, bundled; MIT licence), so any other JSON file is rejected before its contents are used. Prefer it to copying GeoJSON coordinates into waypoints, because GeoJSON stores positions as [lon, lat]. LineString and MultiLineString pieces are joined end to end (pieces may be reversed or out of order, but branching or disconnected pieces are rejected). Point features with a name property, such as a summit or hut, are placed on the line as named waypoints and must be within 500 m of it. A file with only Points uses them, in order, as the route.

Caveats

  • Vegetation and buildings are not modelled (bare-earth DEM), so results may be optimistic through dense forest.

  • Route analysis samples the route and the line of sight at discrete spacing (default 200 m along the route); narrow ridges between samples can be missed.

  • Ocean is treated as sea level (0 m). A missing land tile is an error, never 0.

Terrain cache

Tiles are cached in $RF_LOS_CACHE_DIR, else $XDG_CACHE_HOME/rf-los, else ~/.cache/rf-los. Each 1 degree square tile is about 12 MB. The first query in a new area downloads the tile(s) it needs, which can take about a minute per tile. To avoid that (or to work offline), prefetch before you go, e.g. ask the agent to call dem_prefetch with a bounding box (south, west, north, east) or center_lat, center_lon, radius_km; areas needing more than max_tiles (default 16) tiles are refused. Offline queries over cached tiles work normally; uncached land tiles produce an error naming the tile. Up to 6 decoded tiles are held in memory (about 52 MB each, ~310 MB peak).

Attribution and licence

Code: copyright (C) 2026 Aaron Brown, licensed under the GNU Affero General Public License v3.0 only (AGPL-3.0-only); see LICENSE. If you run a modified version as a network service (for example a hosted MCP server), you must offer its users the source of your version under the same licence.

GeoJSON schema: src/rf_los/data/GeoJSON.schema.json is from geojson/schema, copyright (c) 2018 Tim Schaub, MIT licence (see src/rf_los/data/GeoJSON.schema.LICENSE.md).

Terrain: FABDEM V1-2 (Neal & Hawker 2023, University of Bristol, doi:10.5523/bris.s5hqmjcdj8yo2ibzi9b4ew3sn), licensed CC BY-NC-SA 4.0, derived from Copernicus DEM GLO-30. FABDEM is for non-commercial use only; do not redistribute the downloaded tiles. Results carry this attribution.

Related MCP Connectors

Related MCP Servers