e-StatのMCPサーバを作ったのですが、その過程で試行錯誤したことを書いておこうと思います。
Non-syntacticな変数名
まずサンプルプログラムとして公開されているKotlin MCP Weather STDIO Serverをベースにして作り始めることにしました。このサンプルではAPIから受け取るレスポンスがJSON形式になっていましたので、e-Stat APIも初めは同様にJSON形式で受け取るように作りました。ところがe-Stat APIのJSON形式は要素にアクセスする変数名が@
や$
で始まる少し変な仕様をしています(参考: 公式のAPI仕様)。このままだとプログラミングしづらかったので、JSONはやめてXML形式で受け取ることにしました。これならまともな変数名で受け取ることができます。
省略されるタグがある
APIのレスポンスには仕様で定められた全てのタグが含まれるわけではなく、省略されるタグもあります。例えば統計表情報取得のクエリに対しては見つかった統計表情報がDATALIST_INF
タグで返されますが、統計表が1つも見つからなかったときはDATALIST_INF
タグ自体がレスポンスに含まれません。サンプルプログラムは全てのタグが含まれる前提で書かれていましたので最初はこの対処法が分からなかったのですが、レスポンスを受け取るdata class
の引数にデフォルト値を与えておけばよいことが分かり解決しました。
data class StatsList(
val result: RESULT, val parameter: PARAMETER, val dataListInf: DATALIST_INF? = null
)
AIに渡すデータ形式
XML形式で受け取ったデータをそのままAIに渡すのは伝達効率が悪いかも知れないと思って、どんな形式がいいかGeminiに聞いてみたらJSON形式がいいとのことでした。それなら最初からJSON形式で受け取っておいてそのまま渡せばよかったかもと思いましたが、実際のe-Stat APIのレスポンスは非常に多くの情報が入っているのでAIに渡すコンテキストとしては長過ぎるという問題があります。そこで、AIに渡すべき情報を取捨選択した上で改めてJSON形式にフォーマットするような処理を書きました。KotlinのListをtoStringするとほぼJSONに近い形になるのでそれを利用しています。(Kotlinの仕様依存…)
e-Stat APIに関する話
surveyYears
は経常調査には登録されていない
統計表情報取得のクエリが動くようになって最初に直面した問題は、古い調査年の統計表や参考表などの重要度が低いものが大量にある場合に検索結果が大きくなってしまうということでした。これに対処するために初めはsurveyYears
に直近の3年間を常に渡すようにしてみたのですが、その状態で総務省の家計調査の統計表を検索したところ、検査結果が1つも出て来なくなってしまいました。不思議に思ってe-Statの元のデータを見てみたら、家計調査の統計表にはsurveyYears
(調査年月)が登録されていませんでした。下の画面で確認できます。
家計調査の統計表には複数の年月の結果が並んでいるため1つの年月だけを登録するのが適さないことと、そういう場合に年月の範囲をセットすることもおそらくできないことから登録なしになっているようです。このような統計表は統計データ取得クエリで時系列情報として特定の調査年月を選ぶことはできますが、ほしい年月が含まれているかどうかは統計表メタ情報を見てみるまで分かりません。調査実施側から見れば自然な整理の仕方かも知れませんが、利用者としては経常調査と周期調査で調査年月の登録に違いがあるのは分かりにくいですね。
note
の内容が使いづらい
note
は統計表の中に現れる数値以外の記号に対する説明に使われるタグで、よくあるのは-
(該当数値がない)やX
(秘匿された数値)のような説明です。ところが統計表の中にはnote
にあまり有用でなさそうな情報を含んでいるものもあります。例えば厚生労働省の令和5年国民生活基礎調査 世帯 02 基本項目(第18表~第33表) 世帯数,世帯種・市郡・世帯類型別の統計表では、note
に次のような内容が含まれています。(一部抜粋です)
"NOTE": [
{
"@char": " ・",
"$": "統計項目のあり得ない場合"
},
{
"@char": " ・",
"$": "H22の内H3265csv内データ[ ・]"
},
{
"@char": "* 211.9",
"$": "H21のH2011.csv内データ [* 211.9]"
},
{
"@char": "-",
"$": "計数のない場合"
},
{
"@char": ".",
"$": "."
},
{
"@char": "0",
"$": "推計数が表章単位の2分の1未満の場合"
},
{
"@char": "0.0",
"$": "比率が微小(0.05未満)の場合"
},
{
"@char": "…",
"$": "計数不明又は計数を表章することが不適切な場合"
},
{
"@char": "△",
"$": "減少数(率)の場合"
},
{
"@char": " ・",
"$": "統計項目のあり得ない場合"
},
{
"@char": " ・",
"$": " ・"
},
{
"@char": " ・",
"$": " ・"
},
{
"@char": "・",
"$": "全角「・」"
},
{
"@char": "・・・",
"$": "統計項目のあり得ない場合"
},
{
"@char": "注:4)",
"$": "注:4)"
},
{
"@char": "注:5)",
"$": "注:5)"
}
]
“ ・”のようにスペースが含まれる記号があったり、説明が書かれていない記号があったりしていて実際に使われているのか疑問が残る内容です。このような例もあるので、MCPサーバではとりあえずnote
は利用しないようにしています。
仕様にないタグがある
統計データ取得のクエリにおいて統計表メタ情報のパラメータを間違えてしまうと「正常に終了しましたが、該当データはありませんでした。」というメッセージが返ってきますが、このレスポンスの場合のみ、TABLE_INF
タグになぜかreleaseCount
という仕様にないタグが含まれています。MCPサーバではあらかじめ全てのタグを定義しておく必要があるため、これに対応するために便宜的にreleaseCount
というタグを定義しました。
collectArea
を指定すると家計調査を検索できない
collectArea
は集計地域区分を指定するパラメータで、1が全国、2が都道府県、3が市区町村を表します。これだけ見るとどの統計表も1から3のいずれかに設定されているように思えますが、実際には集計地域区分が設定されていない統計表もあるようです。家計調査の統計表はcollectArea
を1から3のどれに設定しても検索結果が0件になってしまいました。そのような例もありますので、MCPサーバではとりあえずcollectArea
は利用しないようにしています。
ユーザ目線も大事
MCPサーバを作ったことでe-Stat APIを本格的に利用することができ、上のような面白い発見もあって勉強になりました。利用者にならないとなかなか見えてこないことも多いので、何らかのサービスを提供する際にはユーザ目線を大事にすることを忘れないようにしたいなと思います。