1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

FastMCP 3 + Docker でGIS MCP Serverを作る ― Step 1 Dataset CatalogとVector GIS登録基盤

1
Last updated at Posted at 2026-09-09

FastMCP 3 + Docker でGIS MCP Serverを作る ― Step 1 Dataset CatalogとVector GIS登録基盤

はじめに

Step 0では、Docker Composeで同じ開発環境を再現し、Ruff、mypy、pytestをまとめて実行できる土台を作りました。

Step 1では、その環境の上にGISデータを安全に登録する仕組みを追加します。

いきなりMCP ToolからGeoPackageを開くのではなく、最初にGISファイルを確認し、問題がないデータだけをDataset Catalogへ登録します。登録後は物理ファイル名ではなく、dataset_idversionで参照します。

20260826_f01.png

この仕組みを先に作っておくと、後でFastMCPからGIS処理を呼ぶときに、LLMへサーバー内部のファイルパスを渡す必要がありません。


1. Step 1で作るもの

今回追加する範囲は次です。

  • control.duckdb
  • Dataset / DatasetVersion
  • Dataset Catalog
  • SQL migration
  • GISファイルの登録前確認
  • GeoPackage / GeoJSON / GeoParquetの確認
  • CRS、Geometry、件数、bboxの取得
  • SHA-256の記録
  • dataset_id / versionによる参照
  • Dataset登録CLI
  • DuckDBと実GISファイルを使ったテスト

FastMCPのTool / Resource、HTTP Serverはまだ追加しません。MCPへの公開はStep 2で行います。

参考のために、ZIPファイルを置いておきますので、ダウンロードして活用してください。


2. 使用するライブラリ

2026年8月24日時点では、次のversionを固定しています。

ライブラリ version 用途
Python 3.13.15 実行環境
FastMCP 3.4.7 Step 2以降で使用
DuckDB 1.5.5 Dataset Catalog
GeoPandas 1.1.4 Vector GIS
Pyogrio 0.13.0 GeoPackage / GeoJSON I/O
Shapely 2.1.2 Geometry
pyproj 3.7.2 CRS
PyArrow 25.0.1 GeoParquet

PyogrioのPyPI wheelにはGDALが含まれるため、ホストOSへGDALを個別に入れる構成にはしていません。Container内で実際に使われるGDAL versionとDriverをテスト時に確認します。

GeoPandas 1.xではPyogrioが標準のI/O engineとして使われるため、今回の構成とも合わせやすくなっています。


3. 対応するVector形式

Step 1では次の3形式から始めます。

形式 対応
GeoPackage
GeoJSON
GeoParquet
Shapefile 今回は対象外
FlatGeobuf 後で追加可能
GeoTIFF / COG Raster Stepで対応

Shapefileを最初から対象にしないのは、.shpだけでは完結せず、.dbf.shxなど複数ファイルをまとめて管理する必要があるためです。Dataset Catalogの基本動作を確認してから追加する方が実装を単純に保てます。


4. workspaceの構成

共有volumeの中は次のようにします。

/workspace
├─ control/
│  └─ control.duckdb
├─ incoming/
│  └─ 登録前のGISファイル
├─ datasets/
│  └─ 登録済みGISファイル
├─ work/
├─ results/
├─ previews/
└─ quarantine/

利用者が登録するGISファイルは、まずincomingへ置きます。

登録後は、例えば次のようになります。

/workspace/datasets/
└─ landslide_area/
   ├─ v0001/
   │  └─ data.gpkg
   └─ v0002/
      └─ data.gpkg

同じデータセットを更新しても、古いversionを残せます。


5. Dataset Catalog

control.duckdbにはGISのGeometryそのものをすべて入れるのではなく、まず管理情報を保存します。

主な情報は次です。

Dataset
├─ dataset_id
├─ name
└─ created_at

DatasetVersion
├─ dataset_id
├─ version
├─ status
├─ source_filename
├─ storage_path
├─ checksum_sha256
├─ driver
├─ layer_name
├─ crs
├─ geometry_type
├─ feature_count
├─ bbox
├─ columns
└─ created_at

実データはGeoPackageやGeoParquetとして/workspace/datasetsへ保存します。

この分け方なら、大きなGISファイルをControl DBへ毎回取り込まずに済みます。


6. migrationでschemaを管理する

