Claude Code Firebase MCP CLAUDE.md Firestore Cloud Functions

Claude Code + Firebase: MCP Server, CLAUDE.md Templates, and Firestore Security Rules (2026)

The Prompt Shelf ·

Claude Code and Firebase now talk to each other directly. Firebase ships an official MCP server (general availability since October 2025) plus a catalogue of Agent Skills (early 2026), so Claude Code can read your Firestore schema, write security rules, deploy Cloud Functions, and drive the Emulator Suite without you pasting API docs into every prompt.

This guide covers the full setup: MCP server configuration, Agent Skills installation, a production-ready CLAUDE.md template for Firestore/Cloud Functions/Auth projects, security rules patterns Claude Code can actually follow, and an Emulator Suite workflow that keeps AI-generated changes off production data.

We also keep a collection of real-world CLAUDE.md and AGENTS.md rules, including a React + Tailwind + Firebase example, if you want to see how other projects are configured.

Why This Combination Works Now

Until late 2025, “Claude Code + Firebase” meant Claude reasoning about Firebase from training data and general documentation knowledge — it could write plausible-looking Firestore queries and security rules, but it had no way to check them against your actual project. That gap is closed:

The MCP server gives Claude Code live access. Through firebase-tools’ MCP server, Claude Code can list your Firestore collections, read existing Security Rules, inspect Cloud Functions, and query Remote Config — all grounded in your real project, not a guess.

Agent Skills encode Firebase-specific judgment. A generic coding model doesn’t know that Cloud Functions using the Admin SDK bypass Security Rules entirely, or that Firestore composite indexes need to exist before certain queries will run. Firebase’s Agent Skills bundle exactly these facts so Claude Code applies them without you re-explaining every session.

The Emulator Suite makes local iteration safe. Firebase’s emulators reproduce Firestore, Auth, Functions, and Storage locally. Point the MCP server at the emulator and Claude Code can write, test, and break things with zero network egress and zero risk to production data.

Security Rules are where AI assistance is riskiest — and most valuable. A Firestore database with no rules, or rules that accidentally allow allow read, write: if true;, is one of the most common real-world data leaks. Claude Code writing rules without project-specific constraints in CLAUDE.md is exactly how that mistake happens. This guide exists to prevent it.

Setting Up the Firebase MCP Server

Firebase’s MCP server ships inside the standard Firebase CLI (firebase-tools), so there’s no separate package to install.

Step 1: Install or Update the Firebase CLI

npm install -g firebase-tools
firebase --version   # confirm 13.x or later — the MCP server requires a recent CLI
firebase login

Step 2: Add the MCP Server to Claude Code

Add this to your project’s .claude/mcp.json (or the global ~/.claude/mcp.json for cross-project use):

{
  "mcpServers": {
    "firebase": {
      "command": "firebase",
      "args": ["experimental:mcp"]
    }
  }
}

Restart Claude Code after saving. The server starts in the context of whatever directory Claude Code is running in, so it picks up the .firebaserc and firebase.json for the active project automatically.

Step 3: Scope It to a Project

If you manage multiple Firebase projects from one machine, be explicit about which one Claude Code is targeting:

{
  "mcpServers": {
    "firebase": {
      "command": "firebase",
      "args": ["experimental:mcp", "--project", "my-app-staging"]
    }
  }
}

Rule: point Claude Code’s MCP connection at a staging or dev project, never at production. Firebase doesn’t have Supabase-style read-only query flags for its MCP server yet — the safer control is which project the CLI is authenticated against.

For local development, run the emulators and let the MCP server talk to them instead of live Firebase services:

firebase emulators:start --import=./emulator-data --export-on-exit

With the emulators running, Firebase’s MCP tools automatically detect and prefer the local emulator endpoints over the live project — no separate MCP config needed. This is the safest default: Claude Code can create, delete, and query data all day with no network egress and nothing touching your real Firestore instance.

Step 5: Verify the Connection

Ask Claude Code:

List all collections in my Firestore database and show me the current security rules.

