東京都練馬区のオープンデータをAIクライアントから検索しやすくするMCPサーバーです。Claude、Cursor、ChatGPT系クライアントなどから、練馬区の公共施設、AED設置箇所、避難所・防災関連施設、公園、行政手続情報、ごみ・資源収集曜日、新着情報を検索できるようにします。
練馬区について広く検索する入口toolです。公共施設、AED、避難所、公園、ごみ・資源収集曜日、公式ニュース、オープンデータのデータセット候補をまとめて検索します。ChatGPTなどのAIクライアントで、ユーザーが「練馬区」「練馬」「区役所」「施設」「防災」「ごみの日」「行政手続」「文化財」「人口」「オープンデータ」などについて質問したとき、まず候補にしやすいtoolです。
引数:
keyword?: stringarea?: stringcategory?: stringincludeOpenDataRows?: booleanlimit?: number
練馬区オープンデータの「公共施設一覧」を検索します。区役所、図書館、地域集会所、区民館、体育館、文化施設、住所、電話番号、施設分類を調べる質問に向いています。
引数:
keyword?: stringcategory?: stringarea?: stringlimit?: number
練馬区オープンデータの「AED設置箇所一覧」を検索します。AED、救命設備、設置施設、住所、地域、公共施設内のAEDを探す質問に向いています。
引数:
keyword?: stringarea?: stringlimit?: number
練馬区オープンデータの「指定緊急避難場所一覧」「避難拠点」を検索します。避難所、避難場所、防災、地震、火災、洪水、災害時の行き先を探す質問に向いています。
引数:
keyword?: stringarea?: stringlimit?: number
避難所・防災関連施設を一覧表示します。収容人数順や名称順で並び替えできます。
引数:
area?: stringdisasterType?: stringsortBy?: "name" | "capacity"sortOrder?: "asc" | "desc"limit?: number
公園を検索します。初期版では、公共施設一覧から公園相当データを抽出したJSONを使います。
引数:
keyword?: stringarea?: stringlimit?: number
練馬区オープンデータサイトから取り込んだ全CSVを横断検索します。公共施設一覧、AED設置箇所一覧、行政手続情報、人口統計、文化財、防災、子育て、教育、産業、まちづくりなど、専用toolがないデータセットを広く調べる質問に向いています。
引数:
keyword?: stringcategory?: stringdataset?: stringlimit?: number
練馬区オープンデータサイトから取り込んだデータセット一覧を検索します。練馬について調べるときに、人口、施設、防災、子育て、教育、文化財、行政手続、産業、まちづくりなど、どのデータセットが存在するか探す質問に向いています。
引数:
keyword?: stringcategory?: stringsortBy?: "title" | "rowCount" | "updatedAt"sortOrder?: "asc" | "desc"limit?: number
練馬区オープンデータサイトから取り込んだデータセット一覧を表示します。利用可能なデータ、カテゴリ別のデータ、行数が多いデータ、更新されたデータを把握したい質問に向いています。
引数:
category?: stringsortBy?: "title" | "rowCount" | "updatedAt"sortOrder?: "asc" | "desc"limit?: number
練馬区オープンデータの指定データセットからCSV行をページング取得します。search_open_data は本文検索用ですが、このtoolは「世帯と人口(総括表)」の過去月分、人口推移、統計時系列、文化財一覧など、検索語に依存せずデータセット全体を分析したい質問に向いています。
引数:
dataset: stringfileTitle?: stringfileIndex?: numberoffset?: numberlimit?: number
例:
get_open_data_rows(dataset="世帯と人口(総括表)", limit=1000)nextOffsetが返った場合はoffsetに指定して続きの行を取得します。
練馬区公式サイトの新着情報RSSを検索します。
引数:
keyword?: stringcategory?: stringfrom?: stringto?: stringlimit?: number
練馬区公式サイトの新着情報RSSを新しい順に一覧表示します。
引数:
category?: stringfrom?: stringto?: stringlimit?: number
練馬区公式サイトの「地域別収集曜日一覧」を検索します。ごみの日、可燃ごみ、不燃ごみ、容器包装プラスチック、古紙、びん、缶、ペットボトル、町名、丁目、収集カレンダーPDFを調べる質問に向いています。
引数:
keyword?: stringtown?: stringdistrict?: stringday?: stringwasteType?: stringlimit?: number
練馬区オープンデータの「行政手続情報」を検索します。申請、届出、証明書、相談、必要書類、担当課、担当係、窓口、場所、電話番号、電子申請URLを調べる質問に向いています。
引数:
keyword?: stringdepartment?: stringlocation?: stringhasOnlineApplication?: booleanlimit?: number
練馬区オープンデータの「行政手続情報」から、場所・担当課・担当係・電話番号ごとに手続き窓口候補を検索します。どこで申請するか、証明書や相談の窓口、担当窓口、本庁舎や石神井庁舎の窓口を調べる質問に向いています。
引数:
keyword?: stringdepartment?: stringlocation?: stringlimit?: number
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/healthMCP endpoint:
http://localhost:3000/mcp
キャッシュ状態:
curl http://localhost:3000/open-data/cacheHerokuでは Procfile のWebプロセスが npm start を実行します。
npm run build
heroku create
git push heroku mainHeroku上のMCP endpoint:
https://<your-app-name>.herokuapp.com/mcp
PORT はHerokuが設定するため、アプリケーション側では process.env.PORT || 3000 をlistenします。
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-dataGCSキャッシュを別環境で更新した後、本番サーバーのメモリ内キャッシュだけをクリアする場合:
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国際
- https://www.city.nerima.tokyo.jp/kusei/tokei/opendata/opendatasite/index.html
- https://creativecommons.org/licenses/by/4.0/deed.ja
練馬区オープンデータサイトで公開しているコンテンツは、特段の定めがあるものを除き、クリエイティブ・コモンズ・ライセンス表示4.0国際の下に提供されています。改変して利用する場合は、編集・加工等を行ったことを明記してください。
AED・避難所・防災関連情報は命に関わる可能性があります。本サーバーの結果だけに依存せず、最新情報は必ず練馬区公式情報を確認してください。
初期版ではMCP endpointは認証なしで公開できますが、公開運用時はレート制限や監視を検討してください。キャッシュ更新エンドポイント /tasks/import-open-data と /tasks/clear-cache は IMPORT_TOKEN が必要です。Herokuのファイルシステムは永続保存に使わないでください。