Elevation Mcp Server

io.github.cyanheadsv0.1.1更新於 Oct 1, 2026

Look up elevation worldwide, profile route ascent/descent, grid areas, check terrain line of sight.

概覽

AI 產生的概覽

讓助理查詢全球地面高程、分析路線的爬升與下降、取樣網格區域,並判斷地形視線是否被遮蔽。

功能
四個工具查詢免金鑰的高程資料來源:elevation_get_points 回傳 1-100 個座標點的地面高程(公尺與英尺),並附上資料集與解析度;elevation_get_profile 沿路線取樣並彙總距離、爬升、下降、高程範圍與最陡坡度;elevation_get_grid 在邊界框上取樣節點網格,回報最高點、最低點、平均高程與起伏;elevation_check_line_of_sight 結合地球曲率與折射判斷地形是否遮蔽視線。USGS 3DEP 涵蓋美國及其屬地以及加拿大與墨西哥大部分地區,其餘地區由 Open Topo Data(先 SRTM,再 Mapzen)提供。剖面、網格與視線都由點取樣在本機計算。
適用情境
適合助理需要地形事實的情境:某地點的地面高程、規劃路線的爬升與下降數據、某區域的最高與最低點,或兩點之間地形是否通視。它是唯讀查詢服務,適用於登山、測繪、無線電選址與地圖類問題,而非編輯或儲存資料。
執行需求
以本機 stdio 程序(或本機 Streamable HTTP 伺服器)執行,透過 npm 套件 @cyanheads/elevation-mcp-server 使用 Bun v1.4.0+ 或 Node.js v24+,也可使用已發佈的 Docker 映像。不需要 API 金鑰。需要連線至 USGS 3DEP 與 Open Topo Data 的網路。選用環境變數包括 OPENTOPODATA_BASE_URL(自架實例)、MCP_TRANSPORT_TYPE、MCP_HTTP_PORT、MCP_AUTH_MODE 與 MCP_LOG_LEVEL。
安裝前請注意
唯讀:這些工具只查詢高程並計算結果,不寫入、傳送或刪除資料。座標會傳送給第三方高程服務(USGS 與 Open Topo Data),因此位置查詢會離開本機。公開的 Open Topo Data 實例有速率限制(每請求 100 個位置、每秒 1 次請求、每地址每天 1,000 次),高頻或託管使用可能耗盡每日額度,之後全球查詢會失敗直到額度重置;託管部署建議自架實例。數值僅為地形模型,不建模建築物、植被與菲涅爾區。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 Elevation Mcp Server,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

@cyanheads/elevation-mcp-server

Look up elevation worldwide, profile route ascent/descent, grid areas, check terrain line of sight via MCP. STDIO or Streamable HTTP.

4 Tools


Overview

Ground elevation and terrain analysis from two keyless sources: USGS 3DEP across the US and its territories (plus much of Canada and Mexico), and Open Topo Data everywhere else, which answers from SRTM GL1 v3 and, where SRTM has no tile, Mapzen terrain tiles. Look up spot heights, measure a route's ascent, descent, and grades, find an area's high and low points, and check whether terrain blocks a sightline. Runs as a stdio process or a local Streamable HTTP server.

Tools

ToolDescription
elevation_get_pointsGround elevation at 1–100 coordinates, in meters and feet, with the dataset and resolution behind each value
elevation_get_profileSample a route at evenly spaced points and summarize distance, ascent, descent, elevation range, and steepest grades
elevation_get_gridSample a node grid over a bounding box and report its highest and lowest points, mean elevation, and relief
elevation_check_line_of_sightDecide whether terrain blocks the sightline between two points, with earth curvature and refraction

Capability reference

elevation_get_points tool

  • points: 1–100 {lat, lon} objects in decimal degrees (WGS84)
  • Each point comes back ok or no_data, with elevation_m / elevation_ft, dataset, and resolution_m (omitted for mapzen); 3DEP answers add raster_id and the upstream acquisition_date. points_with_data counts the hits, and a point without data never fails the call

