Firebase MCP
by gannonh
Verified
- src
#!/usr/bin/env node
/**
* Firebase MCP Server
*
* This server implements the Model Context Protocol (MCP) for Firebase services.
* It provides tools for interacting with Firebase Authentication, Firestore, and Storage
* through a standardized interface that can be used by AI assistants and other MCP clients.
*
* @module firebase-mcp
*/
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { CallToolRequestSchema, ListToolsRequestSchema, ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
import { addDocument, getDocument, updateDocument, deleteDocument, listDocuments, list_collections } from './lib/firebase/firestoreClient';
import { listDirectoryFiles, getFileInfo } from './lib/firebase/storageClient';
import { getUserByIdOrEmail } from './lib/firebase/authClient';
/**
* Main server class that implements the MCP protocol for Firebase services.
* Handles tool registration, request routing, and server lifecycle.
*/
class FirebaseMcpServer {
/** The MCP server instance */
private server: Server;
/**
* Initializes the Firebase MCP server with configuration and event handlers.
*/
constructor() {
this.server = new Server(
{
name: 'firebase-mcp',
version: '0.6.0'
},
{
capabilities: {
tools: {}
}
}
);
this.setupToolHandlers();
// Set up error handling and graceful shutdown
this.server.onerror = (error) => console.error('[MCP Error]', error);
process.on('SIGINT', async () => {
await this.server.close();
process.exit(0);
});
}
/**
* Registers all available Firebase tools with the MCP server.
* This includes tools for Firestore, Authentication, and Storage operations.
* @private
*/
private setupToolHandlers() {
// Register the list of available tools
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: 'firestore_add_document',
description: 'Add a document to a Firestore collection',
inputSchema: {
type: 'object',
properties: {
collection: {
type: 'string',
description: 'Collection name'
},
data: {
type: 'object',
description: 'Document data'
}
},
required: ['collection', 'data']
}
},
{
name: 'firestore_list_collections',
description: 'List collections in Firestore. If documentPath is provided, returns subcollections under that document; otherwise returns root collections.',
inputSchema: {
type: 'object',
properties: {
documentPath: {
type: 'string',
description: 'Optional parent document path'
},
limit: {
type: 'number',
description: 'Number of collections to return',
default: 20
},
pageToken: {
type: 'string',
description: 'Token for pagination to get the next page of results'
}
},
required: []
}
},
{
name: 'firestore_list_documents',
description: 'List documents from a Firestore collection with optional filtering',
inputSchema: {
type: 'object',
properties: {
collection: {
type: 'string',
description: 'Collection name'
},
filters: {
type: 'array',
description: 'Array of filter conditions',
items: {
type: 'object',
properties: {
field: {
type: 'string',
description: 'Field name to filter'
},
operator: {
type: 'string',
description: 'Comparison operator'
},
value: {
type: 'any',
description: 'Value to compare against (use ISO format for dates)'
}
},
required: ['field', 'operator', 'value']
}
},
limit: {
type: 'number',
description: 'Number of documents to return',
default: 20
},
pageToken: {
type: 'string',
description: 'Token for pagination to get the next page of results'
}
},
required: ['collection']
}
},
{
name: 'firestore_get_document',
description: 'Get a document from a Firestore collection',
inputSchema: {
type: 'object',
properties: {
collection: {
type: 'string',
description: 'Collection name'
},
id: {
type: 'string',
description: 'Document ID'
}
},
required: ['collection', 'id']
}
},
{
name: 'firestore_update_document',
description: 'Update a document in a Firestore collection',
inputSchema: {
type: 'object',
properties: {
collection: {
type: 'string',
description: 'Collection name'
},
id: {
type: 'string',
description: 'Document ID'
},
data: {
type: 'object',
description: 'Updated document data'
}
},
required: ['collection', 'id', 'data']
}
},
{
name: 'firestore_delete_document',
description: 'Delete a document from a Firestore collection',
inputSchema: {
type: 'object',
properties: {
collection: {
type: 'string',
description: 'Collection name'
},
id: {
type: 'string',
description: 'Document ID'
}
},
required: ['collection', 'id']
}
},
{
name: "auth_get_user",
description: "Get a user by ID or email from Firebase Authentication",
inputSchema: {
type: "object",
properties: {
identifier: {
type: "string",
description: "User ID or email address"
}
},
required: ["identifier"]
}
},
{
"name": "storage_list_files",
"description": "List files in a given path in Firebase Storage",
"inputSchema": {
"type": "object",
"properties": {
"directoryPath": {
"type": "string",
"description": "The optional path to list files from. If not provided, the root is used."
}
},
"required": []
}
},
{
"name": "storage_get_file_info",
"description": "Get file information including metadata and download URL",
"inputSchema": {
"type": "object",
"properties": {
"filePath": {
"type": "string",
"description": "The path of the file to get information for"
}
},
"required": ["filePath"]
}
}
]
}));
// Handle tool execution requests
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args = {} } = request.params;
// Route the request to the appropriate handler based on the tool name
switch (name) {
case 'firestore_add_document':
return addDocument(args.collection as string, args.data as object);
case 'firestore_list_documents':
return listDocuments(
args.collection as string,
args.filters as Array<{ field: string, operator: FirebaseFirestore.WhereFilterOp, value: any }>,
args.limit as number,
args.pageToken as string | undefined
);
case 'firestore_get_document':
return getDocument(args.collection as string, args.id as string);
case 'firestore_update_document':
return updateDocument(args.collection as string, args.id as string, args.data as object);
case 'firestore_delete_document':
return deleteDocument(args.collection as string, args.id as string);
case 'firestore_list_collections':
return list_collections(
args.documentPath as string | undefined,
args.limit as number | undefined,
args.pageToken as string | undefined
);
case 'auth_get_user':
return getUserByIdOrEmail(args.identifier as string);
case 'storage_list_files':
return listDirectoryFiles(
args.directoryPath as string | undefined,
args.pageSize as number | undefined,
args.pageToken as string | undefined
);
case 'storage_get_file_info':
return getFileInfo(args.filePath as string);
default:
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
}
});
}
/**
* Starts the MCP server using stdio transport.
* This method connects the server to stdin/stdout for communication with MCP clients.
*/
async run() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error('Firebase MCP server running on stdio');
}
}
// Create and start the server
const server = new FirebaseMcpServer();
server.run().catch(console.error);