Add MCP prompts, README, LICENSE, and PyPI metadata
Five new prompts guide LLMs through multi-tool workflows: satellite_snapshot, climate_monitor, layer_deep_dive, multi_layer_story, polar_watch. README documents all 11 tools, 5 resources, and 7 prompts with example conversations. MIT license, project URLs, and updated classifiers for PyPI.
This commit is contained in:
parent
4a5035ca52
commit
fc05ccd52a
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Ryan Malloy
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
168
README.md
Normal file
168
README.md
Normal file
@ -0,0 +1,168 @@
|
||||
# mcgibs
|
||||
|
||||
NASA Earth science visualizations for LLMs.
|
||||
|
||||
An [MCP](https://modelcontextprotocol.io/) server that connects language models to [NASA GIBS](https://www.earthdata.nasa.gov/engage/open-data-services-software/earthdata-developer-portal/gibs-api) (Global Imagery Browse Services) — 1000+ visualization layers covering satellite imagery, scientific data products, and derived Earth observations, updated daily.
|
||||
|
||||
**Three pillars:**
|
||||
|
||||
- **Discovery** — search layers by keyword, browse measurement categories, check date availability
|
||||
- **Visualization** — fetch imagery and data products by place name and date, compare dates side-by-side, composite multiple layers
|
||||
- **Interpretation** — natural-language colormap explanations, legend graphics, scientific context
|
||||
|
||||
No API key required. All data is freely available from NASA.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### From PyPI
|
||||
|
||||
```bash
|
||||
uvx mcgibs
|
||||
```
|
||||
|
||||
### Add to Claude Code
|
||||
|
||||
```bash
|
||||
claude mcp add mcgibs -- uvx mcgibs
|
||||
```
|
||||
|
||||
### Local development
|
||||
|
||||
```bash
|
||||
git clone https://git.supported.systems/rpm/mcgibs.git
|
||||
cd mcgibs
|
||||
uv sync --all-extras
|
||||
uv run mcgibs
|
||||
```
|
||||
|
||||
Or add a local dev server to Claude Code:
|
||||
|
||||
```bash
|
||||
claude mcp add mcgibs-local -- uv run --directory /path/to/mcgibs mcgibs
|
||||
```
|
||||
|
||||
## Tools
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `search_gibs_layers` | Search 1000+ layers by keyword, measurement, period, or status |
|
||||
| `get_layer_info` | Full metadata for a layer — instrument, platform, resolution, dates |
|
||||
| `list_measurements` | All measurement categories with layer counts |
|
||||
| `check_layer_dates` | Available date range for a layer (capabilities + live DescribeDomains) |
|
||||
| `get_imagery` | Fetch a visualization by layer, date, and place name or bbox |
|
||||
| `compare_dates` | Side-by-side comparison of two dates for change detection |
|
||||
| `get_imagery_composite` | Overlay up to 5 layers into a single composite image |
|
||||
| `explain_layer_colormap` | Natural-language explanation of what colors represent |
|
||||
| `get_legend` | Pre-rendered legend graphic for a layer |
|
||||
| `resolve_place` | Geocode a place name to coordinates and bounding box |
|
||||
| `build_tile_url` | Construct a direct WMTS tile URL for embedding |
|
||||
|
||||
## Resources
|
||||
|
||||
| URI | Description |
|
||||
|-----|-------------|
|
||||
| `gibs://catalog` | Full layer catalog grouped by measurement category |
|
||||
| `gibs://layer/{layer_id}` | Individual layer metadata as JSON |
|
||||
| `gibs://colormap/{layer_id}` | Colormap explanation for a layer |
|
||||
| `gibs://dates/{layer_id}` | Available date range for a layer |
|
||||
| `gibs://projections` | Supported GIBS projections and endpoints |
|
||||
|
||||
## Prompts
|
||||
|
||||
| Prompt | Parameters | Description |
|
||||
|--------|------------|-------------|
|
||||
| `earth_overview` | *(none)* | Introduction to GIBS with suggested explorations |
|
||||
| `investigate_event` | `event_type`, `location`, `date` | Guided workflow for investigating natural events |
|
||||
| `satellite_snapshot` | `place`, `date` | Quick satellite view of any location |
|
||||
| `climate_monitor` | `indicator`, `location`, `start_date`, `end_date` | Track climate changes over time |
|
||||
| `layer_deep_dive` | `layer_id`, `location`, `date` | Full scientific analysis of a single layer |
|
||||
| `multi_layer_story` | `topic`, `location`, `date` | Data journalism — composite layers to tell a story |
|
||||
| `polar_watch` | `pole`, `date`, `compare_date` | Arctic/Antarctic ice and snow monitoring |
|
||||
|
||||
## Example Workflows
|
||||
|
||||
### Investigating a wildfire
|
||||
|
||||
```
|
||||
User: Use the investigate_event prompt for the 2025 LA wildfires
|
||||
|
||||
→ search_gibs_layers("fire")
|
||||
→ search_gibs_layers("true color")
|
||||
→ check_layer_dates("MODIS_Terra_CorrectedReflectance_TrueColor")
|
||||
→ get_imagery("MODIS_Terra_CorrectedReflectance_TrueColor", "2025-01-08", place="Los Angeles")
|
||||
→ get_imagery("MODIS_Fires_Terra", "2025-01-08", place="Los Angeles")
|
||||
→ compare_dates("MODIS_Terra_CorrectedReflectance_TrueColor", "2025-01-01", "2025-01-10", place="Los Angeles")
|
||||
→ explain_layer_colormap("MODIS_Fires_Terra")
|
||||
```
|
||||
|
||||
### Monitoring sea ice decline
|
||||
|
||||
```
|
||||
User: Use climate_monitor to track Arctic sea ice from March to September
|
||||
|
||||
→ search_gibs_layers("sea ice")
|
||||
→ check_layer_dates("AMSR2_Sea_Ice_Concentration_12km")
|
||||
→ explain_layer_colormap("AMSR2_Sea_Ice_Concentration_12km")
|
||||
→ compare_dates("AMSR2_Sea_Ice_Concentration_12km", "2025-03-01", "2025-09-01", bbox=[-180,60,180,90])
|
||||
→ get_legend("AMSR2_Sea_Ice_Concentration_12km")
|
||||
```
|
||||
|
||||
### Quick look at anywhere on Earth
|
||||
|
||||
```
|
||||
User: Use satellite_snapshot for the Great Barrier Reef
|
||||
|
||||
→ resolve_place("Great Barrier Reef")
|
||||
→ get_imagery("MODIS_Terra_CorrectedReflectance_TrueColor", "2025-06-01", place="Great Barrier Reef")
|
||||
→ search_gibs_layers("chlorophyll")
|
||||
→ get_imagery_composite(["MODIS_Terra_CorrectedReflectance_TrueColor", "MODIS_Terra_Chlorophyll_A"], "2025-06-01", place="Great Barrier Reef")
|
||||
```
|
||||
|
||||
## Projections
|
||||
|
||||
| EPSG | Description | Use case |
|
||||
|------|-------------|----------|
|
||||
| 4326 | Geographic (WGS84) | Default — global coverage, most layers |
|
||||
| 3857 | Web Mercator | Web map tiles, Leaflet/Mapbox integration |
|
||||
| 3413 | Arctic Polar Stereographic | Arctic-focused imagery |
|
||||
| 3031 | Antarctic Polar Stereographic | Antarctic-focused imagery |
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
uv sync --all-extras
|
||||
|
||||
# Lint
|
||||
uv run ruff check src/ tests/
|
||||
|
||||
# Tests
|
||||
uv run pytest
|
||||
|
||||
# Build
|
||||
uv build
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
src/mcgibs/
|
||||
server.py MCP server — tools, resources, prompts, middleware
|
||||
client.py GIBS HTTP client — WMS, WMTS, colormaps, geocoding
|
||||
capabilities.py WMTS GetCapabilities parser and layer search
|
||||
colormaps.py Colormap XML parser and natural-language interpreter
|
||||
models.py Pydantic models — Layer, BBox, GeoResult, ColormapEntry
|
||||
constants.py API endpoints, projections, tile matrix definitions
|
||||
geo.py Bounding box math and geocoding helpers
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
## Links
|
||||
|
||||
- [NASA GIBS](https://www.earthdata.nasa.gov/engage/open-data-services-software/earthdata-developer-portal/gibs-api)
|
||||
- [GIBS API Documentation](https://nasa-gibs.github.io/gibs-api-docs/)
|
||||
- [Worldview](https://worldview.earthdata.nasa.gov/) — NASA's browser-based GIBS viewer
|
||||
- [FastMCP](https://gofastmcp.com/) — the MCP framework powering this server
|
||||
- [Source](https://git.supported.systems/rpm/mcgibs)
|
||||
@ -1,15 +1,17 @@
|
||||
[project]
|
||||
name = "mcgibs"
|
||||
version = "2026.02.18"
|
||||
version = "2026.02.18.1"
|
||||
description = "FastMCP server for NASA Global Imagery Browse Services (GIBS)"
|
||||
readme = "CLAUDE.md"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
license = {text = "MIT"}
|
||||
authors = [{name = "Ryan Malloy", email = "ryan@supported.systems"}]
|
||||
keywords = ["nasa", "gibs", "mcp", "satellite", "imagery", "earth-science"]
|
||||
classifiers = [
|
||||
"Development Status :: 4 - Beta",
|
||||
"Intended Audience :: Developers",
|
||||
"Intended Audience :: Science/Research",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Programming Language :: Python :: 3.14",
|
||||
@ -21,6 +23,11 @@ dependencies = [
|
||||
"defusedxml>=0.7.1",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://git.supported.systems/rpm/mcgibs"
|
||||
Documentation = "https://nasa-gibs.github.io/gibs-api-docs/"
|
||||
"Bug Tracker" = "https://git.supported.systems/rpm/mcgibs/issues"
|
||||
|
||||
[project.scripts]
|
||||
mcgibs = "mcgibs.server:main"
|
||||
|
||||
|
||||
@ -1 +1 @@
|
||||
__version__ = "2026.02.18"
|
||||
__version__ = "2026.02.18.1"
|
||||
|
||||
@ -746,6 +746,251 @@ def earth_overview() -> str:
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
@mcp.prompt
|
||||
def satellite_snapshot(
|
||||
place: str,
|
||||
date: str = "today",
|
||||
) -> str:
|
||||
"""Quick satellite view of any place on Earth.
|
||||
|
||||
Args:
|
||||
place: Location to view (e.g. "Tokyo", "Amazon Rainforest", "Sahara Desert").
|
||||
date: Date to view (YYYY-MM-DD), or "today" for the most recent imagery.
|
||||
"""
|
||||
lines = [
|
||||
f"Show me a satellite view of {place} on {date}.",
|
||||
"",
|
||||
"Follow this workflow:",
|
||||
"",
|
||||
f'1. **Geocode**: Use resolve_place to get coordinates for "{place}".',
|
||||
"",
|
||||
f"2. **True color first**: Fetch imagery for "
|
||||
f'"MODIS_Terra_CorrectedReflectance_TrueColor" '
|
||||
f"on {date} at that location. This gives a natural-looking view.",
|
||||
"",
|
||||
f"3. **Find science layers**: Search for layers relevant to "
|
||||
f'"{place}" — consider what would be scientifically interesting '
|
||||
f"for this region (vegetation for forests, sea surface temperature "
|
||||
f"for coastal areas, snow cover for mountains, etc.).",
|
||||
"",
|
||||
"4. **Check dates**: Verify the science layers have data for "
|
||||
f"{date} using check_layer_dates.",
|
||||
"",
|
||||
"5. **Overlay**: If a relevant science layer is available, "
|
||||
"use get_imagery_composite to overlay it on the true color base.",
|
||||
"",
|
||||
"6. **Interpret**: If the overlaid layer has a colormap, "
|
||||
"use explain_layer_colormap to describe what the colors mean.",
|
||||
"",
|
||||
f"7. **Describe**: Summarize what's visible in the satellite view "
|
||||
f"of {place} — notable features, weather patterns, land use, "
|
||||
f"or any phenomena visible from orbit.",
|
||||
]
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
@mcp.prompt
|
||||
def climate_monitor(
|
||||
indicator: str,
|
||||
location: str,
|
||||
start_date: str,
|
||||
end_date: str,
|
||||
) -> str:
|
||||
"""Track a climate indicator over time at a specific location.
|
||||
|
||||
Args:
|
||||
indicator: What to monitor (e.g. "sea ice", "vegetation", "temperature", "snow cover").
|
||||
location: Region to monitor (e.g. "Arctic", "Amazon Basin", "Greenland").
|
||||
start_date: Beginning of monitoring period (YYYY-MM-DD).
|
||||
end_date: End of monitoring period (YYYY-MM-DD).
|
||||
"""
|
||||
lines = [
|
||||
f"Monitor {indicator} changes near {location} "
|
||||
f"from {start_date} to {end_date}.",
|
||||
"",
|
||||
"Follow this workflow:",
|
||||
"",
|
||||
f'1. **Find layers**: Search for GIBS layers related to "{indicator}". '
|
||||
f"Look for layers with colormaps — these encode quantitative data.",
|
||||
"",
|
||||
"2. **Check availability**: Use check_layer_dates on the best "
|
||||
f"matching layer to confirm data exists for both {start_date} "
|
||||
f"and {end_date}.",
|
||||
"",
|
||||
"3. **Understand the scale**: Use explain_layer_colormap to learn "
|
||||
"what the color values represent (units, range). "
|
||||
"Also fetch the legend with get_legend for visual reference.",
|
||||
"",
|
||||
f"4. **Compare endpoints**: Use compare_dates to create a "
|
||||
f"side-by-side view of {start_date} vs {end_date} "
|
||||
f'for "{location}". This immediately shows the magnitude of change.',
|
||||
"",
|
||||
"5. **Intermediate snapshots**: Fetch 1-2 additional dates between "
|
||||
"start and end using get_imagery to show the progression "
|
||||
"(e.g. mid-point of the range).",
|
||||
"",
|
||||
"6. **Synthesize**: Describe the observed trend, referencing "
|
||||
"specific colormap values where possible. Is the indicator "
|
||||
"increasing, decreasing, or showing seasonal variation? "
|
||||
"What does the spatial pattern reveal?",
|
||||
]
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
@mcp.prompt
|
||||
def layer_deep_dive(
|
||||
layer_id: str,
|
||||
location: str = "global",
|
||||
date: str = "",
|
||||
) -> str:
|
||||
"""Full scientific analysis of a single GIBS layer.
|
||||
|
||||
Args:
|
||||
layer_id: The GIBS layer identifier (e.g. "AIRS_L3_Surface_Air_Temperature_Daily_Day").
|
||||
location: Region to focus on, or "global" for worldwide view.
|
||||
date: Specific date (YYYY-MM-DD), or empty for the most recent available.
|
||||
"""
|
||||
location_desc = f'for "{location}"' if location != "global" else "at global scale"
|
||||
date_desc = f"on {date}" if date else "using the most recent available date"
|
||||
|
||||
lines = [
|
||||
f"Do a deep scientific analysis of the {layer_id} layer "
|
||||
f"{location_desc} {date_desc}.",
|
||||
"",
|
||||
"Follow this workflow:",
|
||||
"",
|
||||
"1. **Layer metadata**: Use get_layer_info to retrieve full metadata "
|
||||
f'for "{layer_id}" — instrument, platform, resolution, date range, '
|
||||
"and scientific description.",
|
||||
"",
|
||||
"2. **Color interpretation**: Use explain_layer_colormap to get a "
|
||||
"detailed description of what each color represents, including "
|
||||
"physical units and value ranges. Fetch the legend with get_legend.",
|
||||
"",
|
||||
f"3. **Sample imagery**: Use get_imagery to fetch the layer "
|
||||
f"{location_desc} {date_desc}. If no date was specified, "
|
||||
"use check_layer_dates to find the latest available date first.",
|
||||
"",
|
||||
"4. **Image interpretation**: Walk through what's visible in the "
|
||||
"image, referencing specific colormap values. Identify notable "
|
||||
"spatial patterns, anomalies, or features.",
|
||||
"",
|
||||
"5. **Scientific context**: Explain what this layer measures, "
|
||||
"why it matters, what instrument collects the data, and how "
|
||||
"scientists use it. Connect the visualization to the underlying "
|
||||
"Earth science.",
|
||||
]
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
@mcp.prompt
|
||||
def multi_layer_story(
|
||||
topic: str,
|
||||
location: str,
|
||||
date: str,
|
||||
) -> str:
|
||||
"""Compose a data-driven narrative by combining multiple GIBS layers.
|
||||
|
||||
Args:
|
||||
topic: Story theme (e.g. "wildfire smoke", "ocean productivity", "dust storm").
|
||||
location: Region of interest.
|
||||
date: Date for the story (YYYY-MM-DD).
|
||||
"""
|
||||
lines = [
|
||||
f'Tell the story of "{topic}" near {location} on {date} '
|
||||
f"by compositing multiple satellite layers.",
|
||||
"",
|
||||
"Follow this workflow:",
|
||||
"",
|
||||
f'1. **Find related layers**: Search for 2-4 layers related to "{topic}". '
|
||||
f"Choose layers that complement each other — for example, "
|
||||
f"fire detections + aerosol optical depth + true color for wildfires, "
|
||||
f"or SST + chlorophyll + true color for ocean events.",
|
||||
"",
|
||||
"2. **Verify dates**: Use check_layer_dates on each candidate "
|
||||
f"to confirm data is available for {date}.",
|
||||
"",
|
||||
"3. **Understand each layer**: For each layer with a colormap, "
|
||||
"use explain_layer_colormap to understand the value encoding.",
|
||||
"",
|
||||
"4. **Composite view**: Use get_imagery_composite to overlay "
|
||||
"the layers into a single image. Put the base layer "
|
||||
"(true color or reference) first in the list.",
|
||||
"",
|
||||
"5. **Individual layers**: Also fetch get_imagery for each layer "
|
||||
"separately so you can describe what each one contributes.",
|
||||
"",
|
||||
"6. **Narrative**: Write a data-journalism style narrative "
|
||||
"describing how the layers interact and what story they tell "
|
||||
"together. Reference specific colormap values and spatial "
|
||||
"patterns. Explain cause and effect where the data supports it.",
|
||||
]
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
@mcp.prompt
|
||||
def polar_watch(
|
||||
pole: str,
|
||||
date: str,
|
||||
compare_date: str = "",
|
||||
) -> str:
|
||||
"""Monitor Arctic or Antarctic ice and snow conditions.
|
||||
|
||||
Args:
|
||||
pole: Which pole — "arctic" or "antarctic".
|
||||
date: Primary observation date (YYYY-MM-DD).
|
||||
compare_date: Optional second date for before/after comparison (YYYY-MM-DD).
|
||||
"""
|
||||
if pole.lower().startswith("ant"):
|
||||
region = "Antarctic"
|
||||
bbox = "[-180, -90, 180, -60]"
|
||||
else:
|
||||
region = "Arctic"
|
||||
bbox = "[-180, 60, 180, 90]"
|
||||
|
||||
lines = [
|
||||
f"Monitor {region} ice and snow conditions on {date}.",
|
||||
"",
|
||||
"Follow this workflow:",
|
||||
"",
|
||||
f'1. **Find polar layers**: Search for "sea ice", "snow", '
|
||||
f'and "ice sheet" layers. Focus on layers that cover the '
|
||||
f"{region} region.",
|
||||
"",
|
||||
f"2. **Check availability**: Use check_layer_dates to verify "
|
||||
f"data exists for {date} on the selected layers.",
|
||||
"",
|
||||
f"3. **Polar imagery**: Fetch imagery using the {region} "
|
||||
f"bounding box {bbox}. Get true color first, then ice/snow "
|
||||
f"concentration layers.",
|
||||
"",
|
||||
"4. **Color interpretation**: Use explain_layer_colormap on each "
|
||||
"science layer and fetch legends with get_legend to understand "
|
||||
"concentration values and classifications.",
|
||||
"",
|
||||
"5. **Embeddable tiles**: Use build_tile_url to generate a "
|
||||
"WMTS URL for the primary ice layer — useful for embedding "
|
||||
"in reports or dashboards.",
|
||||
]
|
||||
|
||||
if compare_date:
|
||||
lines.extend([
|
||||
"",
|
||||
f"6. **Temporal comparison**: Use compare_dates to show "
|
||||
f"{compare_date} vs {date} side-by-side. Describe the "
|
||||
f"change in ice extent and concentration between these dates.",
|
||||
])
|
||||
else:
|
||||
lines.extend([
|
||||
"",
|
||||
f"6. **Summarize**: Describe the current {region} ice/snow "
|
||||
f"state visible in the imagery, referencing colormap values "
|
||||
f"for concentration percentages.",
|
||||
])
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
# Entry point
|
||||
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
@ -277,6 +277,65 @@ async def test_list_resources(capabilities_xml):
|
||||
server_module._client = None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Prompt tests
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@respx.mock
|
||||
async def test_list_prompts(capabilities_xml):
|
||||
"""All expected prompts are registered on the MCP server."""
|
||||
respx.get(url__regex=r".*WMTSCapabilities\.xml").mock(
|
||||
return_value=httpx.Response(200, text=capabilities_xml)
|
||||
)
|
||||
|
||||
server_module._client = await _init_mock_client(capabilities_xml)
|
||||
try:
|
||||
async with Client(mcp) as client:
|
||||
prompts = await client.list_prompts()
|
||||
prompt_names = {p.name for p in prompts}
|
||||
|
||||
expected = {
|
||||
"investigate_event",
|
||||
"earth_overview",
|
||||
"satellite_snapshot",
|
||||
"climate_monitor",
|
||||
"layer_deep_dive",
|
||||
"multi_layer_story",
|
||||
"polar_watch",
|
||||
}
|
||||
|
||||
for name in expected:
|
||||
assert name in prompt_names, f"Missing prompt: {name}"
|
||||
finally:
|
||||
await server_module._client.close()
|
||||
server_module._client = None
|
||||
|
||||
|
||||
@respx.mock
|
||||
async def test_satellite_snapshot_prompt(capabilities_xml):
|
||||
"""satellite_snapshot prompt includes the place name and expected tool references."""
|
||||
respx.get(url__regex=r".*WMTSCapabilities\.xml").mock(
|
||||
return_value=httpx.Response(200, text=capabilities_xml)
|
||||
)
|
||||
|
||||
server_module._client = await _init_mock_client(capabilities_xml)
|
||||
try:
|
||||
async with Client(mcp) as client:
|
||||
result = await client.get_prompt(
|
||||
"satellite_snapshot", {"place": "Tokyo", "date": "2025-06-01"}
|
||||
)
|
||||
text = result.messages[0].content.text
|
||||
|
||||
assert "Tokyo" in text
|
||||
assert "resolve_place" in text
|
||||
assert "get_imagery" in text or "get_imagery_composite" in text
|
||||
assert "MODIS_Terra_CorrectedReflectance_TrueColor" in text
|
||||
finally:
|
||||
await server_module._client.close()
|
||||
server_module._client = None
|
||||
|
||||
|
||||
@respx.mock
|
||||
async def test_colormap_resource(capabilities_xml, colormap_xml):
|
||||
"""gibs://colormap/{layer_id} returns colormap explanation text."""
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user