elevation_get_profile tool

  • path: 2–1,000 vertices in travel order (consecutive duplicates dropped); samples: 2–250 evenly spaced points along it, endpoints included (default 100)
  • summary carries total_distance_m, ascent_m / descent_m (and feet), start, end, min, and max elevation, highest_point / lowest_point, and max_grade_pct / min_grade_pct with where each occurs; ascent depends on the reported sample_interval_m. Fails as degenerate_path (under 1 m of route) or no_coverage (no sample has data)
  • include_samples (default true) returns samples[] (distance, position, elevation, grade, dataset, and resolution per sample) and the sample table; false omits both, while the summary, spacing, coverage counts, datasets, notices, and attribution are unchanged, still computed from every sample

elevation_get_grid tool

  • south, west, north, east edges in decimal degrees (a box can't cross longitude 180); rows and cols 2–25 each (default 10), with rows × cols at most 250
  • elevations_m and cell_datasets matrices indexed [row][col] (row 0 north, column 0 west, null without data), plus summary.highest, summary.lowest, mean_elevation_m, and relief_m. Fails as invalid_bbox, too_many_cells, or no_coverage
  • Nodes are point samples with the edges included, not cell averages; re-grid a smaller box around summary.highest to refine a summit

elevation_check_line_of_sight tool

  • observer and target points, at most 1,000 km apart; observer_height_m (default 1.7) and target_height_m (default 0), each 0–10,000 m above ground; earth_model flat, geometric, optical (default, κ 0.13), or radio (κ 0.25); samples 3–250 (default 100)
  • verdict is clear, blocked, or indeterminate (samples without data leave the line unconfirmed), with min_clearance_m / min_clearance_ft, the limiting_point, and the first_obstruction when blocked. Fails as same_endpoints (under 1 m apart), sightline_too_long (over 1,000 km apart), or endpoint_no_data
  • Terrain only: buildings, vegetation, and Fresnel zones aren't modeled beyond what the elevation source captures; over open water, clearance is measured to the sea surface

Failures shared by every tool

ReasonCodeWhen
usgs_unavailableServiceUnavailableUSGS 3DEP did not answer or rejected the request; data.retryable says whether retrying can help
opentopodata_unavailableServiceUnavailableOpen Topo Data did not answer, rejected the request, or sent an unusable response; data.retryable as above
opentopodata_rate_limitedRateLimitedOpen Topo Data kept answering HTTP 429, or asked for a wait over 8 s; data.retryAfter in seconds when it sent one of at most a day
opentopodata_daily_limitRateLimitedPublic instance only: this server already sent 1,000 requests in the trailing 24 hours, so none was sent; data.retryAfter is the seconds until a slot frees
opentopodata_config_rejectedConfigurationErrorThe instance at OPENTOPODATA_BASE_URL answered 401, 403, or 404, redirected (self-hosted only; redirects are never followed), lacks srtm30m or mapzen, caps locations below 100, or answered from a dataset this server didn't request; the operator must fix it
sampling_deadline_exceededTimeoutThe call's 45 s sampling budget ran out, and data.provider names the provider it was waiting on; or the USGS 3DEP lookups other calls had queued left no time for this call's, so it sent none and data.retryAfter is the seconds until they drain

A coverage miss is never an error: it degrades to no data for that point. Any provider failure fails the whole call with no partial result, and each reason's recovery text names the call to make next.

Features

Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.

Elevation-specific:

  • source on every tool: auto (default) queries USGS 3DEP inside its coverage and sends 3DEP misses and everything outside it to Open Topo Data; usgs_3dep and opentopodata pin one provider. Only a coverage miss falls back, never an outage
  • Open Topo Data is asked for SRTM GL1 v3 first (about 30 m, land between 60°N and 56°S), then Mapzen terrain tiles where SRTM has no tile: high latitudes, Antarctica, and ocean bathymetry
  • Per-call caps of 100 points or 250 samples or grid cells, inside a 45 s sampling budget; each 3DEP point is its own request, paced at 6 concurrent and 10 per second, while Open Topo Data takes 100 points per request
  • The public Open Topo Data instance is paced under its published limits: one request at a time, starts at least 1.1 s apart, at most 1,000 in any trailing 24 hours per server process
  • Profiles, grids, and sightlines are computed locally from point samples: haversine distances, great-circle resampling of routes and sightlines, and earth curvature with standard refraction

Agent-friendly output:

  • Provenance on every value: each point, profile sample, grid cell, and sightline point names its dataset (usgs_3dep, srtm30m, mapzen), with the source's resolution_m (a resolution_m_range on profiles and grids) except for Mapzen; computed results add datasets_used counts, and a Sources: line credits every dataset that answered
  • Absent stays absent: a point or sample without data omits its elevation and a grid cell is null, never 0, and computed summaries report how many values had data
  • Notices flag what changes interpretation: results mixing 3DEP and Open Topo Data, Mapzen values below 0 m (sea-floor depths), sample spacing coarser or finer than the source, and thin clearance margins

Getting started

Add the following to your MCP client configuration file.

json
{  "mcpServers": {    "elevation-mcp-server": {      "type": "stdio",      "command": "bunx",      "args": ["@cyanheads/elevation-mcp-server@latest"],      "env": {        "MCP_TRANSPORT_TYPE": "stdio",        "MCP_LOG_LEVEL": "info"      }    }  }}

Or with npx (no Bun required):

json
{  "mcpServers": {    "elevation-mcp-server": {      "type": "stdio",      "command": "npx",      "args": ["-y", "@cyanheads/elevation-mcp-server@latest"],      "env": {        "MCP_TRANSPORT_TYPE": "stdio",        "MCP_LOG_LEVEL": "info"      }    }  }}

Or with Docker:

json
{  "mcpServers": {    "elevation-mcp-server": {      "type": "stdio",      "command": "docker",      "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/elevation-mcp-server:latest"]    }  }}