Database schemaはPythonコードの中へ直接書かず、migrations/へSQLとして置きます。

migrations/
└─ 001_dataset_catalog.sql

適用済みmigrationにはSHA-256を記録します。

そのため、すでに適用したSQLを後から書き換えた場合は、起動時に検出できます。

また、migrationはtransaction内で実行し、途中でSQLエラーになった場合はそのmigration全体をrollbackします。


7. 登録前に何を確認するか

GISファイルを見つけたら、すぐにCatalogへ登録するわけではありません。

最低限、次を確認します。

ファイルが存在する
    ↓
対応形式か
    ↓
開けるか
    ↓
CRSがあるか
    ↓
Geometryがあるか
    ↓
featureが1件以上あるか
    ↓
bboxを取得できるか
    ↓
PASSなら登録へ

CRSなしデータはStep 1では登録しません。

GIS処理では距離、面積、座標変換を行うため、CRSが分からない状態をそのまま後工程へ流すより、入口で止めた方が安全です。


8. GeoPackageの複数layer

GeoPackageは1ファイルに複数layerを持てます。

例えば、

hazard.gpkg
├─ landslide_area
├─ shelters
└─ roads

というファイルがあります。

この状態でlayer指定なしに登録すると、どのlayerをDatasetとするのか分かりません。

そのため、layerが1つだけなら自動選択し、複数ある場合は登録時に明示するようにしています。

20260826_f02.png


9. ファイルをcopyした後にも確認する

登録時は、元ファイルを確認するだけでは終わりません。

一時領域へcopyしたあと、もう一度GISファイルを開きます。

元ファイルとcopy後で、CRS、Geometry、件数などが変わっていた場合は登録を止めます。

Databaseへの登録に失敗した場合は、正式領域へ移したファイルも削除します。

Catalogには載っていないのに、datasetsにはファイルだけ残っているという状態をなるべく作らないためです。


10. dataset_idで管理する

物理pathをMCP Clientへ渡さないため、Datasetには次のようなIDを付けます。

landslide_area
shelters
river_network
admin_boundary

使用できる文字は英小文字、数字、_-です。

例えば、

/workspace/datasets/landslide_area/v0003/data.gpkg

という実ファイルがあっても、後のMCP Toolでは、

{
  "dataset_id": "landslide_area",
  "version": 3
}

のように指定できる構成にします。


11. 実装の構成

gis_mcp_server_step1_r001/
├─ compose.yaml
├─ Dockerfile
├─ pyproject.toml
├─ migrations/
│  └─ 001_dataset_catalog.sql
├─ scripts/
│  ├─ catalog_cli.py
│  ├─ check_runtime.py
│  ├─ check_step_completion.py
│  ├─ init_workspace.py
│  └─ validate_workspace.py
├─ src/gis_mcp/
│  ├─ config.py
│  ├─ contracts.py
│  ├─ domain/
│  │  ├─ dataset.py
│  │  └─ errors.py
│  ├─ ports/
│  │  ├─ catalog.py
│  │  └─ vector.py
│  ├─ services/
│  │  ├─ catalog_service.py
│  │  ├─ registration_service.py
│  │  └─ runtime_service.py
│  └─ adapters/
│     ├─ duckdb_repository.py
│     └─ vector_io.py
└─ tests/
   ├─ unit/
   ├─ architecture/
   └─ integration/

処理手順を書くServiceからDuckDBやGeoPandasを直接呼ばないようにしています。

Registration Service
      ↓
ControlRepository
      ↓
DuckDB実装

Vector側も同じです。

Registration Service
      ↓
VectorInspector
      ↓
Pyogrio / GeoPandas実装

これで、GISライブラリの使い方を変えたときに修正箇所を限定しやすくします。


12. Dataset CatalogをDocker Composeから操作する

Step 1では、Dataset Catalogを確認するためのcatalog serviceをDocker Composeへ用意します。

4つの操作ごとに別serviceを作るのではなく、1つのcatalog serviceへサブコマンドを渡す形にしています。

x-test-image: &test-image
  image: gis-mcp-server-step1:test
  build:
    context: .
    target: test
    args:
      UV_FROZEN: ${UV_FROZEN:-0}

