Claude Code × Medical Application

【Claude Code】Medicare Part D × Text-to-SQL Part1:環境設定と MCP

日本語 / English

1. はじめに

Part0 で、この連載の主題は agentic coding であり、人が渡すのはコードではなく環境・判断基準・停止条件だと書きました。Part1 はその「環境」を作る回です。

扱うのは2つです。

  • 権限:どのコマンドを確認なしで通し、どれを人が確認し、どれを拒否するか(.claude/settings.json)
  • MCP:BigQuery を、エージェントが自分で確かめられる道具として読み取り専用でつなぐ(.mcp.json)

題材は、CMS の公開 CSV を取得して BigQuery に載せる工程です。載せた結果を、人がコンソールで見て貼るのではなく、エージェント自身が MCP で確かめるところまでをお見せします。

2. 作業環境

2-1. VS Code 拡張の Claude Code

この連載では、Claude Code を VS Code 拡張で使います。左にリポジトリのファイル、右に Claude Code の会話が並び、設定ファイルを開いたまま、それが効いている様子を横で確かめられます。以下の画面はすべてこの構成です。

2-2. ハーネスの置き場所

ハーネスは、リポジトリ直下の .claude/ と .mcp.json に置きます。どちらも git にコミットするので、clone した読者は同じ設定で作業を始められます。

.claude/
├── settings.json         権限(allow / ask / deny)と hook の登録  ← Part1
├── settings.local.json   個人の上書き(gitignore)
├── skills/               繰り返す手順                              ← Part2〜4
└── hooks/                人が覚えていなくても走る検査              ← Part3
.mcp.json                 BigQuery MCP の接続                      ← Part1
mcp/bigquery.tools.yaml   MCP に渡すツールの定義                    ← Part1

3. 権限設計

3-1. allow / ask / deny

.claude/settings.json。上から allow(確認なし)、ask(人が確認)、deny(拒否)
区分 入れたもの 考え方
allow git status/diff/log、ls、pytest、ruff、tsc、./data/load.sh --plan、./deploy.sh --plan、run_eval.py --validate、BigQuery MCP 読むだけ・何も変えないもの。止めると作業が進まない
ask ./data/load.sh --run、./deploy.sh --run、bq(全部)、gcloud run deploy、gcloud builds submit、gcloud storage、gsutil、git push 課金や外部への影響があるもの。実行前に人が読む
deny .env と鍵ファイルの読み取り、gcloud iam、IAM ポリシーの変更、bq rm エージェントに選ばせないもの。人の端末でやる

優先順位は deny > ask > allow です。広い ask の中に狭い allow で例外を作ることはできません。

3-2. 3つの区分の動き

allow:--plan は確認なしで走り、投入で実行されるコマンドの一覧が返ってきます。

allow。./data/load.sh –plan は確認なしで実行され、計画だけが表示される

ask:git push は、実行の直前で必ず止まり、人に確認を求めます。

ask。git push –dry-run の直前で「Allow this bash command?」が出る

ここで No を選ぶと、コマンドは実行されず、断られたことがエージェントに返ります。

ask で No を選んだあと。実行されずに止まる

deny:bq rm は、--help のように何も消さない使い方でも止まります。エージェントは止まった理由を設定ファイルから自分で探し、どの行に当たったかを説明しています。

deny。bq rm –help も拒否され、エージェントが settings.json の該当行を示す

3-3. 設計で決めたこと

  • bq は丸ごと ask にしました。 bq --project_id=X query のように全体の引数が先に来る形は、bq query だけを ask にするパターンでは捕まえられません。読み取りの確認は、次節の MCP に任せます
  • load.sh --run は名指しで ask に入れました。 スクリプトの中で呼ばれる bq load は権限のルールから見えません。見えるのは ./data/load.sh --run という文字列だけです
  • .env の保護は権限だけに頼りません。 deny の Read(./.env) は Read ツールを止めますが、Bash 経由の読み取りまで完全には防げません。鍵の本命の守りは、従来どおり .gitignore と Secret Manager です

