MCP server for Swiss federal geodata (Swisstopo APIs) — 13 tools for geocoding, height, STAC, WMTS, ÖREB and more
Drift inferred · capture-to-capture
tool count over time
tools
-
feature_by_id
Give me this one feature in full, with geometry
-
features_at_point
What is at this coordinate?
-
features_by_attribute
Which features carry this value? (e.g. buildings by EGID)
-
layer_info
What fields can I query on this layer, and what is its legend?
-
operation
Answers
-
search_layers
Which layers exist for a keyword? (500+ catalogue)
-
swisstopo_convert_coordinates
Official WGS84 ↔ LV95 conversion via the swisstopo REFRAME service
-
swisstopo_elevation_profile
Compute an elevation profile along a line
-
swisstopo_find_commune
Resolve a commune both directions (name ↔ bfs_number) or list all communes of a canton / district. Accepts canton abbreviation (ZH) or key (1); resolution happens server-side
-
swisstopo_geocode
Convert Swiss addresses, place names, or postal codes to coordinates
-
swisstopo_get_collection
Get details and download links for a STAC collection
-
swisstopo_get_egrid
Resolve a cadastral property ID (EGRID) from coordinates
-
swisstopo_get_height
Get elevation above sea level (m a.s.l.) at a coordinate
-
swisstopo_get_oereb_extract
Retrieve public-law land-use restrictions (OEREB) for a parcel
-
swisstopo_list_available_layers
Discover layer keys for swisstopo_query_geodata (strassenverzeichnis, oereb-verfuegbarkeit, geodienste:<topic>:<canton>); filters to contract-free geodienste datasets
-
swisstopo_lookup_postal_code
Resolve a Swiss postal code → locality, commune (+BFS number), district, canton
-
swisstopo_map_query
The national map catalogue (api3.geo.admin.ch). One operation per call — see the five below
-
swisstopo_map_url
Generate a map.geo.admin.ch URL for browser display
-
swisstopo_municipality_at
Municipality, canton and official BFS number at a coordinate
-
swisstopo_query_geodata
Query a chosen layer by point / bbox / commune — amtliches Strassenverzeichnis, interkantonale geodienste.ch data (OGC API Features), or ÖREB availability
-
swisstopo_query_osm_features
OpenStreetMap POIs (schools, playgrounds, pharmacies, …) around a point via Overpass — separate source, ODbL (© OpenStreetMap contributors)
-
swisstopo_reverse_geocode
Find the nearest address for given coordinates
-
swisstopo_search_address
Full-text search over Swiss streets and localities, returning commune + BFS number per hit
-
swisstopo_search_geodata
Search the STAC catalog for downloadable geodatasets
-
swisstopo_zoning_at
Harmonised building zone at a coordinate — one call, no layer lookup (not legally binding)
analyzed commit a1e3b6f · analyzer v33 · 1w ago
danger signals2
- suspicious endpoint 185.19.28.1 malkreide-swisstopo-mcp-a1e3b6f/tests/test_dns_pinning.py :150
seen = await _capture(monkeypatch, _request("https://185.19.28.1/x")) - suspicious endpoint 169.254.169.254 (cloud metadata) malkreide-swisstopo-mcp-a1e3b6f/tests/test_egress_allowlist.py :35
"http://169.254.169.254/latest/meta-data/",
code files: 59
filesystem 14
- fs (weak) malkreide-swisstopo-mcp-a1e3b6f/scripts/check_version_sync.py :35
from pathlib import Path - fs (weak) malkreide-swisstopo-mcp-a1e3b6f/scripts/record_fixtures.py :45
from pathlib import Path - fs (weak) malkreide-swisstopo-mcp-a1e3b6f/scripts/render_egress_acl.py :15
import pathlib - fs (weak) malkreide-swisstopo-mcp-a1e3b6f/scripts/snapshot_tool_hashes.py :22
import pathlib - fs (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/fixture_data.py :3
Ein Loader statt `open()` an jeder Stelle: so gibt es genau einen Ort, der weiss, - fs (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_context.py :304
import pathlib - fs (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_deploy_manifests.py :26
import pathlib - fs (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_egress_allowlist.py :166
import pathlib - fs (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_empty_results.py :21
import pathlib - fs (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_live_coverage.py :22
import pathlib - fs (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_responses.py :211
import pathlib - fs (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_tool_hygiene.py :301
import pathlib - fs (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_tool_namespace.py :16
import pathlib - fs (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_version.py :12
from pathlib import Path
shell / exec 2
- shell (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_live_coverage.py :199
import subprocess - shell (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_tool_namespace.py :17
import subprocess
network 29
- net (weak) malkreide-swisstopo-mcp-a1e3b6f/scripts/record_fixtures.py :48
import httpx - net malkreide-swisstopo-mcp-a1e3b6f/src/swisstopo_mcp/api_client.py :8
import socket - net malkreide-swisstopo-mcp-a1e3b6f/src/swisstopo_mcp/observability.py :29
from urllib.parse import urlsplit, urlunsplit - net malkreide-swisstopo-mcp-a1e3b6f/src/swisstopo_mcp/oereb.py :6
import httpx - net (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/conftest.py :9
`httpx.AsyncHTTPTransport` — which is precisely the layer `respx` patches. With - net (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/fixture_data.py :30
import httpx - net (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_api_client.py :4
import httpx - net (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_context.py :91
import httpx - net (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_coords.py :6
import httpx - net (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_deploy_manifests.py :345
import httpx - net (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_dns_pinning.py :25
import httpx - net (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_egress_allowlist.py :6
import socket - net (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_geocoding.py :267
import httpx - net (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_geodata.py :6
import httpx - net (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_height.py :237
import httpx - net (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_http_app.py :6
import httpx - net (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_lv95_input.py :11
import httpx - net (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_observability.py :189
import httpx - net (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_oereb.py :6
import httpx - net (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_openplz.py :7
import httpx - net (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_overpass.py :8
import httpx - net (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_places.py :11
import httpx - net (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_protocol_version.py :78
import httpx - net (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_recorded_fixtures.py :33
import httpx - net (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_rest_api.py :345
import httpx - net (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_retry.py :6
import httpx - net (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_retry_policy.py :12
import httpx - net (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_shared_client.py :2
"""Regression tests for SDK-001: shared httpx.AsyncClient reuse via lifespan.""" - net (weak) malkreide-swisstopo-mcp-a1e3b6f/tests/test_stac.py :314
import httpx
declared dependencies 16
- mcp@>=2.0.0,<3
- httpx@>=0.27.0
- pydantic@>=2.0.0
- pydantic-settings@>=2.0.0
- structlog@>=24.1.0,<27
- opentelemetry-api@>=1.27.0
- opentelemetry-sdk@>=1.27.0
- opentelemetry-exporter-otlp@>=1.27.0
- opentelemetry-instrumentation-httpx@>=0.48b0
- pytest@>=8.0.0
- pytest-asyncio@>=0.23.0
- pytest-cov@>=5.0.0
- respx@>=0.21.0
- ruff@>=0.4.0,<0.17
- mypy@>=1.10.0
- PyYAML@>=6.0