Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
- Removed MCP tools and prompts that required Gemini in Firebase terms of service.
- Fixes an issue where the `--only` flag was not always respected for `firebase mcp`
- Removed timeout when connecting to Cloud SQL. Hopefully, should mitigate issue #9314. (#9725)
19 changes: 0 additions & 19 deletions src/mcp/errors.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,11 @@
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
import { mcpError } from "./util";
import { ensureGIFApiTos } from "../dataconnect/ensureApis";

export const NO_PROJECT_ERROR = mcpError(
"To proceed requires an active project. Use the `firebase_update_environment` tool to set a project ID",
"PRECONDITION_FAILED",
);

const GEMINI_TOS_ERROR = mcpError(
"To proceed requires features from Gemini in Firebase. You can enable the usage of this service and accept its associated terms of service using `firebase_update_environment`.\n" +
"Learn more about Gemini in Firebase and how it uses your data: https://firebase.google.com/docs/gemini-in-firebase#how-gemini-in-firebase-uses-your-data",
"PRECONDITION_FAILED",
);

/** Enable the Gemini in Firebase API or return an error to accept it */
export async function requireGeminiToS(projectId: string): Promise<CallToolResult | undefined> {
if (!projectId) {
return NO_PROJECT_ERROR;
}
if (!(await ensureGIFApiTos(projectId))) {
return GEMINI_TOS_ERROR;
}
return undefined;
}