services:
  catalog:
    <<: *test-image
    profiles: ["demo"]
    depends_on:
      workspace-init:
        condition: service_completed_successfully
    environment:
      GIS_MCP_WORKSPACE: /workspace
      GIS_MCP_LOG_LEVEL: INFO
    volumes:
      - gis_mcp_workspace:/workspace
      - ./data/incoming:/workspace/incoming:ro
    entrypoint: ["python", "scripts/catalog_cli.py"]

12.1 catalog serviceのDocker imageを作る

catalog serviceは、Step 1のPython環境とCatalog実装を含むDocker imageを使って実行します。
まずCompose定義を確認し、catalog serviceのimageを作成します。

docker compose config

docker compose --profile demo build --no-cache catalog

作成後、CLIがcontainer内から起動できることを確認します。

docker compose --profile demo run --rm catalog -h

ここで、次の4サブコマンドが表示されれば、Docker ComposeからCatalog CLIを呼び出せる状態です。

register
list
info
versions

このserviceから、次の4コマンドを実行します。

コマンド 用途
catalog register GISファイルをCatalogへ登録する
catalog list 登録済みDatasetの最新状態を一覧表示する
catalog info 指定Datasetの情報を表示する
catalog versions 指定Datasetのversion履歴を表示する

実際のCLIはcatalog_cli.pyにまとめています。

Docker Compose
      ↓
catalog service
      ↓
scripts/catalog_cli.py
      ├─ register
      ├─ list
      ├─ info
      └─ versions
      ↓
CatalogService / RegistrationService
      ↓
ControlRepository
      ↓
control.duckdb

CLIから直接DuckDBへSQLを書くのではなく、Step 1で作ったServiceとRepositoryを通して処理します。Step 2でMCP Toolを追加するときも同じServiceを再利用できます。


13. 実行環境を準備する

参考のために、ZIPファイルを置いておきますので、ダウンロードして活用してください。

13.1 lock fileを作る

cp .env.example .env

docker compose --profile maintenance run --rm lock

uv.lockを生成したら、.envを次へ変更します。

UV_FROZEN=1

13.2 Docker imageを作り直してテストする

docker compose config

docker compose build --no-cache test

docker compose --profile test run --rm test

すべて通った場合だけStep 1を完了とします。

STEP1_COMPLETION=PASS

14. Catalogの4コマンドを実際に使う

14.1 登録するGISファイルを配置する

サンプルのgpkgファイルを置いておきますので、ダウンロードして活用してください。

catalog serviceでは、ホスト側のdata/incoming/をcontainer内の/workspace/incomingへ読み取り専用でmountします。

まずフォルダーを作り、登録したいGeoPackageを置きます。

mkdir -p data/incoming
cp landslide_area.gpkg data/incoming/

確認します。

ls -lh data/incoming/landslide_area.gpkg

14.2 catalog list - 登録前の状態を確認する

何も登録していない状態では、次を実行します。

docker compose --profile demo run --rm catalog list

空のCatalogなら、次のように表示されます。

[]

この確認を先に行っておくと、以前のDocker volumeに登録情報が残っていないかも確認できます。

14.3 catalog register - GISファイルを登録する

landslide_area.gpkglandslide_areaレイヤーを登録します。

docker compose --profile demo run --rm catalog register \
  --file /workspace/incoming/landslide_area.gpkg \
  --dataset-id landslide_area \
  --name "土砂災害警戒区域" \
  --layer landslide_area

登録処理では、GISファイルを確認してからdatasetsへ保存し、最後にcontrol.duckdbへ管理情報を登録します。

incoming
   ↓
登録前の確認
   ↓
workへcopy
   ↓
copy後に再確認
   ↓
datasetsへ確定
   ↓
control.duckdbへ登録

14.4 catalog list - 登録済みDatasetを一覧表示する

登録後にもう一度一覧を確認します。

docker compose --profile demo run --rm catalog list

listではDatasetごとの最新versionを返します。

表示される主な項目は次です。

dataset_id
name
version
status
driver
layer_name
crs
geometry_type
feature_count
created_at

例えば、landslide_areaを登録していれば、概ね次のようなJSONになります。

[
  {
    "dataset_id": "landslide_area",
    "name": "土砂災害警戒区域",
    "version": 1,
    "status": "READY",
    "driver": "GPKG",
    "layer_name": "landslide_area",
    "crs": "EPSG:6677",
    "geometry_type": "Polygon",
    "feature_count": 3
  }
]

