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

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

| 区分 | 入れたもの | 考え方 |
|---|---|---|
| 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 は確認なしで走り、投入で実行されるコマンドの一覧が返ってきます。

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

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

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

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 が表示されます。

4-3. エージェント自身による確認
「MCP で行数を確認して」と頼むと、Claude は MCP の SQL 実行ツールで年別の行数を取り、docs/DEPLOY.md の投入記録と突き合わせて一致を報告します。

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

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

拒否されただけでなく、エージェントは投げ方を変えてどの層が止めたかを切り分けました。
DELETEは、Toolbox が文を判定するより前に、サービスアカウントに書き込み権限が無いことで止まった(IAM の 403)BEGIN … ENDで包んだ文は、Toolbox の読み取り専用設定で止まった
書き込みを止める層が、サービスアカウントの権限と Toolbox の設定の2つあり、さらに外側に settings の deny があります。1つの設定を間違えても、ほかの層で止まる作りです。
5. 題材:データ取得と BigQuery 投入
5-1. 3つのコマンド

./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つのテーブル


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

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日後で、データセットの既定値が効いていたものです。放置すると、ある日アプリが黙って動かなくなります。

期限を外すのは 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
