Build secure, typed MCP tools backed by Firestore.
Node 20+. ESM-first. Early API.
firestore-mcp-kit is a minimal TypeScript library for defining explicit MCP tools in userland, validating them with Zod, and wiring them to document-centric Firestore operations.
It stays intentionally small:
- Firestore is storage, not the public contract
- schemas and policies live in app code
- writes should be narrow and explicit
- transports should stay thin
Install the package:
npm install firestore-mcp-kit zodBuild a minimal MCP tool with explicit schemas and a Firestore-backed path.
The flow is:
- define a resource schema with Zod
- define explicit tool input/output schemas
- create a Firestore resource path function
- implement tools with
defineTool(...) - run them directly or expose them over stdio or HTTP
import {
createFirestoreResource,
defineTool,
getDocument,
} from 'firestore-mcp-kit'
import { z } from 'zod'
const NoteSchema = z.object({
id: z.string(),
title: z.string(),
body: z.string(),
})
const GetNoteInputSchema = z.object({
id: z.string().min(1),
})
const GetNoteOutputSchema = z.object({
note: NoteSchema,
})
const resource = createFirestoreResource(firestore, (id) => `notes/${id}`)
const getNote = defineTool({
name: 'notes.get',
inputSchema: GetNoteInputSchema,
outputSchema: GetNoteOutputSchema,
async execute({ input }) {
const note = await getDocument<z.infer<typeof NoteSchema>>(
resource,
input.id
)
if (!note) {
throw new Error(`Note ${input.id} not found`)
}
return { note }
},
})For a full end-to-end example, use examples/notes/src/index.ts in this repository.
If you are working in this repository, install dependencies first:
npm installRun the example:
npm run dev:stdio
npm run dev:httpUse a real Firestore project with a local service-account file:
export GOOGLE_APPLICATION_CREDENTIALS=./credentials.json
npm run dev:http:firestoreKeep credentials out of git. The repo ignores common credential file names by default.
Status: early and intentionally small. The API is designed to stay narrow, explicit, and example-driven.
HTTP default endpoint:
http://localhost:8000/mcp- health check:
http://localhost:8000/health
- Define a resource schema in userland.
- Define explicit input/output schemas per tool.
- Create a
FirestoreResource:
const resource = createFirestoreResource(firestore, (id) => `notes/${id}`)- Implement tools with
defineTool(...). - Execute them directly or expose them through a transport.
Use createPatchSchema(...) and pickPatchedFields(...).
const NotePatchSchema = createPatchSchema(['title', 'body']).extend({
title: z.string().min(1).optional(),
body: z.string().optional(),
})This keeps update behavior explicit and avoids broad arbitrary patching.
await startStdioServer({
name: 'notes-example',
version: '0.1.0',
tools,
getContext: async () => ({ actorId: 'local-user', canDelete: true }),
})await startHttpServer({
name: 'notes-example',
version: '0.1.0',
port: 8000,
tools,
getContext: async () => ({ actorId: 'local-user', canDelete: true }),
})defineTool(...)executeTool(...)createMcpServer(...)
startStdioServer(...)startHttpServer(...)
getDocument(...)setDocument(...)updateDocument(...)deleteDocument(...)
createPatchSchema(...)pickPatchedFields(...)
FirestoreMcpErrorAuthorizationErrorValidationErrorNotFoundError
FirestoreClientFirestoreDocumentRef<TDocument>FirestoreDocumentSnapshot<TDocument>FirestoreResource
These notes explain the design choices behind the library.
Because MCP tools should expose narrow, typed operations instead of arbitrary database access.
Because applications own their resource shapes, policy checks, and dangerous-field rules.
Because unrestricted patching is too broad for the default path. Safe write behavior should feel deliberate.
Because the same tool definitions should work over stdio or HTTP without changing domain logic.
Use this library when you want Firestore behind a narrow app contract instead of exposing broader platform capabilities directly. It is a better fit when you want tool names like notes.create or tickets.assign, app-owned Zod schemas, explicit authorization hooks, and constrained writes. Firebase's MCP server is a better fit when you want a more complete Firebase-integrated server with less userland code and are comfortable with a more platform-shaped surface area.