If it returns your actual collection names and rule content, the MCP connection is live. If it answers from general Firebase knowledge instead, check that the CLI is authenticated (firebase login:list) and that .firebaserc points at the right project.

Installing Firebase Agent Skills

Agent Skills are separate from the MCP server — they’re structured, task-specific instructions (deploy a function, write a security rule, configure App Hosting) that Claude Code loads on demand rather than keeping in context permanently.

firebase init hosting  # or the relevant product — this scaffolds .firebase/ metadata Skills read

Firebase’s skills catalogue covers Authentication setup, Firestore data modeling, Security Rules authoring, Cloud Functions deployment, and Firebase Hosting/App Hosting. They activate automatically when Claude Code detects a matching task, the same way any Claude Code skill does — you don’t need to invoke them by name.

The practical effect: instead of writing “make sure Firestore rules deny by default and Cloud Functions use the Admin SDK correctly” into every CLAUDE.md, the Skills catalogue provides that baseline judgment. CLAUDE.md is still where you document what’s specific to your project — which is the rest of this guide.

CLAUDE.md Template for Firebase Projects

This template assumes a React or Next.js frontend with Firestore, Cloud Functions, and Firebase Auth. Trim the sections you don’t use.

# Project: [Your App Name]

## Stack
- Frontend: React 19 / Next.js 15
- Firebase: Firestore, Cloud Functions (2nd gen), Auth, Storage, Hosting
- TypeScript (strict mode)
- Firebase SDK: modular v9+ syntax only (no namespaced `firebase.firestore()` calls)

## Commands
- Dev server: `npm run dev`
- Start emulators: `firebase emulators:start --import=./emulator-data --export-on-exit`
- Deploy functions only: `firebase deploy --only functions`
- Deploy rules only: `firebase deploy --only firestore:rules,storage:rules`
- Deploy everything: `firebase deploy`
- Run Security Rules unit tests: `npm run test:rules`
- View function logs: `firebase functions:log`

## Firebase Client Usage
- Client SDK import path: `src/lib/firebase/client.ts` (browser-safe config only)
- Admin SDK import path: `functions/src/admin.ts` (Cloud Functions only — never bundle into client code)
- Always use modular imports: `import { getFirestore, collection, query } from 'firebase/firestore'`
- Never import `firebase-admin` in any file under `src/` — it will break the client bundle and expose service account behavior client-side

## Architecture
- `src/lib/firebase/client.ts` — Firebase client SDK initialization (public config, safe to expose)
- `functions/src/` — Cloud Functions source (Admin SDK, elevated privileges)
- `firestore.rules` — Firestore Security Rules (source of truth for data access)
- `storage.rules` — Cloud Storage Security Rules
- `firestore.indexes.json` — composite index definitions (must be deployed before queries that need them work in production)

## Firestore Data Conventions
- Every document has `createdAt` and `updatedAt` as `Timestamp` (use `serverTimestamp()`, never client-generated dates)
- Ownership field is `ownerId: string` (the Firebase Auth `uid`), consistent across all collections
- Soft deletes via `deletedAt: Timestamp | null` — never hard-delete documents users can restore
- Subcollections for one-to-many owned data (e.g., `users/{uid}/notifications`), top-level collections with an owner field for anything queried across users

