Skip to content

code4nerima/nerima-open-data-mcp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

21 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

nerima-open-data-mcp

東京都練馬区のオープンデータをAIクライアントから検索しやすくするMCPサーバーです。Claude、Cursor、ChatGPT系クライアントなどから、練馬区の公共施設、AED設置箇所、避難所・防災関連施設、公園、行政手続情報、ごみ・資源収集曜日、新着情報を検索できるようにします。

Tools

search_nerima

練馬区について広く検索する入口toolです。公共施設、AED、避難所、公園、ごみ・資源収集曜日、公式ニュース、オープンデータのデータセット候補をまとめて検索します。ChatGPTなどのAIクライアントで、ユーザーが「練馬区」「練馬」「区役所」「施設」「防災」「ごみの日」「行政手続」「文化財」「人口」「オープンデータ」などについて質問したとき、まず候補にしやすいtoolです。

引数:

  • keyword?: string
  • area?: string
  • category?: string
  • includeOpenDataRows?: boolean
  • limit?: number

search_facilities

練馬区オープンデータの「公共施設一覧」を検索します。区役所、図書館、地域集会所、区民館、体育館、文化施設、住所、電話番号、施設分類を調べる質問に向いています。

引数:

  • keyword?: string
  • category?: string
  • area?: string
  • limit?: number

search_aed

練馬区オープンデータの「AED設置箇所一覧」を検索します。AED、救命設備、設置施設、住所、地域、公共施設内のAEDを探す質問に向いています。

引数:

  • keyword?: string
  • area?: string
  • limit?: number

search_shelters

練馬区オープンデータの「指定緊急避難場所一覧」「避難拠点」を検索します。避難所、避難場所、防災、地震、火災、洪水、災害時の行き先を探す質問に向いています。

引数:

  • keyword?: string
  • area?: string
  • limit?: number

list_shelters

避難所・防災関連施設を一覧表示します。収容人数順や名称順で並び替えできます。

引数:

  • area?: string
  • disasterType?: string
  • sortBy?: "name" | "capacity"
  • sortOrder?: "asc" | "desc"
  • limit?: number

search_parks

公園を検索します。初期版では、公共施設一覧から公園相当データを抽出したJSONを使います。

引数:

  • keyword?: string
  • area?: string
  • limit?: number

search_open_data

練馬区オープンデータサイトから取り込んだ全CSVを横断検索します。公共施設一覧、AED設置箇所一覧、行政手続情報、人口統計、文化財、防災、子育て、教育、産業、まちづくりなど、専用toolがないデータセットを広く調べる質問に向いています。

引数:

  • keyword?: string
  • category?: string
  • dataset?: string
  • limit?: number

search_open_data_datasets

練馬区オープンデータサイトから取り込んだデータセット一覧を検索します。練馬について調べるときに、人口、施設、防災、子育て、教育、文化財、行政手続、産業、まちづくりなど、どのデータセットが存在するか探す質問に向いています。

引数:

  • keyword?: string
  • category?: string
  • sortBy?: "title" | "rowCount" | "updatedAt"
  • sortOrder?: "asc" | "desc"
  • limit?: number

list_open_data_datasets

練馬区オープンデータサイトから取り込んだデータセット一覧を表示します。利用可能なデータ、カテゴリ別のデータ、行数が多いデータ、更新されたデータを把握したい質問に向いています。

引数:

  • category?: string
  • sortBy?: "title" | "rowCount" | "updatedAt"
  • sortOrder?: "asc" | "desc"
  • limit?: number

get_open_data_rows

練馬区オープンデータの指定データセットからCSV行をページング取得します。search_open_data は本文検索用ですが、このtoolは「世帯と人口(総括表)」の過去月分、人口推移、統計時系列、文化財一覧など、検索語に依存せずデータセット全体を分析したい質問に向いています。

引数:

  • dataset: string
  • fileTitle?: string
  • fileIndex?: number
  • offset?: number
  • limit?: number

例:

  • get_open_data_rows(dataset="世帯と人口(総括表)", limit=1000)
  • nextOffset が返った場合は offset に指定して続きの行を取得します。

search_news

練馬区公式サイトの新着情報RSSを検索します。

引数:

  • keyword?: string
  • category?: string
  • from?: string
  • to?: string
  • limit?: number

list_recent_news

練馬区公式サイトの新着情報RSSを新しい順に一覧表示します。

引数:

  • category?: string
  • from?: string
  • to?: string
  • limit?: number

search_garbage_collection

練馬区公式サイトの「地域別収集曜日一覧」を検索します。ごみの日、可燃ごみ、不燃ごみ、容器包装プラスチック、古紙、びん、缶、ペットボトル、町名、丁目、収集カレンダーPDFを調べる質問に向いています。

引数:

  • keyword?: string
  • town?: string
  • district?: string
  • day?: string
  • wasteType?: string
  • limit?: number

search_procedures

