firebase-tools 14.2.2 → 14.3.1

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 (139) hide show
  1. package/lib/api.js +4 -2
  2. package/lib/apphosting/backend.js +12 -23
  3. package/lib/apphosting/config.js +9 -20
  4. package/lib/apphosting/githubConnections.js +65 -99
  5. package/lib/apphosting/repo.js +22 -36
  6. package/lib/apphosting/secrets/dialogs.js +2 -3
  7. package/lib/apphosting/utils.js +1 -3
  8. package/lib/auth.js +1 -4
  9. package/lib/commands/apphosting-backends-delete.js +5 -5
  10. package/lib/commands/apphosting-secrets-set.js +2 -6
  11. package/lib/commands/apps-create.js +2 -3
  12. package/lib/commands/apps-sdkconfig.js +2 -7
  13. package/lib/commands/database-import.js +4 -6
  14. package/lib/commands/database-remove.js +4 -6
  15. package/lib/commands/database-set.js +6 -6
  16. package/lib/commands/database-update.js +5 -5
  17. package/lib/commands/dataconnect-sql-shell.js +4 -6
  18. package/lib/commands/ext-dev-deprecate.js +1 -0
  19. package/lib/commands/ext-dev-init.js +3 -12
  20. package/lib/commands/ext-dev-register.js +2 -4
  21. package/lib/commands/ext-dev-undeprecate.js +1 -2
  22. package/lib/commands/ext-dev-usage.js +1 -3
  23. package/lib/commands/ext-export.js +1 -2
  24. package/lib/commands/ext-install.js +1 -0
  25. package/lib/commands/ext-sdk-install.js +1 -0
  26. package/lib/commands/ext-update.js +1 -0
  27. package/lib/commands/firestore-backups-delete.js +1 -2
  28. package/lib/commands/firestore-backups-schedules-delete.js +1 -2
  29. package/lib/commands/firestore-databases-delete.js +1 -5
  30. package/lib/commands/firestore-delete.js +6 -6
  31. package/lib/commands/functions-artifacts-setpolicy.js +12 -2
  32. package/lib/commands/functions-config-export.js +5 -9
  33. package/lib/commands/functions-delete.js +5 -5
  34. package/lib/commands/functions-deletegcfartifacts.js +4 -4
  35. package/lib/commands/functions-secrets-destroy.js +8 -10
  36. package/lib/commands/functions-secrets-prune.js +12 -13
  37. package/lib/commands/functions-secrets-set.js +11 -15
  38. package/lib/commands/hosting-channel-create.js +1 -2
  39. package/lib/commands/hosting-channel-delete.js +4 -4
  40. package/lib/commands/hosting-channel-open.js +2 -3
  41. package/lib/commands/hosting-disable.js +5 -5
  42. package/lib/commands/hosting-sites-delete.js +4 -4
  43. package/lib/commands/init.js +41 -58
  44. package/lib/commands/login.js +1 -5
  45. package/lib/commands/logout.js +2 -3
  46. package/lib/commands/open.js +1 -2
  47. package/lib/commands/projects-create.js +1 -2
  48. package/lib/commands/remoteconfig-rollback.js +5 -5
  49. package/lib/commands/use.js +110 -110
  50. package/lib/config.js +1 -2
  51. package/lib/dataconnect/build.js +2 -4
  52. package/lib/dataconnect/client.js +20 -1
  53. package/lib/dataconnect/dataplaneClient.js +16 -1
  54. package/lib/dataconnect/schemaMigration.js +1 -6
  55. package/lib/dataconnect/types.js +5 -1
  56. package/lib/deploy/functions/params.js +10 -17
  57. package/lib/deploy/functions/prompts.js +6 -28
  58. package/lib/emulator/commandUtils.js +4 -13
  59. package/lib/emulator/controller.js +1 -2
  60. package/lib/emulator/downloadableEmulators.js +12 -12
  61. package/lib/emulator/initEmulators.js +9 -20
  62. package/lib/ensureApiEnabled.js +11 -1
  63. package/lib/extensions/askUserForEventsConfig.js +6 -15
  64. package/lib/extensions/askUserForParam.js +23 -52
  65. package/lib/extensions/checkProjectBilling.js +3 -9
  66. package/lib/extensions/diagnose.js +3 -6
  67. package/lib/extensions/extensionsHelper.js +17 -27
  68. package/lib/extensions/manifest.js +1 -2
  69. package/lib/extensions/tos.js +10 -2
  70. package/lib/extensions/utils.js +1 -10
  71. package/lib/frameworks/index.js +1 -2
  72. package/lib/frameworks/next/index.js +7 -4
  73. package/lib/frameworks/vite/index.js +1 -2
  74. package/lib/functions/secrets.js +14 -15
  75. package/lib/gcp/auth.js +29 -1
  76. package/lib/gcp/storage.js +19 -1
  77. package/lib/gif/fdcExperience.js +45 -0
  78. package/lib/hosting/interactive.js +2 -3
  79. package/lib/init/features/account.js +2 -4
  80. package/lib/init/features/database.js +11 -20
  81. package/lib/init/features/dataconnect/index.js +7 -14
  82. package/lib/init/features/dataconnect/sdk.js +15 -22
  83. package/lib/init/features/emulators.js +19 -41
  84. package/lib/init/features/firestore/index.js +2 -6
  85. package/lib/init/features/firestore/indexes.js +18 -31
  86. package/lib/init/features/firestore/rules.js +18 -31
  87. package/lib/init/features/functions/index.js +9 -16
  88. package/lib/init/features/functions/javascript.js +16 -30
  89. package/lib/init/features/functions/npm-dependencies.js +4 -8
  90. package/lib/init/features/functions/python.js +1 -3
  91. package/lib/init/features/functions/typescript.js +24 -43
  92. package/lib/init/features/genkit/index.js +23 -38
  93. package/lib/init/features/hosting/github.js +20 -51
  94. package/lib/init/features/hosting/index.js +36 -57
  95. package/lib/init/features/project.js +6 -16
  96. package/lib/init/features/remoteconfig.js +2 -8
  97. package/lib/init/features/storage.js +1 -3
  98. package/lib/management/apps.js +19 -44
  99. package/lib/management/projects.js +17 -28
  100. package/lib/mcp/index.js +13 -11
  101. package/lib/mcp/tools/auth/{disable_auth_user.js → disable_user.js} +3 -3
  102. package/lib/mcp/tools/auth/{get_auth_user.js → get_user.js} +4 -4
  103. package/lib/mcp/tools/auth/index.js +5 -4
  104. package/lib/mcp/tools/auth/{set_auth_claims.js → set_claims.js} +3 -3
  105. package/lib/mcp/tools/auth/set_sms_region_policy.js +36 -0
  106. package/lib/mcp/tools/core/consult_assistant.js +27 -0
  107. package/lib/mcp/tools/core/index.js +5 -3
  108. package/lib/mcp/tools/dataconnect/converter.js +50 -0
  109. package/lib/mcp/tools/dataconnect/execute_graphql.js +32 -0
  110. package/lib/mcp/tools/dataconnect/execute_graphql_read.js +32 -0
  111. package/lib/mcp/tools/dataconnect/execute_mutation.js +50 -0
  112. package/lib/mcp/tools/dataconnect/execute_query.js +50 -0
  113. package/lib/mcp/tools/dataconnect/generate_operation.js +33 -0
  114. package/lib/mcp/tools/dataconnect/generate_schema.js +25 -0
  115. package/lib/mcp/tools/dataconnect/get_connector.js +31 -0
  116. package/lib/mcp/tools/dataconnect/get_schema.js +31 -0
  117. package/lib/mcp/tools/dataconnect/index.js +20 -2
  118. package/lib/mcp/tools/dataconnect/{list_dataconnect_services.js → list_services.js} +4 -4
  119. package/lib/mcp/tools/{core/get_firebase_directory.js → directory/get_project_directory.js} +5 -5
  120. package/lib/mcp/tools/directory/index.js +6 -0
  121. package/lib/mcp/tools/{core/set_firebase_directory.js → directory/set_project_directory.js} +3 -3
  122. package/lib/mcp/tools/firestore/get_documents.js +2 -2
  123. package/lib/mcp/tools/firestore/{get_firestore_rules.js → get_rules.js} +6 -6
  124. package/lib/mcp/tools/firestore/index.js +2 -2
  125. package/lib/mcp/tools/index.js +26 -8
  126. package/lib/mcp/tools/storage/get_download_url.js +31 -0
  127. package/lib/mcp/tools/storage/get_rules.js +26 -0
  128. package/lib/mcp/tools/storage/index.js +6 -0
  129. package/lib/mcp/types.js +1 -1
  130. package/lib/prompt.js +78 -65
  131. package/lib/requireTosAcceptance.js +4 -0
  132. package/lib/rulesDeploy.js +10 -15
  133. package/lib/track.js +1 -34
  134. package/lib/utils.js +27 -5
  135. package/package.json +2 -3
  136. package/lib/mcp/tools/project/index.js +0 -7
  137. /package/lib/mcp/tools/{project → core}/get_project.js +0 -0
  138. /package/lib/mcp/tools/{project → core}/get_sdk_config.js +0 -0
  139. /package/lib/mcp/tools/{project → core}/list_apps.js +0 -0
