社内の Excel や CSV を AI に分析させようとして、こんな壁にぶつかったことはないでしょうか。
- 数万行の CSV を LLM に読ませたら、平均値の計算が微妙に合わない
- 「1 実施済」のようなコード値の意味を勝手に推測されて、集計がズレる
- 空欄を「0」と解釈されて、率の計算が全部おかしくなる
- そもそもデータ量が多くて、コンテキストに入り切らない
この「LLM にデータを直接読ませると壊れる」問題に対する、実装つきの回答がデジタル庁の Tech ブログに出ていました。行政手続等の棚卸調査結果(約 75,000 件)を MCP(Model Context Protocol)経由で自然言語分析できるようにした技術検証実装が、GitHub で MIT ライセンス公開されています。
面白いのは、対象が行政データであることよりも、設計思想がそのまま自社データに転用できる点です。この記事では次の 3 点を追っていきます。
- LLM とサーバーの役割分担をどこで切ったか
dataset.yamlによる意味定義の具体的な書き方- 自社データに持ち込むときに引っかかる注意点
何を作ったのか — 行政手続 75,000 件を MCP で自然言語分析
対象データは、デジタル庁が公表している行政手続等の棚卸調査(令和 6 年度悉皆調査)の結果です。配布されている CSV は 38 列 × 約 75,000 行あります。
これを「引越しに関する手続を出して」「府省庁別のオンライン化率を比較して」のように、対話で分析できるようにした技術検証実装です。Claude Desktop や ChatGPT などの MCP 対応クライアントから接続して使います。
執筆したのはデジタル庁プロダクトマネージャーの土岐竜一氏です。デジタル庁公式 X アカウントの告知投稿は 2026 年 8 月時点で約 197 万ビュー・いいね 4,800 と、かなり広く読まれています。
最大のポイント — LLM に計算させない
この実装の核心は、Tech ブログの次の一文に集約されています。
本実装のポイントは、約75,000件のデータをLLMに直接読み込ませて分析させるのではなく、LLMには検索・集計条件の指定を担わせ、実際の計算をMCPサーバー側で実行する点です。
集計の算術処理がサーバー側で完結するため、算術ミスもコード値の誤用も起きにくく、結果の検証もしやすくなります。この役割分担だけでも、自社データに AI をつなぐときの設計指針になります。
全体像は次のとおりです。AI クライアントは検索・集計の条件だけを送り、サーバーが dataset.yaml の意味定義と Parquet の実データを突き合わせて計算し、結果に出典と品質情報を添えて返します。
MCP ツールは 4 つだけ — Discovery 層と Data 層の分離
list_datasets/inspect_dataset— Discovery 層(データの存在と構造を把握)query_records/summarize_records— Data 層(検索と集計)
エージェントは「一覧 → 構造把握 → 検索・集計」の順で辿ります。ツールを増やさず流れで縛る設計です。
summarize_records の集計メトリクスは ["count", "sum:総手続件数", "avg:オンライン率"] のように指定します。このツールは集計後フィルタの having や、セミコロン区切りフィールドを展開する explode も備えています。
AI の誤読を防ぐ 5 つの工夫
1. dataset.yaml で意味を定義する
項目の意味・許容値(codelist)・欠損の扱いを機械可読で書きます。
参考にしたのは国際標準 SDMX です。SDMX は統計データとメタデータの交換に関する国際標準で、その DSD(Data Structure Definition)は項目を Dimension・Measure・Attribute に分け、取りうる値を Codelist で定義します。この考え方を下敷きにした独自の軽量形式が dataset.yaml です。いわば、AI に読ませるための軽量なセマンティックレイヤーにあたります。
実際の dataset.yaml から抜粋するとこうなっています。
# 分析軸: 許容値を明示 → 不正な値での絞り込みを防止
- role: dim
name: 手続類型
codelist:
- 1 申請等: 申請、届出その他の法令の規定に基づき行政機関等に対して行われる通知
- 2-1 申請等に基づく処分通知等: 上記1の申請等に基づき、処分の通知その他の法令の規定により行政機関等が行う通知
- 3 縦覧等: 書面等又は電磁的記録を、縦覧若しくは閲覧に供すること又は謄写させること
role には dim(分析軸)、measure(数値項目)、attr(属性)、id があります。検索・集計に使う分類項目を便宜上 dim として定義する、という整理です。
2. 欠損の意味を明示する
数値項目には notes で解釈上の注意を書きます。
- role: measure
name: オンライン手続件数
notes:
- null(欠損)は「件数不明」を意味する。0 は基本的に「オンライン手続なし」だが、地方等で件数集計が困難な一部の手続では 0 と記録されている場合がある。
- 件数は有効数字1〜2桁程度の概数であり、一部試算値を含む。
この notes が効くのは、query_records や summarize_records の結果にも自動で付いてくる点です。「概数である」という但し書きが常に結果に随伴するので、AI が概数を精密値として引用しにくくなります。
「空欄を 0 と解釈されて率の計算がおかしくなる」という冒頭の問題への、直接的な対処です。
3. 計算式をサーバー側に置く
「オンライン率」のような指標の定義は computed_measures として YAML に書いておき、サーバーが計算します。
computed_measures:
- name: オンライン率
mode: count_where
condition_field: オンライン化の実施状況
condition_values:
- 1 実施済
desc: オンライン化実施済の手続種類数 / 全手続種類数
指標の定義が一箇所に集約され、加重平均などもサーバーが正しく計算します。LLM が毎回「オンライン率とは何か」を解釈し直す余地がなくなります。
4. 品質情報を動的に返す
inspect_dataset ツールは、YAML の定義に加えて実データから充填率(fill_rate)・数値統計(numeric_stats)・品質要約(quality_summary)を算出して返します。SDMX が品質や方法論の情報を Reference Metadata として扱う考え方を参考にしたものです。
AI が「このデータはどこまで信用できるか」を判断できるようになります。
5. 項目名のあいまい照合を「限定的に」許す
ここが実装として一番示唆に富む部分です。
LLM が似た漢字を混同して出力するケース(例:「懸」→「憸」)が実際に発生したため、項目名の照合を 完全一致 → Unicode NFKC 正規化 → Python 標準ライブラリ difflib による近似一致 の 3 段階で行っています。
ただし近似一致は無条件に許すのではなく、次のガードがかかっています。
- データ値には適用せず、フィールド名という閉じた語彙に限定する
- 類似度しきい値と、候補 1 件のガードを設ける
- 補正した事実を
resolved_fieldsとしてレスポンスに明示する - それでも救えない入力はエラーとし、類似候補と使用例を返す
「AI の揺れを吸収する」と「勝手に直して間違える」の境界を、適用範囲を絞ることで引いています。閉じた語彙の中でだけファジーマッチを許し、直したことを必ず申告する — この発想は、自前の MCP サーバーを書くときにも移植しやすいはずです。
実装面で効いている 4 つの工夫
Parquet 採用と遅延ロード
CSV ではなく列指向の圧縮バイナリ形式にすることで、約 75,000 行 × 39 列(Parquet 変換後の列数)が約 3MB に収まります。
さらに Parquet はファイル末尾のフッターにスキーマや行数などのメタデータを保持しています。この性質を活かし、サーバー起動時はメタデータだけを読んで list_datasets に必要なレコード数を確定させ、データ本体はクエリが来た時点で初めてメモリにロードします。
列指向でトークン削減
応答を list[dict] ではなく {"columns": [...], "rows": [[...]]} 形式にして、レコードごとにキー名が繰り返される無駄を消しています。
実装内のツール定義でも、出力形式は「columnar 形式: columns(配列) + rows(配列の配列)。各行は columns の順に値が並ぶ」と明記されています。応答の骨格はこういう形です。
{
// 各行は columns の順に値が並ぶ(キー名は繰り返されない)
"columns": ["所管府省庁", "count"],
"rows": [["厚生労働省", ...], ["国土交通省", ...]],
// 集計結果に自動で随伴するメタデータ
"notes": { /* フィールドごとの解釈上の注意 */ },
"quality_summary": { /* 充填率などの品質情報 */ },
"provenance": { /* 出典・取得日 */ }
}
地味ですが、数百行返すときには効きます。
入力の型揺れを吸収
ツールの docstring で型を指定していても、LLM は引数を dict で送ったり JSON 文字列で送ったりします。単一値を "field" と ["field"] のどちらで送るかも一定しません。サーバー側で複数の入力形式を受け入れて正規化しています。
MCP 依存を server.py に閉じる
クエリ実行やレスポンス構築のロジックは MCP / FastMCP に依存しないモジュールに集約し、MCP 固有の整形は server.py だけに閉じています。
おかげで CLI・HTTP・stdio で同じロジックを再利用でき、MCP サーバーを起動せずにユニットテストも回せます。分析機能そのものが MCP なしでも成立する構成になっている、というのが要点です。
MCP Apps でチャット内にグラフが出る
MCP サーバーがチャット UI 内に直接グラフや表を表示できる公式拡張「MCP Apps」を試作採用しています。集計軸の数でチャートが変わります。
- 1 軸(例:府省庁別):円グラフ・棒グラフ・ツリーマップ・テーブル
- 2 軸(例:府省庁 × 手続類型):積上げ棒グラフ・ヒートマップ・ツリーマップ・ネットワーク・テーブル
- 3 軸以上:ツリーマップ・サンキーダイアグラム・テーブル
UI を持たないエージェントからは、同じツールをテキスト API として使えます。
なお MCP は 2025 年 12 月に Anthropic から Linux Foundation 傘下の Agentic AI Foundation へ寄贈されました。MCP Apps は、2026 年 7 月 28 日公開の改訂で導入された拡張(Extensions)の枠組みのもとで規定される公式拡張のひとつです。
セットアップ手順 — setup.sh と Claude Code での接続
Python 3.10 以上が必要です。setup.sh が依存パッケージの導入からデータ取得までを一括で行います。
git clone https://github.com/digital-go-jp/administrative-procedures-mcp.git
cd administrative-procedures-mcp
./setup.sh
Claude Code の場合はリポジトリに .mcp.json が同梱されているため、クローンしたディレクトリで起動するだけで接続されます。追加設定は不要です。
LLM を使わず CLI から叩くこともできます。setup.sh は依存を仮想環境に入れるため、uv がある環境では uv run 経由で実行します(セットアップ完了時に環境に応じたコマンドが案内されます)。
uv run apcli summarize procedures-survey-r6 \
--group-by '["所管府省庁"]' \
--metrics '["count", "avg:オンライン率"]'
MCP 対応のチャット UI が無くても、apcli preview で Chrome の内蔵 AI(Prompt API)を使い、API キーなしでブラウザだけで動作を確認できます。
uv run apcli preview --port 8765
ただし内蔵 AI は単純なクエリ向けで、複数条件の組み合わせや複雑なフィルタは期待と異なることがあります。本格的な分析には Claude などフル機能の LLM を使ってください。Prompt API 自体も Chrome 138 以降、Microsoft Edge Canary/Dev 138.0.3309.2 以降という前提があります。
データはリポジトリに同梱されない
調査結果データはリポジトリに含まれていません。公表後に修正や更新が入るためで、apcli fetch が配布ページから最新版を取得して Parquet に変換します。
取得日は .fetch.json に記録され、ツール応答の provenance.fetched_at に出力されます。これはデータの基準時点を表す as_of_date とは別項目である点に注意してください。
本番投入する前に読むべき注意書き
README に明記されていますが、これは技術検証を目的としたサンプルコードです。ローカルまたは単一利用者向けの実験用と位置づけられており、複数利用者を収容する本番サービスは対象としていません。
特に押さえておきたいのは次の 3 点です。
- HTTP transport 自体に認証・認可・レート制限・監査ログは実装されていない。 外部から到達可能にする場合は、認証と通信制限を備えたリバースプロキシを前段に置く必要があります。
- 既定では
127.0.0.1にのみバインドされる。 同一ホスト上のリバースプロキシで HTTPS 終端して転送する構成なら、このままで問題ありません。コンテナなどでプロセス自体を外部公開する場合は、バインド先を明示的に指定することになります。 dataset.yamlは信頼済みの設定ファイルとして扱われる。 出所不明の YAML に対してapcli fetchやapcli addを実行してはいけません。
出力が政府の公式見解ではないこと、搭載データが調査時点の情報であることも免責事項に書かれています。
本質は「MCP で公開する」ではなく「意味定義を添える」こと
公共データを MCP で公開するだけでは足りず、意味定義と品質情報をセットで渡さないと AI は誤読する。この一点を、実装と実際の失敗事例つきで示したのが価値だと思います。
デジタル庁の記事自体も、扱っているのは AI-ready 化の一要素(意味定義と品質情報の提供)だと範囲を明示しています。出典・来歴、バージョン管理、ライセンス、ガバナンスを含むデータ基盤全体を実現するものではない、という但し書きです。
そのうえで、dataset.yaml + Parquet という枠組み自体は自社データにも持ち込めます。新しいデータセットの追加は YAML と Parquet を datasets/ に置くだけで、サーバーやツールのコード変更は不要という構成です。
社内の数万行の Excel を AI に読ませて困っている人ほど、設計の参考になるはずです。