## Security Rules Rules (Read Before Editing firestore.rules)
- Default posture: deny all. Every collection needs an explicit `allow` statement — nothing is implicitly readable.
- Ownership check pattern: `allow read, write: if request.auth != null && request.auth.uid == resource.data.ownerId;`
- Creation check pattern (resource doesn't exist yet, use `request.resource` not `resource`):
  `allow create: if request.auth != null && request.auth.uid == request.resource.data.ownerId;`
- Never write `allow read, write: if true;` outside of a scratch/prototype project that will never hold real user data
- Cloud Functions using the Admin SDK bypass Security Rules entirely — that's expected, but document *why* whenever a function writes data a client couldn't write directly
- After editing firestore.rules, always run the emulator-based rules unit tests before deploying: `npm run test:rules`

## Cloud Functions Conventions
- 2nd gen functions only (`onCall`, `onRequest` from `firebase-functions/v2`) — do not write 1st gen syntax
- Every callable function checks `request.auth` and throws `HttpsError('unauthenticated', ...)` if missing, before touching Firestore
- Secrets and API keys: use `defineSecret()` from `firebase-functions/params`, never `process.env` with hardcoded values, never in CLAUDE.md
- Region: pin to `asia-northeast1` (or your region) explicitly on every function — do not rely on the default

## What NOT to Put in CLAUDE.md
- Firebase Admin SDK service account JSON (any form of it)
- API keys for third-party services called from Cloud Functions
- Real user data, even as "example" values

Keep the CLAUDE.md itself lean. If your Security Rules get complex (role-based access, multi-tenant scoping), extract the full rule patterns to docs/security-rules-patterns.md and reference it — Claude Code reads linked files when the task calls for them.

Firestore Security Rules Patterns Claude Code Needs to Know

Security Rules are the single highest-leverage thing to get right in CLAUDE.md, because a wrong rule doesn’t fail loudly — it fails by silently exposing data.

User-Owned Documents (Most Common Case)

// firestore.rules
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /posts/{postId} {
      allow read: if true; // public content
      allow create: if request.auth != null
                     && request.auth.uid == request.resource.data.ownerId;
      allow update, delete: if request.auth != null
                              && request.auth.uid == resource.data.ownerId;
    }
  }
}

Document this pattern explicitly in CLAUDE.md rather than relying on Claude Code to infer it from one example — the distinction between resource.data (existing document) and request.resource.data (incoming write) is the single most common Security Rules mistake, AI-generated or not.

Role-Based Access (Admin Override)

match /reports/{reportId} {
  allow read: if request.auth != null &&
    (request.auth.uid == resource.data.ownerId ||
     get(/databases/$(database)/documents/users/$(request.auth.uid)).data.role == 'admin');
}

Tell Claude Code the cost implication in CLAUDE.md: every get() call inside a rule is a document read that counts against your Firestore quota and adds latency. For frequently-checked roles, a custom claim on the Auth token (request.auth.token.role == 'admin') avoids the extra read entirely — set the claim from a Cloud Function, not from client code.

Team/Multi-Tenant Scoping

## Security Rules: Team Access Pattern
- Team-scoped documents store `teamId: string`
- Membership lives in `teams/{teamId}/members/{uid}` — presence of the doc means membership
- Rule pattern:
  allow read: if request.auth != null &&
    exists(/databases/$(database)/documents/teams/$(resource.data.teamId)/members/$(request.auth.uid));

Storage Rules Follow the Same Logic, Separately

storage.rules is a different file with its own syntax — Claude Code sometimes conflates the two. Be explicit:

// storage.rules
rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /users/{userId}/{allPaths=**} {
      allow read: if true;
      allow write: if request.auth != null && request.auth.uid == userId
                    && request.resource.size < 5 * 1024 * 1024
                    && request.resource.contentType.matches('image/.*');
    }
  }
}

Add file size and content-type checks to every write rule that handles user uploads — this is the most common thing Claude Code omits unless CLAUDE.md asks for it explicitly.

Testing Security Rules Before You Deploy

Never let Claude Code deploy a rules change without a passing test run. Firebase’s Rules Unit Testing library runs against the emulator:

npm install --save-dev @firebase/rules-unit-testing
// tests/firestore.rules.test.ts
import { initializeTestEnvironment, assertFails, assertSucceeds } from '@firebase/rules-unit-testing';
import { readFileSync } from 'fs';

const testEnv = await initializeTestEnvironment({
  projectId: 'demo-project',
  firestore: { rules: readFileSync('firestore.rules', 'utf8') },
});

