OpenLandMap MCP Server
MCP Server for the OpenLandMap STAC catalog — full access to global geospatial environmental data for AI agents.
Overview
OpenLandMap provides 104+ collections of global environmental data as Cloud-Optimized GeoTIFFs (COG), covering:
| Theme | Examples |
|---|---|
| Soil | Organic carbon, pH, texture, bulk density, water content |
| Vegetation | EVI, FAPAR, forest cover, plant functional types |
| Land Cover | Land cover, land use, cropland, pasture, urban areas |
| Climate | Land surface temperature (LST), precipitation, bioclim |
| Terrain | DEM, slope, aspect, curvature, geomorphometry |
| Water | Water occurrence, snow cover |
| Atmosphere | NO2, water vapor, aerosol optical depth |
| Population | Population density, human footprint, wilderness |
Installation
cd openlandmap-mcp
# Install with uv
uv pip install -e .
# Or run directly (uv resolves dependencies automatically)
uv run openlandmap-mcp
Configuration
Prerequisites: uv must be installed and available in
PATH.
Claude Code
Add to .claude/settings.json (project-level) or ~/.claude/settings.json (global):
{
"mcpServers": {
"openlandmap": {
"command": "uv",
"args": ["run", "--directory", "/path/to/openlandmap-mcp", "openlandmap-mcp"]
}
}
}
Claude Desktop
Add to your claude_desktop_config.json:
| OS | Path |
|---|---|
| macOS | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Windows | %APPDATA%\Claude\claude_desktop_config.json |
| Linux | ~/.config/Claude/claude_desktop_config.json |
{
"mcpServers": {
"openlandmap": {
"command": "uv",
"args": ["run", "--directory", "/path/to/openlandmap-mcp", "openlandmap-mcp"]
}
}
}
Gemini CLI
Add to ~/.gemini/settings.json (global) or .gemini/settings.json (project-level):
| OS | Global path |
|---|---|
| Linux / macOS | ~/.gemini/settings.json |
| Windows | %USERPROFILE%\.gemini\settings.json |
{
"mcpServers": {
"openlandmap": {
"command": "uv",
"args": ["run", "--directory", "/path/to/openlandmap-mcp", "openlandmap-mcp"],
"timeout": 60000
}
}
}
After configuration, verify with the /mcp command inside Gemini CLI to list connected servers and available tools.
ChatGPT Desktop
ChatGPT supports MCP servers exclusively via remote HTTP/SSE transport — local stdio processes are not supported. You need to expose the server over HTTP first.
Step 1 — Start the server with SSE transport:
uv run openlandmap-mcp --transport sse --port 8811
Step 2 — Expose locally (for development) or deploy publicly:
For local development, use a tunnel like ngrok:
ngrok http 8811
# Example output: https://abc123.ngrok.app
For production, deploy behind a reverse proxy with a public HTTPS URL.
Step 3 — Register in ChatGPT Desktop:
- Open Settings → Apps & Connectors → Advanced Settings
- Enable Developer Mode
- Go to Settings → Connectors → Create
- Fill in:
- Name:
OpenLandMap - Connector URL:
https://abc123.ngrok.app/mcp(or your public URL) - Authentication: None (or configure as needed)
- Name:
Note: ChatGPT does not support local config files for MCP. All registration is done through the UI. The server must be reachable over HTTPS.
Cursor / VS Code
Add to .cursor/mcp.json (Cursor) or .vscode/mcp.json (VS Code):
{
"mcpServers": {
"openlandmap": {
"command": "uv",
"args": ["run", "--directory", "/path/to/openlandmap-mcp", "openlandmap-mcp"]
}
}
}
Compatibility Matrix
| Client | Transport | Config file | Local stdio |
|---|---|---|---|
| Claude Code | stdio | .claude/settings.json | Yes |
| Claude Desktop | stdio | claude_desktop_config.json | Yes |
| Gemini CLI | stdio | ~/.gemini/settings.json | Yes |
| ChatGPT Desktop | HTTP/SSE | UI only (no file) | No |
| Cursor | stdio | .cursor/mcp.json | Yes |
| VS Code | stdio | .vscode/mcp.json | Yes |
Capabilities
1. Catalog Discovery
Browse and search the entire OpenLandMap catalog.
catalog_info()
Returns root catalog metadata: ID, description, STAC version, total collection count.
"What data is available on OpenLandMap?"
list_collections(theme?, keyword?, limit?)
List collections with optional filters by theme alias or keyword search across titles, descriptions, and keywords.
"List all soil collections"
→ list_collections(theme="soil")
"Find collections related to organic carbon"
→ list_collections(keyword="organic carbon")
"Show the first 5 vegetation datasets"
→ list_collections(theme="vegetation", limit=5)
Available theme aliases: soil, vegetation, land_cover, climate, terrain, water, atmosphere, population
get_collection_schema(collection_id)
Returns the full schema of a collection: asset type definitions (MIME types, roles), STAC extensions used, spatial/temporal extent, contact info, style URLs (SLD/QML), and related links.
"Show me everything about the organic carbon collection"
→ get_collection_schema("organic.carbon_usda.6a1c")
discover_data_for_topic(topic)
Natural language search across all collection titles, descriptions, and keywords. Returns results ranked by relevance score.
"Find data about deforestation in the Amazon"
→ discover_data_for_topic("Amazon deforestation forest loss")
"What datasets exist for air quality analysis?"
→ discover_data_for_topic("air quality NO2 aerosol")
2. Collection Operations
Analyze, compare, and explore collections in depth.
compare_collections(collection_ids)
Side-by-side comparison of 2–10 collections: temporal coverage, resolution, units, keywords, and theme.
"Compare soil organic carbon and pH datasets"
→ compare_collections(["organic.carbon_usda.6a1c", "ph.h2o_usda.4c1a2a"])
get_collection_temporal_stats(collection_id)
Temporal statistics: earliest/latest dates, item count, year-by-year distribution, and median update interval.
"How often is the EVI dataset updated?"
→ get_collection_temporal_stats("evi_mod13q1.tmwm.inpaint")
find_related_collections(collection_id)
Finds collections sharing the same theme or native category. Useful for discovering complementary datasets.
"What other datasets are related to land surface temperature?"
→ find_related_collections("lst_mod11a2.daytime")
get_soil_collections()
Shortcut returning all soil-related collections (bulk density, organic carbon, pH, sand/clay/silt, texture, water content, taxonomy).
"List all available soil datasets"
→ get_soil_collections()
get_vegetation_collections()
Shortcut returning all vegetation-related collections (EVI, FAPAR, forest cover, plant functional types).
"What vegetation indices are available?"
→ get_vegetation_collections()
get_land_cover_collections()
Shortcut returning all land cover/land use collections (classification, cropland, pasture, urban, change detection).
"Show me land cover datasets"
→ get_land_cover_collections()
3. Item Search & Access
Search and retrieve individual temporal snapshots within collections.
search_items(collection_id, bbox?, datetime_range?, limit?, offset?)
Search items with spatial (bounding box) and temporal (ISO 8601 interval) filters. Supports pagination.
"Find organic carbon data for the Cerrado region"
→ search_items("organic.carbon_usda.6a1c", bbox=[-60.47, -24.68, -41.28, -2.33])
"Get EVI data from 2015 to 2020"
→ search_items("evi_mod13q1.tmwm.inpaint", datetime_range="2015-01-01/2020-12-31", limit=10)
"Show all items in the land cover collection"
→ search_items("land.cover_esacci.lc.l4", limit=50)
get_item_detail(collection_id, item_id)
Full item details: geometry (GeoJSON), bounding box, temporal range, all assets with resolved URLs, MIME types, file sizes, and checksums.
"Show full details for the 2018 organic carbon item"
→ get_item_detail("organic.carbon_usda.6a1c", "organic.carbon_usda.6a1c_19500101_20171231")
list_items_temporal(collection_id)
Lists all items sorted chronologically. Shows the complete temporal coverage and update pattern of a dataset.
"What time periods are available for EVI?"
→ list_items_temporal("evi_mod13q1.tmwm.inpaint")
find_items_by_point(lon, lat, collection_ids?)
Given a WGS84 coordinate, finds all items covering that point across one or more collections.
"What data is available for Brasília?"
→ find_items_by_point(-47.9, -15.8)
"Find soil data at this location"
→ find_items_by_point(-47.9, -15.8, ["organic.carbon_usda.6a1c", "ph.h2o_usda.4c1a2a"])
4. Asset Access & Download
Resolve URLs and generate download instructions for data files.
get_asset_url(collection_id, item_id, asset_key)
Resolves the full S3 URL for a specific asset. Returns MIME type, roles, COG status, file size, checksum, and S3 bucket/key.
"Get the URL for the 0cm depth organic carbon layer"
→ get_asset_url("organic.carbon_usda.6a1c", "organic.carbon_usda.6a1c_19500101_20171231", "organic.carbon_usda.6a1c_m_250m_b0cm")
list_assets_for_item(collection_id, item_id)
Lists all assets (data layers, thumbnails, style files) with complete metadata for each.
"What files are available in this item?"
→ list_assets_for_item("organic.carbon_usda.6a1c", "organic.carbon_usda.6a1c_19500101_20171231")
get_all_data_assets(collection_id, item_id)
Filters to only data assets (role="data"), excluding thumbnails, QML, and SLD files. Returns direct COG URLs.
"Give me only the data layers, not the styles"
→ get_all_data_assets("organic.carbon_usda.6a1c", "organic.carbon_usda.6a1c_19500101_20171231")
get_asset_download_info(collection_id, item_id, asset_key)
Returns the direct URL plus ready-to-use commands and code snippets:
- curl command for download
- wget command for download
- Python snippet using
rasterio(streaming, no download needed) - R snippet using
terra(streaming, no download needed)
"How do I access the soil carbon data programmatically?"
→ get_asset_download_info("organic.carbon_usda.6a1c", "organic.carbon_usda.6a1c_19500101_20171231", "organic.carbon_usda.6a1c_m_250m_b0cm")
get_visualization_assets(collection_id, item_id)
Returns visualization-related assets: thumbnails (PNG), QGIS layer styles (QML), and WMS style descriptors (SLD).
"Get the QGIS style file for this layer"
→ get_visualization_assets("organic.carbon_usda.6a1c", "organic.carbon_usda.6a1c_19500101_20171231")
5. Spatial Queries
Work with geographic regions and spatial coverage.
get_bbox_for_region(region_name)
Returns the WGS84 bounding box [west, south, east, north] for a named region. Supports ~50 regions:
- Countries:
brazil,usa,china,india,australia,germany,france,japan,mexico,argentina,colombia,south_africa,kenya,indonesia, etc. - Brazilian states:
goias,mato_grosso,para,minas_gerais,sao_paulo,bahia,amazonas,tocantins,maranhao,rondonia,acre,roraima,amapa, etc. - Brazilian biomes:
cerrado,amazon,amazonia_legal,caatinga,pantanal,mata_atlantica,pampa - Continents/regions:
south_america,north_america,europe,africa,asia,southeast_asia,oceania,middle_east,global
"What's the bounding box for the Cerrado biome?"
→ get_bbox_for_region("cerrado")
# Returns: {"region": "cerrado", "bbox": [-60.47, -24.68, -41.28, -2.33]}
"Get the bbox for Goiás state"
→ get_bbox_for_region("goias")
find_collections_for_bbox(bbox, theme?)
Finds all collections whose spatial extent intersects the given bounding box. Optionally filters by theme.
"What soil data covers the Cerrado?"
→ find_collections_for_bbox([-60.47, -24.68, -41.28, -2.33], theme="soil")
"Find all datasets available for Southeast Asia"
→ find_collections_for_bbox([92.0, -11.0, 141.0, 28.0])
get_collection_spatial_coverage(collection_id)
Returns full spatial coverage: bounding box, GeoJSON polygon of the extent, ground sample distance (resolution in meters), and CRS.
"What is the spatial resolution of the elevation model?"
→ get_collection_spatial_coverage("dtm.bareearth_ensemble")
6. Analysis & Code Generation
Discover data by topic, analyze temporal coverage, and generate ready-to-use code.
get_data_timeline(collection_ids)
Generates a structured timeline showing data availability across 1–10 collections. Useful for planning multi-temporal analyses.
"Show me when soil and vegetation data overlap"
→ get_data_timeline(["organic.carbon_usda.6a1c", "evi_mod13q1.tmwm.inpaint"])
find_overlapping_datasets(collection_id, start_year, end_year)
Finds all other collections with temporal overlap in the given period. Useful for multi-variable correlation studies.
"What datasets overlap with land cover data between 2010 and 2020?"
→ find_overlapping_datasets("land.cover_esacci.lc.l4", 2010, 2020)
get_stac_item_as_geojson(collection_id, item_id)
Returns the item as a pure GeoJSON Feature, ready for use in any GIS tool, web map, or geospatial pipeline.
"Export this item as GeoJSON"
→ get_stac_item_as_geojson("organic.carbon_usda.6a1c", "organic.carbon_usda.6a1c_19500101_20171231")
build_python_snippet(collection_id, item_id, asset_key, operation)
Generates a ready-to-run Python code snippet using rasterio and matplotlib.
Operations: open, info, plot, clip_bbox, stats, export_csv
"Generate Python code to plot organic carbon data"
→ build_python_snippet("organic.carbon_usda.6a1c", "organic.carbon_usda.6a1c_19500101_20171231", "organic.carbon_usda.6a1c_m_250m_b0cm", "plot")
"Generate code to compute statistics"
→ build_python_snippet(..., operation="stats")
"Generate code to clip to a bounding box"
→ build_python_snippet(..., operation="clip_bbox")
build_r_snippet(collection_id, item_id, asset_key, operation)
Generates a ready-to-run R code snippet using terra and sf.
Operations: open, info, plot, clip_bbox, stats, export_csv
"Generate R code to open and plot this raster"
→ build_r_snippet("organic.carbon_usda.6a1c", "organic.carbon_usda.6a1c_19500101_20171231", "organic.carbon_usda.6a1c_m_250m_b0cm", "plot")
MCP Resources
Static data accessible via URI patterns:
| URI | Description |
|---|---|
stac://openlandmap/catalog | Root catalog metadata (ID, version, collection count) |
stac://openlandmap/themes | Index of all collections organized by thematic category |
stac://openlandmap/guide | Usage guide with quick-start instructions |
stac://openlandmap/collection/{id} | Full metadata for a specific collection |
stac://openlandmap/collection/{id}/item/{item_id} | Full item details including all assets |
MCP Prompts
Pre-built workflow templates for common analysis patterns:
| Prompt | Description | Parameters |
|---|---|---|
explore_collection | Step-by-step exploration of a collection (schema, temporal coverage, assets, related data) | collection_id |
find_data_for_analysis | Guided workflow to find data for a specific analysis (region + topic + period) | topic, region, period |
download_workflow | Complete data access workflow with URLs, commands, and code snippets | collection_id, item_id |
Usage Examples
"What data is available on OpenLandMap?"
"List all soil datasets"
"Find collections about organic carbon"
"Show the temporal coverage of evi_mod13q1.tmwm.inpaint"
"Find vegetation data for the Cerrado between 2015 and 2020"
"Generate a Python snippet to access EVI data"
"Compare soil collections: organic.carbon vs ph.h2o"
"What is the spatial resolution of the elevation dataset?"
"Find datasets that overlap temporally with land cover between 2010-2020"
"Generate R code to plot soil organic carbon"
"What data is available at coordinates -47.9, -15.8?"
"Export this STAC item as GeoJSON for use in QGIS"
"How do I download the bulk density dataset with curl?"
"Show me the QGIS style file for the pH collection"
Architecture
- Static catalog: Data is served from S3 (Wasabi) as static STAC JSONs — no server-side search API
- In-memory cache: Configurable TTL per resource type (1h catalog, 30min collections, 10min items)
- Lazy loading: Collections fetched on demand with concurrent batch fetch (semaphore-limited) when global filtering is needed
- No native dependencies: Pure Python and pre-built wheels only (no GDAL/GEOS compilation required)
- Retry with backoff: Automatic retry (3 attempts, exponential delays 1s/2s/4s) for all HTTP requests
- Thundering herd protection: Per-key async locks prevent duplicate fetches for the same resource
License
OpenLandMap data is distributed under CC-BY-SA-4.0.