Firestore 数据库 MCP 服务器

MCP Firestore Server 是一个模型上下文协议(MCP)服务器,提供 Firestore 数据库操作能力,支持模拟器与生产环境双端点,并具备查询、创建、更新、删除文档及写入确认安全机制。

  • by markhilton
  • JavaScript
  • 本地部署
  • 更新于
在 GitHub 查看 markhilton/mcp-firestore-server

使用说明

MCP Firestore Server

A Model Context Protocol (MCP) server that provides Firestore database operations with dual-endpoint support (emulator + production).

Installation

npx @nerd305/mcp-firestore-server

Configuration

Environment Variable Purpose Required?
GOOGLE_CLOUD_PROJECT / FIREBASE_PROJECT_ID Project ID Yes (or auto-detected from gcloud / .firebaserc)
FIRESTORE_EMULATOR_HOST Emulator address (e.g. 127.0.0.1:8080) No (enables emulator target)
GOOGLE_APPLICATION_CREDENTIALS Service account key path No (enables production target)
MCP_FIRESTORE_DEFAULT_TARGET Default target: "emulator" or "production" No (auto-resolved)
MCP_FIRESTORE_READONLY 1/all: hide and reject write tools. production: reject writes to production only No
MCP_FIRESTORE_MAX_RESPONSE_CHARS Default response budget for document payloads (default 60000) No
MCP_FIRESTORE_DEBUG / DEBUG Include stack traces in error responses No (off by default)

At least one of FIRESTORE_EMULATOR_HOST or GOOGLE_APPLICATION_CREDENTIALS must be set. When both are configured, the server connects to both endpoints simultaneously and defaults to the emulator.

Production write safety

Reads run freely against any target. Writes against the production target (create_document, update_document, delete_document) require confirm: true — without it, the tool returns an error and makes no change. Writes against the emulator never need confirmation. This prevents an assistant from mutating live data without a deliberate opt-in.

Usage with Claude Desktop

Add to your .mcp.json configuration:

Emulator only

{
  "mcpServers": {
    "firestore": {
      "command": "npx",
      "args": ["-y", "@nerd305/mcp-firestore-server"],
      "env": {
        "GOOGLE_CLOUD_PROJECT": "your-project-id",
        "FIRESTORE_EMULATOR_HOST": "127.0.0.1:8080"
      }
    }
  }
}

Production only

{
  "mcpServers": {
    "firestore": {
      "command": "npx",
      "args": ["-y", "@nerd305/mcp-firestore-server"],
      "env": {
        "GOOGLE_CLOUD_PROJECT": "your-project-id",
        "GOOGLE_APPLICATION_CREDENTIALS": "/path/to/service-account.json"
      }
    }
  }
}

Dual-endpoint (emulator + production)

{
  "mcpServers": {
    "firestore": {
      "command": "npx",
      "args": ["-y", "@nerd305/mcp-firestore-server"],
      "env": {
        "GOOGLE_CLOUD_PROJECT": "your-project-id",
        "FIRESTORE_EMULATOR_HOST": "127.0.0.1:8080",
        "GOOGLE_APPLICATION_CREDENTIALS": "/path/to/service-account.json"
      }
    }
  }
}

When both are configured, use the target parameter on any tool to choose which endpoint to query. The default target is the emulator (override with MCP_FIRESTORE_DEFAULT_TARGET).

Available Tools

All tools support an optional target parameter ("emulator" | "production") and include the resolved target in every response. Collection paths support subcollections (e.g. "users/uid/posts").

query_collection

Query documents from a Firestore collection with pagination support.

{
  collection: string;       // Collection path
  limit?: number;           // Default: 10
  orderBy?: string;
  orderDirection?: "asc" | "desc";
  startAfter?: string;      // Document ID for pagination (use lastDocId from previous response)
  target?: "emulator" | "production";
}

get_document

Get a specific document by ID.

{
  collection: string;
  docId: string;
  target?: "emulator" | "production";
}

query_with_where

Query documents with where conditions. Supports multiple clauses and automatic value type coercion (numbers, booleans, arrays are auto-detected from strings). JSON-encoded Timestamp values are decoded too.

{
  collection: string;
  // Multi-clause format (preferred)
  where?: [field: string, operator: string, value: string][];
  // Legacy single-clause format
  field?: string;
  operator?: "==" | "!=" | "<" | "<=" | ">" | ">=" | "array-contains" | "in" | "array-contains-any";
  value?: string;
  limit?: number;
  orderBy?: string;
  orderDirection?: "asc" | "desc";
  startAfter?: string;
  target?: "emulator" | "production";
}

