1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Claude Code × Firebase Crashlytics MCP を、Firestore に触らせずにチームへ配った話

1
Last updated at Posted at 2026-10-29

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 サーバーの起動設定(本体)
.claude-plugin/marketplace.json
{
  "name": "company-plugins",
  "owner": { "name": "your-company" },
  "plugins": [
    { "name": "firebase-crashlytics", "source": "./plugins/firebase-crashlytics" }
  ]
}
plugins/firebase-crashlytics/.claude-plugin/plugin.json
{ "name": "firebase-crashlytics", "version": "1.0.0" }

肝心なのは .mcp.json の env です。

plugins/firebase-crashlytics/.mcp.json
{
  "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 に便利に使わせたいけど、本番の権限は渡したくない」と悩んでいる方の参考になればうれしいです。

1
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?