MCP server for Swiss federal geodata (Swisstopo APIs)
Project description
๐จ๐ญ Part of the Swiss Public Data MCP Portfolio
๐บ๏ธ swisstopo-mcp
MCP server for Swiss federal geodata -- maps, elevation, geocoding, cadastral extracts, and downloadable datasets via Swisstopo APIs
Overview
swisstopo-mcp gives AI assistants access to Switzerland's official geodata infrastructure through 13 tools across 6 API families, all without authentication:
| Source | Data | API |
|---|---|---|
| Swisstopo REST API | 500+ geodata layers (buildings, boundaries, land use) | REST/JSON |
| Geocoding | Official addresses, place names, postal codes | REST/JSON |
| Height Service | Elevation above sea level, elevation profiles | REST/JSON |
| STAC Catalog | Orthophotos, elevation models, 3D buildings | STAC 0.9 |
| WMTS | National maps, aerial images, zoning maps | URL builder |
| OEREB Cadastre | Public-law restrictions, parcels | REST/JSON (cantonal) |
Anchor demo query: "What land-use restrictions apply to the parcel at Musterstrasse 5, Zurich? Show me the location on a map." โ More use cases by audience โ
Features
- ๐บ๏ธ 13 tools across 6 API families (REST, Geocoding, Height, STAC, WMTS, OEREB)
- ๐ Geocode Swiss addresses and reverse-geocode coordinates
- ๐๏ธ Query elevation and compute elevation profiles
- ๐ฆ Discover and download geodatasets (orthophotos, 3D buildings, historical maps)
- ๐๏ธ Identify map features at coordinates across 500+ Swisstopo layers
- ๐ Generate shareable map.geo.admin.ch links
- ๐ Look up cadastral property IDs (EGRID) and retrieve OEREB extracts
- ๐ No API key required for 11 of 13 tools
- โ๏ธ Dual transport -- stdio (Claude Desktop) + Streamable HTTP (cloud)
Prerequisites
- Python 3.11+
- uv (recommended) or pip
Installation
# Clone the repository
git clone https://github.com/malkreide/swisstopo-mcp.git
cd swisstopo-mcp
# Install
pip install -e .
# or with uv:
uv pip install -e .
Or with uvx (no permanent installation):
uvx swisstopo-mcp
Quickstart
# stdio (for Claude Desktop)
python -m swisstopo_mcp.server
# Streamable HTTP (port 8000)
python -m swisstopo_mcp.server --http --port 8000
Try it immediately in Claude Desktop:
"Where is Bahnhofstrasse 1, Zurich? Give me the coordinates." "What is the elevation at the Uetliberg summit?" "What buildings are at coordinates 2683500, 1247500 (LV95)?"
Configuration
Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"swisstopo": {
"command": "python",
"args": ["-m", "swisstopo_mcp.server"]
}
}
}
Or with uvx:
{
"mcpServers": {
"swisstopo": {
"command": "uvx",
"args": ["swisstopo-mcp"]
}
}
}
Config file locations:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
Cloud Deployment (SSE for browser access)
For use via claude.ai in the browser (e.g. on managed workstations without local software):
Render.com (recommended):
- Push/fork the repository to GitHub
- On render.com: New Web Service -> connect GitHub repo
- Set start command:
python -m swisstopo_mcp.server --http --port 8000 - In claude.ai under Settings -> MCP Servers, add:
https://your-app.onrender.com/sse
Available Tools
REST API (Layer & Feature Queries)
| Tool | Description |
|---|---|
swisstopo_search_layers |
Search the Swisstopo layer catalog (500+ layers) by keyword |
swisstopo_identify_features |
Find map features at a specific coordinate (spatial query) |
swisstopo_find_features |
Search features by attribute value within a layer (e.g. buildings by EGID) |
swisstopo_get_feature |
Retrieve full attributes and geometry for a feature by ID |
Geocoding
| Tool | Description |
|---|---|
swisstopo_geocode |
Convert Swiss addresses, place names, or postal codes to coordinates |
swisstopo_reverse_geocode |
Find the nearest address for given coordinates |
Height Service
| Tool | Description |
|---|---|
swisstopo_get_height |
Get elevation above sea level (m a.s.l.) at a coordinate |
swisstopo_elevation_profile |
Compute an elevation profile along a line |
STAC Catalog (Geodata Downloads)
| Tool | Description |
|---|---|
swisstopo_search_geodata |
Search the STAC catalog for downloadable geodatasets |
swisstopo_get_collection |
Get details and download links for a STAC collection |
WMTS (Map URLs)
| Tool | Description |
|---|---|
swisstopo_map_url |
Generate a map.geo.admin.ch URL for browser display |
OEREB Cadastre
| Tool | Description |
|---|---|
swisstopo_get_egrid |
Resolve a cadastral property ID (EGRID) from coordinates |
swisstopo_get_oereb_extract |
Retrieve public-law land-use restrictions (OEREB) for a parcel |
Example Use Cases
| Query | Tool |
|---|---|
| "Where is Bahnhofstrasse 1, Zurich?" | swisstopo_geocode |
| "What is the elevation at the Uetliberg summit?" | swisstopo_get_height |
| "What buildings are at coordinates 2683500, 1247500?" | swisstopo_identify_features |
| "Find orthophoto datasets for download" | swisstopo_search_geodata |
| "Show me a map of Bern at zoom level 10" | swisstopo_map_url |
| "What restrictions apply to parcel at Musterstrasse 5?" | swisstopo_get_egrid + swisstopo_get_oereb_extract |
Architecture
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Claude / AI โโโโโโถโ swisstopo-mcp โโโโโโถโ Swisstopo REST API โ
โ (MCP Host) โโโโโโโ (MCP Server) โโโโโโโ api3.geo.admin.ch โ
โโโโโโโโโโโโโโโโโโโ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ 13 Tools โโโโโโถโ Geocoding โ
โ Stdio | Streamable HTTP โโโโโโโ api3.geo.admin.ch โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ No authentication required โโโโโโถโ STAC Catalog โ
โ (11 of 13 tools) โโโโโโโ data.geo.admin.ch โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โโโโโโถโ OEREB Cadastre โ
โ โโโโโโโ (cantonal endpoints) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Project Structure
swisstopo-mcp/
โโโ src/swisstopo_mcp/
โ โโโ __init__.py # Package version
โ โโโ server.py # MCP server wiring (tool registrations)
โ โโโ api_client.py # Shared HTTP client (httpx + error handling)
โ โโโ geocoding.py # swisstopo_geocode, swisstopo_reverse_geocode
โ โโโ rest_api.py # swisstopo_search_layers, identify, find, get_feature
โ โโโ height.py # swisstopo_get_height, swisstopo_elevation_profile
โ โโโ stac.py # swisstopo_search_geodata, swisstopo_get_collection
โ โโโ wmts.py # swisstopo_map_url
โ โโโ oereb.py # swisstopo_get_egrid, swisstopo_get_oereb_extract
โโโ tests/
โ โโโ test_api_client.py
โ โโโ test_geocoding.py
โ โโโ test_height.py
โ โโโ test_oereb.py
โ โโโ test_rest_api.py
โ โโโ test_stac.py
โ โโโ test_wmts.py
โโโ .github/workflows/ci.yml # GitHub Actions (Python 3.11/3.12/3.13)
โโโ pyproject.toml
โโโ CHANGELOG.md
โโโ CONTRIBUTING.md # Contribution guide (English)
โโโ CONTRIBUTING.de.md # Contribution guide (German)
โโโ SECURITY.md # Security policy (English)
โโโ SECURITY.de.md # Security policy (German)
โโโ LICENSE
โโโ README.md # This file (English)
โโโ README.de.md # German version
Security & Compliance
The full security policy and posture is documented in SECURITY.md.
Phase
This server is in Phase 1 โ Read-only wrapper. All 13 tools are
readOnlyHint: true / destructiveHint: false; there are no write or send
capabilities. See docs/roadmap.md for later phases.
Lethal Trifecta assessment
| Capability | Status | Rationale |
|---|---|---|
| Access to private data | โ No | Public Open Data only (federal/cantonal geodata) |
| Exposure to untrusted content | โ ๏ธ Limited | Reads only from a fixed allow-list of trusted geo.admin / OEREB hosts |
| External communication (write/send) | โ No | Read-only; no mail/webhook/write tools |
Trifecta score: at most 1 of 3 โ safe by design.
Egress
Outbound requests are restricted to an explicit code-layer allow-list and redirects are disabled โ see docs/network-egress.md.
Container deployment
For containerised HTTP deployments, a hardened Dockerfile and Kubernetes
manifests (non-root, read-only root filesystem, dropped capabilities, egress
NetworkPolicy) are provided โ see docs/deployment.md.
MCP Protocol Version
The MCP protocol version is negotiated by the mcp SDK, which is pinned to the
1.x major in pyproject.toml so an update cannot silently change the
negotiated version. SDK bumps are proposed monthly via Dependabot and tracked
in CHANGELOG.md.
Sessions & Authentication
The server is unauthenticated by design โ it serves only public open data. Over HTTP, session IDs are managed entirely by the FastMCP framework; there is no per-user state, so there is nothing user-specific to bind a session to. If an authenticated deployment is ever introduced, session IDs must be bound to the validated user identity (audit finding SEC-009).
Error handling
- Execution errors (upstream failure, invalid value) are returned as a
ToolResponsewithis_error: trueand a user-friendlysummary; raw exception text is never leaked to the client (it is logged to stderr instead). - Protocol errors (unknown tool, malformed/invalid arguments) are emitted by
the MCP SDK as JSON-RPC errors with standard codes (e.g.
-32602invalid params). Input validation happens at the Pydantic boundary (SEC-018).
MCP Primitives
This server intentionally exposes Tools only (no Resources or Prompts): it is a Phase-1 read-only wrapper, and every result is a live, parameterised API query rather than a static addressable document. Resources/Prompts may be added in a later phase if stable URI schemes emerge.
Tool workflows
Most tools return a thought-complete result in a single call. Two domains use a short, documented discovery chain (each tool's description states the next step):
- Feature query:
swisstopo_search_layers(find layer IDs) โswisstopo_identify_features/swisstopo_find_featuresโswisstopo_get_feature(full detail). - Cadastre:
swisstopo_geocodeโswisstopo_get_egridโswisstopo_get_oereb_extract. - Downloads:
swisstopo_search_geodataโswisstopo_get_collection.
Response Format
Every tool returns a structured ToolResponse (FastMCP emits it as structured
content with an output schema, plus a JSON text block):
| Field | Meaning |
|---|---|
summary |
Human-readable Markdown summary |
results |
Machine-readable structured records |
count |
Number of results |
match_type |
exact / fuzzy / none (search-style tools) |
source / license |
Data attribution (OGD-CH, CC/OGD terms) |
provenance / retrieved_at |
How and when the data was obtained |
is_error |
true for handled errors |
Known Limitations
- OEREB tools require a canton parameter; not all cantons expose the same API format
- STAC catalog uses Swisstopo's v0.9 endpoint; some collections may lack complete metadata
- Geocoding covers Swiss addresses only (no Liechtenstein)
- Rate limits are enforced by Swisstopo; high-frequency usage may be throttled
Testing
# Unit tests (no network required)
pytest tests/ -m "not live"
# Integration tests (live API calls)
pytest tests/ -m "live"
Changelog
See CHANGELOG.md
Contributing
See CONTRIBUTING.md
License
MIT License -- see LICENSE
Data provided by swisstopo under Open Government Data terms.
Author
Hayal Oezkan ยท malkreide
Credits & Related Projects
- Swisstopo: www.swisstopo.admin.ch -- Swiss Federal Office of Topography
- Swisstopo APIs: api3.geo.admin.ch / data.geo.admin.ch
- Protocol: Model Context Protocol -- Anthropic / Linux Foundation
- Related: zurich-opendata-mcp -- Zurich city open data
- Related: swiss-transport-mcp -- Swiss public transport
- Related: swiss-cultural-heritage-mcp -- Swiss cultural heritage
- Portfolio: Swiss Public Data MCP Portfolio
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file swisstopo_mcp-0.1.3.tar.gz.
File metadata
- Download URL: swisstopo_mcp-0.1.3.tar.gz
- Upload date:
- Size: 99.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
992bd6d895a76609e24f21ebb19d1fe9eab5db5e169c96e3c441b13bbd874f46
|
|
| MD5 |
af18dfb6d2dca33d330feaa0faa8187d
|
|
| BLAKE2b-256 |
a1acceb1a441f0cfb6146c02ae89a185aa221e663d58ccbd3aba43bd1da4d6df
|
File details
Details for the file swisstopo_mcp-0.1.3-py3-none-any.whl.
File metadata
- Download URL: swisstopo_mcp-0.1.3-py3-none-any.whl
- Upload date:
- Size: 33.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5249d17b46b26484648473493e873499c3b36a7ddf022c1746112453f579dcfe
|
|
| MD5 |
edf38e3d13d93aa89e309b17182d47e8
|
|
| BLAKE2b-256 |
2765291d5a4f5c3e11d22f6ad8ab57db1f8b4608a9833b191a58af5a28b5681f
|