-
Notifications
You must be signed in to change notification settings - Fork 1.2k
[MCP] Support firestore emulator in MCP server #8700
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,6 +28,7 @@ | |
| total: Number.MAX_SAFE_INTEGER, | ||
| }); | ||
|
|
||
| private urlPrefix: string; | ||
| private apiClient: apiv2.Client; | ||
|
|
||
| public isDocumentPath: boolean; | ||
|
|
@@ -50,14 +51,15 @@ | |
| private parent: string; | ||
|
|
||
| /** | ||
| * Construct a new Firestore delete operation. | ||
| * | ||
| * @param project the Firestore project ID. | ||
| * @param path path to a document or collection. | ||
|
Check warning on line 57 in src/firestore/delete.ts
|
||
| * @param options options object with three optional parameters: | ||
| * - options.recursive true if the delete should be recursive. | ||
| * - options.shallow true if the delete should be shallow (non-recursive). | ||
| * - options.allCollections true if the delete should universally remove all collections and docs. | ||
| * - options.urlPrefix if specified initializes the client to use the given url, otherwise determine from environment | ||
| */ | ||
| constructor( | ||
| project: string, | ||
|
|
@@ -67,6 +69,7 @@ | |
| shallow?: boolean; | ||
| allCollections?: boolean; | ||
| databaseId: string; | ||
| urlPrefix?: string; | ||
| }, | ||
| ) { | ||
| this.project = project; | ||
|
|
@@ -75,6 +78,7 @@ | |
| this.shallow = Boolean(options.shallow); | ||
| this.allCollections = Boolean(options.allCollections); | ||
| this.databaseId = options.databaseId; | ||
| this.urlPrefix = options.urlPrefix ?? firestoreOriginOrEmulator(); | ||
|
|
||
| // Tunable deletion parameters | ||
| this.readBatchSize = 7500; | ||
|
|
@@ -113,7 +117,7 @@ | |
| this.apiClient = new apiv2.Client({ | ||
| auth: true, | ||
| apiVersion: "v1", | ||
| urlPrefix: firestoreOriginOrEmulator(), | ||
| urlPrefix: this.urlPrefix, | ||
| }); | ||
| } | ||
|
|
||
|
|
@@ -156,7 +160,7 @@ | |
| * Construct a StructuredQuery to find descendant documents of a collection. | ||
| * | ||
| * See: | ||
| * https://firebase.google.com/docs/firestore/reference/rest/v1/StructuredQuery | ||
| * | ||
| * @param allDescendants true if subcollections should be included. | ||
| * @param batchSize maximum number of documents to target (limit). | ||
|
|
@@ -394,7 +398,7 @@ | |
|
|
||
| numPendingDeletes++; | ||
| firestore | ||
| .deleteDocuments(this.project, toDelete, true) | ||
| .deleteDocuments(this.project, toDelete, this.urlPrefix) | ||
| .then((numDeleted) => { | ||
| FirestoreDelete.progressBar.tick(numDeleted); | ||
| numDocsDeleted += numDeleted; | ||
|
|
@@ -499,7 +503,7 @@ | |
| let initialDelete; | ||
| if (this.isDocumentPath) { | ||
| const doc = { name: this.root + "/" + this.path }; | ||
| initialDelete = firestore.deleteDocument(doc, true).catch((err) => { | ||
| initialDelete = firestore.deleteDocument(doc, this.urlPrefix).catch((err) => { | ||
| logger.debug("deletePath:initialDelete:error", err); | ||
| if (this.allDescendants) { | ||
| // On a recursive delete, we are insensitive to | ||
|
|
@@ -526,7 +530,7 @@ | |
| */ | ||
| public deleteDatabase(): Promise<any[]> { | ||
| return firestore | ||
| .listCollectionIds(this.project, true) | ||
| .listCollectionIds(this.project, this.urlPrefix) | ||
| .catch((err) => { | ||
| logger.debug("deleteDatabase:listCollectionIds:error", err); | ||
| return utils.reject("Unable to list collection IDs"); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| import { EmulatorHubClient } from "../../../emulator/hubClient.js"; | ||
| import { Emulators } from "../../../emulator/types.js"; | ||
|
|
||
| /** | ||
| * Gets the Firestore emulator host and port from the Emulator Hub. | ||
| * Throws an error if the Emulator Hub or Firestore emulator is not running. | ||
| * @param hubClient The EmulatorHubClient instance. | ||
| * @returns A string in the format "host:port". | ||
| */ | ||
| export async function getFirestoreEmulatorUrl(hubClient?: EmulatorHubClient): Promise<string> { | ||
| if (!hubClient) { | ||
| throw Error( | ||
| "Emulator Hub not found or is not running. You can start the emulator by running `firebase emulators:start` in your firebase project directory.", | ||
| ); | ||
| } | ||
|
|
||
| const emulators = await hubClient.getEmulators(); | ||
| const firestoreEmulatorInfo = emulators[Emulators.FIRESTORE]; | ||
| if (!firestoreEmulatorInfo) { | ||
| throw Error( | ||
| "No Firestore Emulator found running. Make sure your project firebase.json file includes firestore and then rerun emulator using `firebase emulators:start` from your project directory.", | ||
| ); | ||
| } | ||
|
|
||
| return `http://${firestoreEmulatorInfo.host}:${firestoreEmulatorInfo.port}`; | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.