test('users cannot read other users posts marked private', async () => {
  const alice = testEnv.authenticatedContext('alice');
  const bob = testEnv.authenticatedContext('bob');

  await testEnv.withSecurityRulesDisabled(async (ctx) => {
    await ctx.firestore().doc('posts/private1').set({ ownerId: 'bob', visibility: 'private' });
  });

  await assertFails(alice.firestore().doc('posts/private1').get());
  await assertSucceeds(bob.firestore().doc('posts/private1').get());
});

Instruct Claude Code with this pattern when it changes rules:

Update firestore.rules so that documents in the `reports` collection are 
only readable by the owner or a user with role=admin (via custom claim).

After editing the rules, write or update a rules unit test in 
tests/firestore.rules.test.ts covering: owner can read, non-owner cannot 
read, admin can read. Run the tests against the emulator before telling 
me it's done.

This turns “trust the AI-generated rule” into “the rule passed an explicit access-control test” — a meaningfully different guarantee.

Cloud Functions: What CLAUDE.md Needs to Say

Cloud Functions are where the Admin SDK’s elevated privileges meet AI-generated code, so the failure mode is different from Security Rules: instead of leaking data to end users, a bad function can leak data or perform unintended writes at the server level, bypassing every rule you wrote.

## Cloud Functions: Admin SDK Boundaries
- The Admin SDK bypasses ALL Security Rules — every read/write it performs is unchecked
- Every function that mutates data must independently verify `request.auth` and the specific 
  permission being exercised, since Security Rules won't catch a mistake here
- Do not write functions that accept an arbitrary `collectionPath` string from the client and 
  operate on it — this is the equivalent of SQL injection for Firestore
- Log every Admin SDK write that touches another user's data, with the acting uid, for audit purposes

Example of the pattern Claude Code should follow for a callable function that needs elevated access:

// functions/src/moderateContent.ts
import { onCall, HttpsError } from 'firebase-functions/v2/https';
import { getFirestore } from 'firebase-admin/firestore';

export const moderateContent = onCall(async (request) => {
  if (!request.auth) {
    throw new HttpsError('unauthenticated', 'Sign-in required');
  }

  const callerDoc = await getFirestore().doc(`users/${request.auth.uid}`).get();
  if (callerDoc.data()?.role !== 'moderator') {
    throw new HttpsError('permission-denied', 'Moderator role required');
  }

  const { postId, action } = request.data;
  await getFirestore().doc(`posts/${postId}`).update({
    moderationStatus: action,
    moderatedBy: request.auth.uid,
    moderatedAt: new Date(),
  });

  return { success: true };
});

The explicit role check inside the function is the whole point — the Admin SDK would happily perform this write for anyone who could call the function, so the function itself is the only place enforcement can happen.

Emulator-First Workflow

The single biggest safety improvement for Claude Code + Firebase is defaulting to the Emulator Suite for anything that isn’t a final, reviewed deploy.

1. firebase emulators:start --import=./emulator-data --export-on-exit
2. Claude Code inspects current rules/schema via MCP (pointed at the emulator)
3. Claude Code drafts a Security Rules change or Cloud Function
4. Rules unit tests run against the emulator, not production
5. You review the diff — rules and functions both, every time
6. firebase deploy --only firestore:rules,functions (from CI or manual, after review)

Add this to CLAUDE.md so Claude Code defaults to it without being asked each session:

## Default Workflow
- Assume the Emulator Suite is running unless told otherwise
- Never run `firebase deploy` directly — draft the change, run tests against the 
  emulator, and report readiness. I deploy manually after reviewing the diff.
- If Security Rules changed, always run `npm run test:rules` before reporting completion

AGENTS.md for Team Firebase Projects

For teams where multiple engineers use different AI tools against the same Firebase project, AGENTS.md at the repo root gives a shared baseline any AI assistant will pick up.

# AGENTS.md

## Firebase Security Rules
- firestore.rules and storage.rules are reviewed like production code — no direct 
  edits without a PR