For Streamable HTTP, set the transport and start the server:

sh
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http# Server listens at http://localhost:3010/mcp

Prerequisites

  • Bun v1.4.0 or higher (or Node.js v24+).
  • No API key: USGS 3DEP and Open Topo Data are both keyless.
  • Optional: a self-hosted Open Topo Data instance with the srtm30m and mapzen datasets, for heavy or hosted use (see Configuration).

Installation

  1. Clone the repository:
sh
git clone https://github.com/cyanheads/elevation-mcp-server.git
  1. Navigate into the directory:
sh
cd elevation-mcp-server
  1. Install dependencies:
sh
bun install
  1. Configure environment:
sh
cp .env.example .env# optionally set OPENTOPODATA_BASE_URL to a self-hosted Open Topo Data instance

Configuration

VariableDescriptionDefault
OPENTOPODATA_BASE_URLOpen Topo Data instance used outside USGS 3DEP coverage, as an http or https URL. Unset or blank means the public instance; any other URL is treated as a self-hosted instance.https://api.opentopodata.org
MCP_TRANSPORT_TYPETransport: stdio or http.stdio
MCP_HTTP_PORTHTTP server port.3010
MCP_SESSION_MODEHTTP session mode: stateless, stateful, or auto. .env.example and the Docker image set stateless.auto
MCP_AUTH_MODEAuthentication: none, jwt, or oauth. Under jwt or oauth, each tool requires the scope tool:<tool_name>:read.none
MCP_LOG_LEVELLog level (debug, info, warning, error, etc.).info
LOGS_DIRDirectory for log files (Node.js only).<app-root>/logs
OTEL_ENABLEDEnable OpenTelemetry.false

With OPENTOPODATA_BASE_URL unset, the server uses the public instance at api.opentopodata.org and paces itself under its published limits: 100 locations per request, 1 request a second, and 1,000 a day per address. Any other URL is treated as a self-hosted instance (Open Topo Data is MIT-licensed and runs in Docker) and gets 4 concurrent requests with no rate windows. That instance must serve datasets named srtm30m and mapzen and accept at least 100 locations per request; otherwise calls fail with opentopodata_config_rejected.