文書だけで任せていたときは、CLAUDE.md の「課金が出るコマンドは実行前に見せること」という一文が頼りでした。守られたかどうかは人が毎回目で確かめるしかありません。権限の設定は、その文章の約束を設定による強制に置き換えます。

4. BigQuery MCP の接続

4-1. サーバーの選定

BigQuery を MCP でつなぐ方法を3つ比べました。基準は「読み取り専用にできるか」「課金に上限をかけられるか」「読者が再現できるか」です。

候補 読み取り専用 課金の上限 読者の再現
MCP Toolbox for Databases(Google 公式 OSS) 設定で強制できる 設定できる brew install と .mcp.json だけ
Google のリモート BigQuery MCP 書き込みできるツールも同居 設定できない ローカルの MCP クライアントから認証を通す公式手順が無い
コミュニティ製 実装次第 実装次第 非公式のため除外

MCP Toolbox for Databases を採用しました。課金に上限をかけられたので、MCP のクエリ実行は ask にせず allow に置いています。

4-2. エージェントに見せる範囲

MCP は「何ができるか」をエージェントに渡す仕組みなので、渡す範囲を人が決めます。

  • ツールは4本だけ:データセット一覧、テーブル一覧、テーブル情報、SQL 実行。既製の設定(prebuilt)には予測や対話分析など不要なツールが入るため、mcp/bigquery.tools.yaml で自作しました
  • データセットは partd だけ:他のデータセットへのクエリは Toolbox が拒否します
  • 書き込みは拒否:Toolbox を読み取り専用モードで動かします
  • 権限は専用のサービスアカウント:partd の閲覧とジョブ実行のロールだけを持つ partd-mcp-reader を作り、人のアカウントがそれになりすまして接続します(鍵ファイルは作りません)

接続できると、Claude Code の MCP 一覧にプロジェクトの bigquery が表示されます。

MCP の一覧。.mcp.json で定義した bigquery が Connected

4-3. エージェント自身による確認

「MCP で行数を確認して」と頼むと、Claude は MCP の SQL 実行ツールで年別の行数を取り、docs/DEPLOY.md の投入記録と突き合わせて一致を報告します。

MCP で provider_drug の年別行数を取り、投入記録と照合する

BigQuery 側のジョブ履歴を見ると、同じ時刻のクエリの実行者は人のアカウントではなく、partd-mcp-reader になっています。エージェントは、人が用意した狭い権限の中で動いています。

BigQuery のジョブ履歴。実行者は読み取り専用のサービスアカウント

4-4. 書き込みの拒否

読み取り専用が本当に効いているかは、書き込みを実際に投げて確かめます。消える行が無いように WHERE 1=0 を付けた DELETE を頼みました。

書き込みの拒否。DELETE は IAM で、BEGIN…END で包んだ文は Toolbox の読み取り専用設定で止まる

拒否されただけでなく、エージェントは投げ方を変えてどの層が止めたかを切り分けました。

  • DELETE は、Toolbox が文を判定するより前に、サービスアカウントに書き込み権限が無いことで止まった(IAM の 403)
  • BEGIN … END で包んだ文は、Toolbox の読み取り専用設定で止まった

書き込みを止める層が、サービスアカウントの権限と Toolbox の設定の2つあり、さらに外側に settings の deny があります。1つの設定を間違えても、ほかの層で止まる作りです。

5. 題材:データ取得と BigQuery 投入

5-1. 3つのコマンド

3つのコマンドで、CSV が BigQuery の5テーブルになる
./data/download.sh                     # CMS から CSV を取得
.venv/bin/python data/preprocess.py    # year 付与・型明示・Parquet 化
./data/load.sh --plan                  # 実行されるコマンドを確認(allow)
./data/load.sh --run                   # BigQuery に投入(ask)

--plan と --run を分けてあるのは、3章の権限設計と対になっています。計画の表示は確認なしで通し、自分の GCP プロジェクトに書き込む実行だけを人が確認します。

データそのもの(抑制された空欄を 0 にしない、NPI を文字列で持つ、薬剤名が切り詰められている)の話は、データ説明ページ「Medicare Part D」にまとめました。