練馬区オープンデータの「行政手続情報」を検索します。申請、届出、証明書、相談、必要書類、担当課、担当係、窓口、場所、電話番号、電子申請URLを調べる質問に向いています。

引数:

  • keyword?: string
  • department?: string
  • location?: string
  • hasOnlineApplication?: boolean
  • limit?: number

search_service_counters

練馬区オープンデータの「行政手続情報」から、場所・担当課・担当係・電話番号ごとに手続き窓口候補を検索します。どこで申請するか、証明書や相談の窓口、担当窓口、本庁舎や石神井庁舎の窓口を調べる質問に向いています。

引数:

  • keyword?: string
  • department?: string
  • location?: string
  • limit?: number

get_dataset_stats

GCSキャッシュと主要データセットの統計情報を返します。避難所については、指定緊急避難場所と避難拠点を合算した総件数、収容人数ありの件数、最大収容人数、収容人数上位10件を返します。

引数はありません。

ローカル起動

.env を編集して、GCSを使う場合は GCS_BUCKET とGoogle Cloudのサービスアカウント認証情報を設定してください。GCS未設定の場合は、少数サンプルデータで起動します。

サービスアカウントJSONはBase64化して GOOGLE_APPLICATION_CREDENTIALS_BASE64 に設定する方法を推奨します。

base64 -i service-account.json | tr -d '\n'
npm install
npm run build
npm run dev

ヘルスチェック:

curl http://localhost:3000/health

MCP endpoint:

http://localhost:3000/mcp

キャッシュ状態:

curl http://localhost:3000/open-data/cache

Herokuデプロイ

Herokuでは Procfile のWebプロセスが npm start を実行します。

npm run build
heroku create
git push heroku main

Heroku上のMCP endpoint:

https://<your-app-name>.herokuapp.com/mcp

PORT はHerokuが設定するため、アプリケーション側では process.env.PORT || 3000 をlistenします。

Heroku Scheduler

Herokuのファイルシステムは永続ではないため、公式CSVから生成したキャッシュはGoogle Cloud Storageに保存します。SchedulerはWeb dynoの保護付きエンドポイントを呼び出し、公式CSVの再取得とGCSキャッシュ更新を行わせます。

設定する環境変数:

heroku config:set IMPORT_TOKEN=<long-random-token>
heroku config:set APP_BASE_URL=https://<your-app-name>.herokuapp.com
heroku config:set GCS_BUCKET=<bucket-name>
heroku config:set GCS_PREFIX=nerima-open-data/cache
heroku config:set GOOGLE_APPLICATION_CREDENTIALS_BASE64='<base64-encoded-service-account-json>'

GOOGLE_APPLICATION_CREDENTIALS_JSON も利用できますが、Heroku Config VarsではJSON内の改行やクォートが崩れやすいため、Base64形式を推奨します。両方を設定している場合は GOOGLE_APPLICATION_CREDENTIALS_JSON が優先されます。

Heroku Schedulerに登録するコマンド:

npm run scheduler:import

手元でGCSキャッシュを生成して動作確認する場合:

export GCS_BUCKET=<bucket-name>
export GCS_PREFIX=nerima-open-data/cache
export GOOGLE_APPLICATION_CREDENTIALS_BASE64='<base64-encoded-service-account-json>'
npm run build
npm run import:open-data

ローカルでGCSキャッシュを更新し、本番サーバーを再起動せずにメモリ内キャッシュだけクリアする場合:

npm run import:open-data -- --full --notify-production

通知先の既定値は https://nod-mcp.code4nerima.org/tasks/clear-cache です。別のURLや本番専用トークンを使う場合は次を設定します。

export PRODUCTION_APP_BASE_URL=https://nod-mcp.code4nerima.org
export PRODUCTION_IMPORT_TOKEN='<production-import-token>'

通常の npm run import:open-data は差分更新です。既存の catalog.json と新しいオープンデータ一覧を比較し、データセットID、タイトル、分類、最終更新日、ページURLが変わっていないデータセットは既存JSONを再利用します。全データセットを取り直したい場合は次を実行します。

npm run import:open-data -- --full

外部スケジューラーから直接呼ぶ場合:

curl -X POST \
  -H "Authorization: Bearer $IMPORT_TOKEN" \
  https://<your-app-name>.herokuapp.com/tasks/import-open-data

GCSキャッシュを別環境で更新した後、本番サーバーのメモリ内キャッシュだけをクリアする場合:

curl -X POST \
  -H "Authorization: Bearer $IMPORT_TOKEN" \
  https://nod-mcp.code4nerima.org/tasks/clear-cache

サーバー経由の通常インポートも差分更新です。フル更新したい場合はJSON bodyで forceRefresh を指定します。

curl -X POST \
  -H "Authorization: Bearer $IMPORT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"forceRefresh":true}' \
  https://<your-app-name>.herokuapp.com/tasks/import-open-data

