Skip to main content
Glama
README.md
# EO-MCP

EO-MCP is a Python [Model Context Protocol (MCP)](https://modelcontextprotocol.io/)
server for discovering and analysing Sentinel-2 Level-2A imagery. It gives AI
agents a small, focused toolset for finding scenes, checking their suitability,
computing spectral indices, and summarising an index inside a polygon.

The server reads only the required windows from cloud-optimized GeoTIFFs hosted
by the [Microsoft Planetary Computer](https://planetarycomputer.microsoft.com/).
It does not download complete Sentinel-2 scenes.

## Features

- Search the live Sentinel-2 L2A STAC catalog by bounding box, date, and cloud
  cover.
- Inspect scene-level cloud and nodata metadata before performing analysis.
- Confirm that a scene footprint covers the requested area of interest.
- Compute NDVI, NDWI, and NBR from remote raster windows.
- Calculate polygon-clipped min, max, mean, and pixel count.
- Return structured, agent-friendly results with processing metadata.

The intended workflow is:

```text
stac_search → scene_quality → compute_index
                          └─→ quick_stats
```

Always call `scene_quality` before computing an index.

## Tools

| Tool | Purpose | Key inputs |
| --- | --- | --- |
| `stac_search` | Find the clearest Sentinel-2 L2A scenes intersecting an area. | `bbox`, `start_date`, `end_date`, `max_cloud_cover`, `limit` |
| `scene_quality` | Check cloud cover, nodata, and scene-footprint coverage. | `scene_id`, `bbox` |
| `compute_index` | Compute summary statistics for NDVI, NDWI, or NBR over a rectangular area. | `scene_id`, `bbox`, `index_name` |
| `quick_stats` | Compute index statistics clipped to a WGS84 GeoJSON Polygon or MultiPolygon. | `scene_id`, `polygon`, `index_name`, `all_touched` |

Bounding boxes use WGS84 coordinates in this order:

```text
[west, south, east, north]
```

Dates use ISO `YYYY-MM-DD` format.

## Supported indices

| Index | Formula | Typical use |
| --- | --- | --- |
| NDVI | `(B08 - B04) / (B08 + B04)` | Vegetation condition and greenness |
| NDWI | `(B03 - B08) / (B03 + B08)` | Surface-water detection |
| NBR | `(B08 - B12) / (B08 + B12)` | Burn severity and fire impact |

Sentinel-2 digital numbers are converted to reflectance before the index is
calculated. EO-MCP also accounts for the offset introduced with processing
baseline 04.00 and later.

## Requirements

- Python 3.12 or newer
- [`uv`](https://docs.astral.sh/uv/)
- Internet access to the Planetary Computer STAC API and Sentinel-2 COG assets

No Planetary Computer API key is required for the current public-data workflow.

## Quick start

From the repository root:

```bash
uv sync --frozen
uv run python -c "import eo_mcp; print('eo_mcp import: ok')"
uv run fastmcp inspect src/eo_mcp/app.py
```

The inspect command should report the server name `EO-MCP` and four tools.

To start the STDIO server manually:

```bash
uv run fastmcp run src/eo_mcp/app.py
```

An STDIO server waits for an MCP client and usually produces no interactive
prompt. Use an MCP client or `fastmcp call` to invoke its tools.

## Test from the command line

Search for Sentinel-2 scenes:

```bash
uv run fastmcp call \
  --server-spec src/eo_mcp/app.py \
  --target stac_search \
  --input-json '{
    "bbox": [73.0, 33.65, 73.12, 33.75],
    "start_date": "2024-06-01",
    "end_date": "2024-08-31",
    "max_cloud_cover": 10,
    "limit": 1
  }' \
  --json
```

Check the selected scene:

```bash
uv run fastmcp call \
  --server-spec src/eo_mcp/app.py \
  --target scene_quality \
  --input-json '{
    "scene_id": "S2B_MSIL2A_20240603T054639_R048_T43SCT_20240603T090813",
    "bbox": [73.0, 33.65, 73.12, 33.75]
  }' \
  --json
```

Compute NDVI after the quality check passes:

```bash
uv run fastmcp call \
  --server-spec src/eo_mcp/app.py \
  --target compute_index \
  --input-json '{
    "scene_id": "S2B_MSIL2A_20240603T054639_R048_T43SCT_20240603T090813",
    "bbox": [73.0, 33.65, 73.02, 33.67],
    "index_name": "NDVI"
  }' \
  --json
```

For the installed FastMCP version, pass `src/eo_mcp/app.py` as the server
specification without appending `:mcp`.

## Connect to Codex or another MCP client

EO-MCP currently runs as a local STDIO server. Configure the client with an
absolute project path so it can launch the server from any working directory.

```json
{
  "mcpServers": {
    "eo-mcp": {
      "command": "uv",
      "args": [
        "run",
        "--project",
        "/absolute/path/to/eo-mcp",
        "--frozen",
        "fastmcp",
        "run",
        "/absolute/path/to/eo-mcp/src/eo_mcp/app.py"
      ]
    }
  }
}
```

If the client cannot find `uv`, replace `"uv"` with the absolute path returned
by:

```bash
command -v uv
```

With the Codex CLI, the equivalent registration command is:

```bash
codex mcp add eo-mcp -- \
  /absolute/path/to/uv run \
  --project /absolute/path/to/eo-mcp \
  --frozen fastmcp run \
  /absolute/path/to/eo-mcp/src/eo_mcp/app.py
```

Restart the client or open a new session after saving the configuration. In
Codex, use `/mcp` to confirm that `eo-mcp` is connected.

Example agent request:

```text
Find a low-cloud Sentinel-2 scene for Lahore during July 2026. Check the
scene's quality, then compute NDVI for a small area and report the mean.
```

## GeoJSON polygon example

`quick_stats` accepts either a GeoJSON geometry or a GeoJSON Feature:

```json
{
  "type": "Polygon",
  "coordinates": [
    [
      [73.002, 33.652],
      [73.018, 33.652],
      [73.018, 33.668],
      [73.002, 33.668],
      [73.002, 33.652]
    ]
  ]
}
```

Coordinates must be WGS84 longitude/latitude values. Polygon and MultiPolygon
geometries are supported.

## How index processing works

1. EO-MCP retrieves the requested STAC item and signs its asset URLs.
2. It transforms the WGS84 area into the source raster coordinate system.
3. Rasterio reads only the intersecting COG window over HTTPS.
4. Bands on a different grid are aligned to the reference band.
5. Digital numbers are converted to reflectance and the selected index is
   calculated.
6. The server returns statistics and processing metadata rather than the full
   raster array.

`compute_index` reports min, max, mean, median, standard deviation, 2nd and
98th percentiles, pixel counts, CRS, resolution, and the source window.
`quick_stats` clips the index raster to the supplied polygon and reports min,
max, mean, and count.

## Quality rules and operational limits

A scene is marked usable when all of the following are true:

- Scene-level cloud cover is at most 20%.
- Scene-level nodata is at most 10%.
- The scene footprint covers at least 99% of the requested area.

Important limitations:

- Cloud and nodata percentages come from scene-level STAC metadata; they are
  not calculated specifically for the requested bounding box.
- Index calculations do not apply a per-pixel cloud mask. Select a suitable
  scene with `scene_quality` first.
- A rectangular index request is limited to 4,000,000 reference-grid pixels.
  Reduce the bounding box if this limit is exceeded.
- `compute_index` summarises the full rectangular window. Use `quick_stats`
  when results must be clipped to an exact administrative, farm, water, or
  other polygon boundary.
- Signed Planetary Computer asset URLs expire. Search again instead of storing
  them as permanent download links.
- Large requests depend on remote-network speed and upstream service
  availability.

## Project structure

```text
eo-mcp/
├── pyproject.toml
├── src/
│   └── eo_mcp/
│       ├── __init__.py
│       ├── app.py       # FastMCP server and registered tools
│       ├── tools.py     # STAC, raster, index, and zonal-stat logic
│       └── utils.py     # Input validation helpers
└── README.md
```

## Technology

- [FastMCP](https://gofastmcp.com/) for the MCP server
- [PySTAC Client](https://pystac-client.readthedocs.io/) for STAC discovery
- [Microsoft Planetary Computer](https://planetarycomputer.microsoft.com/) for
  the Sentinel-2 L2A catalog and signed assets
- [Rasterio](https://rasterio.readthedocs.io/) and NumPy for windowed raster
  processing
- [Rasterstats](https://pythonhosted.org/rasterstats/) and
  [Shapely](https://shapely.readthedocs.io/) for polygon statistics
- [`uv`](https://docs.astral.sh/uv/) for dependency and project management

## Remote deployment

STDIO is intended for local clients. To share EO-MCP across machines, expose it
using FastMCP's Streamable HTTP transport, add authentication, and deploy it on
a Python or container hosting platform. Do not publish an unauthenticated
analysis endpoint to the public internet.

See the
[FastMCP HTTP deployment guide](https://gofastmcp.com/v2/deployment/http) for
the supported remote-server patterns.

TDQS

B3.4/5.0

Scored across 4 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: searching for scenes, checking scene quality, computing indices, and getting statistics. No overlap or ambiguity.

Naming Consistency4/5

All tool names use snake_case with a noun_noun or verb_noun pattern (e.g., stac_search, scene_quality). While not all are strictly verb_noun, they are consistent in format and readable.

Tool Count5/5

Four tools is appropriate for the domain of satellite imagery analysis, covering the essential workflow from search to statistics without unnecessary bloat.

Completeness4/5

The tool set covers the key steps: search, quality check, index computation, and statistics. A minor gap is the lack of scene metadata retrieval, but stac_search likely provides some metadata.

Maintenance

ActivitySlowing
ResponsivenessNo issues