Examples:

{ "collection": "users", "where": [["age", ">", "18"], ["status", "==", "active"]] }
{ "collection": "users", "field": "email", "operator": "==", "value": "user@example.com" }

list_collections

List top-level collections or subcollections of a specific document.

{
  documentPath?: string;    // e.g. "users/uid" to list subcollections
  target?: "emulator" | "production";
}

count_documents

Count documents using Firestore's native aggregation (no document fetching). Supports optional where conditions.

{
  collection: string;
  where?: [field: string, operator: string, value: string][];
  target?: "emulator" | "production";
}

batch_get

Fetch multiple documents by ID in a single request.

{
  collection: string;
  docIds: string[];
  target?: "emulator" | "production";
}

create_document

Create a new document. data supports typed values except $delete.

{
  collection: string;
  docId?: string;           // Auto-generated if not provided
  data: object;
  confirm?: boolean;        // Required true to write to production
  target?: "emulator" | "production";
}

update_document

Update an existing document. data supports typed values, including {"$delete": true} to remove a field.

{
  collection: string;
  docId: string;
  data: object;
  merge?: boolean;          // Default: true
  confirm?: boolean;        // Required true to write to production
  target?: "emulator" | "production";
}

delete_document

Delete a document.

{
  collection: string;
  docId: string;
  confirm?: boolean;        // Required true to delete from production
  target?: "emulator" | "production";
}

Large results: projection, compaction, budget

Read tools (query_collection, query_with_where, get_document, batch_get) accept:

  • fields: field paths to return (dotted paths allowed). Queries use Firestore select().
  • raw: true disables compaction. By default, numeric arrays longer than 64 items (for example embedding vectors) collapse to a placeholder, and strings longer than 4000 characters are truncated. Every compacted path is listed in compacted.
  • maxChars: response budget for this call (1000–500000). Queries drop trailing documents to fit and set truncated: true; continue with startAfter: <lastDocId>. Single documents list dropped fields in omittedFields, and batch_get lists dropped IDs in omittedIds.

Errors and recovery

Errors keep the error message and add a code and hint where the cause is known:

  • AUTH_EXPIRED: Application Default Credentials expired. Run gcloud auth application-default login. The server re-creates the client and retries once, so no restart is needed.
  • FAILED_PRECONDITION_INDEX: the query needs a composite index; indexUrl links to create it.
  • Unknown arguments are rejected with a suggestion, for example documentId → docId.

Tools carry MCP annotations (readOnlyHint, destructiveHint, idempotentHint) so clients can tell reads from writes.

Typed values (Timestamps and field sentinels)

Tool input is plain JSON, so Firestore-native types use an encoding. create_document and update_document decode these forms anywhere in data, including nested maps and arrays:

JSON form Stored as Notes
{"_seconds": 1791681420, "_nanoseconds": 0} Timestamp The same shape reads return, so read → write round-trips.
{"$timestamp": "2026-10-11T01:17:00Z"} Timestamp ISO-8601 with an explicit timezone (Z or ±hh:mm), or a bare date (UTC midnight). Up to 9 fractional digits.
{"$serverTimestamp": true} FieldValue.serverTimestamp() Not allowed inside arrays.
{"$increment": 1} FieldValue.increment(n) Any finite number. Not allowed inside arrays.
{"$delete": true} FieldValue.delete() update_document only. Nested maps require merge: true (the default). Rejected by create_document.

Bare ISO strings are not converted, so ordinary string fields are never retyped. A tagged object must contain only its tag key; malformed values fail with an error that names the field path.

{
  "collection": "image_advisories",
  "docId": "abc123",
  "data": {
    "resolvedAt": { "$timestamp": "2026-10-11T01:17:00Z" },
    "updatedAt": { "$serverTimestamp": true },
    "tempNote": { "$delete": true }
  }
}

Where-clause values in query_with_where and count_documents accept the two Timestamp forms as JSON-encoded strings, so you can filter on timestamps (write sentinels are rejected in queries):

{
  "collection": "image_advisories",
  "where": [["resolvedAt", ">=", "{\"$timestamp\": \"2026-10-01T00:00:00Z\"}"]]
}

Local Development

  1. Clone the repository
  2. Install dependencies: npm install
  3. Run locally: npm start

Testing with npm link

cd mcp-firestore-server
npm link
# In your project
npm link @nerd305/mcp-firestore-server

License

MIT