swisstopo-mcp について
swisstopo-mcpは、AIアシスタントからスイス連邦などの地理データを利用するためのMCPサーバーです。地図検索、住所・標高サービス、地籍情報、データセット、行政区検索をAPIキーなしで提供します。
主な機能
- 01500以上の地図レイヤーを検索
- 02住所のジオコーディングと座標の逆引き
- 03標高と標高プロファイルの取得
- 04地理データの検索と地図URLの作成
- 05地籍制限と行政区情報の照会
活用シーン
- 01スイスの住所の座標を調べる
- 02ルート沿いの標高を確認する
- 03地点の地図上の地物を特定する
- 04行政区のBFS番号や地籍制限を調べる
MCP 対応
stdioまたはStreamable HTTPを通じて、MCPクライアントにスイスの地理データ用ツールを20個提供します。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
🇨🇭 Swiss Public Data MCP Portfolio の一部です
swisstopo-mcp
Swisstopo API を通じて、地図、標高、ジオコーディング、地籍情報の抜粋、ダウンロード可能なデータセットを利用できる、スイス連邦地理データ用の MCP サーバーです
概要
swisstopo-mcp は、認証不要で利用できる 20 個のツールを通じて、AI アシスタントからスイスの公式地理データ基盤にアクセスできるようにします。
| データソース | データ | API |
|---|---|---|
| Swisstopo REST API | 500 以上の地理データレイヤー(建物、境界、土地利用) | REST/JSON |
| ジオコーディング | 公式住所、地名、郵便番号 | REST/JSON |
| 標高サービス | 海抜標高、標高プロファイル | REST/JSON |
| STAC カタログ | オルソ画像、標高モデル、3D 建物 | STAC 0.9 |
| WMTS | 国土地図、航空写真、ゾーニングマップ | URL ビルダー |
| OEREB 地籍 | 公法上の制限、地籍区画 | REST/JSON(州単位) |
| geodienste.ch | 州間基礎地理データ(地籍測量、汚染地、ハザードマップなど) | OGC API Features / WMS / WFS |
| OpenStreetMap | 施設(学校、遊び場、薬局など) | Overpass API(ODbL) |
| OpenPLZ API | 行政住所レベル:郵便番号 → 自治体(BFS 番号)→ 地区 → 州 | REST/JSON(BFS + swisstopo OGD) |
主なデモクエリ: 「Uster 地区にはどの自治体があり、BFS 統計データとの結合に使う各自治体の BFS 番号は何ですか?」
(BFS 自治体番号は、swiss-statistics-mcp と zurich-opendata-mcp を結合するための公式キーです。これにより、単なる地理データラッパーが自治体レベルのセマンティックコネクターになります。)
→ 対象別のユースケースを見る →
デモ
機能
- 🗺️ 20 個のツール(REST、ジオコーディング、標高、STAC、WMTS、OEREB、geodienste.ch、OpenStreetMap/Overpass、OpenPLZ)
- 🏛️ OpenPLZ を通じて行政住所レベル(郵便番号 → 自治体/BFS 番号 → 地区 → 州)を解決
- 🔍 スイスの住所をジオコーディングし、座標から逆ジオコーディング
- 🏔️ 標高を照会し、標高プロファイルを計算
- 📦 地理データセット(オルソ画像、3D 建物、歴史的地図)を検索してダウンロード
- 🏗️ 500 以上の Swisstopo レイヤーから座標地点の地図フィーチャーを特定
- 🔗 共有可能な map.geo.admin.ch リンクを生成
- 📋 地籍不動産 ID(EGRID)を検索し、OEREB の抜粋を取得
- 🔓 すべてのツールで API キーは不要(OEREB の抜粋には対応する州が必要です)
- ☁️ 2 種類のトランスポート -- stdio(Claude Desktop)+ Streamable HTTP(クラウド)
前提条件
- Python 3.11+
- uv(推奨)または pip
インストール
# 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 .
または uvx を使用します(恒久的なインストールは不要です)。
uvx swisstopo-mcp
クイックスタート
# stdio (for Claude Desktop)
python -m swisstopo_mcp.server
# Streamable HTTP (port 8000)
python -m swisstopo_mcp.server --http --port 8000
Claude Desktop ですぐに試せます。
「チューリッヒの Bahnhofstrasse 1 はどこですか?座標を教えてください。」 「Uetliberg 山頂の標高はいくつですか?」 「座標 2683500, 1247500(LV95)にはどのような建物がありますか?」
設定
Claude Desktop
~/Library/Application Support/Claude/claude_desktop_config.json(macOS)または %APPDATA%\Claude\claude_desktop_config.json(Windows)を編集します。
{
"mcpServers": {
"swisstopo": {
"command": "python",
"args": ["-m", "swisstopo_mcp.server"]
}
}
}
または uvx を使います。
{
"mcpServers": {
"swisstopo": {
"command": "uvx",
"args": ["swisstopo-mcp"]
}
}
}
設定ファイルの場所:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
クラウドへのデプロイ(ブラウザーアクセス用の SSE)
ブラウザーで claude.ai を使う場合(ローカルソフトウェアのない管理対象ワークステーションなど):
Render.com(推奨):
- リポジトリを GitHub にプッシュするか、フォークします
- render.com で「New Web Service」-> GitHub リポジトリを接続します
- 起動コマンドを設定します:
python -m swisstopo_mcp.server --http --port 8000 - claude.ai の「Settings」->「MCP Servers」で、
https://your-app.onrender.com/sseを追加します
利用可能なツール
REST API(レイヤーと地物のクエリ)
| ツール | 説明 |
|---|---|
swisstopo_map_query | 全国地図カタログ(api3.geo.admin.ch)。1 回の呼び出しにつき 1 つの operation を指定します。以下の 5 つから選んでください |
swisstopo_zoning_at | 座標における調和済み建築ゾーンを、レイヤー検索なしで 1 回の呼び出しで取得します(法的拘束力はありません) |
swisstopo_municipality_at | 座標における自治体、州、および公式 BFS 番号 |
swisstopo_map_query の操作
operation | 回答内容 | 必須引数 |
|---|---|---|
search_layers | キーワードに一致するレイヤーはありますか?(500 件以上のカタログ) | query |
layer_info | このレイヤーでクエリできるフィールドと凡例は何ですか? | layer |
features_at_point | この座標には何がありますか? | layers + 1 点(lat/lon または easting/northing) |
features_by_attribute | この値を持つ地物はどれですか?(例:EGID による建物検索) | layer、search_field、search_text |
feature_by_id | この地物 1 件の全情報をジオメトリ付きで取得します | layer、feature_id |
別の操作に属する引数は無視されず、拒否されます。エラーには、選択した操作で受け付けられる引数が示されます。誤った場所に指定された search_field を黙って破棄すると、誰も尋ねていない質問にもっともらしい回答を返してしまいます。エンベロープ設計全体が防ごうとしているのは、このような失敗です。
ジオコーディング
| ツール | 説明 |
|---|---|
swisstopo_geocode | スイスの住所、地名、郵便番号を座標に変換します |
swisstopo_reverse_geocode | 指定された座標に最も近い住所を検索します |
高度サービス
| ツール | 説明 |
|---|---|
swisstopo_get_height | 座標における海抜高度(m)を取得します |
swisstopo_elevation_profile | 線に沿った高度プロファイルを計算します |
swisstopo_convert_coordinates | swisstopo REFRAME サービスを使って、公式の WGS84 ↔ LV95 変換を行います |
STAC カタログ(地理データのダウンロード)
| ツール | 説明 |
|---|---|
swisstopo_search_geodata | ダウンロード可能な地理データセットを STAC カタログで検索します |
swisstopo_get_collection | STAC コレクションの詳細とダウンロードリンクを取得します |
WMTS(地図 URL)
| ツール | 説明 |
|---|---|
swisstopo_map_url | ブラウザーで表示するための map.geo.admin.ch URL を生成します |
OEREB 地籍
| ツール | 説明 |
|---|---|
swisstopo_get_egrid | 座標から地籍地番 ID(EGRID)を特定します |
swisstopo_get_oereb_extract | 区画に関する公法上の土地利用制限(OEREB)を取得します |
統合ジオデータファサード
複数の地図/レイヤーソースに対する単一のファサードで、25 ツールの上限内に収めています(
docs/geodaten-erweiterung-phase1.mdを参照):
| ツール | 説明 |
|---|---|
swisstopo_list_available_layers | swisstopo_query_geodata用のレイヤーキー(strassenverzeichnis、oereb-verfuegbarkeit、geodienste:<topic>:<canton>)を確認します。契約不要のgeodiensteデータセットに絞り込みます |
swisstopo_query_geodata | point/bbox/communeを指定して選択したレイヤーを検索します。amtliches Strassenverzeichnis、州間geodienste.chデータ(OGC API Features)、またはÖREBの利用可能状況が対象です |
swisstopo_query_osm_features | Overpassを通じて、地点周辺のOpenStreetMap POI(学校、遊び場、薬局など)を取得します。独立したソースであり、ライセンスはODbL(© OpenStreetMap contributors)です |
行政住所階層(OpenPLZ)
OpenPLZ APIが提供する正式な住所階層 PLZ → commune → district → canton(データ:BFS municipal directory + swisstopo street directory、スイスのOGD。上記のswisstopoジオデータとは別のソースおよびライセンスです)。communeを含むすべての応答では、名前付きのトップレベルフィールドbfs_commune_numberが公開されます。これはBFS統計(swiss-statistics-mcp)およびzurich-opendata-mcpと結合するための正式な結合キーです。
| ツール | 説明 |
|---|---|
swisstopo_lookup_postal_code | スイスの郵便番号を地域、commune(+BFS番号)、district、cantonに解決します |
swisstopo_find_commune | communeを双方向に解決します(name ↔ bfs_number)。またはcanton/districtのすべてのcommuneを一覧表示します。cantonの略称(ZH)またはキー(1)を指定できます。解決はサーバー側で行われます |
swisstopo_search_address | スイスの通り名と地域を全文検索し、検索結果ごとにcommuneとBFS番号を返します |
使用例
| クエリ | ツール |
|---|---|
| 「チューリッヒのBahnhofstrasse 1はどこですか?」 | swisstopo_geocode |
| 「Uetliberg山頂の標高はいくつですか?」 | swisstopo_get_height |
| 「座標2683500, 1247500にはどのような建物がありますか?」 | swisstopo_map_query(operation='features_at_point') |
| 「ダウンロード可能なオルソフォトデータセットを探してください」 | swisstopo_search_geodata |
| 「ズームレベル10でベルンの地図を表示してください」 | swisstopo_map_url |
| 「Musterstrasse 5の区画にはどのような制限がありますか?」 | swisstopo_oereb_at |
| 「8002 ZürichのBederstrasse 109から500 m以内にある学校と、そこへ続く通りを教えてください」 | swisstopo_query_osm_features + swisstopo_query_geodata(strassenverzeichnis) |
| 「州ZHで利用できる、無料の汚染サイトデータはどれですか?」 | swisstopo_list_available_layers + swisstopo_query_geodata(geodienste:kataster_belasteter_standorte:ZH) |
| 「Uster districtにあるcommuneと、それぞれのBFS番号を教えてください」 | swisstopo_find_commune(district=109) |
| 「郵便番号8001は、どのcommuneとcantonに属していますか?」 | swisstopo_lookup_postal_code |
| 「WinterthurのBFS番号を教えてください(BFS統計との結合用)」 | swisstopo_find_commune(name=Winterthur) |
アーキテクチャ
┌─────────────────┐ ┌──────────────────────────────┐ ┌──────────────────────────┐
│ Claude / AI │────▶│ swisstopo-mcp │────▶│ Swisstopo REST API │
│ (MCP Host) │◀────│ (MCP Server) │◀────│ api3.geo.admin.ch │
└─────────────────┘ │ │ ├──────────────────────────┤
│ 20 Tools │────▶│ Geocoding │
│ Stdio | Streamable HTTP │◀────│ api3.geo.admin.ch │
│ │ ├──────────────────────────┤
│ No authentication required │────▶│ STAC Catalog │
│ (all tools; OEREB canton opt) │◀────│ data.geo.admin.ch │
│ │ ├──────────────────────────┤
│ │────▶│ OEREB Cadastre │
│ │◀────│ (cantonal endpoints) │
│ │ ├──────────────────────────┤
│ │────▶│ geodienste.ch (OGC API) │
│ │◀────│ overpass.osm.ch (ODbL) │
│ │ ├──────────────────────────┤
│ BFS-Nr = join key to │────▶│ OpenPLZ API │
│ swiss-statistics-mcp │◀────│ openplzapi.org (BFS/OGD)│
└──────────────────────────────┘ └──────────────────────────┘
プロジェクト構成
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_map_query (5 operations), zoning_at, municipality_at
│ ├── 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
│ ├── geodata.py # swisstopo_query_geodata + swisstopo_list_available_layers (façade)
│ ├── overpass.py # swisstopo_query_osm_features (OpenStreetMap / Overpass)
│ └── openplz.py # swisstopo_lookup_postal_code, swisstopo_find_commune, swisstopo_search_address (OpenPLZ)
├── tests/
│ ├── test_api_client.py
│ ├── test_geocoding.py
│ ├── test_height.py
│ ├── test_oereb.py
│ ├── test_rest_api.py
│ ├── test_stac.py
│ ├── test_wmts.py
│ ├── test_geodata.py
│ ├── test_overpass.py
│ ├── test_openplz.py
│ └── test_retry.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.mdに記載されています。
フェーズ
このサーバーは フェーズ 2.5 — swiss-geodata-mcp の統合
にあります(フェーズの状態を定める唯一の情報源である
docs/roadmap.md を参照してください)。
| プロパティ | 状態 |
|---|---|
| 読み取りツール | 24 個。すべて readOnlyHint: true / destructiveHint: false |
| 書き込みツール | なし — フェーズ 3。予定なし |
| トランスポート | stdio(デフォルト)および Streamable-HTTP |
| ISDS 分類 | docs/isds-dsg.md — 保護要件は低い |
| DSG 処理記録 | 理由を記載したうえで未作成 — docs/isds-dsg.md §5 |
| 最終監査 | audits/2026-07-27T162602-Z-swisstopo-mcp/ |
フェーズを進めるには、そのフェーズのロードマップ項目にチェックが付き、
critical の未解決指摘がない状態で監査を再実施し、新しいフェーズを明記した
CHANGELOG の項目が必要です。フェーズ 3(書き込みツール)では、実装を開始する前に、
Lethal-Trifecta 評価の再実施とセキュリティレビューも必要です。
ツール予算と統合
独自に設定した上限 25 個に対して、ツールは 20 個です。このチェックの理想値は 12 個以下なので、単に数を示すのではなく、根拠を説明する必要があります。クラスターごとに:
5 つの api3 ツールは統合されています(0.4.1、破壊的変更)。search_layers、
layer_info、identify_features、find_features、get_feature は現在、
swisstopo_map_query の operation 値です。これらはチェックで名指しされている
「REST エンドポイントごとにツールを 1 つ割り当てる」典型例でしたが、そうした形では
なくなりました。
以前のリリースでは、ここでは逆の主張をしていました。その主張は間違いというより、 別の要因に優先されたものなので、残しておく価値があります。統合すると、判断の場が ツール選択からスキーマの操作に移ります。モデルが実際に読むのはツールの説明なので、 スキーマの操作では手掛かりが少なくなります。重要ではないと期待するのではなく、 次の 3 点で直接対処しています。
- 操作名はエンドポイントではなく質問に基づいています。
identifyとfindは ESRI の用語です。何を尋ねているかではなく、どの MapServer ルートを呼び出すかを 示しており、ArcGIS の経験がなければ両者の違いは分かりません。features_at_pointとfeatures_by_attributeなら、操作一覧だけで正しく選べます。 - 誤った引数の組み合わせは、黙って無視されるのではなくエラーになります。 5 つの ツールでは、スキーマによって誤った組み合わせが不可能でした。1 つのツールではそうは ならないため、検証で対応します。各操作が受け付けるフィールドを宣言し、それ以外は 代替候補を示すメッセージとともに拒否されます。
- ARCH-003 の
noteヒントは、引き続き次のステップを示します。 現在はツール名 ではなく操作を示します。たとえば属性検索が空の場合、有効なフィールド名を確認するoperation='layer_info'などを示します。
もう 1 つのコストは可観測性でしたが、これは損なわれていません。各操作は固有のログと
トレースラベル(swisstopo_map_query:features_at_point)を保持するため、操作ごとの
処理時間とエラー率は統合後も確認できます。
分離したままの 2 組は、どちらも監査で指摘されています。
geocodeとreverse_geocodeは同じ SearchServer エンドポイントを呼び出すため、 API の観点では、どちらも 1 対 1 の対応です。これらを分けているのは、ツール選択で 重要な観点です。「住所 → 座標」と「座標 → 住所」は、入力の種類が異なる別々の質問です。 モードを設けて 1 つのツールにまとめると、モデルはツールではなくバリエーションを選ぶ ことになります。エンドポイントの共有は、上流側の実装詳細です。search_geodata→get_collectionは、STAC における検索 → 詳細の正規の組み合わせです。 以下を参照してください。
命名の曖昧さは解消されています。 監査では指摘されていませんでしたが、前の版では
「次の破壊的変更リリース」で対応すると記録されていました。それが今回です。
swisstopo_search_layers と swisstopo_list_available_layers は、どちらも異なる
カタログを扱いながら「layers」と称していました。前者は現在
operation='search_layers' を指定する swisstopo_map_query となり、ツール名に国別
カタログを示すようになりました。そのため、list_available_layers は統合された
ファサードを明確に指すようになっています。
検索→詳細のペア。 search_geodata → get_collection は正真正銘のペアです。STAC コレクションのメタデータは大きく、呼び出し元が必要とするのは通常、多数の検索結果のうちの 1 つです。get_egrid → get_oereb_extract も同じ形でしたが、統合されています。swisstopo_oereb_at は実際の質問に 1 回の呼び出しで回答し、EGRID は上流の識別子であって呼び出し元が要求したものではないため、内部で解決します。区画 ID 自体が必要な呼び出し元向けに、get_egrid は引き続き利用できます。
すでに実現している正真正銘の集約。 query_geodata は 3 つのソースを 1 つのツールの背後にまとめています。zoning_at と municipality_at は、それぞれ以前は 2 回の呼び出しが必要だった検索チェーンを 1 つにまとめています。
次のソースを追加するときは、上限を引き上げるか、統合するかを選ぶことになります。api3 の 5 つを統合した今、余地を生み出せる明らかな統合先は残っていません。そのため、次の表面の拡大では、先送りされた整理ではなく、上限について実際に議論することになります。tests/test_tool_namespace.py::TestToolBudget でその議論が必須になります。上限を引き上げるには、そこの数値と両方の README を編集する必要があります。
データソースとライセンス
すべてのレスポンスには source と license が含まれます。ARE は swisstopo とは異なる連邦機関なので、そのライセンスは継承せず、明示しています。
| ソース | 提供元 | ライセンス |
|---|---|---|
| swisstopo / geo.admin.ch | ほとんどのツール | Swiss OGD(opendata.swiss) |
| swisstopo REFRAME(geodesy.geo.admin.ch) | swisstopo_convert_coordinates | Swiss OGD(opendata.swiss) |
| swissBOUNDARIES3D(swisstopo) | swisstopo_municipality_at | Swiss OGD(opendata.swiss) |
ch.are.bauzonen(ARE) | swisstopo_zoning_at | Swiss OGD — Bundesamt für Raumentwicklung ARE |
| 州の ÖREB 地籍 | swisstopo_get_egrid、swisstopo_get_oereb_extract、swisstopo_oereb_at、swisstopo_query_geodata | 州の ÖREB 利用規約 |
| geodienste.ch(州) | swisstopo_query_geodata | 無料利用可 — 帰属表示が必要 |
| OpenStreetMap(Overpass) | swisstopo_query_osm_features | ODbL — © OpenStreetMap contributors |
| OpenPLZ(BFS + swisstopo) | swisstopo_lookup_postal_code、swisstopo_find_commune、swisstopo_search_address | 無料利用可 — 帰属表示が必要 |
ch.are.bauzonen は州をまたいだ比較可能性のための連邦レベルの統合データであり、法的拘束力はありません。法的拘束力を持つのは、州または自治体の Nutzungsplanung のみです。この注意事項は、すべての swisstopo_zoning_at 結果レコードに含まれます。
プロジェクト構成
ツールモジュールは tools/ サブパッケージではなく、src/swisstopo_mcp/ の直下に配置されています。各モジュールは上流 API のファミリー 1 つに正確に対応しています。rest_api.py → api3 MapServer、stac.py → STAC、oereb.py → 州の ÖREB、openplz.py → OpenPLZ、overpass.py → OSM、coords.py → REFRAME です。これが、このサーバーのコードが実際に異なる軸です。tools/ 階層を設けても、区別が増えることなくディレクトリが増えるだけです。
server.py に含まれるのはツール登録のみです。各ツールの本体は、それぞれのドメインモジュールにあります。さらに分割するかどうかは、構造上の問題ではなく、可読性の問題です。
Lethal Trifecta の評価
| 機能 | 状態 | 理由 |
|---|---|---|
| プライベートデータへのアクセス | ❌ いいえ | 公開オープンデータのみ(連邦・州の地理空間データ) |
| 信頼できないコンテンツへの接触 | ⚠️ 限定的 | 固定された信頼できる geo.admin / OEREB ホストの許可リストからのみ読み込みます |
| 外部への通信(書き込み/送信) | ❌ いいえ | 読み取り専用です。メール/Webhook/書き込みツールはありません |
Trifecta スコアは最大でも 3 項目中 1 項目です。設計上、安全です。
外向き通信
送信リクエストはコード層の明示的な許可リストに制限され、リダイレクトは無効になっています。docs/network-egress.md を参照してください。
コンテナーのデプロイ
コンテナー化された HTTP デプロイメント向けに、強化された Dockerfile と Kubernetes マニフェスト(非 root、読み取り専用のルートファイルシステム、ケイパビリティの削除、egress NetworkPolicy)を用意しています。docs/deployment.md を参照してください。
MCP プロトコルバージョン
サーバーは 2026-07-28 にネイティブ対応し、すべての旧リビジョンも引き続き利用できます。どのバージョンが使われるかは接続方法によって決まり、設定によるものではありません。1つのプロセスで、どちらの世代にも同時に対応します。
| モダン世代 | ハンドシェイク世代 | |
|---|---|---|
| リビジョン | 2026-07-28 | 2024-11-05 … 2025-11-25 |
| 開始時のやり取り | なし — 各リクエストは単独で完結します | initialize + notifications/initialized |
| プロトコルバージョンの指定場所 | params._meta と MCP-Protocol-Version ヘッダー | initialize の params |
| セッション | なし | Mcp-Session-Id。DELETE /mcp で終了します |
| ディレクトリ | server/discover | initialize の結果 |
| サーバーの識別情報 | 各レスポンスの _meta にある io.modelcontextprotocol/serverInfo スタンプ | initialize の結果にある serverInfo |
| このリビジョンで廃止されたもの | initialize、ping、logging/setLevel、resources/subscribe | — |
2026-07-28 のリクエストは、/mcp への自己完結型の POST です。
POST /mcp
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: swisstopo_map_url
Content-Type: application/json
Accept: application/json, text/event-stream
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{
"name":"swisstopo_map_url",
"arguments":{"params":{"lat":46.9481,"lon":7.4474}},
"_meta":{
"io.modelcontextprotocol/protocolVersion":"2026-07-28",
"io.modelcontextprotocol/clientCapabilities":{}
}}}
MCP-Protocol-Version ヘッダーによってリクエストがモダンな経路に振り分けられます。これがない場合、同じ本文でもハンドシェイク用トランスポートに送られます。Mcp-Method と Mcp-Name は本文の内容と一致している必要があり、一致しない場合は -32020 でリクエストが拒否されます。これら3つはすべて CORS の許可リストに含まれているため、ブラウザクライアントのプリフライトは成功します。
実際には、次のようになります。
- 既存のクライアントに影響はありません。
2025-06-18を要求するクライアントには引き続き2025-06-18が返されます。initializeで2026-07-28を要求するクライアントには、ハンドシェイクの上限である2025-11-25が返されます。これは想定ではなく、実測によるものです —tests/test_protocol_version.py。 - どちらの世代でも同じ20個のツールを利用できます。 リビジョンによる制限はありません(
tests/test_modern_protocol.py)。 - スティッキーセッションが必要なのはハンドシェイク世代のみです。 モダンなリクエストにはセッションが含まれないため、どのレプリカでも処理できます — docs/deployment.md を参照してください。
どちらのリビジョンも作成者が設定することはできません。SDK がハンドシェイク世代をネゴシエートし、モダン世代のリクエストを振り分けます。また、pyproject.toml では mcp をメジャーバージョン 2.x に固定しているため、アップデートによっていずれかのバージョンが予告なく変わることはありません。tests/test_protocol_version.py では両方の上限を固定し、Dependabot による更新でどちらかが変わった場合に失敗します。
更新ポリシー
- SDK の更新は、マージ前にフィーチャーブランチでテストします。
- いずれかのプロトコル上限を変更した場合、旧バージョンと新バージョンを明記して、CHANGELOG.md の
### Changedに記録します。 - 既存のクライアントを破壊するプロトコル変更があった場合、メジャーリリースを実施します。
セッションと認証
このサーバーは、公開されているオープンデータのみを提供する設計のため、認証を行いません。
セッションが存在するのはハンドシェイク世代のみです。その世代では、セッション ID は MCP SDK が全面的に管理します。ユーザーごとの状態はないため、セッションをユーザー固有の情報に関連付ける必要もありません。アイドル状態のセッションは SWISSTOPO_SESSION_IDLE_TIMEOUT 秒後(デフォルトは1800秒)に破棄されます。これにより、DELETE /mcp を送信せずに切断したクライアントが、プロセスの稼働中にセッションを残し続けることを防ぎます。2026-07-28 のリクエストではセッションが作成されないため、このタイムアウトもセッション破棄処理も適用されません。
認証が必要なデプロイを将来導入する場合、セッション ID は検証済みのユーザー ID に関連付ける必要があります(監査指摘事項 SEC-009)。
エラー処理
- 実行エラー(上流の失敗、無効な値)は、
is_error: trueとユーザーにわかりやすいsummaryを含むToolResponseとして返されます。予期しない例外のテキストはマスクされ、代わりに標準エラー出力に記録されます。上流のレスポンス本文や内部設定が転送されることはありません。Overpass のエラーページは固定のシグネチャテーブルと照合して分類され、外部への送信が拒否された場合は許可リストではなく固定メッセージが返されます(OBS-002)。 - プロトコルエラー(不明なツール、不正な形式または無効な引数)は、プロトコルの
isErrorフラグが設定されたツール結果として SDK から返され、JSON-RPC エラーオブジェクトとしては返されません。mcp 1.28.1 に対する実行時プローブで検証済みです。このセクションの以前のバージョンでは-32602と記載していましたが、誤りでした。入力の検証は Pydantic の境界で行われます(SEC-018)。 - 両方のフラグは一致します。 処理済みの実行エラーでは、プロトコルの
isErrorフラグとペイロードフィールドのis_errorの両方が設定されるため、クライアントはいずれかを基準に分岐できます。エラー時もsourceとlicenseを含むエンベロープは維持されます(OBS-001)。
MCP プリミティブ
ツールが主要なインターフェースであり、ほぼすべてを占めます。すべての結果は、静的でアドレス指定可能なドキュメントではなく、パラメーター化されたライブ API クエリです。
1 つのリソース — swisstopo://catalogue/layers — は、ファサードレイヤーカタログを提供します。これはドキュメントのように動作する唯一のもので、決定的かつ冪等であり、すでに provenance: "cached" として提供されています。swisstopo_list_available_layers はフィルタリングしたクエリ向けに引き続き利用でき、このリソースはカタログ自体をアドレス指定して取得したいクライアント向けです。
2 つのプロンプトは、点に関する質問の優先順位ルールを含む、以下のワークフローを定義します。このルールはツールの説明とサーバーの指示にも記載されていますが、24 個の説明のうちの 1 つとしてではなく、ガイダンスとしてモデルが読む場所はプロンプトです(監査 ARCH-007/ARCH-008)。
| プロンプト | 引数 |
|---|---|
swisstopo_feature_lookup | ort, was |
swisstopo_geodata_download | thema |
ツールのワークフロー
ほとんどのツールは、1 回の呼び出しで完結した結果を返します。2 つの領域では、短い、ドキュメント化された検索チェーンを使用します(各ツールの説明に次のステップが記載されています)。
- 地物クエリ: 4 つのステップすべてで、
operationを変えてswisstopo_map_queryを使用します。search_layers(レイヤー ID を検索)→layer_info(クエリ可能なフィールドを確認)→features_at_point/features_by_attribute→feature_by_id(詳細を取得)。 - 地籍:
swisstopo_geocode→swisstopo_oereb_at(1 回の呼び出しで、座標 → EGRID → 抄本)。地番 ID 自体が必要な場合に限り、swisstopo_get_egrid→swisstopo_get_oereb_extractを使用します。 - ダウンロード:
swisstopo_search_geodata→swisstopo_get_collection。
レスポンス形式
すべてのツールは構造化された ToolResponse を返します(FastMCP は出力スキーマ付きの構造化コンテンツと JSON テキストブロックとして出力します)。
| フィールド | 意味 |
|---|---|
summary | 人が読める Markdown の要約 |
results | 機械可読な構造化レコード |
count | results の件数 |
match_type | exact / fuzzy / none(検索形式のツール) |
source / license | データの帰属情報(OGD-CH、CC/OGD の利用条件) |
provenance / retrieved_at | データの取得方法と取得日時 |
is_error | 処理済みエラーの場合は true |
既知の制限事項
- OEREB ツールには州のパラメーターが必要です。すべての州が同じ API 形式を公開しているわけではありません
- STAC カタログは Swisstopo の v0.9 エンドポイントを使用しているため、一部のコレクションではメタデータが不完全な場合があります
- ジオコーディングの対象はスイスの住所のみです(リヒテンシュタインは対象外)
- レート制限は Swisstopo によって適用されます。高頻度で使用すると、スロットリングされる場合があります
既知の調査結果 — OpenPLZ のライブプローブ(2026-07-20)
実装前に OpenPLZ のエンドポイントをライブ環境で調査しました。調査結果はツールに反映されています。
| エンドポイント/動作 | 結果 | 対応 |
|---|---|---|
/Cantons | 200、26 件のレコード、key = BFS 州番号(ZH = 1) | 州の略称はこのリストから解決 |
/Cantons/{key}/Districts|Communes | 200 | パスパラメーターは数値キー |
/Cantons/ZH/Districts(略称) | 200 + [] — エラーではありません | ZH→1 はサーバー側で解決されます。空の結果には説明を付記します |
/Localities?postalCode=8001 | 200、commune.key = 261(BFS チューリッヒ) | bfs_commune_number をトップレベルに表示 |
/Localities?postalCode=9999(不明) | 200 + [] | 空の結果は注記として報告 — 空は存在しないことを意味しません |
| リストエンドポイントのページネーション | デフォルトの pageSize=10、最大 50(100 → HTTP 400) | ツールは x-total-count を使ってページを反復 |
クエリ内の生ウムラウト(?name=Zürich) | HTTP 400 | httpx がパラメーターを自動的に URL エンコードします |
historicalCode フィールド | コミューンでは key と異なる(履歴ディレクトリ ID) | 使用しません。結合キーは現在の key です |
| 一括ダンプ | OpenPLZ にはありません(/swagger のみ) | アーキテクチャ A(ライブ API のみ)— 検索コネクターとして十分です |
略称とキーの落とし穴を一言でいうと: OpenPLZ のリストが空でも、何かが 存在しない 証拠とはほとんど限りません。通常は、誤ったパスパラメーター(数値キーが必要な場所に略称を指定)が原因です。ツールは略称をサーバー側で解決し、空の結果すべてに注記を付けます。
テスト
# Unit tests (no network required)
pytest tests/ -m "not live"
# Integration tests (live API calls)
pytest tests/ -m "live"
変更履歴
CHANGELOG.md を参照してください。
コントリビューション
CONTRIBUTING.md を参照してください。
セキュリティ
読み取り専用、認証なし、公開地理データのみを扱います。セキュリティ方針および脆弱性の報告方法については、SECURITY.md(ドイツ語)をお読みください。
ライセンス
MIT ライセンス — LICENSE を参照してください。
データは Open Government Data の利用条件に基づき、swisstopo が提供しています。
作者
Hayal Oezkan · malkreide
クレジットと関連プロジェクト
- Swisstopo: www.swisstopo.admin.ch -- スイス連邦地形局
- Swisstopo API: api3.geo.admin.ch / data.geo.admin.ch
- プロトコル: Model Context Protocol -- Anthropic / Linux Foundation
- 関連: zurich-opendata-mcp -- チューリッヒ市のオープンデータ
- 関連: swiss-transport-mcp -- スイスの公共交通機関
- 関連: swiss-cultural-heritage-mcp -- スイスの文化遺産
- ポートフォリオ: Swiss Public Data MCP Portfolio
インストール
uv の uvx を使って実行できます — クローンや手動インストールは不要です。MCP クライアントの設定に追加してください(Claude Desktop、Cursor、Windsurf では mcpServers を使用します。VS Code では .vscode/mcp.json のトップレベルキー servers を使用してください)。
{
"mcpServers": {
"swisstopo-mcp": {
"command": "uvx",
"args": [
"swisstopo-mcp"
]
}
}
}
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。