See .env.example for every server setting and the common framework overrides.

Known limitations

  • Open Topo Data's public limits. The public instance allows 100 locations per request, 1 request per second, and 1,000 requests per day per IP address. Each server process paces itself under them, but its count is process-local and resets on restart. Several processes on one machine (a stdio server per client session), or other clients on the same address, also draw on the upstream's count, so the upstream can refuse first (opentopodata_rate_limited).
  • Hosted deployments. A hosted deployment sends every user's requests from one address, so on the public instance its whole user base shares one 1,000-a-day allowance: about 1,000 point lookups, or 333 full 250-sample calls, and as few as 111 when retries spend requests too. One caller can spend that day in about 18 minutes; for up to 24 hours after, every call that needs Open Topo Data fails with opentopodata_daily_limit, including a US call in auto mode with a single 3DEP miss. A hosted deployment needs both its own Open Topo Data instance (OPENTOPODATA_BASE_URL; MIT-licensed, Docker, loaded with the srtm30m and mapzen datasets) and a per-client rate limit at its edge. Without its own instance, global answers on a hosted deployment are best-effort.
  • No per-caller limits in the server. A stateless deployment without auth has no caller identity, so the server paces each upstream for the whole process and can't ration one caller against another. A hosted endpoint needs a per-client rate limit at its edge (reverse proxy, CDN, or API gateway); without one, a caller sending many heavy calls at once slows USGS 3DEP for everyone, gets other callers' heavy calls refused until its lookups drain, and can spend the public Open Topo Data day.
  • Global resolution. SRTM is a 30 m radar surface model. Mapzen's 1 arc-second grid is interpolated in places from coarser sources (GMTED2010 at 7.5 arc-seconds over parts of the high latitudes, ETOPO1 at about 1.8 km over the open ocean). Outside 3DEP coverage, profile ascent is therefore underestimated, and line of sight misses terrain narrower than the source's true resolution.
  • Whole-meter values. Open Topo Data's datasets are integer rasters, and the upstream rounds the interpolated result to the nearest meter, so ascent and descent over gentle terrain from Open Topo Data include 1 m quantization steps.
  • Water. SRTM reports water inside a land tile as 0 m. Beyond SRTM's tiles, Mapzen reports sea-floor depth, and points, profiles, and grids include it. 3DEP values over water depend on the raster: a hydro-flattened surface, sea level, or bathymetry. Line of sight measures clearance to 0 m over Mapzen values below 0, but uses 3DEP bathymetric values as received.
  • Mixed models. 3DEP is a bare-earth DEM referenced to NAVD 88 in the conterminous US (local datums in some territories). SRTM is a radar surface model that partly includes canopy and buildings, referenced to EGM96. Mapzen blends both kinds by region. Differences of a meter or more between datasets at the same point are expected; per-value provenance shows which applies.
  • Sampling. Features narrower than the sample spacing are missed: a ridge between line-of-sight samples, a summit between grid nodes, short climbs between profile samples. Spacing is always reported.
  • USGS 3DEP throughput. The point query service has no published limit and no batch endpoint: about 10 points per second at this server's pacing, hence the 250-sample cap and 45 s budget. Each call keeps at most 6 lookups outstanding, so concurrent calls interleave and a short call isn't stuck behind a long one. A call whose lookups would push the queue past a running call's budget is refused at once with sampling_deadline_exceeded and data.retryAfter, so two 250-sample calls at once run in turn: the first finishes, and the second can be retried after about 25 s.
  • Acquisition dates from USGS are passed through raw when they have the M/D/YYYY form, and sometimes carry a zero month or day; a value in any other form is omitted.
  • Antimeridian. A grid box cannot cross longitude 180, so split it into two calls. Paths and sightlines crossing it are handled by great-circle math.
  • Line of sight models terrain only, with no Fresnel-zone, building, or vegetation clearance. Observer and target must be at most 1,000 km apart; within that, the curvature term is the standard parabolic approximation, which overstates the curve's rise by at most about 10 m at the midpoint.

