Skip to main content
Glama

IGN-IGME-MCP

Ask an AI agent about Spanish geology and get an answer with a map.

This is an MCP server that puts the public cartography of Spain — the IGME's geology and the IGN's base mapping and orthophotos — behind a single set of tools an agent can use, and composes them into one image when a question needs both.

One question, two publishers, one answer:

"¿Hay fallas cuaternarias cerca de este punto? Enséñame un mapa sobre la ortofoto."

The faults come from the IGME over ArcGIS REST, the orthophoto from the IGN over OGC WMS, and they arrive as a single aligned image with a legend saying which is which.

Read-only, always. Every operation is a GET; there is no editing path anywhere in the codebase.


Why it exists

Spain publishes an enormous amount of high-quality geospatial data for free — and almost none of it is reachable from an AI assistant. Three problems stand in the way, and this project is the answer to each:

The data lives behind two incompatible protocol families. The IGME runs ArcGIS Server; the IGN publishes OGC WMS and WMTS. An agent should not have to know, so the tools are the same for both — and a source that cannot do something says so and names the alternative, instead of returning an empty result that reads like "there is nothing there".

Real questions cross publishers. "Are there active faults nearby, and what does the ground look like?" is one question and two organisations. Splitting it across two servers pushes the join into the agent, which is where scale confusion and misattribution creep in.

A map without provenance is a liability. A geological contact read from a 1:1,000,000 synthesis and one from a 1:50,000 sheet are not interchangeable, and an agent that cannot tell them apart will use the first to answer about a parcel. Every answer here carries its service, publisher and nominal scale.

New to WMS, WMTS or ArcGIS REST? → docs/theory.md


Related MCP server: groundstation

What it can do

Find a place

turn "Suances" into a map envelope — a municipality's real boundary, not a guessed square

Say what exists

enumerate every source, from both publishers, before assuming one

Answer at a point

which geological unit, which lithology, which flight date — with the scale it was read at

Query by attributes

SQL-style filtering with transparent paging, where the source supports it

Draw maps

one source framed on a place, or several stacked and aligned to the pixel with a combined legend

Describe itself

what each source publishes, what it can do, and what its fields mean

Refuse usefully

when a source cannot answer, it says why and what can

See it in use → docs/examples.md


Quick start

Requires Python ≥ 3.11 and uv.

git clone <this repository> && cd IGN-IGME-MCP
uv sync --all-extras
uv run pytest                                       # the whole suite, offline
uv run mcp-geoes --list-profiles
uv run mcp-geoes --profile general --validate

Then connect a client. The configuration files ship with the repository, with all five profiles already declared — open it and start the server you want:

Anything else that speaks MCP runs the same command:

uv run mcp-geoes --profile geologia

Wiring, hosted transport and multi-profile caveats → examples/mcp_clients.md

Then ask it "¿qué cartografía tienes de Suances?" or "¿qué unidad geológica hay ahí?".

Five profiles, one process each — the tool list an agent sees is what keeps its choices accurate. The split is by domain, never by publisher: real questions cross the IGME and the IGN in the same sentence.

Profile

For

Tools

general

"what do you have for this place?" — both publishers, short answer

19

cartografia_ign

the IGN as subject: orthophoto, MTN sheets, historical series, relief

25

geologia

the IGME's geology, with the IGN's cartography to read it against

27

riesgos

active faults, seismicity, ground movements, neotectonics

21

agua

hydrogeology and permeability over the IGN's hydrography

21

Counts include the thirteen generic tools, which always register.

What each profile declares, which one to pick, and how to write another → docs/profiles.md

Prefer to see it work before wiring up a client?

uv run python scripts/demo_tools.py --out working/     # the tools, live
uv run python scripts/demo_compose.py --out working/   # a composed map
uv run python scripts/demo_discovery.py --place Suances --out working/

Full instructions, container and hosting → docs/deployment.md


How it fits together

   AI agent  ──MCP──▶  mcp_geoes  ──▶  domain profile (YAML, Spanish)
                           │
                           ├──▶ arcgis_client ──▶ IGME   (ArcGIS REST)
                           ├──▶ ogc_client    ──▶ IGN    (WMS · WMTS)
                           └──▶ map_compose   ──▶ one image, one legend

Three ideas carry the design:

Capabilities are declared, never assumed. Each source states what it can answer. Asking for anything else raises an error naming the alternative — never a plausible empty result.

Questions start from places, not coordinates. resolve_place turns a name into an envelope, and every map tool takes place= instead of bbox=.

Composition happens in EPSG:3857. Layers from different publishers can only be stacked if all were rendered for the same envelope in the same projection. Tool signatures still speak lon/lat; the conversion is done for you.

Profiles add knowledge, never code. A YAML file names its sources across both publishers, their semantics, its closed vocabularies and its convenience tools. A new domain — hydrogeology, geological risk, mining — is a new file.

Why each of those, and what was rejected → docs/architecture.md · Requirements and their tests → docs/specs.md


Documentation

Document

For

docs/theory.md

the GIS concepts, if you are new to them, with pointers to the standards

docs/licensing.md

what you must write when you publish — the IGN's and IGME's attribution terms

docs/profiles.md

what a profile declares, the five shipped ones, and how to add one

docs/examples.md

what you can ask, what comes back, and what it refuses

docs/architecture.md

the design decisions and why they were taken

docs/specs.md

numbered requirements, acceptance criteria, milestones

docs/deployment.md

build, test, run, containerise, host, and keep it current

examples/mcp_clients.md

client configuration, copy-paste

Each package also has its own README: geo_core, geo_http, arcgis_client, ogc_client, map_compose, mcp_geoes.