export function noProjectDirectory(projectRoot: string | undefined): CallToolResult {

Check warning on line 8 in src/mcp/errors.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing JSDoc comment
return mcpError(
`The current project directory '${
projectRoot || "<NO PROJECT DIRECTORY FOUND>"
Expand All @@ -32,7 +13,7 @@
);
}

export function mcpAuthError(skipADC: boolean): CallToolResult {

Check warning on line 16 in src/mcp/errors.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing JSDoc comment
if (skipADC) {
return mcpError(
`The user is not currently logged into the Firebase CLI, which is required to use this tool. Please run the 'firebase_login' tool to log in.`,
Expand Down
9 changes: 2 additions & 7 deletions src/mcp/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
import { requireAuth } from "../requireAuth";
import { timeoutFallback } from "../timeout";
import { trackGA4 } from "../track";
import { mcpAuthError, NO_PROJECT_ERROR, noProjectDirectory, requireGeminiToS } from "./errors";
import { mcpAuthError, NO_PROJECT_ERROR, noProjectDirectory } from "./errors";
import { LoggingStdioServerTransport } from "./logging-transport";
import { ServerPrompt } from "./prompt";
import { availablePrompts } from "./prompts/index";
Expand Down Expand Up @@ -154,7 +154,7 @@
}

/** Wait until initialization has finished. */
ready() {

Check warning on line 157 in src/mcp/index.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing return type on function
if (this._ready) return Promise.resolve();
return new Promise((resolve, reject) => {
this._readyPromises.push({ resolve: resolve as () => void, reject });
Expand All @@ -165,19 +165,19 @@
return this.clientInfo?.name ?? (isFirebaseStudio() ? "Firebase Studio" : "<unknown-client>");
}

private get clientConfigKey() {

Check warning on line 168 in src/mcp/index.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing return type on function
return `mcp.clientConfigs.${this.clientName}:${this.startupRoot || process.cwd()}`;
}

getStoredClientConfig(): ClientConfig {
return configstore.get(this.clientConfigKey) || {};

Check warning on line 173 in src/mcp/index.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe return of an `any` typed value
}

updateStoredClientConfig(update: Partial<ClientConfig>) {

Check warning on line 176 in src/mcp/index.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing return type on function
const config = configstore.get(this.clientConfigKey) || {};

Check warning on line 177 in src/mcp/index.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe assignment of an `any` value
const newConfig = { ...config, ...update };

Check warning on line 178 in src/mcp/index.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe assignment of an `any` value
configstore.set(this.clientConfigKey, newConfig);
return newConfig;

Check warning on line 180 in src/mcp/index.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe return of an `any` typed value
}

async detectProjectSetup(): Promise<void> {
Expand All @@ -192,7 +192,7 @@
if (this.cachedProjectDir) return this.cachedProjectDir;
const storedRoot = this.getStoredClientConfig().projectRoot;
this.cachedProjectDir = storedRoot || this.startupRoot || process.cwd();
this.logger.debug(`detected and cached project root: ${this.cachedProjectDir}`);

Check warning on line 195 in src/mcp/index.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
return this.cachedProjectDir;
}

Expand Down Expand Up @@ -371,19 +371,14 @@
}
projectId = projectId || "";

// Check if the user is logged in.
// Check if the user is logged in.
const skipAutoAuthForStudio = isFirebaseStudio();
const accountEmail = await this.getAuthenticatedUser(skipAutoAuthForStudio);
if (tool.mcp._meta?.requiresAuth && !accountEmail) {
return mcpAuthError(skipAutoAuthForStudio);
}

// Check if the tool requires Gemini in Firebase API.
if (tool.mcp._meta?.requiresGemini) {
const err = await requireGeminiToS(projectId);
if (err) return err;
}

const isBillingEnabled = projectId ? await checkBillingEnabled(projectId) : false;
const toolsCtx = this._createMcpContext(projectId, accountEmail, isBillingEnabled);
try {
Expand Down
58 changes: 0 additions & 58 deletions src/mcp/prompts/core/consult.ts

This file was deleted.

3 changes: 1 addition & 2 deletions src/mcp/prompts/core/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { init } from "./init";
import { deploy } from "./deploy";
import { consult } from "./consult";

const corePrompts = [deploy, init, consult];
const corePrompts = [deploy, init];

export { corePrompts };
3 changes: 1 addition & 2 deletions src/mcp/tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,8 @@ export interface ServerTool<InputSchema extends ZodTypeAny = ZodTypeAny> {
/** Set this on a tool if it *always* requires a project to work. */
requiresProject?: boolean;
/** Set this on a tool if it *always* requires a signed-in user to work. */
/** Set this on a tool if it *always* requires a signed-in user to work. */
requiresAuth?: boolean;
/** Set this on a tool if it uses Gemini in Firebase API in any way. */
requiresGemini?: boolean;
/** Tools are grouped by feature. --only can configure what tools is available. */
feature?: string;
};
Expand Down
14 changes: 1 addition & 13 deletions src/mcp/tools/core/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { toContent } from "../../util";
import { DEFAULT_RULES } from "../../../init/features/database";
import { actuate, Setup, SetupInfo } from "../../../init/index";
import { freeTrialTermsLink } from "../../../dataconnect/freeTrial";
import { requireGeminiToS } from "../../errors";
import { FirebaseError } from "../../../error";
import {
parseAppId,
Expand Down Expand Up @@ -69,12 +68,6 @@ export const init = tool(
.describe("Provide this object to initialize Cloud Firestore in this project directory."),
dataconnect: z
.object({
app_description: z
.string()
.optional()
.describe(
"Provide a description of the app you are trying to build. If present, Gemini will help generate Data Connect Schema, Connector and seed data",
),
service_id: z
.string()
.optional()
Expand Down Expand Up @@ -202,16 +195,11 @@ export const init = tool(
};
}
if (features.dataconnect) {
if (features.dataconnect.app_description) {
// If app description is provided, ensure the Gemini in Firebase API is enabled.
const err = await requireGeminiToS(projectId);
if (err) return err;
}
featuresList.push("dataconnect");
featureInfo.dataconnectSource = "mcp_init";
featureInfo.dataconnect = {
flow: "",
appDescription: features.dataconnect.app_description || "",
appDescription: "",
serviceId: features.dataconnect.service_id || "",
locationId: features.dataconnect.location_id || "",
cloudSqlInstanceId: features.dataconnect.cloudsql_instance_id || "",
Expand Down
16 changes: 1 addition & 15 deletions src/mcp/tools/core/update_environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { mcpError, toContent } from "../../util";
import { setNewActive } from "../../../commands/use";
import { assertAccount, setProjectAccount } from "../../../auth";
import { existsSync } from "node:fs";
import { configstore } from "../../../configstore";

export const update_environment = tool(
"core",
Expand All @@ -31,12 +30,6 @@ export const update_environment = tool(
.describe(
"The email address of the signed-in user to authenticate as when interacting with the current project directory.",
),
accept_gemini_tos: z
.boolean()
.optional()
.describe(
"Accept the Gemini in Firebase terms of service. Always prompt the user for confirmation before accepting on their behalf.",
),
}),
annotations: {
title: "Update Firebase Environment",
Expand All @@ -48,10 +41,7 @@ export const update_environment = tool(
requiresProject: false,
},
},
async (
{ project_dir, active_project, active_user_account, accept_gemini_tos },
{ config, rc, host },
) => {
async ({ project_dir, active_project, active_user_account }, { config, rc, host }) => {
let output = "";
if (project_dir) {
if (!existsSync(project_dir))
Expand All @@ -70,10 +60,6 @@ export const update_environment = tool(
setProjectAccount(host.cachedProjectDir!, active_user_account);
output += `- Updated active account to '${active_user_account}'\n`;
}
if (accept_gemini_tos) {
configstore.set("gemini", true);
output += `- Accepted the Gemini in Firebase terms of service\n`;
}
if (output === "") output = "No changes were made.";
return toContent(output);
},
Expand Down
53 changes: 0 additions & 53 deletions src/mcp/tools/dataconnect/generate_operation.ts

This file was deleted.

29 changes: 0 additions & 29 deletions src/mcp/tools/dataconnect/generate_schema.ts

This file was deleted.

10 changes: 1 addition & 9 deletions src/mcp/tools/dataconnect/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
import type { ServerTool } from "../../tool";
import { generate_operation } from "./generate_operation";
import { generate_schema } from "./generate_schema";
import { list_services } from "./list_services";
import { compile } from "./compile";
import { execute } from "./execute";

export const dataconnectTools: ServerTool[] = [
compile,
generate_schema,
generate_operation,
list_services,
execute,
];
export const dataconnectTools: ServerTool[] = [compile, list_services, execute];
Loading