5-2. 5つのテーブル

5つのテーブルと、結合に使う列
BigQuery コンソール。partd の5テーブルと、provider_drug の列定義(prscrbr_npi は STRING)

主表3つは共通で、year でパーティション、州と正規化した一般名でクラスタリングしています。

provider_drug のストレージ情報。3年分で約8,000万行

6. 人が決めたことと、実行が見つけたこと

6-1. 走らせる前に人が用意したもの

  • CLAUDE.md と docs/(設計書・データ辞書・DECISIONS)
  • .claude/settings.json の allow / ask / deny
  • MCP 用のサービスアカウントと、なりすましの権限(IAM の操作は人の端末で行う)

6-2. 指示に入れた境界条件

BigQuery を MCP でつなぐ。候補を比べて比較表を出したら、いったん止まること。
接続は専用のサービスアカウントで、partd の閲覧とジョブ実行だけ。サービスアカウントの作成は私がやる。
課金上限をかけられないなら、クエリ実行ツールは ask にすること。
動作確認は、テーブル一覧・スキーマ・年別行数を投入記録と突き合わせ、書き込みが拒否されることまで。

止めどころ(比較表のあと)、人がやる操作(IAM)、判断の分岐(上限がなければ ask)、完了の条件(照合と拒否の確認)を、先に書いています。

6-3. 実行が見つけたこと

表の有効期限。行数を確かめる過程で、5つの表すべてに有効期限が付いていることが分かりました。投入日の60日後で、データセットの既定値が効いていたものです。放置すると、ある日アプリが黙って動かなくなります。

provider_drug の詳細。Table expiration に投入60日後の日付が入っている

期限を外すのは bq update で、ask に入れた操作なので、人が確認して実行します。以後は投入結果の検証手順に「期限の確認」を加えました(Part2 で skill にします)。

deny の副作用。gcloud iam を deny にしたので、読み取りだけの gcloud iam service-accounts list も止まります。サービスアカウントの確認は人の端末でやる運用になりました。意図どおりですが、広い deny には「見ることすらできない」という代償があります。

VS Code 拡張と .mcp.json。VS Code 拡張のセッションでは、プロジェクトの MCP を使うかどうかの確認が出ませんでした。一度ターミナルで claude を起動し、フォルダの信頼と bigquery の利用を承認する必要があります。承認後も、起動済みのセッションには読み込まれず、開き直しが要りました。

6-4. 今回のハーネス

道具 このリポジトリでの実体 文書だけのとき人がやっていたこと
権限と停止条件 .claude/settings.json(allow / ask / deny) 課金が出るコマンドを、実行前に目で確認する
MCP .mcp.json、mcp/bigquery.tools.yaml、partd-mcp-reader 行数・スキーマを BigQuery コンソールで見て貼る
プロジェクト文書 CLAUDE.md、docs/(既存) —

7. まとめ

  • 権限は3つに分ける。 読むだけのものは通し、課金や外部への影響は人が確認し、選ばせたくないものは拒否する
  • MCP は渡す範囲を人が決める。 ツールの数、データセット、書き込みの可否、サービスアカウントの権限まで
  • 拒否は実際に投げて確かめる。 設定を書いただけでは、どの層が止めているのか分からない

任せられる範囲は、事前にどれだけ検証可能にしておいたかで決まります。 今回はその「検証可能」の土台として、エージェントが自分で確かめられる道具と、止まる場所を用意しました。

次のステップ

Part2 では、プロジェクト文書と skill を扱います。CLAUDE.md・設計書・DECISIONS に何を書き分けるか、そして今回手で追った「CSV を BigQuery に載せて確かめる」工程を、呼び出せる手順(skill)にする判断を見ていきます。

出典

  • Anthropic, Claude Code Docs(settings / permissions / mcp)
  • Google, MCP Toolbox for Databases(BigQuery)
  • CMS, Medicare Part D Prescribers - by Provider and Drug / by Provider / by Geography and Drug, Data Dictionary (data.cms.gov)

コードは GitHub で公開しています: github.com/HerzLeben/medicare-partd-text-to-sql