- Every PR touching *.rules must include a corresponding test in tests/*.rules.test.ts
- Default posture is deny-all; new collections need an explicit allow rule before merge

## Cloud Functions
- 2nd gen only (firebase-functions/v2)
- Every callable/HTTP function validates request.auth before any Firestore/Storage access
- Secrets via defineSecret(), never committed, never in any .md file

## Deployment
- Rules and Functions deploy from CI on merge to main, never manually from a laptop
- Emulator Suite is the default target for local development and AI-assisted changes

## Reviewing AI-Generated Firebase Code
Before merging AI-generated Security Rules or Cloud Functions:
1. Read the full rules file, not just the diff — a narrow allow clause elsewhere 
   can be overridden by a broader one added later in the same match block
2. Confirm Admin SDK usage in Cloud Functions has an explicit auth/role check
3. Confirm rules unit tests actually exercise the negative case (access denied), 
   not just the happy path

Security: What NOT to Put in CLAUDE.md

Never include in CLAUDE.md or any tracked file:

  • Service account JSON key content, in any form
  • FIREBASE_ADMIN_PRIVATE_KEY or any Admin SDK credential
  • Third-party API keys used inside Cloud Functions (Stripe, Resend, etc.)
  • Real Firebase Auth UIDs or real user document contents as “examples”

What’s safe to include:

  • The client-side Firebase config object (apiKey, authDomain, etc.) — these are not secrets; Security Rules are what actually protects your data
  • Collection and field names, schema structure
  • Security Rules patterns and access-control logic (this is documentation, not a credential)

Using .claudeignore

# .claudeignore
serviceAccountKey.json
*.json.enc
.env
.env.local
functions/.runtimeconfig.json
emulator-data/

This keeps Claude Code from reading service account files or emulator export data during file searches. It doesn’t block Claude Code from using environment variables already exported in your shell — that’s intended.

Common Issues and Fixes

MCP server not appearing in Claude Code: Confirm firebase --version returns 13.x+ and re-run firebase login. Restart Claude Code after any change to .claude/mcp.json — environment and CLI auth state are only read at startup.

Claude Code writes rules that pass tests locally but fail after deploy: Composite indexes required by a new query aren’t created automatically outside the emulator. Check firestore.indexes.json was updated and deployed with firebase deploy --only firestore:indexes.

Cloud Functions deploy succeeds but calls fail with permission-denied: Usually a missing IAM binding for a 2nd-gen function calling another Google Cloud service, not a Security Rules problem — 2nd gen functions run on Cloud Run under the hood and need their own service account permissions.

Claude Code writing 1st-gen function syntax: Reinforce in the prompt: “This project uses Cloud Functions 2nd gen exclusively — import from firebase-functions/v2, not the default export.”

Emulator data resets unexpectedly: Without --export-on-exit, emulator state is wiped every restart. Add it to the standard start command in CLAUDE.md so Claude Code (and everyone else) uses it by default.

What the MCP Server and Agent Skills Make Possible Together

Before the MCP server, Claude Code’s Firebase knowledge was frozen at training time and blind to your actual project — you’d describe your schema in CLAUDE.md, hope it stayed accurate, and manually check every generated rule against the Firebase console. With the MCP server and Agent Skills combined:

  • Claude Code inspects real Firestore collections and existing rules before writing new ones
  • It can run against the Emulator Suite directly, so “should this rule work” becomes a tested fact instead of a guess
  • Agent Skills supply Firebase-specific judgment (Admin SDK bypass behavior, 2nd-gen function syntax, index requirements) without you re-explaining it every session
  • CLAUDE.md supplies what neither can know on its own: your ownership model, your role structure, and which collections hold data sensitive enough to need extra scrutiny

Neither piece replaces the other. The MCP server and Skills give Claude Code Firebase-specific capability; CLAUDE.md gives it your project’s specific constraints. Firestore Security Rules — the part of a Firebase project most likely to cause real harm when wrong — depend on getting both right.


For more CLAUDE.md examples and AI assistant configuration patterns, including the React + Tailwind + Firebase rules referenced above, browse the rules collection on The Prompt Shelf.

Related Articles

Explore the collection

Browse all AI coding rules — CLAUDE.md, .cursorrules, AGENTS.md, and more.

Browse Rules