Running the server

Local development

  • Build and run the production version:

    sh
    # One-time buildbun run rebuild
    # Run the built serverbun run start:http# orbun run start:stdio
  • Run checks and tests:

    sh
    bun run devcheck  # Lints, formats, type-checks, and morebun run test      # Runs the test suite

Project structure

DirectoryPurpose
src/index.tscreateApp() entry point: registers the four tools, builds the server instructions from config, and starts and disposes the elevation services.
src/configOPENTOPODATA_BASE_URL parsing and validation with Zod.
src/mcp-server/tools/definitionsTool definitions (*.tool.ts).
src/mcp-server/tools/sharedInput schemas, output schemas, and format() helpers shared by the four tools.
src/services/elevationElevationSampler (routing, 3DEP coverage envelope, per-call budget), geometry, attribution, and unit helpers.
src/services/usgs-epqsUSGS Elevation Point Query Service client.
src/services/opentopodataOpen Topo Data client and its request pacers.
src/services/sharedTimed fetch attempts and bounded body reads shared by both clients.
docs/design.mdDesign: tool contracts, computation, services, decisions, and limitations.
tests/Unit and tool tests against a mocked upstream.

Development guide

See CLAUDE.md for development guidelines and architectural rules. The short version:

  • Handlers throw, framework catches — no try/catch in tool logic
  • Use ctx.log for logging, ctx.state for storage
  • Register new tools in allToolDefinitions in src/mcp-server/tools/definitions/index.ts
  • Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields

Data sources and attribution

Datasetdataset idServed byTerms
USGS 3D Elevation Program (3DEP)usgs_3depUSGS Elevation Point Query ServicePublic domain (U.S. federal government work). Credit the U.S. Geological Survey, 3D Elevation Program.
SRTM GL1 v3srtm30mOpen Topo DataPublic domain (NASA/USGS).
Mapzen terrain tiles v1.1mapzenOpen Topo DataRequires the multi-source attribution below.

Every successful result's Sources: line credits each dataset that answered, and a response that used any Mapzen value carries the full Mapzen attribution there. The public Open Topo Data instance publishes no terms beyond its usage limits; its server software is MIT-licensed and self-hostable.

Mapzen terrain tiles attribution, verbatim from the tilezen/joerd attribution document:

  • ArcticDEM terrain data DEM(s) were created from DigitalGlobe, Inc., imagery and funded under National Science Foundation awards 1043681, 1559691, and 1542736;
  • Australia terrain data © Commonwealth of Australia (Geoscience Australia) 2017;
  • Austria terrain data © offene Daten Österreichs – Digitales Geländemodell (DGM) Österreich;
  • Canada terrain data contains information licensed under the Open Government Licence – Canada;
  • Europe terrain data produced using Copernicus data and information funded by the European Union - EU-DEM layers;
  • Global ETOPO1 terrain data U.S. National Oceanic and Atmospheric Administration
  • Mexico terrain data source: INEGI, Continental relief, 2016;
  • New Zealand terrain data Copyright 2011 Crown copyright (c) Land Information New Zealand and the New Zealand Government (All rights reserved);
  • Norway terrain data © Kartverket;
  • United Kingdom terrain data © Environment Agency copyright and/or database right 2015. All rights reserved;
  • United States 3DEP (formerly NED) and global GMTED2010 and SRTM terrain data courtesy of the U.S. Geological Survey.

This server is independent of the U.S. Geological Survey and of Open Topo Data, and is not endorsed by either.

Contributing

Issues are welcome. Run checks and tests before submitting:

sh
bun run devcheckbun run test

License

This project is licensed under the Apache 2.0 License. See the LICENSE file for details.

來源:README.md,提交 314a295

工具

0
工具後設資料尚未被收錄。

版本歷史

1
  1. v0.1.1最新Oct 1, 2026