国土交通データプラットフォームMCPサーバー触ってみる
この記事の目次
本記事は【Advent Calendar 2025】の8日目の記事です。
ITD 1-2 開発課のO・Aです。最近は流行りの3周遅れくらいで麻辣湯にハマっているのと、データベース周りに興味があります。
今月11月4日に、国土交通省が無償でMCP serverを公開したことが話題(たぶん)になっていて気になったので、触ってみました。
MCPとは
「MCP」(Model Context Protocol)は、大規模言語モデル(LLM)などを使ったAIアプリケーションと、外部のツールやデータとの連携を標準化するプロトコル。2024年にAnthropicが発表したもの。
国土交通省とは
日本の国土の総合的な利用・開発・保全、社会資本整備、交通政策、気象業務、海上の安全確保などを担う中央省庁。
現在の国土交通大臣は金子 恭之氏
国土交通データプラットフォームとは
2020年4月に1.0版として国土交通省によって一般公開された。
交通、河川、港湾、航空、都市開発など、国土交通省及び民間の様々なデータを、一元的に検索・可視化・ダウンロードを可能にするデータプラットフォーム。
略して国交DPF

Webブラウザを通した検索・取得だけでなく、様々API機能の開発・提供も行っている。
APIリファレンスや開発環境を構築せず簡易にAPIを利用したい時のためのGraphiQL(GraphQL作成支援UIツール)環境の提供も行っている。
主な活用
- インフラ維持管理と計画策定
- 都市環境の改善
- 物流効率化
- 観光復興の推進
- 防災・減災 etc…
参考
https://www.mlit.go.jp/tec/tec_tk_000066.html
https://www.mlit.go.jp/report/press/content/001362442.pdf
国土交通データプラットフォーム MCPとは
国土交通省が保有するデータと民間等のデータを連携し、一元的に検索・表示・ダウンロードを可能にする国土交通データプラットフォームが提供する利用者向けAPIと接続するMCP(Model Context Protocol)サーバー
(リポジトリの概要欄より)
これまではAPI利用のために、API仕様を調べる -> GraphQLでクエリを書く -> APIに投げる必要がありましたが、MCPサーバーの活用で自然言語の対話のみでデータが取得可能になりました。
準備
リポジトリのREADMEに大体わかりやすく記載されています。
何が必要?
- Claude Desktop
- Python 3.10以上
- 国土交通省DPFのAPIキー(こちらから取得。アカウント作成必要あり。)
1.リポジトリをcloneして仮想環境を有効化
git clone https://github.com/MLIT-DATA-PLATFORM/mlit-dpf-mcp.git
cd mlit-dpf-mcp
python -m venv .venv
.venv\Scripts\activate # Windows
source .venv/bin/activate # macOS/Linux
2.依存ライブラリインストール
pip install -e .
pip install aiohttp pydantic tenacity python-json-logger mcp python-dotenv
3..env.sampleを.envにコピー + 環境変数設定
MLIT_API_KEY=`取得したAPIキー`
MLIT_BASE_URL=https://www.mlit-data.jp/api/v1/
4.mcpサーバー起動
python -m src.server
5.Claude Desktopの設定を開いてclaude_desktop_config.jsonに MCP構成を追加

{
"mcpServers": {
"mlit-dpf-mcp": {
"command": "....../mlit-dpf-mcp/.venv/Scripts/python.exe",
"args": [
"....../mlit-dpf-mcp/src/server.py"
],
"env": {
"MLIT_API_KEY": "取得したAPIキー",
"MLIT_BASE_URL": "https://www.mlit-data.jp/api/v1/",
"PYTHONUNBUFFERED": "1",
"LOG_LEVEL": "WARNING"
}
}
}
}
※commandとargsの....の部分は実際のパスに変更する
6.保存してClaude Desktop再起動
試してみる
「新宿駅近くの避難所を5件教えて」
Claude Desktop上では以下のようなリクエストとレスポンスがMCPサーバーから返っていることがわかります
リクエスト
{
`size`: 5, // 取得件数上限
`term`: `避難所`, // 検索キーワード
`location_lat`: 35.6896, // 中心地点の緯度(新宿駅周辺)
`location_lon`: 139.7006, // 中心地点の経度(新宿駅周辺)
`location_distance`: 1000 // 検索半径(1km)
}
レスポンス
{
"search": {
"totalNumber": 6,
"searchResults": [
{
"id": "fb4a919a-09f6-4a3b-b7ca-9fb294f16dc8",
"title": "新宿中央公園・高層ビル群一帯",
"lat": 35.689666,
"lon": 139.689875,
"year": "2020",
"dataset_id": "nlni_ksj-p20",
"catalog_id": "nlni_ksj"
},
{
"id": "fe039eed-bf70-4638-89d7-283da22739c2",
"title": "西新宿中学校",
"lat": 35.695942,
"lon": 139.694819,
"year": "2020",
"dataset_id": "nlni_ksj-p20",
"catalog_id": "nlni_ksj"
},
{
"id": "161a9969-7d98-4ea9-b239-2196c8e4ac0e",
"title": "鳩森小学校",
"lat": 35.682625,
"lon": 139.705994,
"year": "2020",
"dataset_id": "nlni_ksj-p20",
"catalog_id": "nlni_ksj"
},
{
"id": "feeeaa72-79be-4b02-89ef-ad82e7a64986",
"title": "都立新宿高等学校",
"lat": 35.688478,
"lon": 139.705121,
"year": "2020",
"dataset_id": "nlni_ksj-p20",
"catalog_id": "nlni_ksj"
},
{
"id": "b69d0381-a860-45c7-aad0-87d3f9e076fd",
"title": "新宿御苑",
"lat": 35.685518,
"lon": 139.709165,
"year": "2020",
"dataset_id": "nlni_ksj-p20",
"catalog_id": "nlni_ksj"
}
]
}
}
実際に何が起こっているのか