listは、Step 2で追加するgis_list_datasetsの元になる処理でもあります。

14.5 catalog info - Datasetの詳細を確認する

指定したDatasetの最新versionを確認します。

docker compose --profile demo run --rm catalog info \
  --dataset-id landslide_area

特定versionを確認したい場合は、--versionも指定できます。

docker compose --profile demo run --rm catalog info \
  --dataset-id landslide_area \
  --version 1

infoでは、Catalogに保存した情報に加えて、登録済みファイルを参照するための情報を確認できます。

14.6 catalog versions - version履歴を確認する

同じdataset_idを更新していくと、versionが増えていきます。

docker compose --profile demo run --rm catalog versions \
  --dataset-id landslide_area

例えば、同じDatasetを3回登録した場合は、次のように履歴を追えるようにします。

landslide_area
├─ version 1
├─ version 2
└─ version 3

これで、日常的なCatalog確認はDuckDBを直接開かなくても、Docker Composeから行えます。

14.7 4コマンドを一連の流れで確認する

新しいworkspaceでStep 1のCatalog操作を最初から確認する場合は、次の順序で実行すると分かりやすくなります。

まず登録前のCatalogを確認します。

docker compose --profile demo run --rm catalog list

次にGISファイルを登録します。

docker compose --profile demo run --rm catalog register \
  --file /workspace/incoming/landslide_area.gpkg \
  --dataset-id landslide_area \
  --name "土砂災害警戒区域" \
  --layer landslide_area

登録後、最新状態を一覧で確認します。

docker compose --profile demo run --rm catalog list

Datasetの詳細を確認します。

docker compose --profile demo run --rm catalog info \
  --dataset-id landslide_area

最後にversion履歴を確認します。

docker compose --profile demo run --rm catalog versions \
  --dataset-id landslide_area

操作の流れは次のようになります。

data/incoming/landslide_area.gpkg
            ↓
catalog list
  登録前の状態を確認
            ↓
catalog register
  GISファイルを検査して登録
            ↓
catalog list
  最新versionとREADY状態を確認
            ↓
catalog info
  CRS・Geometry・件数・保存先などを確認
            ↓
catalog versions
  version履歴を確認

これにより、control.duckdbを直接操作せず、Docker ComposeだけでDataset Catalogの登録と確認を完結できます。


15. Step 1のテスト

Step 0で作った確認項目は残したまま、Step 1のテストを追加します。

コード形式
型チェック
Unit Test
構成ルールのテスト
    +
DuckDB migration
transaction / rollback
constraint
GeoPackage実ファイル
GeoJSON実ファイル
GeoParquet実ファイル
CRSなしデータ
複数layer GeoPackage
copy後の再確認
Catalog CLI
  ├─ register
  ├─ list
  ├─ info
  └─ versions

Docker Composeのtest serviceで、Ruff、mypy、pytestをまとめて実行します。

docker compose --profile test run --rm test

Step 1では、テスト件数そのものよりも、STEP1_COMPLETION=PASSまで一連の確認が通ることを重視します。

また、catalog listはRepository、Service、CLIの各層を通るため、単にCLIの文字列だけを追加した状態にならないようにテストします。


16. Step 1の完了条件

Step 2へ進む前に、少なくとも次を確認します。

  • Docker Compose clean build
  • UV_FROZEN=1
  • Ruff / mypy
  • Unit Test
  • DuckDB migration / rollback
  • Dataset / DatasetVersion transaction
  • GeoPackage / GeoJSON / GeoParquet
  • CRS / Geometry validation
  • GDAL Driver
  • Architecture Test
  • catalog register
  • catalog list
  • catalog info
  • catalog versions
  • STEP1_COMPLETION=PASS

テストはStep 1で終わりではありません。Step 2以降でもこの確認をそのまま回帰テストとして残します。


17. 次のStep

次はStep 2です。

Step 1で作ったDataset CatalogをFastMCPへ接続します。

例えば、

gis_list_datasets
gis_describe_dataset
gis_list_versions
gis_runtime_status

のようなTool / Resourceを追加し、FastMCPのin-memory testとStreamable HTTP接続確認まで進めます。

GISファイルを読む処理そのものは作り直さず、今回作ったServiceをそのまま呼び出す構成にします。


参考資料


1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?