firebase-tools 14.15.0 → 14.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/lib/command.js +19 -5
  2. package/lib/commands/dataconnect-sdk-generate.js +28 -24
  3. package/lib/commands/init.js +8 -0
  4. package/lib/deploy/functions/build.js +2 -13
  5. package/lib/deploy/functions/prepare.js +2 -1
  6. package/lib/deploy/functions/runtimes/discovery/index.js +1 -1
  7. package/lib/emulator/auth/operations.js +10 -1
  8. package/lib/emulator/dataconnectEmulator.js +27 -24
  9. package/lib/emulator/functionsEmulator.js +1 -1
  10. package/lib/emulator/hub.js +2 -4
  11. package/lib/experiments.js +5 -0
  12. package/lib/functions/env.js +12 -1
  13. package/lib/init/features/dataconnect/index.js +10 -25
  14. package/lib/init/features/dataconnect/sdk.js +0 -1
  15. package/lib/init/features/index.js +1 -2
  16. package/lib/init/index.js +0 -1
  17. package/lib/mcp/index.js +1 -1
  18. package/lib/mcp/prompts/core/deploy.js +1 -1
  19. package/lib/mcp/prompts/dataconnect/index.js +9 -0
  20. package/lib/mcp/prompts/dataconnect/schema.js +68 -0
  21. package/lib/mcp/prompts/index.js +2 -1
  22. package/lib/mcp/tools/core/init.js +11 -2
  23. package/lib/mcp/tools/dataconnect/compile.js +43 -0
  24. package/lib/mcp/tools/dataconnect/execute.js +71 -0
  25. package/lib/mcp/tools/dataconnect/index.js +6 -14
  26. package/lib/mcp/tools/dataconnect/info.js +120 -0
  27. package/lib/mcp/tools/firestore/list_collections.js +0 -3
  28. package/lib/mcp/tools/index.js +3 -0
  29. package/lib/mcp/util/dataconnect/compile.js +18 -0
  30. package/lib/mcp/util/dataconnect/content.js +655 -0
  31. package/lib/track.js +16 -0
  32. package/package.json +1 -1
  33. package/lib/mcp/tools/dataconnect/execute_graphql.js +0 -48
  34. package/lib/mcp/tools/dataconnect/execute_graphql_read.js +0 -48
  35. package/lib/mcp/tools/dataconnect/execute_mutation.js +0 -62
  36. package/lib/mcp/tools/dataconnect/execute_query.js +0 -62
  37. package/lib/mcp/tools/dataconnect/get_connector.js +0 -31
  38. package/lib/mcp/tools/dataconnect/get_schema.js +0 -31
  39. package/lib/mcp/tools/dataconnect/list_services.js +0 -23
  40. /package/lib/mcp/{tools → util}/dataconnect/converter.js +0 -0
  41. /package/lib/mcp/{tools → util}/dataconnect/emulator.js +0 -0
package/lib/command.js CHANGED
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.validateProjectId = exports.Command = void 0;
4
4
  const clc = require("colorette");
5
+ const path = require("node:path");
5
6
  const lodash_1 = require("lodash");
6
7
  const error_1 = require("./error");
7
8
  const utils_1 = require("./utils");
@@ -193,17 +194,15 @@ class Command {
193
194
  }
194
195
  }
195
196
  async applyRC(options) {
196
- var _a, _b;
197
+ var _a;
197
198
  const rc = (0, rc_1.loadRC)(options);
198
199
  options.rc = rc;
199
- let activeProject = options.projectRoot
200
- ? ((_a = configstore_1.configstore.get("activeProjects")) !== null && _a !== void 0 ? _a : {})[options.projectRoot]
201
- : undefined;
200
+ let activeProject = this.configstoreProject(options.projectRoot || process.cwd());
202
201
  const isUseCommand = process.argv.includes("use");
203
202
  if ((0, env_1.isFirebaseStudio)() && !options.project && !isUseCommand) {
204
203
  activeProject = await (0, studio_1.reconcileStudioFirebaseProject)(options, activeProject);
205
204
  }
206
- options.project = (_b = options.project) !== null && _b !== void 0 ? _b : activeProject;
205
+ options.project = (_a = options.project) !== null && _a !== void 0 ? _a : activeProject;
207
206
  if (options.config && !options.project) {
208
207
  options.project = options.config.defaults.project;
209
208
  }
@@ -222,6 +221,21 @@ class Command {
222
221
  options.project = aliases["default"];
223
222
  }
224
223
  }
224
+ configstoreProject(dir) {
225
+ var _a;
226
+ const projectMap = (_a = configstore_1.configstore.get("activeProjects")) !== null && _a !== void 0 ? _a : {};
227
+ let currentDir = path.resolve(dir);
228
+ while (true) {
229
+ if (projectMap[currentDir]) {
230
+ return projectMap[currentDir];
231
+ }
232
+ const parentDir = path.dirname(currentDir);
233
+ if (parentDir === currentDir) {
234
+ return null;
235
+ }
236
+ currentDir = parentDir;
237
+ }
238
+ }
225
239
  async resolveProjectIdentifiers(options) {
226
240
  var _a;
227
241
  if ((_a = options.project) === null || _a === void 0 ? void 0 : _a.match(/^\d+$/)) {
@@ -8,37 +8,41 @@ const projectUtils_1 = require("../projectUtils");
8
8
  const load_1 = require("../dataconnect/load");
9
9
  const logger_1 = require("../logger");
10
10
  const auth_1 = require("../auth");
11
+ const utils_1 = require("../utils");
11
12
  exports.command = new command_1.Command("dataconnect:sdk:generate")
12
13
  .description("generate typed SDKs for your Data Connect connectors")
13
14
  .option("--watch", "watch for changes to your connector GQL files and regenerate your SDKs when updates occur")
14
15
  .action(async (options) => {
15
16
  const projectId = (0, projectUtils_1.needProjectId)(options);
16
17
  const serviceInfos = await (0, load_1.loadAll)(projectId, options.config);
17
- for (const serviceInfo of serviceInfos) {
18
- const configDir = serviceInfo.sourceDirectory;
19
- const hasGeneratables = serviceInfo.connectorInfo.some((c) => {
20
- var _a, _b, _c, _d;
21
- return (((_a = c.connectorYaml.generate) === null || _a === void 0 ? void 0 : _a.javascriptSdk) ||
22
- ((_b = c.connectorYaml.generate) === null || _b === void 0 ? void 0 : _b.kotlinSdk) ||
23
- ((_c = c.connectorYaml.generate) === null || _c === void 0 ? void 0 : _c.swiftSdk) ||
24
- ((_d = c.connectorYaml.generate) === null || _d === void 0 ? void 0 : _d.dartSdk));
18
+ const serviceInfosWithSDKs = serviceInfos.filter((serviceInfo) => serviceInfo.connectorInfo.some((c) => {
19
+ var _a, _b, _c, _d;
20
+ return (((_a = c.connectorYaml.generate) === null || _a === void 0 ? void 0 : _a.javascriptSdk) ||
21
+ ((_b = c.connectorYaml.generate) === null || _b === void 0 ? void 0 : _b.kotlinSdk) ||
22
+ ((_c = c.connectorYaml.generate) === null || _c === void 0 ? void 0 : _c.swiftSdk) ||
23
+ ((_d = c.connectorYaml.generate) === null || _d === void 0 ? void 0 : _d.dartSdk));
24
+ }));
25
+ if (!serviceInfosWithSDKs.length) {
26
+ logger_1.logger.warn("No generated SDKs have been declared in connector.yaml files.");
27
+ logger_1.logger.warn(`Run ${clc.bold("firebase init dataconnect:sdk")} to configure a generated SDK.`);
28
+ logger_1.logger.warn(`See https://firebase.google.com/docs/data-connect/web-sdk for more details of how to configure generated SDKs.`);
29
+ return;
30
+ }
31
+ async function generateSDK(serviceInfo) {
32
+ return dataconnectEmulator_1.DataConnectEmulator.generate({
33
+ configDir: serviceInfo.sourceDirectory,
34
+ watch: options.watch,
35
+ account: (0, auth_1.getProjectDefaultAccount)(options.projectRoot),
25
36
  });
26
- if (!hasGeneratables) {
27
- logger_1.logger.warn("No generated SDKs have been declared in connector.yaml files.");
28
- logger_1.logger.warn(`Run ${clc.bold("firebase init dataconnect:sdk")} to configure a generated SDK.`);
29
- logger_1.logger.warn(`See https://firebase.google.com/docs/data-connect/web-sdk for more details of how to configure generated SDKs.`);
30
- return;
31
- }
32
- for (const conn of serviceInfo.connectorInfo) {
33
- const account = (0, auth_1.getProjectDefaultAccount)(options.projectRoot);
34
- const output = await dataconnectEmulator_1.DataConnectEmulator.generate({
35
- configDir,
36
- connectorId: conn.connectorYaml.connectorId,
37
- watch: options.watch,
38
- account,
39
- });
40
- logger_1.logger.info(output);
41
- logger_1.logger.info(`Generated SDKs for ${conn.connectorYaml.connectorId}`);
37
+ }
38
+ if (options.watch) {
39
+ await Promise.race(serviceInfosWithSDKs.map(generateSDK));
40
+ }
41
+ else {
42
+ for (const s of serviceInfosWithSDKs) {
43
+ await generateSDK(s);
42
44
  }
45
+ const services = serviceInfosWithSDKs.map((s) => s.dataConnectYaml.serviceId).join(", ");
46
+ (0, utils_1.logLabeledSuccess)("dataconnect", `Successfully Generated SDKs for services: ${clc.bold(services)}`);
43
47
  }
44
48
  });
@@ -16,6 +16,7 @@ const utils = require("../utils");
16
16
  const experiments_1 = require("../experiments");
17
17
  const templates_1 = require("../templates");
18
18
  const error_1 = require("../error");
19
+ const utils_1 = require("../utils");
19
20
  const homeDir = os.homedir();
20
21
  const BANNER_TEXT = (0, templates_1.readTemplateSync)("banner.txt");
21
22
  const GITIGNORE_TEMPLATE = (0, templates_1.readTemplateSync)("_gitignore");
@@ -166,6 +167,7 @@ async function initAction(feature, options) {
166
167
  json: true,
167
168
  fallback: {},
168
169
  }),
170
+ instructions: [],
169
171
  };
170
172
  if (process.platform === "win32") {
171
173
  if (!(await (0, prompt_1.confirm)("Are you ready to proceed?"))) {
@@ -218,5 +220,11 @@ async function initAction(feature, options) {
218
220
  }
219
221
  logger_1.logger.info();
220
222
  utils.logSuccess("Firebase initialization complete!");
223
+ if (setup.instructions.length) {
224
+ logger_1.logger.info(`\n${clc.bold("To get started:")}\n`);
225
+ for (const i of setup.instructions) {
226
+ (0, utils_1.logBullet)(i + "\n");
227
+ }
228
+ }
221
229
  }
222
230
  exports.initAction = initAction;
@@ -3,11 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.applyPrefix = exports.toBackend = exports.envWithTypes = exports.resolveBackend = exports.AllIngressSettings = exports.AllVpcEgressSettings = exports.AllFunctionsPlatforms = exports.isBlockingTriggered = exports.isTaskQueueTriggered = exports.isScheduleTriggered = exports.isEventTriggered = exports.isCallableTriggered = exports.isHttpsTriggered = exports.of = exports.empty = void 0;
4
4
  const backend = require("./backend");
5
5
  const proto = require("../../gcp/proto");
6
- const api = require("../../.../../api");
6
+ const api = require("../../api");
7
7
  const params = require("./params");
8
8
  const error_1 = require("../../error");
9
9
  const functional_1 = require("../../functional");
10
- const env_1 = require("../../functions/env");
11
10
  const cel_1 = require("./cel");
12
11
  function empty() {
13
12
  return {
@@ -56,17 +55,7 @@ exports.AllIngressSettings = [
56
55
  "ALLOW_INTERNAL_AND_GCLB",
57
56
  ];
58
57
  async function resolveBackend(opts) {
59
- let paramValues = {};
60
- paramValues = await params.resolveParams(opts.build.params, opts.firebaseConfig, envWithTypes(opts.build.params, opts.userEnvs), opts.nonInteractive, opts.isEmulator);
61
- const toWrite = {};
62
- for (const paramName of Object.keys(paramValues)) {
63
- const paramValue = paramValues[paramName];
64
- if (Object.prototype.hasOwnProperty.call(opts.userEnvs, paramName) || paramValue.internal) {
65
- continue;
66
- }
67
- toWrite[paramName] = paramValue.toString();
68
- }
69
- (0, env_1.writeUserEnvs)(toWrite, opts.userEnvOpt);
58
+ const paramValues = await params.resolveParams(opts.build.params, opts.firebaseConfig, envWithTypes(opts.build.params, opts.userEnvs), opts.nonInteractive, opts.isEmulator);
70
59
  return { backend: toBackend(opts.build, paramValues), envs: paramValues };
71
60
  }
72
61
  exports.resolveBackend = resolveBackend;
@@ -59,6 +59,7 @@ async function prepare(context, options, payload) {
59
59
  if (allowFunctionsConfig && checkAPIsEnabled[1]) {
60
60
  runtimeConfig = Object.assign(Object.assign({}, runtimeConfig), (await (0, prepareFunctionsUpload_1.getFunctionsConfig)(projectId)));
61
61
  }
62
+ context.hasRuntimeConfig = Object.keys(runtimeConfig).some((k) => k !== "firebase");
62
63
  const wantBuilds = await loadCodebases(context.config, options, firebaseConfig, runtimeConfig, context.filters);
63
64
  if (Object.values(wantBuilds).some((b) => b.extensions)) {
64
65
  const extContext = {};
@@ -83,11 +84,11 @@ async function prepare(context, options, payload) {
83
84
  const { backend: wantBackend, envs: resolvedEnvs } = await build.resolveBackend({
84
85
  build: wantBuild,
85
86
  firebaseConfig,
86
- userEnvOpt,
87
87
  userEnvs,
88
88
  nonInteractive: options.nonInteractive,
89
89
  isEmulator: false,
90
90
  });
91
+ functionsEnv.writeResolvedParams(resolvedEnvs, userEnvs, userEnvOpt);
91
92
  let hasEnvsFromParams = false;
92
93
  wantBackend.environmentVariables = envs;
93
94
  for (const envName of Object.keys(resolvedEnvs)) {
@@ -6,7 +6,7 @@ const fs = require("fs");
6
6
  const path = require("path");
7
7
  const yaml = require("yaml");
8
8
  const logger_1 = require("../../../../logger");
9
- const api = require("../../.../../../../api");
9
+ const api = require("../../../../api");
10
10
  const v1alpha1 = require("./v1alpha1");
11
11
  const error_1 = require("../../../../error");
12
12
  const TIMEOUT_OVERRIDE_ENV_VAR = "FUNCTIONS_DISCOVERY_TIMEOUT";
@@ -1512,7 +1512,16 @@ async function mfaSignInFinalize(state, reqBody) {
1512
1512
  (0, errors_1.assert)(sessionInfo, "MISSING_SESSION_INFO");
1513
1513
  const phoneNumber = verifyPhoneNumber(state, sessionInfo, code);
1514
1514
  let { user, signInProvider } = parsePendingCredential(state, reqBody.mfaPendingCredential);
1515
- const enrollment = (_b = user.mfaInfo) === null || _b === void 0 ? void 0 : _b.find((enrollment) => enrollment.unobfuscatedPhoneInfo === phoneNumber);
1515
+ const enrollment = (_b = user.mfaInfo) === null || _b === void 0 ? void 0 : _b.find((enrollment) => {
1516
+ if (enrollment.unobfuscatedPhoneInfo === phoneNumber) {
1517
+ return true;
1518
+ }
1519
+ if (!!enrollment.unobfuscatedPhoneInfo &&
1520
+ obfuscatePhoneNumber(enrollment.unobfuscatedPhoneInfo) === phoneNumber) {
1521
+ return true;
1522
+ }
1523
+ return false;
1524
+ });
1516
1525
  const { updates, extraClaims } = await fetchBlockingFunction(state, state_1.BlockingFunctionEvents.BEFORE_SIGN_IN, user, { signInMethod: signInProvider, signInSecondFactor: "phone" });
1517
1526
  user = state.updateUserByLocalId(user.localId, Object.assign(Object.assign({}, updates), { lastLoginAt: Date.now().toString() }));
1518
1527
  (0, errors_1.assert)(enrollment && enrollment.mfaEnrollmentId, "MFA_ENROLLMENT_NOT_FOUND");
@@ -15,7 +15,6 @@ const emulatorLogger_1 = require("./emulatorLogger");
15
15
  const types_2 = require("../dataconnect/types");
16
16
  const portUtils_1 = require("./portUtils");
17
17
  const registry_1 = require("./registry");
18
- const logger_1 = require("../logger");
19
18
  const load_1 = require("../dataconnect/load");
20
19
  const pgliteServer_1 = require("./dataconnect/pgliteServer");
21
20
  const controller_1 = require("./controller");
@@ -156,33 +155,37 @@ class DataConnectEmulator {
156
155
  }
157
156
  static async generate(args) {
158
157
  const commandInfo = await (0, downloadableEmulators_1.downloadIfNecessary)(types_1.Emulators.DATACONNECT);
159
- const cmd = [
160
- "--logtostderr",
161
- "-v=2",
162
- "generate",
163
- `--config_dir=${args.configDir}`,
164
- `--connector_id=${args.connectorId}`,
165
- ];
158
+ const cmd = ["--logtostderr", "-v=2", "sdk", "generate", `--config_dir=${args.configDir}`];
166
159
  if (args.watch) {
167
160
  cmd.push("--watch");
168
161
  }
169
162
  const env = await DataConnectEmulator.getEnv(args.account);
170
- const res = childProcess.spawnSync(commandInfo.binary, cmd, { encoding: "utf-8", env });
171
- if ((0, downloadableEmulators_1.isIncomaptibleArchError)(res.error)) {
172
- throw new error_1.FirebaseError(`Unknown system error when running the Data Connect toolkit. ` +
173
- `You may be able to fix this by installing Rosetta: ` +
174
- `softwareupdate --install-rosetta`);
175
- }
176
- logger_1.logger.info(res.stderr);
177
- if (res.error) {
178
- throw new error_1.FirebaseError(`Error starting up Data Connect generate: ${res.error.message}`, {
179
- original: res.error,
180
- });
181
- }
182
- if (res.status !== 0) {
183
- throw new error_1.FirebaseError(`Unable to generate your Data Connect SDKs (exit code ${res.status}): ${res.stderr}`);
184
- }
185
- return res.stdout;
163
+ return new Promise((resolve, reject) => {
164
+ try {
165
+ const proc = childProcess.spawn(commandInfo.binary, cmd, { stdio: "inherit", env });
166
+ proc.on("close", (code) => {
167
+ if (code === 0) {
168
+ resolve();
169
+ }
170
+ else {
171
+ reject(new Error(`Command failed with exit code ${code}`));
172
+ }
173
+ });
174
+ proc.on("error", (err) => {
175
+ reject(err);
176
+ });
177
+ }
178
+ catch (e) {
179
+ if ((0, downloadableEmulators_1.isIncomaptibleArchError)(e)) {
180
+ reject(new error_1.FirebaseError(`Unknown system error when running the Data Connect toolkit. ` +
181
+ `You may be able to fix this by installing Rosetta: ` +
182
+ `softwareupdate --install-rosetta`));
183
+ }
184
+ else {
185
+ reject(e);
186
+ }
187
+ }
188
+ });
186
189
  }
187
190
  static async build(args) {
188
191
  var _a;
@@ -341,11 +341,11 @@ class FunctionsEmulator {
341
341
  const resolution = await (0, build_1.resolveBackend)({
342
342
  build: discoveredBuild,
343
343
  firebaseConfig: JSON.parse(firebaseConfig),
344
- userEnvOpt,
345
344
  userEnvs,
346
345
  nonInteractive: false,
347
346
  isEmulator: true,
348
347
  });
348
+ functionsEnv.writeResolvedParams(resolution.envs, userEnvs, userEnvOpt);
349
349
  const discoveredBackend = resolution.backend;
350
350
  const endpoints = backend.allEndpoints(discoveredBackend);
351
351
  (0, functionsEmulatorShared_1.prepareEndpoints)(endpoints);
@@ -10,7 +10,6 @@ const types_1 = require("./types");
10
10
  const hubExport_1 = require("./hubExport");
11
11
  const registry_1 = require("./registry");
12
12
  const ExpressBasedEmulator_1 = require("./ExpressBasedEmulator");
13
- const vsCodeUtils_1 = require("../vsCodeUtils");
14
13
  const pkg = require("../../package.json");
15
14
  class EmulatorHub extends ExpressBasedEmulator_1.ExpressBasedEmulator {
16
15
  static readLocatorFile(projectId) {
@@ -20,9 +19,8 @@ class EmulatorHub extends ExpressBasedEmulator_1.ExpressBasedEmulator {
20
19
  }
21
20
  const data = fs.readFileSync(locatorPath, "utf8").toString();
22
21
  const locator = JSON.parse(data);
23
- if (!(0, vsCodeUtils_1.isVSCodeExtension)() && locator.version !== this.CLI_VERSION) {
24
- logger_1.logger.debug(`Found locator with mismatched version, ignoring: ${JSON.stringify(locator)}`);
25
- return undefined;
22
+ if (locator.version !== this.CLI_VERSION) {
23
+ logger_1.logger.debug(`Found emulator locator with different version: ${JSON.stringify(locator)}, CLI_VERSION: ${this.CLI_VERSION}`);
26
24
  }
27
25
  return locator;
28
26
  }
@@ -116,6 +116,11 @@ exports.ALL_EXPERIMENTS = experiments({
116
116
  default: true,
117
117
  public: false,
118
118
  },
119
+ mcpalpha: {
120
+ shortDescription: "Opt-in to early MCP features before they're widely released.",
121
+ default: false,
122
+ public: true,
123
+ },
119
124
  apptesting: {
120
125
  shortDescription: "Adds experimental App Testing feature",
121
126
  public: true,
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.loadFirebaseEnvs = exports.loadUserEnvs = exports.checkForDuplicateKeys = exports.writeUserEnvs = exports.hasUserEnvs = exports.parseStrict = exports.validateKey = exports.KeyValidationError = exports.parse = void 0;
3
+ exports.writeResolvedParams = exports.loadFirebaseEnvs = exports.loadUserEnvs = exports.checkForDuplicateKeys = exports.writeUserEnvs = exports.hasUserEnvs = exports.parseStrict = exports.validateKey = exports.KeyValidationError = exports.parse = void 0;
4
4
  const clc = require("colorette");
5
5
  const fs = require("fs");
6
6
  const path = require("path");
@@ -250,3 +250,14 @@ function loadFirebaseEnvs(firebaseConfig, projectId) {
250
250
  };
251
251
  }
252
252
  exports.loadFirebaseEnvs = loadFirebaseEnvs;
253
+ function writeResolvedParams(resolvedEnvs, userEnvs, userEnvOpt) {
254
+ const toWrite = {};
255
+ for (const paramName of Object.keys(resolvedEnvs)) {
256
+ const paramValue = resolvedEnvs[paramName];
257
+ if (!paramValue.internal && !Object.prototype.hasOwnProperty.call(userEnvs, paramName)) {
258
+ toWrite[paramName] = paramValue.toString();
259
+ }
260
+ }
261
+ writeUserEnvs(toWrite, userEnvOpt);
262
+ }
263
+ exports.writeResolvedParams = writeResolvedParams;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.newUniqueId = exports.toDNSCompatibleId = exports.postSetup = exports.actuate = exports.askQuestions = void 0;
3
+ exports.newUniqueId = exports.toDNSCompatibleId = exports.actuate = exports.askQuestions = void 0;
4
4
  const path_1 = require("path");
5
5
  const clc = require("colorette");
6
6
  const fs = require("fs-extra");
@@ -103,6 +103,15 @@ async function actuate(setup, config, options) {
103
103
  flow: info.analyticsFlow,
104
104
  });
105
105
  }
106
+ if (info.appDescription) {
107
+ setup.instructions.push(`You can visualize the Data Connect Schema in Firebase Console:
108
+
109
+ https://console.firebase.google.com/project/${setup.projectId}/dataconnect/locations/${info.locationId}/services/${info.serviceId}/schema`);
110
+ }
111
+ if (!setup.isBillingEnabled) {
112
+ setup.instructions.push((0, freeTrial_1.upgradeInstructions)(setup.projectId || "your-firebase-project"));
113
+ }
114
+ setup.instructions.push(`Install the Data Connect VS Code Extensions. You can explore Data Connect Query on local pgLite and Cloud SQL Postgres Instance.`);
106
115
  }
107
116
  exports.actuate = actuate;
108
117
  async function actuateWithInfo(setup, config, info, options) {
@@ -222,30 +231,6 @@ function schemasDeploySequence(projectId, info, schemaFiles, linkToCloudSql) {
222
231
  },
223
232
  ];
224
233
  }
225
- async function postSetup(setup) {
226
- var _a;
227
- const info = (_a = setup.featureInfo) === null || _a === void 0 ? void 0 : _a.dataconnect;
228
- if (!info) {
229
- throw new Error("Data Connect feature RequiredInfo is not provided");
230
- }
231
- const instructions = [];
232
- if (info.appDescription) {
233
- instructions.push(`You can visualize the Data Connect Schema in Firebase Console:
234
-
235
- https://console.firebase.google.com/project/${setup.projectId}/dataconnect/locations/${info.locationId}/services/${info.serviceId}/schema`);
236
- }
237
- if (!setup.isBillingEnabled) {
238
- instructions.push((0, freeTrial_1.upgradeInstructions)(setup.projectId || "your-firebase-project"));
239
- }
240
- instructions.push(`Install the Data Connect VS Code Extensions. You can explore Data Connect Query on local pgLite and Cloud SQL Postgres Instance.`);
241
- if (instructions.length) {
242
- logger_1.logger.info(`\n${clc.bold("To get started with Firebase Data Connect:")}`);
243
- for (const i of instructions) {
244
- (0, utils_1.logBullet)(i + "\n");
245
- }
246
- }
247
- }
248
- exports.postSetup = postSetup;
249
234
  async function writeFiles(config, info, serviceGql, options) {
250
235
  const dir = config.get("dataconnect.source") || "dataconnect";
251
236
  const subbedDataconnectYaml = subDataconnectYamlValues(Object.assign(Object.assign({}, info), { connectorDirs: serviceGql.connectors.map((c) => c.path) }));
@@ -153,7 +153,6 @@ async function actuateWithInfo(setup, config, info) {
153
153
  const account = (0, auth_1.getGlobalDefaultAccount)();
154
154
  await dataconnectEmulator_1.DataConnectEmulator.generate({
155
155
  configDir: connectorInfo.directory,
156
- connectorId: connectorInfo.connectorYaml.connectorId,
157
156
  account,
158
157
  });
159
158
  (0, utils_1.logLabeledSuccess)("dataconnect", `Installed generated SDKs for ${clc.bold(apps.map((a) => (0, appFinder_1.appDescription)(a)).join(", "))}`);
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.aitools = exports.apptestingAcutate = exports.apptestingAskQuestions = exports.genkit = exports.apphosting = exports.dataconnectSdkActuate = exports.dataconnectSdkAskQuestions = exports.dataconnectPostSetup = exports.dataconnectActuate = exports.dataconnectAskQuestions = exports.hostingGithub = exports.remoteconfig = exports.project = exports.extensions = exports.emulators = exports.storageActuate = exports.storageAskQuestions = exports.hosting = exports.functions = exports.firestoreActuate = exports.firestoreAskQuestions = exports.databaseActuate = exports.databaseAskQuestions = exports.account = void 0;
3
+ exports.aitools = exports.apptestingAcutate = exports.apptestingAskQuestions = exports.genkit = exports.apphosting = exports.dataconnectSdkActuate = exports.dataconnectSdkAskQuestions = exports.dataconnectActuate = exports.dataconnectAskQuestions = exports.hostingGithub = exports.remoteconfig = exports.project = exports.extensions = exports.emulators = exports.storageActuate = exports.storageAskQuestions = exports.hosting = exports.functions = exports.firestoreActuate = exports.firestoreAskQuestions = exports.databaseActuate = exports.databaseAskQuestions = exports.account = void 0;
4
4
  var account_1 = require("./account");
5
5
  Object.defineProperty(exports, "account", { enumerable: true, get: function () { return account_1.doSetup; } });
6
6
  var database_1 = require("./database");
@@ -29,7 +29,6 @@ Object.defineProperty(exports, "hostingGithub", { enumerable: true, get: functio
29
29
  var dataconnect_1 = require("./dataconnect");
30
30
  Object.defineProperty(exports, "dataconnectAskQuestions", { enumerable: true, get: function () { return dataconnect_1.askQuestions; } });
31
31
  Object.defineProperty(exports, "dataconnectActuate", { enumerable: true, get: function () { return dataconnect_1.actuate; } });
32
- Object.defineProperty(exports, "dataconnectPostSetup", { enumerable: true, get: function () { return dataconnect_1.postSetup; } });
33
32
  var sdk_1 = require("./dataconnect/sdk");
34
33
  Object.defineProperty(exports, "dataconnectSdkAskQuestions", { enumerable: true, get: function () { return sdk_1.askQuestions; } });
35
34
  Object.defineProperty(exports, "dataconnectSdkActuate", { enumerable: true, get: function () { return sdk_1.actuate; } });
package/lib/init/index.js CHANGED
@@ -23,7 +23,6 @@ const featuresList = [
23
23
  name: "dataconnect",
24
24
  askQuestions: features.dataconnectAskQuestions,
25
25
  actuate: features.dataconnectActuate,
26
- postSetup: features.dataconnectPostSetup,
27
26
  },
28
27
  {
29
28
  name: "dataconnect:sdk",
package/lib/mcp/index.js CHANGED
@@ -154,7 +154,7 @@ class FirebaseMcpServer {
154
154
  const emulators = await hubClient.getEmulators();
155
155
  const emulatorInfo = emulators[emulatorType];
156
156
  if (!emulatorInfo) {
157
- 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.");
157
+ throw Error(`No ${emulatorType} Emulator found running. Make sure your project firebase.json file includes ${emulatorType} and then rerun emulator using \`firebase emulators:start\` from your project directory.`);
158
158
  }
159
159
  const host = emulatorInfo.host.includes(":") ? `[${emulatorInfo.host}]` : emulatorInfo.host;
160
160
  return `http://${host}:${emulatorInfo.port}`;
@@ -83,7 +83,7 @@ Follow the steps below taking note of any user instructions provided above.
83
83
  Create \`firebase.json\ with an "apphosting" configuration, setting backendId to the app's name in package.json: \`{"apphosting": {"backendId": "<backendId>"}}\
84
84
  4b. If the app does NOT require SSR, configure Firebase Hosting:
85
85
  Create \`firebase.json\ with a "hosting" configuration. Add a \`{"hosting": {"predeploy": "<build_script>"}}\` config to build before deploying.
86
- 5. Check if there is an active Firebase project for this environment (the \`firebase_get_environment\` tool may be helpful). If there is, proceed using that project. If there is not an active project, give the user two options: Use an existing Firebase project or Create a new one. Wait for their response before proceeding.
86
+ 5. Check if there is an active Firebase project for this environment (the \`firebase_get_environment\` tool may be helpful). If there is, provide the active project ID to the user and ask them if they want to proceed using that project. If there is not an active project, give the user two options: Provide an existing project ID or create a new project. Only use the list_projects tool on user request. Wait for their response before proceeding.
87
87
  5a. If the user chooses to use an existing Firebase project, the \`firebase_list_projects\` tool may be helpful. Set the selected project as the active project (the \`firebase_update_environment\` tool may be helpful).
88
88
  5b. If the user chooses to create a new project, use the \`firebase_create_project \` tool. Then set the new project as the active project (the \`firebase_update_environment\` tool may be helpful).
89
89
  6. If firebase.json contains an "apphosting" configuration, check if a backend exists matching the provided backendId (the \`apphosting_list_backends\` tool may be helpful).
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dataconnectPrompts = void 0;
4
+ const experiments_1 = require("../../../experiments");
5
+ const schema_1 = require("./schema");
6
+ exports.dataconnectPrompts = [];
7
+ if ((0, experiments_1.isEnabled)("mcpalpha")) {
8
+ exports.dataconnectPrompts.push(schema_1.schema);
9
+ }
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.schema = void 0;
4
+ const prompt_1 = require("../../prompt");
5
+ const load_1 = require("../../../dataconnect/load");
6
+ const content_1 = require("../../util/dataconnect/content");
7
+ const compile_1 = require("../../util/dataconnect/compile");
8
+ function renderServices(fdcServices) {
9
+ var _a;
10
+ if (!fdcServices.length)
11
+ return "Data Connect Status: <UNCONFIGURED>";
12
+ return `\n\n## Data Connect Schema
13
+
14
+ The following is the up-to-date content of existing schema files (their paths are relative to the Data Connect source directory).
15
+
16
+ ${(_a = fdcServices[0].schema.source.files) === null || _a === void 0 ? void 0 : _a.map((f) => `\`\`\`graphql ${f.path}\n${f.content}\n\`\`\``).join("\n\n")}`;
17
+ }
18
+ function renderErrors(errors) {
19
+ return `\n\n## Current Schema Build Errors\n\n${errors || "<NO ERRORS>"}`;
20
+ }
21
+ exports.schema = (0, prompt_1.prompt)({
22
+ name: "schema",
23
+ description: "Generate or update your Firebase Data Connect schema.",
24
+ arguments: [
25
+ {
26
+ name: "prompt",
27
+ description: "describe the schema you want generated or the edits you want to make to your existing schema",
28
+ required: true,
29
+ },
30
+ ],
31
+ annotations: {
32
+ title: "Generate Data Connect Schema",
33
+ },
34
+ }, async ({ prompt }, { config, projectId, accountEmail }) => {
35
+ const fdcServices = await (0, load_1.loadAll)(projectId, config);
36
+ const buildErrors = fdcServices.length
37
+ ? await (0, compile_1.compileErrors)(fdcServices[0].sourceDirectory)
38
+ : "";
39
+ return [
40
+ {
41
+ role: "user",
42
+ content: {
43
+ type: "text",
44
+ text: `
45
+ ${content_1.MAIN_INSTRUCTIONS}\n\n${content_1.BUILTIN_SDL}
46
+
47
+ ==== CURRENT ENVIRONMENT INFO ====
48
+
49
+ User Email: ${accountEmail || "<NONE>"}
50
+ Project ID: ${projectId || "<NONE>"}
51
+ ${renderServices(fdcServices)}${renderErrors(buildErrors)}
52
+
53
+ ==== USER PROMPT ====
54
+
55
+ ${prompt}
56
+
57
+ ==== TASK INSTRUCTIONS ====
58
+
59
+ 1. If Data Connect is marked as \`<UNCONFIGURED>\`, first run the \`firebase_init\` tool with \`{dataconnect: {}}\` arguments to initialize it.
60
+ 2. If there is not an existing schema to work with (or the existing schema is the commented-out default schema about a movie app), follow the user's prompt to generate a robust schema meeting the specified requirements.
61
+ 3. If there is already a schema, perform edits to the existing schema file(s) based on the user's instructions. If schema build errors are present and seem relevant to your changes, attempt to fix them.
62
+ 4. After you have performed edits on the schema, run the \`dataconnect_compile\` tool to build the schema and see if there are any errors. Fix errors that are related to the user's prompt or your changes.
63
+ 5. If there are errors, attempt to fix them. If you have attempted to fix them 3 times without success, ask the user for help.
64
+ 6. If there are no errors, write a brief paragraph summarizing your changes.`,
65
+ },
66
+ },
67
+ ];
68
+ });
@@ -2,12 +2,13 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.availablePrompts = void 0;
4
4
  const core_1 = require("./core");
5
+ const dataconnect_1 = require("./dataconnect");
5
6
  const crashlytics_1 = require("./crashlytics");
6
7
  const prompts = {
7
8
  core: core_1.corePrompts,
8
9
  firestore: [],
9
10
  storage: [],
10
- dataconnect: [],
11
+ dataconnect: dataconnect_1.dataconnectPrompts,
11
12
  auth: [],
12
13
  messaging: [],
13
14
  remoteconfig: [],
@@ -149,10 +149,19 @@ exports.init = (0, tool_1.tool)({
149
149
  projectId: projectId,
150
150
  features: [...featuresList],
151
151
  featureInfo: featureInfo,
152
+ instructions: [],
152
153
  };
153
154
  await (0, index_1.actuate)(setup, config, { force: true });
154
155
  config.writeProjectFile("firebase.json", setup.config);
155
156
  config.writeProjectFile(".firebaserc", setup.rcfile);
156
- return (0, util_1.toContent)(`Successfully setup the project ${projectId} with those features: ${featuresList.join(", ")}` +
157
- " To deploy them, you can run `firebase deploy` in command line.");
157
+ if (featureInfo.dataconnectSdk && !featureInfo.dataconnectSdk.apps.length) {
158
+ setup.instructions.push(`No app is found in the current folder. We recommend you create an app (web, ios, android) first, then re-run the 'firebase_init' MCP tool to add Data Connect SDKs to your apps.
159
+ Consider popular commands like 'npx create-react-app my-app', 'npx create-next-app my-app', 'flutter create my-app', etc`);
160
+ }
161
+ return (0, util_1.toContent)(`Successfully setup those features: ${featuresList.join(", ")}
162
+
163
+ To get started:
164
+
165
+ - ${setup.instructions.join("\n\n- ")}
166
+ `);
158
167
  });