Claude Code × Firebase Crashlytics MCP を、Firestore に触らせずにチームへ配った話
追記(2026/10/30)
コメントで、「許可リストは env を照合しないため、同じ起動コマンドのサーバーを別名・env なしで登録されると、本人の認証で動いてしまう」という抜け道をご指摘いただきました。管理された設定に strictPluginOnlyCustomization: ["mcp"] を追加して塞ぎ、「方針」「設定」「検証結果」「気をつけるところ」を修正しています。
TL;DR
全員が本番 Firestore の編集権限を持っている組織で、Claude Code から Crashlytics だけを見られるようにしました。
- MCP は Crashlytics の閲覧だけができるサービスアカウントで動かす(本人の
firebase loginは使わせない) - 配布は 組織のプラグイン + 管理された設定(Managed settings)。メンバーの作業は鍵ファイルを1つ置くだけ
- Bash・Read・WebFetch など MCP 以外の抜け道は、サンドボックスと
permissions.denyで塞ぐ - 本人が
firebase login済みでも MCP はサービスアカウントで動き、Firestore へのcurlは 403 で止まることを確認済み
課題:便利にしたいが、AI に本番の権限は渡したくない
Crashlytics の issue やスタックトレースを Claude Code に読ませると、「このクラッシュの原因をコードから探して」が一発でできます。公式の Firebase MCP サーバー(firebase-tools に同梱)を使えば、つなぐだけなら数分です。
ただ、うちの組織には事情がありました。
| 状態 | 内容 |
|---|---|
| Claude | Team プラン。社員と業務委託のメンバーが混在 |
| Firebase の権限 | 全員が本番 Firestore の編集権限を持っている(歴史的経緯) |
| 端末管理 | MDM なし |
この状態で素直に MCP をつなぐと、MCP は本人の firebase login の権限で動きます。つまり、Claude 経由で本番 Firestore を読み書きできる状態になります。
「権限を整理すればいい」はその通りなのですが、それはそれで時間がかかります。そこで今回の要件は次の1つに絞りました。
Claude 経由では、絶対に Firestore を触らせない
方針:閲覧専用のサービスアカウント + 経路ごとに塞ぐ
ポイントは「Claude が Firestore に届く経路」を全部洗い出して、経路ごとに塞ぐことです。サンドボックスが守るのは Bash だけなので、1つの設定では塞ぎきれません。
| Claude が Firestore に届く経路 | 塞ぎ方 |
|---|---|
| Firebase MCP |
--only crashlytics + Crashlytics 閲覧者ロールだけのサービスアカウントで認証 |
| ほかの MCP | 管理された設定の MCP 許可リスト(allowedMcpServers) |
同じ起動コマンドの MCP を別名・env なしで登録し、本人の認証で動かす(追記) |
strictPluginOnlyCustomization: ["mcp"] で、プラグイン以外からの MCP 登録を禁止 |
MCP の firebase_login で本人に切り替える |
permissions.deny でツールごと禁止 |
Bash(gcloud、curl など) |
サンドボックス(本人の認証情報を読めない・Firestore に通信できない) |
| Read ツールでトークンを読む |
permissions.deny の Read(...)
|
| WebFetch で API を叩く |
permissions.deny の WebFetch(domain:...)
|
なぜ「権限借用」ではなく「鍵の配布」にしたか
サービスアカウントの使い方には、鍵(JSON)を配る方法と、権限借用(gcloud auth application-default login --impersonate-service-account)の2つがあります。一般的には「鍵を配らない権限借用が安全」とされます。
ただ、権限借用で作られるファイルには、本人の Google アカウントのリフレッシュトークンが入っています。今回のように本人が Firestore の編集権限を持っている場合、そのファイルが漏れると本人として Firestore を操作できてしまいます。
一方、閲覧専用サービスアカウントの鍵なら、漏れても Crashlytics を見られるだけです。運用の手間(ローテーションや退職時の削除)は増えますが、今回は鍵の配布を選びました。誰が使っているか区別できるように、鍵はメンバー1人に1つ発行しています。
登場人物と全体の流れ
設定する場所は4つです。メンバーの PC で作るファイルは鍵1つだけで、それ以外は管理者が最初に1回用意します。
| 場所 | 何を置くか | 誰が・いつ |
|---|---|---|
| Google Cloud コンソール(IAM) | Crashlytics を見るためのサービスアカウントと、その鍵 | 管理者が最初に1回。鍵はメンバーごとに発行 |
| GitHub(非公開リポジトリ) | MCP の設定を入れた Claude Code のプラグイン | 管理者が最初に1回 |
| claude.ai の組織設定(管理された設定) | 「このプラグインを全員に入れる」「ほかの MCP は禁止」などのルール | 組織のオーナーが最初に1回 |
| メンバーの PC | サービスアカウントの鍵(sa.json) |
メンバーが1回。置いて再起動するだけ |
メンバーが Claude Code を起動すると、こう動きます。
設定
1. 【Google Cloud コンソール】サービスアカウントを作る
サービスアカウントは、人ではなくプログラムが使う Google Cloud のアカウントです(xxx@<プロジェクトID>.iam.gserviceaccount.com)。Firebase のプロジェクトは Google Cloud のプロジェクトでもあるので、Google Cloud コンソールの「IAM と管理 → サービス アカウント」から作れます。
本番プロジェクトにサービスアカウントを作り、Firebase Crashlytics 閲覧者(roles/firebasecrashlytics.viewer)だけを付けます。開発プロジェクトのアプリも見たい場合は、開発プロジェクトの IAM にも同じサービスアカウントを追加します(ロールはプロジェクト側で付けるので、プロジェクトをまたいで付けられます)。
「Firebase 閲覧者」や「閲覧者」などの広いロールには、Firestore のデータの読み取りが含まれることがあります。付けるのは Crashlytics 閲覧者だけにします。
鍵は、サービスアカウントの「鍵」タブで JSON 形式で発行し、メンバー本人だけに渡します。
2. 【GitHub】プラグインのリポジトリを作る
Claude Code のプラグインは、MCP の設定などをまとめて配れる仕組みです。GitHub のリポジトリを「マーケットプレイス(プラグインの置き場)」として登録すると、Claude Code がそこからプラグインをダウンロードします。
管理者が GitHub に次の3ファイルを push するだけで、メンバーが手元にこのファイルを作る必要はありません。
claude-plugins/ ← GitHub の非公開リポジトリ
├── .claude-plugin/marketplace.json ← 置き場の目録(どんなプラグインがあるか)
└── plugins/firebase-crashlytics/
├── .claude-plugin/plugin.json ← プラグインの名前とバージョン
└── .mcp.json ← MCP サーバーの起動設定(本体)
{
"name": "company-plugins",
"owner": { "name": "your-company" },
"plugins": [
{ "name": "firebase-crashlytics", "source": "./plugins/firebase-crashlytics" }
]
}
{ "name": "firebase-crashlytics", "version": "1.0.0" }
肝心なのは .mcp.json の env です。
{
"mcpServers": {
"firebase": {
"command": "npx",
"args": ["-y", "firebase-tools@latest", "mcp", "--only", "crashlytics"],
"env": {
"XDG_CONFIG_HOME": "${HOME}/.config/claude-firebase-mcp",
"GOOGLE_APPLICATION_CREDENTIALS": "${HOME}/.config/claude-firebase-mcp/sa.json",
"FIREBASE_TOKEN": ""
}
}
}
}
| 値 | 役割 |
|---|---|
XDG_CONFIG_HOME |
firebase-tools の設定フォルダを専用の場所に向け、本人の firebase login を見えなくする
|
GOOGLE_APPLICATION_CREDENTIALS |
サービスアカウントの鍵を指定する |
FIREBASE_TOKEN |
シェルから引き継がれたトークンを空にする |
3. 【claude.ai の組織設定】管理された設定を登録する
Team / Enterprise プランでは、claude.ai の「組織設定 → Claude Code → 管理された設定」に JSON を登録できます。登録した内容は、メンバーが Claude Code を起動したときに自動で読み込まれ、本人の設定では上書きできません。ここで「2 のプラグインを全員に入れる」「ほかの MCP は禁止」「認証情報は読ませない」を決めます(抜粋)。
{
"allowManagedMcpServersOnly": true,
"allowedMcpServers": [
{ "serverCommand": ["npx", "-y", "firebase-tools@latest", "mcp", "--only", "crashlytics"] }
],
"strictPluginOnlyCustomization": ["mcp"],
"extraKnownMarketplaces": {
"company-plugins": { "source": { "source": "github", "repo": "your-org/claude-plugins" } }
},
"enabledPlugins": { "firebase-crashlytics@company-plugins": true },
"permissions": {
"deny": [
"mcp__plugin_firebase-crashlytics_firebase__firebase_login",
"mcp__plugin_firebase-crashlytics_firebase__firebase_logout",
"mcp__plugin_firebase-crashlytics_firebase__crashlytics_update_issue",
"mcp__plugin_firebase-crashlytics_firebase__firebase_deploy",
"Bash(gcloud:*)",
"Bash(firebase:*)",
"Read(~/.config/gcloud/**)",
"Read(~/.config/configstore/**)",
"Read(~/.config/claude-firebase-mcp/**)",
"Edit(~/.claude/**)",
"Edit(~/.claude.json)",
"WebFetch(domain:firestore.googleapis.com)",
"WebFetch(domain:oauth2.googleapis.com)"
]
},
"sandbox": { "enabled": true, "allowUnsandboxedCommands": false }
}
| キー | 役割 |
|---|---|
allowedMcpServers / allowManagedMcpServersOnly
|
この起動コマンドの MCP だけを許可し、ほかは禁止 |
strictPluginOnlyCustomization: ["mcp"](追記) |
~/.claude.json や .mcp.json での MCP 登録を禁止し、プラグインと管理側の MCP だけを読み込む。true にするとスキル・エージェント・フックまで止まるので、["mcp"] に絞る |
extraKnownMarketplaces / enabledPlugins
|
2 の GitHub リポジトリを登録し、プラグインを全員に強制インストール |
permissions.deny |
危ない MCP ツール、認証情報の読み取り、Google API への通信を禁止 |
sandbox |
Bash をサンドボックスの中だけで動かす |
4. 【メンバーの PC】鍵を置いて再起動する
メンバーの作業はこれだけです。
mkdir -p ~/.config/claude-firebase-mcp
mv ~/Downloads/<受け取った鍵>.json ~/.config/claude-firebase-mcp/sa.json
chmod 600 ~/.config/claude-firebase-mcp/sa.json
Claude Code を再起動すると、3 の設定にしたがってプラグインが自動でインストールされ、/mcp に Firebase MCP が表示されます。
検証結果
| 確認したこと | 結果 |
|---|---|
firebase_get_environment の認証アカウント |
✅ サービスアカウント(本人ではない) |
本人が firebase login 済みの状態 |
✅ MCP はサービスアカウントのまま |
| Crashlytics の issue 一覧 | ✅ 取得できる |
firebase_login を呼ばせる |
✅ ツール一覧から消えていて呼べない |
| Read ツールで鍵ファイルを読ませる | ✅ denied by your permission settings
|
Bash で ls ~/.config/gcloud
|
✅ サンドボックスが Operation not permitted で拒否 |
Bash で Firestore に curl
|
✅ サンドボックスのプロキシが 403 |
claude mcp add で MCP を追加(追記:同じ起動コマンドの別名を含む) |
✅ your organization's managed settings allow only MCP servers that plugins provide
|
気をつけるところ
--only crashlytics でも、ログインやデプロイのツールが入っている
--only で絞れるのは機能ごとのツールだけで、firebase_login、firebase_deploy、create 系などの共通ツールは常に読み込まれます(今回は 25 ツール)。特に firebase_login は IAM では防げないので、permissions.deny で禁止します。プラグイン経由のツール名は mcp__plugin_<プラグイン名>_<サーバー名>__<ツール名> です。
本人の firebase login が優先される
firebase-tools の認証は「FIREBASE_TOKEN → firebase login 済みのユーザー → ADC(GOOGLE_APPLICATION_CREDENTIALS)」の順で決まります。GOOGLE_APPLICATION_CREDENTIALS を指定するだけでは、ログイン済みのメンバーは本人の権限で動きます。XDG_CONFIG_HOME で設定フォルダごと切り替えるのが効きました。
allowedMcpServers は1文字違いでも通らない
許可リストは起動コマンドと完全一致で照合されます。--only crashlytics を付け忘れた許可だと、サーバーは /mcp の一覧にも出ません。逆に env は照合されないので、自由に変えられます。
env が照合されないことは、抜け道にもなる(追記)
許可リストは env を照合しません。そのため、メンバーが ~/.claude.json に同じ起動コマンドのサーバーを別名で、env なしで登録すると、許可リストを通過してしまいます。env が無いので MCP は本人の firebase login で動き、ツール名も mcp__<別名>__firebase_deploy になるため、プラグイン名で書いた permissions.deny にも当たりません。
対策として、管理された設定に strictPluginOnlyCustomization: ["mcp"] を追加し、ユーザーやプロジェクトの設定からの MCP 登録そのものを止めました。もともと許可リストで Firebase MCP 以外を止めていたので、メンバーへの影響はありませんでした。
ほかの MCP をユーザー側で使い続けたい組織では、MCP をすべてプラグインか管理側に寄せる必要があります。その場合は、managed-mcp.json で起動コマンドと env をまとめて固定する方法も公式ドキュメントに載っています(ただし各端末への配置が必要です)。
Claude が「! firebase login を実行して」と勧めてくる
MCP で届かないと、Claude は親切に「ご自身でログインしてください」と言ってきます。! で実行したコマンドは制限の外で動くので、メンバー向けの手順書に「勧められても実行しない」と書きました。
小ネタ
-
sandbox.network.allowedDomainsを書くと、全員のnpm installまで止まります。今回は書かずに、WebFetch(domain:...)の拒否がサンドボックスの通信拒否にも反映されることを利用しました - 起動フォルダの
.firebasercにdemo-で始まるエミュレーター用プロジェクトがあると、アクティブプロジェクトとして表示されます。Crashlytics のツールは App ID を指定して呼ぶので、気にしなくて大丈夫です - Crashlytics の読み取りは Cloud Audit Logs に記録されません。鍵ごとの最終認証日時で利用状況を見ています
残る課題
- Claude のデスクトップアプリのチャット:カスタムコネクタや Desktop Extensions は管理画面で止められますが、設定ファイルに手で書くローカル MCP は MDM なしでは止められません。今は運用ルールでカバーしています
- 鍵の運用:ローテーションや退職時の削除を手作業で回す必要があります
- 根本対策:全員が本番 Firestore の編集権限を持っていること自体を見直すのが本筋です。今回の構成は、それまでの安全策という位置づけです
まとめ
- MCP の権限は「誰の認証で動くか」で決まる。閲覧専用のサービスアカウントに寄せるのが一番効く
- ただし MCP 以外の抜け道(Bash、Read、WebFetch、ツールの切り替え、同じコマンドの別名登録)があるので、経路ごとに塞ぐ
- 組織の管理された設定とプラグインを使えば、メンバーの作業は鍵を置くだけにできる
同じように「AI に便利に使わせたいけど、本番の権限は渡したくない」と悩んでいる方の参考になればうれしいです。