diff --git a/CHANGELOG.md b/CHANGELOG.md index 700ddf9cce7..09d1ac47d2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/src/mcp/errors.ts b/src/mcp/errors.ts index 36eaf02d58a..ecab8fc4665 100644 --- a/src/mcp/errors.ts +++ b/src/mcp/errors.ts @@ -1,29 +1,10 @@ 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 { - if (!projectId) { - return NO_PROJECT_ERROR; - } - if (!(await ensureGIFApiTos(projectId))) { - return GEMINI_TOS_ERROR; - } - return undefined; -} - export function noProjectDirectory(projectRoot: string | undefined): CallToolResult { return mcpError( `The current project directory '${ diff --git a/src/mcp/index.ts b/src/mcp/index.ts index 62b35ec26db..118792cbb4e 100644 --- a/src/mcp/index.ts +++ b/src/mcp/index.ts @@ -37,7 +37,7 @@ import { loadRC } from "../rc"; 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"; @@ -371,6 +371,7 @@ export class FirebaseMcpServer { } 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); @@ -378,12 +379,6 @@ export class FirebaseMcpServer { 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 { diff --git a/src/mcp/prompts/core/consult.ts b/src/mcp/prompts/core/consult.ts deleted file mode 100644 index 90bc2b0a4b1..00000000000 --- a/src/mcp/prompts/core/consult.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { getPlatformsFromFolder } from "../../../appUtils"; -import { chatWithFirebase } from "../../../gemini/fdcExperience"; -import { requireGeminiToS } from "../../errors"; -import { prompt } from "../../prompt"; - -export const consult = prompt( - "core", - { - name: "consult", - description: - "Use this command to consult the Firebase Assistant with access to detailed up-to-date documentation for the Firebase platform.", - arguments: [ - { - name: "prompt", - description: "a question to pass to the Gemini in Firebase model", - required: true, - }, - ], - annotations: { - title: "Consult Firebase Assistant", - }, - }, - async ({ prompt }, { config, projectId }) => { - const gifTosError = await requireGeminiToS(projectId); - if (gifTosError) { - return [ - { - role: "user", - content: { - type: "text", - text: `Missing required conditions to run this prompt:\n\n${(gifTosError.content[0] as { text: string })?.text}\n\nPlease ask the user if they would like to accept these terms of service before proceeding. If they decline, inform them that this operation cannot continue without their acceptance.`, - }, - }, - ]; - } - - const platforms = await getPlatformsFromFolder(config.projectDir); - - const gifPrompt = `I am using a coding agent to build with Firebase and I have a specific question that I would like answered. Provide a robust and detailed response that will help the coding agent act on my behalf in a local workspace. - -App Platform(s): ${platforms.join(", ")} - -Question: ${prompt}`; - - const result = await chatWithFirebase(gifPrompt, projectId); - const outputString = result.output.messages?.[0].content ?? ""; - - return [ - { - role: "user", - content: { - type: "text", - text: `I have consulted a Firebase Assistant agent with the following question: "${prompt}". Its response was as follows:\n\n${outputString}\n\nPlease use the information above to respond to my question. I have not seen the response from the Firebase Assistant, so please include all necessary information in your response. Inform the user that they must run the \`firebase:consult\` prompt again if they have followup questions for the Firebase Assistant.`, - }, - }, - ]; - }, -); diff --git a/src/mcp/prompts/core/index.ts b/src/mcp/prompts/core/index.ts index afa32912b2f..90358458b71 100644 --- a/src/mcp/prompts/core/index.ts +++ b/src/mcp/prompts/core/index.ts @@ -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 }; diff --git a/src/mcp/tool.ts b/src/mcp/tool.ts index fc911d12893..4a9ee693691 100644 --- a/src/mcp/tool.ts +++ b/src/mcp/tool.ts @@ -33,9 +33,8 @@ export interface ServerTool { /** 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; }; diff --git a/src/mcp/tools/core/init.ts b/src/mcp/tools/core/init.ts index 03b1133c1ef..6f86b6001ea 100644 --- a/src/mcp/tools/core/init.ts +++ b/src/mcp/tools/core/init.ts @@ -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, @@ -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() @@ -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 || "", diff --git a/src/mcp/tools/core/update_environment.ts b/src/mcp/tools/core/update_environment.ts index 52e5f363627..32782d4bb26 100644 --- a/src/mcp/tools/core/update_environment.ts +++ b/src/mcp/tools/core/update_environment.ts @@ -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", @@ -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", @@ -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)) @@ -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); }, diff --git a/src/mcp/tools/dataconnect/generate_operation.ts b/src/mcp/tools/dataconnect/generate_operation.ts deleted file mode 100644 index 00002b82a9d..00000000000 --- a/src/mcp/tools/dataconnect/generate_operation.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { z } from "zod"; -import { tool } from "../../tool"; -import { toContent } from "../../util"; -import { generateOperation } from "../../../gemini/fdcExperience"; -import { pickOneService } from "../../../dataconnect/load"; - -export const generate_operation = tool( - "dataconnect", - { - name: "generate_operation", - description: - "Use this to generate a single Firebase Data Connect query or mutation based on the currently deployed schema and the provided prompt.", - inputSchema: z.object({ - // Lifted guidance from : https://cloud.google.com/gemini/docs/discover/write-prompts - prompt: z - .string() - .describe( - "Write the prompt like you're talking to a person, describe the task you're trying to accomplish and give details that are specific to the users request", - ), - service_id: z - .string() - .optional() - .describe( - `Service ID of the Data Connect service to compile. Used to disambiguate when there are multiple Data Connect services in firebase.json.`, - ), - location_id: z - .string() - .optional() - .describe( - `Data Connect Service location ID to disambiguate among multiple Data Connect services.`, - ), - }), - annotations: { - title: "Generate Data Connect Operation", - readOnlyHint: true, - }, - _meta: { - requiresProject: true, - requiresAuth: true, - requiresGemini: true, - }, - }, - async ({ prompt, service_id, location_id }, { projectId, config }) => { - const serviceInfo = await pickOneService( - projectId, - config, - service_id || undefined, - location_id || undefined, - ); - const schema = await generateOperation(prompt, serviceInfo.serviceName, projectId); - return toContent(schema); - }, -); diff --git a/src/mcp/tools/dataconnect/generate_schema.ts b/src/mcp/tools/dataconnect/generate_schema.ts deleted file mode 100644 index bccdddb2088..00000000000 --- a/src/mcp/tools/dataconnect/generate_schema.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { z } from "zod"; -import { tool } from "../../tool"; -import { toContent } from "../../util"; -import { generateSchema } from "../../../gemini/fdcExperience"; - -export const generate_schema = tool( - "dataconnect", - { - name: "generate_schema", - description: - "Use this to generate a Firebase Data Connect Schema based on the users description of an app.", - inputSchema: z.object({ - prompt: z.string().describe("A description of an app that you are interested in building"), - }), - annotations: { - title: "Generate Data Connect Schema", - readOnlyHint: true, - }, - _meta: { - requiresProject: true, - requiresAuth: true, - requiresGemini: true, - }, - }, - async ({ prompt }, { projectId }) => { - const schema = await generateSchema(prompt, projectId); - return toContent(schema); - }, -); diff --git a/src/mcp/tools/dataconnect/index.ts b/src/mcp/tools/dataconnect/index.ts index 2bdf339f91a..9935620712f 100644 --- a/src/mcp/tools/dataconnect/index.ts +++ b/src/mcp/tools/dataconnect/index.ts @@ -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];