データ

初期JSONの data/*.json はフォールバックです。通常はGoogle Cloud Storageに保存した公式CSVキャッシュを読み込みます。

公式CSVキャッシュは取得時点の生成物なので、Gitには含めません。Herokuのslugにも含めず、GCSに保存します。

インポート処理は、練馬区オープンデータサイトの「オープンデータ一覧(CSV)」とカテゴリページを起点に、CSV形式で公開されている各データセットページを巡回してページ内のCSVリンクを取得します。取得したCSVは行データを nerima-open-data/cache/dataset-files/*/*.json にチャンク保存し、データセット本体の nerima-open-data/cache/datasets/*.json にはメタデータとチャンク参照を保存します。最後に nerima-open-data/cache/catalog.json を更新します。差分更新では未変更データセットの既存JSONを再利用するため、毎回すべてのCSVを取得し直す必要はありません。

CSVチャンクは既定で5,000行単位です。CSVパースはストリーム処理なので、通常は検索時のGCS読み込み回数を減らすため大きめのchunkを推奨します。Herokuのメモリに余裕がない場合だけ小さくします。

heroku config:set CSV_CHUNK_ROW_COUNT=5000

検索時のchunk読み込みは既定で4並列です。GCSへの同時アクセスを抑えたい場合は小さく、検索速度を優先したい場合は大きくできます。

heroku config:set SEARCH_CHUNK_READ_CONCURRENCY=4

公式サイト取得時の一時的な接続断や 429 / 5xx は自動でリトライします。既定は3回、待機は1秒からの線形バックオフです。必要に応じて調整できます。

heroku config:set FETCH_RETRY_COUNT=5
heroku config:set FETCH_RETRY_DELAY_MS=2000

キャッシュ生成結果の例:

  • データセット数: 34
  • CSVファイル数: 531
  • 行数: 84,802
  • 生成JSONの合計サイズ目安: 約252MB

公式サイトの更新により、対象データセット数や行数は変わります。インポート時点の正確な件数は /open-data/cache または get_dataset_stats で確認してください。

専用toolは公式CSVキャッシュを優先し、キャッシュがない場合のみ data/*.json の少数サンプルにフォールバックします。GCSキャッシュは全件を一括ロードせず、manifestを起点に必要なデータセットだけ遅延読み込みします。

Herokuで起動時にGCSキャッシュがない場合だけ公式CSVを取得するには、次を設定します。

heroku config:set AUTO_IMPORT_ON_START=true

起動時取得は公式サイトへのアクセスとCSVパース、GCSアップロードが発生するため、起動時間が長くなります。通常運用ではHeroku Schedulerまたは外部スケジューラーから /tasks/import-open-data を呼び出して更新してください。

GCSに置くオブジェクト:

gs://<bucket>/nerima-open-data/cache/catalog.json
gs://<bucket>/nerima-open-data/cache/datasets/*.json
gs://<bucket>/nerima-open-data/cache/dataset-files/**/*.json
gs://<bucket>/nerima-open-data/cache/rss/news.json
gs://<bucket>/nerima-open-data/cache/garbage/collection-days.json

運用確認

MCP endpointはStreamable HTTPです。SSE方式の /sse は提供していません。

汎用URL:

https://<your-app-name>.herokuapp.com/mcp

このプロジェクトの運用URL:

https://nod-mcp.code4nerima.org/mcp

汎用URLでの疎通確認:

curl -X POST https://<your-app-name>.herokuapp.com/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  --data '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

運用URLでの疎通確認:

curl -X POST https://nod-mcp.code4nerima.org/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  --data '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

MCP tool実行時は、開始・完了・失敗と処理時間を標準出力へ出します。Herokuでは以下で確認できます。

heroku logs --tail

使用・参照する練馬区オープンデータ:

  • 練馬区オープンデータサイト
  • オープンデータ一覧
  • 公共施設一覧
  • AED設置箇所一覧
  • 指定緊急避難場所一覧
  • 避難拠点
  • 公園トイレ一覧

ライセンス・出典

本リポジトリの初期データは、練馬区オープンデータサイトで公開されているコンテンツを参考に、MCPサーバーの初期検証用に整形したものです。

出典:

練馬区オープンデータサイトで公開しているコンテンツは、特段の定めがあるものを除き、クリエイティブ・コモンズ・ライセンス表示4.0国際の下に提供されています。改変して利用する場合は、編集・加工等を行ったことを明記してください。

注意

AED・避難所・防災関連情報は命に関わる可能性があります。本サーバーの結果だけに依存せず、最新情報は必ず練馬区公式情報を確認してください。

初期版ではMCP endpointは認証なしで公開できますが、公開運用時はレート制限や監視を検討してください。キャッシュ更新エンドポイント /tasks/import-open-data/tasks/clear-cacheIMPORT_TOKEN が必要です。Herokuのファイルシステムは永続保存に使わないでください。

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors