Skip to content

Commit d74f578

Browse files
joehanfredzqmjrothfederJamie Rothfederyuchenshi
authored
Performance improvements and new billing info for get_environment (#9587)
* Update next to match master (#9313) * Remove overrides that diverge the test configuration from the build configuration. (#9300) Co-authored-by: Jamie Rothfeder <rothbutter@google.com> * Fix misleading typing for options.json. (#9275) * feat(dataconnect): Add confirmation for Gemini schema generation (#9282) * feat(dataconnect): add confirmation for Gemini schema generation Instead of directly asking for an app description to generate a schema with Gemini, this change first asks the user to confirm if they want to use Gemini. If the user confirms, it then prompts for the app description with a default value of "an app for ${setup.projectId}". * prompts * changelog * m * feedback * typo * metrics * Update index.ts --------- Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --------- Co-authored-by: Jamie Rothfeder <jamie.rothfeder@gmail.com> Co-authored-by: Jamie Rothfeder <rothbutter@google.com> Co-authored-by: Yuchen Shi <yuchenshi@google.com> Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> * BREAKING: clean up monkey patching in functions emulator runtime (#9402) 1. Removed `functions.config()` Polyfill Removed initializeRuntimeConfig() from `src/emulator/functionsEmulatorRuntime.ts`. This function manually read `.runtimeconfig.json` and set `CLOUD_RUNTIME_CONFIG` env var to polyfill the logic for old clients of the Functions SDK. Functions SDK has included this feature for more than 5+ years now. 2. Removed Callable Auth Monkey Patches Removed wrapCallableHandler from src/emulator/functionsEmulatorRuntime.ts. that handled manual auth header swapping logic in src/emulator/functionsEmulator.ts. We now rely on the SDK's built-in skipTokenVerification feature shipped since 3.16.0. 3. Bumped Minimum SDK Version Increased minimum required firebase-functions version from 3.15.1 to 3.16.0 (launched 4+ years ago) in `src/emulator/functionsEmulatorRuntime.ts`. This ensures users have an SDK that supports skipTokenVerification and likely handles .runtimeconfig.json loading correctly in emulation. * BREAKING: hide functions:config:* commands by default (#9340) We'll gate all mutating functions:config:* commands behind the new `legacyRuntimeConfigCommands` experiment. We'll keep functions:config:{get,export} available for inspection. Includes some minor refactoring to align deprecation message across all surfaces. ```shell $ firebase functions:config:set FOO=bar Error: DEPRECATION NOTICE: Action required before March 2026 The functions.config() API and the Cloud Runtime Config service are deprecated. Deploys that rely on functions.config() will fail once Runtime Config shuts down in March 2026. The legacy functions:config:* CLI commands are deprecated and will be removed before March 2026. Migrate configuration to the Firebase Functions params APIs: import { defineJsonSecret } from "firebase-functions/params"; const config = defineJsonSecret("RUNTIME_CONFIG"); exports.myFunction = functions .runWith({ secrets: [config] }) .https.onRequest((req, res) => { const apiKey = config.value().service.key; // ... }); To convert existing runtime config values, try the interactive migration command: firebase functions:config:export Learn more: https://firebase.google.com/docs/functions/config-env#migrate-config To run this legacy command temporarily, run the following command and try again: firebase experiments:enable legacyRuntimeConfigCommands ``` * BREAKING: Rewrite functions:config:export command (#9341) Target the new defineJsonSecret API as migration target for functions.config() usage. The new API is a simpler migration target for existing functions.config() use cases. Example flow: ```shell $ firebase functions:config:export i This command retrieves your Runtime Config values (accessed via functions.config()) and exports them as a Secret Manager secret. i Fetching your existing functions.config() from danielylee-90... ✔ Fetched your existing functions.config(). i Configuration to be exported: ⚠ This may contain sensitive data. Do not share this output. { <CONFIG> } ✔ What would you like to name the new secret for your configuration? RUNTIME_CONFIG ✔ Created new secret version projects/XXX/secrets/RUNTIME_CONFIG/versions/1 i To complete the migration, update your code: // Before: const functions = require('firebase-functions'); exports.myFunction = functions.https.onRequest((req, res) => { const apiKey = functions.config().service.key; // ... }); // After: const functions = require('firebase-functions'); const { defineJsonSecret } = require('firebase-functions/params'); const config = defineJsonSecret("RUNTIME_CONFIG"); exports.myFunction = functions .runWith({ secrets: [config] }) // Bind secret here .https.onRequest((req, res) => { const apiKey = config.value().service.key; // ... }); i Note: defineJsonSecret requires firebase-functions v6.6.0 or later. Update your package.json if needed. i Then deploy your functions: firebase deploy --only functions ``` * feat(firestore): return listBackupsResponse from firestore:backups:list --json (#9392) The `firestore:backups:list --json` command now returns the full `listBackupsResponse` object instead of just the `backups` array. This provides more information to the user, including a list of unreachable locations. * Break java (#9451) * error out java version below 21 * error out java version below 21 * error out java version below 21 * update formatting * Remove duplicate error message * Update CHANGELOG.md to include breaking change * update CI to java 21 * Add setup-java action to integration --------- Co-authored-by: Joe Hanley <joehanley@google.com> * Removing support for .bolt rules (#9339) * Removing support for .bolt rules * Changelog * Merging in master * Stop following symlinks when archiving directories (#9284) * Stop following symlinks when archiving directories * Update exfil PR (#9289) * Fix filter for symlinks * Fine. Preserve ordering at the cost of readability * Format * Progress on new approach * Remove unused tar path, fix up zip path to ignore symlinks, add test * More tests * Actually good for real this time ; --------- Co-authored-by: Thomas Bouldin <inlined@users.noreply.github.com> * Remove --open-sesame and --close-sesame (#9532) * Lazy load commands (#9519) * Refactor command loading to be lazy - Updated `src/commands/index.ts` to make `loadCommand` return a lazy runner that only `require`s and registers the command when executed or explicitly loaded. - Updated `src/index.ts` to handle lazy loading in the catch-all command handler by traversing the `client` object and loading the matching command before re-parsing arguments. - Updated `src/bin/cli.ts` to load all commands when no arguments are provided (global help), ensuring the help text is fully populated. - Added cycle detection to the `loadAll` logic to handle circular references in the `client` object. * Refactor command loading to be lazy - Updated `src/commands/index.ts` to make `loadCommand` return a lazy runner that only `require`s and registers the command when executed or explicitly loaded. - Updated `src/index.ts` to handle lazy loading in the catch-all command handler by traversing the `client` object and loading the matching command before re-parsing arguments. - Updated `src/bin/cli.ts` to load all commands when no arguments are provided (global help), ensuring the help text is fully populated. - Added cycle detection to the `loadAll` logic to handle circular references in the `client` object. - Added `scripts/benchmark_load.ts` to measure command load time. * Fix help command as well * Get rid of flawed benchmark test; * Clean up unused vars * Typeguards for style points * Move typeguard * Better types * CHANGELOG * More type cleanup * No anys * Fixing getCommand * Bump timeout? * 20s is better * Removing extra timing code * format --------- Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> * BREAKING: Enforce strict timeout validation for functions (#9540) * feat: enforce strict timeout validation for functions * nit: run formatter * docs: add changelog entry for timeout validation * refactor: optimize timeout validation logic * nit: add "breaking" prefix in changelog. * bug: remove redundant timeout validation. * feat: remove obsolete parseTriggers fallback (#9521) * feat: remove obsolete parseTriggers fallback * nit: clean up conditional paths * feat(dataconnect): Update dataconnect:* commands to use flags for --service & --location (#9312) * Update Firestore Emulator to v1.20.2 (#9565) * Bump FS emulator version to 1.20.2 * Update CHANGELOG.md Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --------- Co-authored-by: Joe Hanley <joehanley@google.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Fix test broken by merge conflict * format * Update version in server.json * Performance improvements and new billing info for get_environment * Implement a `firebase init dataconnect:resolver` command. (#9493) * Implement a `firebase init dataconnect:schema` command. * Gate behind experiment flag and a few other fixes. * Add unit tests. * Fix unit tests. * Rename command to `firebase init dataconnect:resolver`. * Commit missed file. * Fix default location in Cloud Run URL. * Don't prompt for Cloud Run URL. * Remove no-op line. * Gate command at init level. * [VS Code] Fix rendering of generate query code lens (#9588) * fix test * Fix usage in mocks too * Fix package-lock.json * Fix missing auth:import and appdistribution:testers:remove commands (#9590) * Fix missing auth:import and appdistribution:testers:remove commands * Update CHANGELOG.md Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * lint * Dont pass undefined to getCommand (#9596) * Fix --help (#9597) * Fix --help * handle firebase --help * bump fdc emulator to v3 (#9602) * FDC V3 * FDC V3 * merging --------- Co-authored-by: oleina <oleina@google.com> Co-authored-by: Joe Hanley <joehanley@google.com> * lint * Merging * Get rid of uneeded intermediate variable * Fix new test conflict * Addressing flaky tests --------- Co-authored-by: Fred Zhang <fredzqm@google.com> Co-authored-by: Jamie Rothfeder <jamie.rothfeder@gmail.com> Co-authored-by: Jamie Rothfeder <rothbutter@google.com> Co-authored-by: Yuchen Shi <yuchenshi@google.com> Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Co-authored-by: Daniel Lee <danielylee@google.com> Co-authored-by: harshyyy21 <harshoza24@gmail.com> Co-authored-by: Thomas Bouldin <inlined@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Rosalyn Tan <rosalyntan@google.com> Co-authored-by: Anthony Oleinik <48811365+antholeole@users.noreply.github.com> Co-authored-by: oleina <oleina@google.com>
1 parent 3ea1168 commit d74f578

13 files changed

Lines changed: 62 additions & 33 deletions

File tree

scripts/agent-evals/src/mock/mocks/get-environment-mock.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const BASE_ENVIRONMENT_CONFIG = {
1212
projectId: DEFAULT_FIREBASE_PROJECT,
1313
projectAliases: [],
1414
projectDir: PROJECT_DIR,
15+
isBillingEnabled: true,
1516
geminiTosAccepted: true,
1617
authenticatedUser: DEFAULT_FIREBASE_USER,
1718
projectAliasMap: {},

scripts/agent-evals/src/mock/mocks/next-js-with-project-mock.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const environmentConfig = {
1515
projectId: DEFAULT_FIREBASE_PROJECT,
1616
projectAliases: [],
1717
projectDir: PROJECT_DIR,
18+
isBillingEnabled: true,
1819
geminiTosAccepted: true,
1920
authenticatedUser: DEFAULT_FIREBASE_USER,
2021
projectAliasMap: {},

src/appUtils.ts

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -59,29 +59,30 @@ export async function getPlatformsFromFolder(dirPath: string): Promise<Platform[
5959
* @return A list of apps detected.
6060
*/
6161
export async function detectApps(dirPath: string): Promise<App[]> {
62-
const packageJsonFiles = await detectFiles(dirPath, "package.json");
63-
const pubSpecYamlFiles = await detectFiles(dirPath, "pubspec.yaml");
64-
const srcMainFolders = await detectFiles(dirPath, "src/main/");
65-
const xCodeProjects = await detectFiles(dirPath, "*.xcodeproj/");
66-
const adminAndWebApps = (
67-
await Promise.all(packageJsonFiles.map((p) => packageJsonToAdminOrWebApp(dirPath, p)))
68-
).flat();
69-
const flutterAppPromises = await Promise.all(
70-
pubSpecYamlFiles.map((f) => processFlutterDir(dirPath, f)),
62+
const [packageJsonFiles, pubSpecYamlFiles, srcMainFolders, xCodeProjects] = await Promise.all([
63+
detectFiles(dirPath, "package.json"),
64+
detectFiles(dirPath, "pubspec.yaml"),
65+
detectFiles(dirPath, "src/main/"),
66+
detectFiles(dirPath, "*.xcodeproj/"),
67+
]);
68+
69+
const [adminAndWebApps, flutterApps, androidAppsRaw, iosAppsRaw] = await Promise.all([
70+
Promise.all(packageJsonFiles.map((p) => packageJsonToAdminOrWebApp(dirPath, p))).then((r) =>
71+
r.flat(),
72+
),
73+
Promise.all(pubSpecYamlFiles.map((f) => processFlutterDir(dirPath, f))).then((r) => r.flat()),
74+
Promise.all(srcMainFolders.map((f) => processAndroidDir(dirPath, f))).then((r) => r.flat()),
75+
Promise.all(xCodeProjects.map((f) => processIosDir(dirPath, f))).then((r) => r.flat()),
76+
]);
77+
78+
const androidApps = androidAppsRaw.filter(
79+
(a) => !flutterApps.some((f) => isPathInside(f.directory, a.directory)),
7180
);
72-
const flutterApps = flutterAppPromises.flat();
7381

74-
const androidAppPromises = await Promise.all(
75-
srcMainFolders.map((f) => processAndroidDir(dirPath, f)),
82+
const iosApps = iosAppsRaw.filter(
83+
(a) => !flutterApps.some((f) => isPathInside(f.directory, a.directory)),
7684
);
77-
const androidApps = androidAppPromises
78-
.flat()
79-
.filter((a) => !flutterApps.some((f) => isPathInside(f.directory, a.directory)));
8085

81-
const iosAppPromises = await Promise.all(xCodeProjects.map((f) => processIosDir(dirPath, f)));
82-
const iosApps = iosAppPromises
83-
.flat()
84-
.filter((a) => !flutterApps.some((f) => isPathInside(f.directory, a.directory)));
8586
return [...flutterApps, ...androidApps, ...iosApps, ...adminAndWebApps];
8687
}
8788

src/mcp/index.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import { availableTools } from "./tools/index";
4747
import { ClientConfig, McpContext, SERVER_FEATURES, ServerFeature } from "./types";
4848
import { mcpError } from "./util";
4949
import { getDefaultFeatureAvailabilityCheck } from "./util/availability";
50+
import { checkBillingEnabled } from "../gcp/cloudbilling";
5051

5152
const SERVER_VERSION = "0.3.0";
5253

@@ -199,7 +200,8 @@ export class FirebaseMcpServer {
199200
this.logger.debug("detecting active features of Firebase MCP server...");
200201
const projectId = (await this.getProjectId()) || "";
201202
const accountEmail = await this.getAuthenticatedUser();
202-
const ctx = this._createMcpContext(projectId, accountEmail);
203+
const isBillingEnabled = projectId ? await checkBillingEnabled(projectId) : false;
204+
const ctx = this._createMcpContext(projectId, accountEmail, isBillingEnabled);
203205
const detected = await Promise.all(
204206
SERVER_FEATURES.map(async (f) => {
205207
const availabilityCheck = getDefaultFeatureAvailabilityCheck(f);
@@ -250,7 +252,8 @@ export class FirebaseMcpServer {
250252
// We need a project ID and user for the context, but it's ok if they're empty.
251253
const projectId = (await this.getProjectId()) || "";
252254
const accountEmail = await this.getAuthenticatedUser();
253-
const ctx = this._createMcpContext(projectId, accountEmail);
255+
const isBillingEnabled = projectId ? await checkBillingEnabled(projectId) : false;
256+
const ctx = this._createMcpContext(projectId, accountEmail, isBillingEnabled);
254257
return availableTools(ctx, features, this.enabledTools);
255258
}
256259

@@ -264,7 +267,8 @@ export class FirebaseMcpServer {
264267
// We need a project ID and user for the context, but it's ok if they're empty.
265268
const projectId = (await this.getProjectId()) || "";
266269
const accountEmail = await this.getAuthenticatedUser();
267-
const ctx = this._createMcpContext(projectId, accountEmail);
270+
const isBillingEnabled = projectId ? await checkBillingEnabled(projectId) : false;
271+
const ctx = this._createMcpContext(projectId, accountEmail, isBillingEnabled);
268272
return availablePrompts(ctx, features);
269273
}
270274

@@ -303,7 +307,11 @@ export class FirebaseMcpServer {
303307
}
304308
}
305309

306-
private _createMcpContext(projectId: string, accountEmail: string | null): McpContext {
310+
private _createMcpContext(
311+
projectId: string,
312+
accountEmail: string | null,
313+
isBillingEnabled: boolean,
314+
): McpContext {
307315
const options = { projectDir: this.cachedProjectDir, cwd: this.cachedProjectDir };
308316
return {
309317
projectId: projectId,
@@ -312,6 +320,7 @@ export class FirebaseMcpServer {
312320
rc: loadRC(options),
313321
accountEmail,
314322
firebaseCliCommand: this._getFirebaseCliCommand(),
323+
isBillingEnabled,
315324
};
316325
}
317326

@@ -376,7 +385,8 @@ export class FirebaseMcpServer {
376385
if (err) return err;
377386
}
378387

379-
const toolsCtx = this._createMcpContext(projectId, accountEmail);
388+
const isBillingEnabled = projectId ? await checkBillingEnabled(projectId) : false;
389+
const toolsCtx = this._createMcpContext(projectId, accountEmail, isBillingEnabled);
380390
try {
381391
const res = await tool.fn(toolArgs, toolsCtx);
382392
await this.trackGA4("mcp_tool_call", {
@@ -430,7 +440,8 @@ export class FirebaseMcpServer {
430440
const skipAutoAuthForStudio = isFirebaseStudio();
431441
const accountEmail = await this.getAuthenticatedUser(skipAutoAuthForStudio);
432442

433-
const promptsCtx = this._createMcpContext(projectId, accountEmail);
443+
const isBillingEnabled = projectId ? await checkBillingEnabled(projectId) : false;
444+
const promptsCtx = this._createMcpContext(projectId, accountEmail, isBillingEnabled);
434445

435446
try {
436447
const messages = await prompt.fn(promptArgs, promptsCtx);
@@ -470,7 +481,8 @@ export class FirebaseMcpServer {
470481
const skipAutoAuthForStudio = isFirebaseStudio();
471482
const accountEmail = await this.getAuthenticatedUser(skipAutoAuthForStudio);
472483

473-
const resourceCtx = this._createMcpContext(projectId, accountEmail);
484+
const isBillingEnabled = projectId ? await checkBillingEnabled(projectId) : false;
485+
const resourceCtx = this._createMcpContext(projectId, accountEmail, isBillingEnabled);
474486

475487
const resolved = await resolveResource(req.params.uri, resourceCtx);
476488
if (!resolved) {

src/mcp/tools/core/get_environment.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ describe("get_environment tool", () => {
5454
rc,
5555
config,
5656
firebaseCliCommand: "firebase",
57+
isBillingEnabled: false,
5758
};
5859
};
5960

@@ -72,6 +73,7 @@ Project Directory: /test-dir
7273
Project Config Path: <NO CONFIG PRESENT>
7374
Active Project ID: <NONE>
7475
Gemini in Firebase Terms of Service: <NOT ACCEPTED>
76+
Billing Enabled: N/A
7577
Authenticated User: <NONE>
7678
Detected App IDs: <NONE>
7779
Available Project Aliases (format: '[alias]: [projectId]'): <NONE>
@@ -120,6 +122,7 @@ Project Directory: /test-dir
120122
Project Config Path: /test-dir/firebase.json
121123
Active Project ID: test-project (alias: my-alias)
122124
Gemini in Firebase Terms of Service: Accepted
125+
Billing Enabled: No
123126
Authenticated User: test@example.com
124127
Detected App IDs:
125128

src/mcp/tools/core/get_environment.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ interface EnvironmentTemplateValues {
2323
// Whether the user has accepted Gemini in Firebase TOS
2424
geminiTosAccepted: boolean;
2525

26+
// Whether billing is enabled for the project
27+
isBillingEnabled: boolean;
28+
2629
// The authenticated user identifier
2730
authenticatedUser?: string;
2831

@@ -48,6 +51,7 @@ export function hydrateTemplate(config: EnvironmentTemplateValues): string {
4851
: "<NONE>";
4952
const projectConfigPath = config.projectConfigPath || "<NO CONFIG PRESENT>";
5053
const geminiTosAccepted = config.geminiTosAccepted ? "Accepted" : "<NOT ACCEPTED>";
54+
const billingEnabled = config.projectId ? (config.isBillingEnabled ? "Yes" : "No") : "N/A";
5155
const authenticatedUser = config.authenticatedUser || "<NONE>";
5256
const detectedApps =
5357
Object.entries(config.detectedAppIds).length > 0
@@ -67,6 +71,7 @@ Project Directory: ${config.projectDir}
6771
Project Config Path: ${projectConfigPath}
6872
Active Project ID: ${activeProject}
6973
Gemini in Firebase Terms of Service: ${geminiTosAccepted}
74+
Billing Enabled: ${billingEnabled}
7075
Authenticated User: ${authenticatedUser}
7176
Detected App IDs: ${detectedApps}
7277
Available Project Aliases (format: '[alias]: [projectId]'): ${availableProjects}${hasOtherAccounts ? `\nAvailable Accounts: \n\n${availableAccounts}` : ""}
@@ -106,7 +111,7 @@ export const get_environment = tool(
106111
requiresProject: false,
107112
},
108113
},
109-
async (_, { projectId, host, accountEmail, rc, config }) => {
114+
async (_, { projectId, host, accountEmail, rc, config, isBillingEnabled }) => {
110115
const aliases = projectId ? getAliases({ rc }, projectId) : [];
111116
const geminiTosAccepted = !!configstore.get("gemini");
112117
const projectFileExists = config.projectFileExists("firebase.json");
@@ -129,6 +134,7 @@ export const get_environment = tool(
129134
projectDir: host.cachedProjectDir,
130135
projectConfigPath: projectFileExists ? config.path("firebase.json") : undefined,
131136
geminiTosAccepted,
137+
isBillingEnabled,
132138
authenticatedUser: accountEmail || undefined,
133139
projectAliasMap: rc.projects,
134140
allAccounts,

src/mcp/tools/index.spec.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,34 +17,35 @@ describe("availableTools", () => {
1717
} as any,
1818
rc: {} as any,
1919
firebaseCliCommand: "firebase",
20+
isBillingEnabled: true,
2021
};
2122

2223
it("should return specific tools when enabledTools is provided", async () => {
2324
const tools = await availableTools(mockContext, [], ["firebase_login"]);
2425

2526
expect(tools).to.have.length(1);
2627
expect(tools[0].mcp.name).to.equal("firebase_login");
27-
});
28+
}).timeout(2000);
2829

2930
it("should return core tools by default", async () => {
3031
const tools = await availableTools(mockContext, []);
3132
// an example of a core tool
3233
const loginTool = tools.find((t) => t.mcp.name === "firebase_login");
3334

3435
expect(loginTool).to.exist;
35-
});
36+
}).timeout(2000);
3637

3738
it("should include feature-specific tools when activeFeatures is provided", async () => {
3839
const tools = await availableTools(mockContext, ["firestore"]);
3940
const firestoreTool = tools.find((t) => t.mcp.name.startsWith("firestore_"));
4041

4142
expect(firestoreTool).to.exist;
42-
});
43+
}).timeout(2000);
4344

4445
it("should not include feature tools if no active features", async () => {
4546
const tools = await availableTools(mockContext, ["core"]);
4647
const firestoreTool = tools.find((t) => t.mcp.name.startsWith("firestore_"));
4748

4849
expect(firestoreTool).to.not.exist;
49-
});
50+
}).timeout(2000);
5051
});

src/mcp/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,5 @@ export interface McpContext {
3030
host: FirebaseMcpServer;
3131
rc: RC;
3232
firebaseCliCommand: string;
33+
isBillingEnabled: boolean;
3334
}

src/mcp/util/apptesting/availability.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ describe("isAppTestingAvailable", () => {
3131
host: new FirebaseMcpServer({}),
3232
rc: {} as RC,
3333
firebaseCliCommand: "firebase",
34+
isBillingEnabled: false,
3435
});
3536

3637
it("returns false for non mobile project", async () => {

src/mcp/util/availability.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ describe("getDefaultFeatureAvailabilityCheck", () => {
1717
host: {} as any,
1818
rc: {} as any,
1919
firebaseCliCommand: "firebase",
20+
isBillingEnabled: false,
2021
});
2122

2223
beforeEach(() => {

0 commit comments

Comments
 (0)