Layout

IGN-IGME-MCP/
├── packages/
│   ├── geo_core/        # backend contract, model, geometry, logging
│   ├── geo_http/        # shared transport: retries, cache, timing
│   ├── arcgis_client/   # ArcGIS Server REST          → IGME
│   ├── ogc_client/      # OGC WMS, WMTS and WFS       → IGN, regional SDIs
│   ├── map_compose/     # composition engine and combined legend
│   └── mcp_geoes/       # the MCP server and the profile loader
├── profiles/            # domain profiles (YAML + prompt), in Spanish
├── docs/                # theory · profiles · examples · architecture · specs · deployment
├── scripts/             # demos, live validation, catalog audit
├── docker/              # container healthcheck
└── tests/

Troubleshooting

Three log levels on one axis, because troubleshooting a chain of agent → server → two origins means asking three questions in order:

Level

Answers

Emits

INFO

what did the agent ask for, and did it work?

one line per tool call: tool, backend, duration, status

DEBUG

what did we ask the origins?

one line per upstream request: URL, status, bytes, cache, duration

TRACE

what exactly went over the wire?

full URL, every parameter, truncated payload

Every record carries a call_id that propagates from the tool call down to the HTTP request, so one grep reconstructs a whole interaction. Pass the agent's own trace id as trace_id and the two timelines join.

uv run mcp-geoes --profile geologia --log-level TRACE --log-format json

Logs go to stderr: under the stdio transport, stdout carries the MCP protocol itself.


Language

The code, its comments and its documentation are in English, because layers 0–2 are reusable well beyond Spain. The domain profiles are in Spanish — tool names, field semantics, prompts, vocabularies — because the data is Spanish (UNIDAD, LITOLOGIA, "Triásico"), users ask in Spanish, and a translation step between question, tool and data is exactly where models introduce mapping errors.


Licence and attribution

The software is MIT.

The data is not. It belongs to the Instituto Geológico y Minero de España (IGME, CSIC) and the Instituto Geográfico Nacional (IGN), who publish it freely on the condition that you cite the source. That condition travels with the data: when you put one of these maps in a report, a site or a product, the obligation to attribute is yours.

This server gives you everything you need to meet it — publisher, nominal scale, edition date and the official product page ride along with every answer — but it cannot meet it for you, because it does not know where your result ends up.

The IGN requires the literal form PNOA 2023 CC-BY 4.0 scne.es, prefixed with "Obra derivada de" if you modified anything — and composing counts. The IGME requires Origen de los datos: © Instituto Geológico y Minero de España (IGME).

Read docs/licensing.md before you publish anything. It has both formulas, what counts as a derived work, and a checklist.

This project is not affiliated with, endorsed by, or maintained by either institute.

Available Tools

8 tools
compose_mapA

Compose one map from several sources, aligned, with a combined legend. Name a composition the profile declares, or list sources bottom-first. legend_mode is panel, below, separate or none — use 'separate' for rich geological keys. The bbox is lon/lat; projection, pixel grid and legend filtering are handled for you.

Inputs: bbox (required), title, width, height, +3 more.

describe_layerA

Describe a layer: fields with their types and coded-value domains, geometry type and record count. WMS sources publish no field schema and say so.

Inputs: source (required), layer_id (required). Returns: object

describe_serviceB

Service metadata: layers with their ids, extent, CRS, formats, limits, and the capabilities this source actually supports.

Inputs: source (required). Returns: object

export_mapA

Render one source for a bbox [minlon,minlat,maxlon,maxlat] in WGS84 degrees and return it as an image. For several sources on one map use compose_map.

Inputs: bbox (required), source (required), width, height, +1 more.

get_legendC

Legend of a source: structured entries (ArcGIS) or an image (WMS).

Inputs: source (required), layer_id. Returns: object

identify_at_pointC

What is at a point: attributes at the given lon/lat (WGS84) for one or more layers of a source.

Inputs: lat (required), lon (required), source (required), layers, +1 more. Returns: object

list_servicesC

List the services published by a source that has a catalog (ArcGIS).

Inputs: source (required), folder. Returns: object

query_layerA

Query layer attributes and return GeoJSON in WGS84. Supports a SQL where, field selection, ordering and a bbox [minlon,minlat,maxlon,maxlat]. If exceededTransferLimit is true, repeat with offset. Only sources that declare the 'query' capability.

Inputs: source (required), layer_id (required), bbox, limit, +5 more. Returns: object

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.7/5.0

    Scored across 8 tools

    Disambiguation5/5

    Each tool targets a distinct operation: listing services, describing services or layers, getting legends, querying attributes, identifying by point, and rendering maps. The overlap between query_layer and identify_at_point is minimal and well-differentiated by interaction style.

    Naming Consistency5/5

    All tool names follow a clear verb_noun snake_case pattern (list_services, describe_layer, get_legend, export_map). Even identify_at_point fits the pattern with a prepositional phrase, and no mixed conventions appear.

    Tool Count5/5

    Eight tools is well-scoped for a geospatial service client, covering discovery, metadata, query, legend retrieval, and map rendering without redundancy or bloat.

    Completeness5/5

    The tool surface covers the full geospatial workflow: catalog browsing, metadata inspection, query, point identification, legend access, single-source export, and multi-source composition. No obvious dead ends or missing core operations.

    Maintenance

    ActivityStale
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables AI assistants to perform geospatial analysis, spatial data processing, vector and raster operations, coordinate transformations, spatial statistics, mapping, and access to climate, satellite, and other geospatial data through MCP-compatible clients.
      MIT