↑geminiに作成してもらった
内部処理を見てみる
STEP 1: Claude Desktopがリクエスト
{
"method": "tools/call",
"params": {
"name": "search_by_location_point_distance",
"arguments": {
"size": 5,
"term": "避難所",
"location_lat": 35.6896,
"location_lon": 139.7006,
"location_distance": 1000
}
}
}
STEP 2: handle_call_toolが呼ばれる
@server.call_tool() # ← MCPサーバーSDKが自動的にルーティング
async def hasync def handle_call_tool(name: str, arguments: dict) -> List[types.TextContent]:
# name = "search_by_location_point_distance"
# arguments = {size: 5, term: "避難所", location_lat: 35.6896, ...}
rid = new_request_id() # リクエストIDを生成
cfg = load_settings() # 設定読み込み(未使用だがロード)
client = MLITClient() # API クライアント作成
STEP3: バリデーション + Pydanticモデルに変換
elif name == "search_by_location_point_distance":
p = SearchByPoint.model_validate({
"term": arguments.get("term"),
"first": arguments.get("first", 0),
"size": arguments.get("size", 50),
"phrase_match": arguments.get("phrase_match", True),
"prefecture_code": arguments.get("prefecture_code"),
"point": {
"lat": arguments["location_lat"],
"lon": arguments["location_lon"],
"distance": arguments["location_distance"],
}
})
STEP4: APIクライアント呼び出し
data = await client.search_by_point(
p.point.lat, p.point.lon, p.point.distance,
term=p.term or "",
first=p.first,
size=p.size,
phrase_match=p.phrase_match,
)
search_by_point メソッドの中で、GraphQLを組み立てている
async def search_by_point(self, lat: float, lon: float, distance_m: float, **kw) -> Dict[str, Any]:
loc = self.make_geodistance_filter(lat, lon, distance_m)
# make_geodistance_filterで地理オブジェクトに整形
{
"geoDistance": {
"lat": 35.6896,
"lon": 139.7006,
"distance": 1000
}
}
# クエリを構築
q = self.build_search(location_filter=loc, **kw)
return await self.post_query(q)
# ==== (GraphQL): operator 'is' ====
async def search_by_attribute_raw(
self,
*,
term: Optional[str] = None,
first: int = 0,
size: int = 20,
phrase_match: bool = True,
attribute_name: str,
attribute_value: Any,
fields: Optional[str] = None,
) -> Dict[str, Any]:
af = self.make_single_attribute_filter(attribute_name, attribute_value)
effective_term = term if term is not None else ""
q = self.build_search(
term=effective_term,
first=first,
size=size,
phrase_match=phrase_match,
attribute_filter=af,
fields=fields or self._fields_basic(),
)
# API送信
return await self.post_query(q)
ここで以下のGraphQLクエリが構築される↓
{
search(
term: "避難所"
phraseMatch: true
first: 0
size: 5
locationFilter: {
geoDistance: {
lat: 35.6896
lon: 139.7006
distance: 1000
}
}
) {
totalNumber
searchResults {
id
title
lat
lon
dataset_id
catalog_id
}
}
}
STEP5: 最終的にjson形式に整形してClaudeDesktopに返却
# 1481行目
text = json.dumps(data, ensure_ascii=False)
if len(text.encode("utf-8")) > 1024 * 1024:
text = text[:1024 * 512] + "\n...<truncated>"
logger.info("tool_done", extra={"rid": rid, "tool": name, "elapsed_ms": t.elapsed_ms})
return [types.TextContent(type="text", text=text)]
おわりに
業務での活用方法は今の所思いつかないですが・・・。
こうしたMCPサーバーが無償で公開されるおかげで、国のデータ取得のハードルが相当下がって便利になっていることを実感しました!
PLATEAU(プラトー)などの3Dモデルデータのダウンロードもできるようなので、何かに活かしたいですね。
イベント告知

12月23日にイベントを開催します!申し込みはこちらから▼
https://mynaviit.connpass.com/event/376769
※本記事は2025年12月時点の情報です。