@@ -33,8 +33,7 @@ async function doSetup(setup, config, options) {
33
33
  hasHostingSite = false;
34
34
  }
35
35
  if (!hasHostingSite) {
36
- const confirmCreate = await (0, prompt_1.promptOnce)({
37
- type: "confirm",
36
+ const confirmCreate = await (0, prompt_1.confirm)({
38
37
  message: "A Firebase Hosting site is required to deploy. Would you like to create one now?",
39
38
  default: true,
40
39
  });
@@ -56,44 +55,33 @@ async function doSetup(setup, config, options) {
56
55
  if (experiments.isEnabled("webframeworks")) {
57
56
  if (discoveredFramework) {
58
57
  const name = frameworks_1.WebFrameworks[discoveredFramework.framework].name;
59
- await (0, prompt_1.promptOnce)({
60
- name: "useDiscoveredFramework",
61
- type: "confirm",
62
- default: true,
58
+ setup.hosting.useDiscoveredFramework = await (0, prompt_1.confirm)({
63
59
  message: `Detected an existing ${name} codebase in the current directory, should we use this?`,
64
- }, setup.hosting);
60
+ default: true,
61
+ });
65
62
  }
66
63
  if (setup.hosting.useDiscoveredFramework) {
67
64
  setup.hosting.source = ".";
68
65
  setup.hosting.useWebFrameworks = true;
69
66
  }
70
67
  else {
71
- await (0, prompt_1.promptOnce)({
72
- name: "useWebFrameworks",
73
- type: "confirm",
74
- default: false,
75
- message: `Do you want to use a web framework? (${clc.bold("experimental")})`,
76
- }, setup.hosting);
68
+ setup.hosting.useWebFrameworks = await (0, prompt_1.confirm)(`Do you want to use a web framework? (${clc.bold("experimental")})`);
77
69
  }
78
70
  }
79
71
  if (setup.hosting.useWebFrameworks) {
80
- await (0, prompt_1.promptOnce)({
81
- name: "source",
82
- type: "input",
83
- default: "hosting",
72
+ setup.hosting.source = await (0, prompt_1.input)({
84
73
  message: "What folder would you like to use for your web application's root directory?",
85
- }, setup.hosting);
74
+ default: "hosting",
75
+ });
86
76
  if (setup.hosting.source !== ".")
87
77
  delete setup.hosting.useDiscoveredFramework;
88
78
  discoveredFramework = await (0, frameworks_1.discover)((0, path_1.join)(config.projectDir, setup.hosting.source));
89
79
  if (discoveredFramework) {
90
80
  const name = frameworks_1.WebFrameworks[discoveredFramework.framework].name;
91
- await (0, prompt_1.promptOnce)({
92
- name: "useDiscoveredFramework",
93
- type: "confirm",
94
- default: true,
81
+ setup.hosting.useDiscoveredFramework = await (0, prompt_1.confirm)({
95
82
  message: `Detected an existing ${name} codebase in ${setup.hosting.source}, should we use this?`,
96
- }, setup.hosting);
83
+ default: true,
84
+ });
97
85
  }
98
86
  if (setup.hosting.useDiscoveredFramework && discoveredFramework) {
99
87
  setup.hosting.webFramework = discoveredFramework.framework;
@@ -108,24 +96,24 @@ async function doSetup(setup, config, options) {
108
96
  }
109
97
  }
110
98
  const defaultChoice = (_a = choices.find(({ value }) => value === (discoveredFramework === null || discoveredFramework === void 0 ? void 0 : discoveredFramework.framework))) === null || _a === void 0 ? void 0 : _a.value;
111
- await (0, prompt_1.promptOnce)({
112
- name: "whichFramework",
113
- type: "list",
114
- message: "Please choose the framework:",
115
- default: defaultChoice,
116
- choices,
117
- }, setup.hosting);
99
+ setup.hosting.whichFramework =
100
+ setup.hosting.whichFramework ||
101
+ (await (0, prompt_1.select)({
102
+ message: "Please choose the framework:",
103
+ default: defaultChoice,
104
+ choices,
105
+ }));
118
106
  if (discoveredFramework)
119
107
  (0, node_fs_1.rmSync)(setup.hosting.source, { recursive: true });
120
108
  await frameworks_1.WebFrameworks[setup.hosting.whichFramework].init(setup, config);
121
109
  }
122
- await (0, prompt_1.promptOnce)({
123
- name: "region",
124
- type: "list",
125
- message: "In which region would you like to host server-side content, if applicable?",
126
- default: constants_1.DEFAULT_REGION,
127
- choices: constants_1.ALLOWED_SSR_REGIONS.filter((region) => region.recommended),
128
- }, setup.hosting);
110
+ setup.hosting.region =
111
+ setup.hosting.region ||
112
+ (await (0, prompt_1.select)({
113
+ message: "In which region would you like to host server-side content, if applicable?",
114
+ default: constants_1.DEFAULT_REGION,
115
+ choices: constants_1.ALLOWED_SSR_REGIONS.filter((region) => region.recommended),
116
+ }));
129
117
  setup.config.hosting = {
130
118
  source: setup.hosting.source,
131
119
  ignore: DEFAULT_IGNORES,
@@ -140,31 +128,22 @@ async function doSetup(setup, config, options) {
140
128
  logger_1.logger.info(`will contain Hosting assets to be uploaded with ${clc.bold("firebase deploy")}. If you`);
141
129
  logger_1.logger.info("have a build process for your assets, use your build's output directory.");
142
130
  logger_1.logger.info();
143
- await (0, prompt_1.prompt)(setup.hosting, [
144
- {
145
- name: "public",
146
- type: "input",
147
- default: "public",
148
- message: "What do you want to use as your public directory?",
149
- },
150
- {
151
- name: "spa",
152
- type: "confirm",
153
- default: false,
154
- message: "Configure as a single-page app (rewrite all urls to /index.html)?",
155
- },
156
- ]);
131
+ setup.hosting.public =
132
+ setup.hosting.public ||
133
+ (await (0, prompt_1.input)({
134
+ message: "What do you want to use as your public directory?",
135
+ default: "public",
136
+ }));
137
+ setup.hosting.spa =
138
+ setup.hosting.spa ||
139
+ (await (0, prompt_1.confirm)("Configure as a single-page app (rewrite all urls to /index.html)?"));
157
140
  setup.config.hosting = {
158
141
  public: setup.hosting.public,
159
142
  ignore: DEFAULT_IGNORES,
160
143
  };
161
144
  }
162
- await (0, prompt_1.promptOnce)({
163
- name: "github",
164
- type: "confirm",
165
- default: false,
166
- message: "Set up automatic builds and deploys with GitHub?",
167
- }, setup.hosting);
145
+ setup.hosting.github =
146
+ setup.hosting.github || (await (0, prompt_1.confirm)("Set up automatic builds and deploys with GitHub?"));
168
147
  if (!setup.hosting.useWebFrameworks) {
169
148
  if (setup.hosting.spa) {
170
149
  setup.config.hosting.rewrites = [{ source: "**", destination: "/index.html" }];
@@ -6,8 +6,8 @@ const _ = require("lodash");
6
6
  const error_1 = require("../../error");
7
7
  const projects_1 = require("../../management/projects");
8
8
  const logger_1 = require("../../logger");
9
- const prompt_1 = require("../../prompt");
10
9
  const utils = require("../../utils");
10
+ const prompt = require("../../prompt");
11
11
  const OPTION_NO_PROJECT = "Don't set up a default project";
12
12
  const OPTION_USE_PROJECT = "Use an existing project";
13
13
  const OPTION_NEW_PROJECT = "Create a new project";
@@ -24,14 +24,11 @@ function toInitProjectInfo(projectMetaData) {
24
24
  async function promptAndCreateNewProject() {
25
25
  utils.logBullet("If you want to create a project in a Google Cloud organization or folder, please use " +
26
26
  `"firebase projects:create" instead, and return to this command when you've created the project.`);
27
- const promptAnswer = {};
28
- await (0, prompt_1.prompt)(promptAnswer, projects_1.PROJECTS_CREATE_QUESTIONS);
29
- if (!promptAnswer.projectId) {
27
+ const { projectId, displayName } = await (0, projects_1.promptProjectCreation)();
28
+ if (!projectId) {
30
29
  throw new error_1.FirebaseError("Project ID cannot be empty");
31
30
  }
32
- return await (0, projects_1.createFirebaseProjectAndLog)(promptAnswer.projectId, {
33
- displayName: promptAnswer.displayName,
34
- });
31
+ return await (0, projects_1.createFirebaseProjectAndLog)(projectId, { displayName });
35
32
  }
36
33
  async function promptAndAddFirebaseToCloudProject() {
37
34
  const projectId = await (0, projects_1.promptAvailableProjectId)();
@@ -41,15 +38,8 @@ async function promptAndAddFirebaseToCloudProject() {
41
38
  return await (0, projects_1.addFirebaseToCloudProjectAndLog)(projectId);
42
39
  }
43
40
  async function projectChoicePrompt(options) {
44
- const choices = [
45
- { name: OPTION_USE_PROJECT, value: OPTION_USE_PROJECT },
46
- { name: OPTION_NEW_PROJECT, value: OPTION_NEW_PROJECT },
47
- { name: OPTION_ADD_FIREBASE, value: OPTION_ADD_FIREBASE },
48
- { name: OPTION_NO_PROJECT, value: OPTION_NO_PROJECT },
49
- ];
50
- const projectSetupOption = await (0, prompt_1.promptOnce)({
51
- type: "list",
52
- name: "id",
41
+ const choices = [OPTION_USE_PROJECT, OPTION_NEW_PROJECT, OPTION_ADD_FIREBASE, OPTION_NO_PROJECT];
42
+ const projectSetupOption = await prompt.select({
53
43
  message: "Please select an option:",
54
44
  choices,
55
45
  });
@@ -6,9 +6,7 @@ const fsutils = require("../../fsutils");
6
6
  const clc = require("colorette");
7
7
  async function doSetup(setup, config) {
8
8
  setup.config.remoteconfig = {};
9
- const jsonFilePath = await (0, prompt_1.promptOnce)({
10
- type: "input",
11
- name: "template",
9
+ const jsonFilePath = await (0, prompt_1.input)({
12
10
  message: "What file should be used for your Remote Config template?",
13
11
  default: "remoteconfig.template.json",
14
12
  });
@@ -17,11 +15,7 @@ async function doSetup(setup, config) {
17
15
  clc.bold(jsonFilePath) +
18
16
  " already exists." +
19
17
  " Do you want to overwrite the existing Remote Config template?";
20
- const overwrite = await (0, prompt_1.promptOnce)({
21
- type: "confirm",
22
- message: msg,
23
- default: false,
24
- });
18
+ const overwrite = await (0, prompt_1.confirm)(msg);
25
19
  if (!overwrite) {
26
20
  return;
27
21
  }
@@ -13,9 +13,7 @@ async function doSetup(setup, config) {
13
13
  logger_1.logger.info("uploads and downloads. You can keep these rules in your project directory");
14
14
  logger_1.logger.info("and publish them with " + clc.bold("firebase deploy") + ".");
15
15
  logger_1.logger.info();
16
- const storageRulesFile = await (0, prompt_1.promptOnce)({
17
- type: "input",
18
- name: "rules",
16
+ const storageRulesFile = await (0, prompt_1.input)({
19
17
  message: "What file should be used for Storage Rules?",
20
18
  default: "storage.rules",
21
19
  });
@@ -11,23 +11,20 @@ const logger_1 = require("../logger");
11
11
  const operation_poller_1 = require("../operation-poller");
12
12
  const types_1 = require("../dataconnect/types");
13
13
  const projectUtils_1 = require("../projectUtils");
14
- const prompt_1 = require("../prompt");
14
+ const prompt = require("../prompt");
15
15
  const projects_1 = require("./projects");
16
16
  const fileUtils_1 = require("../dataconnect/fileUtils");
17
17
  const utils_1 = require("../utils");
18
18
  const TIMEOUT_MILLIS = 30000;
19
19
  exports.APP_LIST_PAGE_SIZE = 100;
20
20
  const CREATE_APP_API_REQUEST_TIMEOUT_MILLIS = 15000;
21
- const DISPLAY_NAME_QUESTION = {
22
- type: "input",
23
- name: "displayName",
24
- default: "",
25
- message: "What would you like to call your app?",
26
- };
21
+ async function getDisplayName() {
22
+ return await prompt.input("What would you like to call your app?");
23
+ }
27
24
  async function getPlatform(appDir, config) {
28
25
  let targetPlatform = await (0, fileUtils_1.getPlatformFromFolder)(appDir);
29
26
  if (targetPlatform === types_1.Platform.NONE) {
30
- appDir = await (0, prompt_1.promptForDirectory)({
27
+ appDir = await (0, utils_1.promptForDirectory)({
31
28
  config,
32
29
  relativeTo: appDir,
33
30
  message: "We couldn't determine what kind of app you're using. Where is your app directory?",
@@ -46,9 +43,8 @@ async function getPlatform(appDir, config) {
46
43
  { name: "Web (JavaScript)", value: types_1.Platform.WEB },
47
44
  { name: "Android (Kotlin)", value: types_1.Platform.ANDROID },
48
45
  ];
49
- targetPlatform = await (0, prompt_1.promptOnce)({
46
+ targetPlatform = await prompt.select({
50
47
  message: "Which platform do you want to set up an SDK for? Note: We currently do not support automatically setting up C++ or Unity projects.",
51
- type: "list",
52
48
  choices: platforms,
53
49
  });
54
50
  }
@@ -69,21 +65,11 @@ https://firebase.google.com/docs/flutter/setup
69
65
  exports.getPlatform = getPlatform;
70
66
  async function initiateIosAppCreation(options) {
71
67
  if (!options.nonInteractive) {
72
- await (0, prompt_1.prompt)(options, [
73
- DISPLAY_NAME_QUESTION,
74
- {
75
- type: "input",
76
- default: "",
77
- name: "bundleId",
78
- message: "Please specify your iOS app bundle ID:",
79
- },
80
- {
81
- type: "input",
82
- default: "",
83
- name: "appStoreId",
84
- message: "Please specify your iOS app App Store ID:",
85
- },
86
- ]);
68
+ options.displayName = options.displayName || (await getDisplayName());
69
+ options.bundleId =
70
+ options.bundleId || (await prompt.input("Please specify your iOS app bundle ID:"));
71
+ options.appStoreId =
72
+ options.appStoreId || (await prompt.input("Please specify your iOS app App Store ID:"));
87
73
  }
88
74
  if (!options.bundleId) {
89
75
  throw new error_1.FirebaseError("Bundle ID for iOS app cannot be empty");
@@ -105,15 +91,9 @@ async function initiateIosAppCreation(options) {
105
91
  }
106
92
  async function initiateAndroidAppCreation(options) {
107
93
  if (!options.nonInteractive) {
108
- await (0, prompt_1.prompt)(options, [
109
- DISPLAY_NAME_QUESTION,
110
- {
111
- type: "input",
112
- default: "",
113
- name: "packageName",
114
- message: "Please specify your Android app package name:",
115
- },
116
- ]);
94
+ options.displayName = options.displayName || (await getDisplayName());
95
+ options.packageName =
96
+ options.packageName || (await prompt.input("Please specify your Android app package name:"));
117
97
  }
118
98
  if (!options.packageName) {
119
99
  throw new error_1.FirebaseError("Package name for Android app cannot be empty");
@@ -134,7 +114,7 @@ async function initiateAndroidAppCreation(options) {
134
114
  }
135
115
  async function initiateWebAppCreation(options) {
136
116
  if (!options.nonInteractive) {
137
- await (0, prompt_1.prompt)(options, [DISPLAY_NAME_QUESTION]);
117
+ options.displayName = options.displayName || (await getDisplayName());
138
118
  }
139
119
  if (!options.displayName) {
140
120
  throw new error_1.FirebaseError("Display name for Web app cannot be empty");
@@ -198,8 +178,7 @@ async function selectAppInteractively(apps, appPlatform) {
198
178
  value: app,
199
179
  };
200
180
  });
201
- return await (0, prompt_1.promptOnce)({
202
- type: "list",
181
+ return await prompt.select({
203
182
  message: `Select the ${appPlatform === AppPlatform.ANY ? "" : appPlatform + " "}` +
204
183
  "app to get the configuration data:",
205
184
  choices,
@@ -436,11 +415,7 @@ async function writeConfigToFile(filename, nonInteractive, fileContents) {
436
415
  if (nonInteractive) {
437
416
  throw new error_1.FirebaseError(`${filename} already exists`);
438
417
  }
439
- const overwrite = await (0, prompt_1.promptOnce)({
440
- type: "confirm",
441
- default: false,
442
- message: `${filename} already exists. Do you want to overwrite?`,
443
- });
418
+ const overwrite = await prompt.confirm(`${filename} already exists. Do you want to overwrite?`);
444
419
  if (!overwrite) {
445
420
  return false;
446
421
  }
@@ -544,7 +519,7 @@ async function findIntelligentPathForIOS(appDir, options) {
544
519
  }
545
520
  let outputPath = null;
546
521
  if (!options.nonInteractive) {
547
- outputPath = await (0, prompt_1.promptForDirectory)({
522
+ outputPath = await (0, utils_1.promptForDirectory)({
548
523
  config: options.config,
549
524
  message: `We weren't able to automatically determine the output directory. Where would you like to output your config file?`,
550
525
  relativeTo: appDir,
@@ -579,7 +554,7 @@ async function findIntelligentPathForAndroid(appDir, options) {
579
554
  return module;
580
555
  }
581
556
  if (!options.nonInteractive) {
582
- module = await (0, prompt_1.promptForDirectory)({
557
+ module = await (0, utils_1.promptForDirectory)({
583
558
  config: options.config,
584
559
  message: `We weren't able to automatically determine the output directory. Where would you like to output your config file?`,
585
560
  });
@@ -1,15 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getProject = exports.getFirebaseProject = exports.listFirebaseProjects = exports.getAvailableCloudProjectPage = exports.getFirebaseProjectPage = exports.addFirebaseToCloudProject = exports.createCloudProject = exports.promptAvailableProjectId = exports.getOrPromptProject = exports.addFirebaseToCloudProjectAndLog = exports.createFirebaseProjectAndLog = exports.PROJECTS_CREATE_QUESTIONS = exports.ProjectParentResourceType = void 0;
3
+ exports.getProject = exports.getFirebaseProject = exports.listFirebaseProjects = exports.getAvailableCloudProjectPage = exports.getFirebaseProjectPage = exports.addFirebaseToCloudProject = exports.createCloudProject = exports.promptAvailableProjectId = exports.getOrPromptProject = exports.addFirebaseToCloudProjectAndLog = exports.createFirebaseProjectAndLog = exports.promptProjectCreation = exports.ProjectParentResourceType = void 0;
4
4
  const clc = require("colorette");
5
5
  const ora = require("ora");
6
6
  const apiv2_1 = require("../apiv2");
7
7
  const error_1 = require("../error");
8
8
  const operation_poller_1 = require("../operation-poller");
9
- const prompt_1 = require("../prompt");
9
+ const prompt = require("../prompt");
10
10
  const api = require("../api");
11
11
  const logger_1 = require("../logger");
12
12
  const utils = require("../utils");
13
+ const ensureApiEnabled_1 = require("../ensureApiEnabled");
13
14
  const TIMEOUT_MILLIS = 30000;
14
15
  const MAXIMUM_PROMPT_LIST = 100;
15
16
  const PROJECT_LIST_PAGE_SIZE = 1000;
@@ -19,11 +20,8 @@ var ProjectParentResourceType;
19
20
  ProjectParentResourceType["ORGANIZATION"] = "organization";
20
21
  ProjectParentResourceType["FOLDER"] = "folder";
21
22
  })(ProjectParentResourceType = exports.ProjectParentResourceType || (exports.ProjectParentResourceType = {}));
22
- exports.PROJECTS_CREATE_QUESTIONS = [
23
- {
24
- type: "input",
25
- name: "projectId",
26
- default: "",
23
+ async function promptProjectCreation() {
24
+ const projectId = await prompt.input({
27
25
  message: "Please specify a unique project id " +
28
26
  `(${clc.yellow("warning")}: cannot be modified afterward) [6-30 characters]:\n`,
29
27
  validate: (projectId) => {
@@ -37,11 +35,9 @@ exports.PROJECTS_CREATE_QUESTIONS = [
37
35
  return true;
38
36
  }
39
37
  },
40
- },
41
- {
42
- type: "input",
43
- name: "displayName",
44
- default: (answers) => answers.projectId,
38
+ });
39
+ const displayName = await prompt.input({
40
+ default: projectId,
45
41
  message: "What would you like to call your project? (defaults to your project ID)",
46
42
  validate: (displayName) => {
47
43
  if (displayName.length < 4) {
@@ -54,8 +50,10 @@ exports.PROJECTS_CREATE_QUESTIONS = [
54
50
  return true;
55
51
  }
56
52
  },
57
- },
58
- ];
53
+ });
54
+ return { projectId, displayName };
55
+ }
56
+ exports.promptProjectCreation = promptProjectCreation;
59
57
  const firebaseAPIClient = new apiv2_1.Client({
60
58
  urlPrefix: api.firebaseApiOrigin(),
61
59
  auth: true,
@@ -130,10 +128,7 @@ async function selectProjectInteractively(pageSize = MAXIMUM_PROMPT_LIST) {
130
128
  return selectProjectFromList(projects);
131
129
  }
132
130
  async function selectProjectByPrompting() {
133
- const projectId = await (0, prompt_1.promptOnce)({
134
- type: "input",
135
- message: "Please input the project ID you would like to use:",
136
- });
131
+ const projectId = await prompt.input("Please input the project ID you would like to use:");
137
132
  return await getFirebaseProject(projectId);
138
133
  }
139
134
  async function selectProjectFromList(projects = []) {
@@ -150,9 +145,7 @@ async function selectProjectFromList(projects = []) {
150
145
  utils.logBullet(`Don't want to scroll through all your projects? If you know your project ID, ` +
151
146
  `you can initialize it directly using ${clc.bold("firebase init --project <project_id>")}.\n`);
152
147
  }
153
- const projectId = await (0, prompt_1.promptOnce)({
154
- type: "list",
155
- name: "id",
148
+ const projectId = await prompt.select({
156
149
  message: "Select a default Firebase project for this directory:",
157
150
  choices,
158
151
  });
@@ -172,10 +165,7 @@ async function promptAvailableProjectId() {
172
165
  throw new error_1.FirebaseError("There are no available Google Cloud projects to add Firebase services.");
173
166
  }
174
167
  if (nextPageToken) {
175
- return await (0, prompt_1.promptOnce)({
176
- type: "input",
177
- message: "Please input the ID of the Google Cloud Project you would like to add Firebase:",
178
- });
168
+ return await prompt.input("Please input the ID of the Google Cloud Project you would like to add Firebase:");
179
169
  }
180
170
  else {
181
171
  const choices = projects
@@ -188,9 +178,7 @@ async function promptAvailableProjectId() {
188
178
  };
189
179
  })
190
180
  .sort((a, b) => a.name.localeCompare(b.name));
191
- return await (0, prompt_1.promptOnce)({
192
- type: "list",
193
- name: "id",
181
+ return await prompt.select({
194
182
  message: "Select the Google Cloud Platform project you would like to add Firebase:",
195
183
  choices,
196
184
  });
@@ -348,6 +336,7 @@ async function getFirebaseProject(projectId) {
348
336
  }
349
337
  exports.getFirebaseProject = getFirebaseProject;
350
338
  async function getProject(projectId) {
339
+ await (0, ensureApiEnabled_1.bestEffortEnsure)(projectId, api.resourceManagerOrigin(), "firebase", true);
351
340
  const response = await resourceManagerClient.get(`/projects/${projectId}`);
352
341
  return response.body;
353
342
  }
package/lib/mcp/index.js CHANGED
@@ -7,11 +7,12 @@ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
7
7
  const util_js_1 = require("./util.js");
8
8
  const index_js_2 = require("./tools/index.js");
9
9
  const configstore_js_1 = require("../configstore.js");
10
- const index_js_3 = require("./tools/core/index.js");
11
10
  const command_js_1 = require("../command.js");
12
11
  const requireAuth_js_1 = require("../requireAuth.js");
13
12
  const projectUtils_js_1 = require("../projectUtils.js");
14
13
  const errors_js_1 = require("./errors.js");
14
+ const track_js_1 = require("../track.js");
15
+ const config_js_1 = require("../config.js");
15
16
  const SERVER_VERSION = "0.0.1";
16
17
  const PROJECT_ROOT_KEY = "mcp.projectRoot";
17
18
  const cmd = new command_js_1.Command("experimental:mcp").before(requireAuth_js_1.requireAuth);
@@ -29,21 +30,14 @@ class FirebaseMcpServer {
29
30
  this.fixedRoot = true;
30
31
  }
31
32
  get availableTools() {
32
- var _a;
33
- const toolDefs = this.fixedRoot ? [] : [...index_js_3.coreTools];
34
- const activeFeatures = ((_a = this.activeFeatures) === null || _a === void 0 ? void 0 : _a.length)
35
- ? this.activeFeatures
36
- : Object.keys(index_js_2.tools);
37
- for (const key of activeFeatures || []) {
38
- toolDefs.push(...index_js_2.tools[key]);
39
- }
40
- return toolDefs;
33
+ return (0, index_js_2.availableTools)(!!this.fixedRoot, this.activeFeatures);
41
34
  }
42
35
  getTool(name) {
43
36
  return this.availableTools.find((t) => t.mcp.name === name) || null;
44
37
  }
45
38
  async mcpListTools() {
46
39
  const hasActiveProject = !!(await this.getProjectId());
40
+ await (0, track_js_1.trackGA4)("mcp_list_tools", {});
47
41
  return {
48
42
  tools: this.availableTools.map((t) => t.mcp),
49
43
  _meta: {
@@ -95,9 +89,17 @@ class FirebaseMcpServer {
95
89
  if (((_b = tool.mcp._meta) === null || _b === void 0 ? void 0 : _b.requiresProject) && !projectId)
96
90
  return errors_js_1.NO_PROJECT_ERROR;
97
91
  try {
98
- return tool.fn(toolArgs, { projectId: await this.getProjectId(), host: this });
92
+ const config = config_js_1.Config.load({ cwd: this.projectRoot });
93
+ const res = await tool.fn(toolArgs, {
94
+ projectId: await this.getProjectId(),
95
+ host: this,
96
+ config,
97
+ });
98
+ await (0, track_js_1.trackGA4)("mcp_tool_call", { tool_name: toolName, error: res.isError ? 1 : 0 });
99
+ return res;
99
100
  }
100
101
  catch (err) {
102
+ await (0, track_js_1.trackGA4)("mcp_tool_call", { tool_name: toolName, error: 1 });
101
103
  return (0, util_js_1.mcpError)(err);
102
104
  }
103
105
  }
@@ -1,12 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.disable_auth_user = void 0;
3
+ exports.disable_user = void 0;
4
4
  const zod_1 = require("zod");
5
5
  const tool_js_1 = require("../../tool.js");
6
6
  const util_js_1 = require("../../util.js");
7
7
  const auth_js_1 = require("../../../gcp/auth.js");
8
- exports.disable_auth_user = (0, tool_js_1.tool)({
9
- name: "disable_auth_user",
8
+ exports.disable_user = (0, tool_js_1.tool)({
9
+ name: "disable_user",
10
10
  description: "Disables or enables a user based on a UID.",
11
11
  inputSchema: zod_1.z.object({
12
12
  uid: zod_1.z.string().describe("The localId or UID of the user to disable or enable"),
@@ -1,12 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.get_auth_user = void 0;
3
+ exports.get_user = void 0;
4
4
  const zod_1 = require("zod");
5
5
  const tool_js_1 = require("../../tool.js");
6
6
  const util_js_1 = require("../../util.js");
7
7
  const auth_js_1 = require("../../../gcp/auth.js");
8
- exports.get_auth_user = (0, tool_js_1.tool)({
9
- name: "get_auth_user",
8
+ exports.get_user = (0, tool_js_1.tool)({
9
+ name: "get_user",
10
10
  description: "Retrieves a user based on an email address, phone number, or UID.",
11
11
  inputSchema: zod_1.z.object({
12
12
  email: zod_1.z.string().optional(),
@@ -23,7 +23,7 @@ exports.get_auth_user = (0, tool_js_1.tool)({
23
23
  },
24
24
  }, async ({ email, phoneNumber, uid }, { projectId }) => {
25
25
  if (email === undefined && phoneNumber === undefined && uid === undefined) {
26
- return (0, util_js_1.mcpError)(`No user identifier supplied in get_auth_user tool`);
26
+ return (0, util_js_1.mcpError)(`No user identifier supplied in auth_get_user tool`);
27
27
  }
28
28
  return (0, util_js_1.toContent)(await (0, auth_js_1.findUser)(projectId, email, phoneNumber, uid));
29
29
  });
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.authTools = void 0;
4
- const get_auth_user_js_1 = require("./get_auth_user.js");
5
- const disable_auth_user_js_1 = require("./disable_auth_user.js");
6
- const set_auth_claims_js_1 = require("./set_auth_claims.js");
7
- exports.authTools = [get_auth_user_js_1.get_auth_user, disable_auth_user_js_1.disable_auth_user, set_auth_claims_js_1.set_auth_claim];
4
+ const get_user_js_1 = require("./get_user.js");
5
+ const disable_user_js_1 = require("./disable_user.js");
6
+ const set_claims_js_1 = require("./set_claims.js");
7
+ const set_sms_region_policy_js_1 = require("./set_sms_region_policy.js");
8
+ exports.authTools = [get_user_js_1.get_user, disable_user_js_1.disable_user, set_claims_js_1.set_claim, set_sms_region_policy_js_1.set_sms_region_policy];
@@ -1,12 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.set_auth_claim = void 0;
3
+ exports.set_claim = void 0;
4
4
  const zod_1 = require("zod");
5
5
  const tool_js_1 = require("../../tool.js");
6
6
  const util_js_1 = require("../../util.js");
7
7
  const auth_js_1 = require("../../../gcp/auth.js");
8
- exports.set_auth_claim = (0, tool_js_1.tool)({
9
- name: "set_auth_claims",
8
+ exports.set_claim = (0, tool_js_1.tool)({
9
+ name: "set_claims",
10
10
  description: "Sets custom claims on a specific user's account. Use to create trusted values associated with a user e.g. marking them as an admin. Claims are limited in size and should be succinct in name and value.",
11
11
  inputSchema: zod_1.z.object({
12
12
  uid: zod_1.z.string().describe("the UID of the user to update"),
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.set_sms_region_policy = void 0;
4
+ const zod_1 = require("zod");
5
+ const tool_js_1 = require("../../tool.js");
6
+ const util_js_1 = require("../../util.js");
7
+ const auth_js_1 = require("../../../gcp/auth.js");
8
+ exports.set_sms_region_policy = (0, tool_js_1.tool)({
9
+ name: "set_sms_region_policy",
10
+ description: "Sets an SMS Region Policy for Firebase Auth to restrict the regions which can receive text messages based on an ALLOW or DENY list of country codes. This policy will override any existing policies when set.",
11
+ inputSchema: zod_1.z.object({
12
+ policy_type: zod_1.z
13
+ .enum(["ALLOW", "DENY"])
14
+ .describe("with an ALLOW policy, only the specified country codes can use SMS auth. with a DENY policy, all countries can use SMS auth except the ones specified"),
15
+ country_codes: zod_1.z
16
+ .array(zod_1.z.string())
17
+ .describe("the country codes to allow or deny based on ISO 3166"),
18
+ }),
19
+ annotations: {
20
+ title: "Set the SMS Region Policy on your Firebase Project",
21
+ idempotentHint: true,
22
+ destructiveHint: true,
23
+ },
24
+ _meta: {
25
+ requiresProject: true,
26
+ requiresAuth: true,
27
+ },
28
+ }, async ({ policy_type, country_codes }, { projectId }) => {
29
+ country_codes = country_codes.map((code) => {
30
+ return code.toUpperCase();
31
+ });
32
+ if (policy_type === "ALLOW") {
33
+ return (0, util_js_1.toContent)(await (0, auth_js_1.setAllowSmsRegionPolicy)(projectId, country_codes));
34
+ }
35
+ return (0, util_js_1.toContent)(await (0, auth_js_1.setDenySmsRegionPolicy)(projectId, country_codes));
36
+ });