firebase-tools 14.2.1 → 14.2.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 (44) hide show
  1. package/lib/bin/cli.js +131 -0
  2. package/lib/bin/firebase.js +7 -123
  3. package/lib/bin/mcp.js +38 -0
  4. package/lib/commands/index.js +3 -0
  5. package/lib/commands/mcp.js +11 -0
  6. package/lib/emulator/commandUtils.js +8 -6
  7. package/lib/emulator/downloadableEmulators.js +9 -9
  8. package/lib/emulator/env.js +19 -20
  9. package/lib/emulator/functionsEmulator.js +0 -1
  10. package/lib/emulator/hub.js +2 -1
  11. package/lib/emulator/ui.js +0 -2
  12. package/lib/experiments.js +5 -0
  13. package/lib/frameworks/index.js +4 -1
  14. package/lib/gcp/auth.js +56 -1
  15. package/lib/gcp/firestore.js +12 -1
  16. package/lib/hosting/api.js +4 -0
  17. package/lib/init/features/dataconnect/sdk.js +9 -7
  18. package/lib/logger.js +11 -2
  19. package/lib/mcp/errors.js +15 -0
  20. package/lib/mcp/index.js +109 -0
  21. package/lib/mcp/tool.js +11 -0
  22. package/lib/mcp/tools/auth/disable_auth_user.js +30 -0
  23. package/lib/mcp/tools/auth/get_auth_user.js +29 -0
  24. package/lib/mcp/tools/auth/index.js +7 -0
  25. package/lib/mcp/tools/auth/set_auth_claims.js +34 -0
  26. package/lib/mcp/tools/core/get_firebase_directory.js +20 -0
  27. package/lib/mcp/tools/core/index.js +6 -0
  28. package/lib/mcp/tools/core/set_firebase_directory.js +33 -0
  29. package/lib/mcp/tools/dataconnect/index.js +5 -0
  30. package/lib/mcp/tools/dataconnect/list_dataconnect_services.js +23 -0
  31. package/lib/mcp/tools/firestore/converter.js +57 -0
  32. package/lib/mcp/tools/firestore/get_documents.js +48 -0
  33. package/lib/mcp/tools/firestore/get_firestore_rules.js +26 -0
  34. package/lib/mcp/tools/firestore/index.js +7 -0
  35. package/lib/mcp/tools/firestore/list_collections.js +30 -0
  36. package/lib/mcp/tools/index.js +14 -0
  37. package/lib/mcp/tools/project/get_project.js +22 -0
  38. package/lib/mcp/tools/project/get_sdk_config.js +38 -0
  39. package/lib/mcp/tools/project/index.js +7 -0
  40. package/lib/mcp/tools/project/list_apps.js +29 -0
  41. package/lib/mcp/types.js +4 -0
  42. package/lib/mcp/util.js +52 -0
  43. package/package.json +5 -2
  44. package/templates/init/dataconnect/connector.yaml +3 -0
@@ -78,19 +78,21 @@ async function askQuestions(setup, config) {
78
78
  const newConnectorYaml = await generateSdkYaml(targetPlatform, connectorYaml, connectorInfo.directory, appDir);
79
79
  if (targetPlatform === types_1.Platform.WEB) {
80
80
  const unusedFrameworks = fileUtils_1.SUPPORTED_FRAMEWORKS.filter((framework) => { var _a; return !((_a = newConnectorYaml.generate) === null || _a === void 0 ? void 0 : _a.javascriptSdk[framework]); });
81
- const hasFrameworkEnabled = unusedFrameworks.length < fileUtils_1.SUPPORTED_FRAMEWORKS.length;
82
81
  if (unusedFrameworks.length > 0) {
83
82
  const additionalFrameworks = await (0, prompt_1.prompt)(setup, [
84
83
  {
85
84
  type: "checkbox",
86
85
  name: "fdcFrameworks",
87
- message: `Which ${hasFrameworkEnabled ? "additional " : ""}frameworks would you like to generate SDKs for? ` +
86
+ message: `Which frameworks would you like to generate SDKs for? ` +
88
87
  "Press Space to select features, then Enter to confirm your choices.",
89
- choices: unusedFrameworks.map((frameworkStr) => ({
90
- value: frameworkStr,
91
- name: frameworkStr,
92
- checked: false,
93
- })),
88
+ choices: fileUtils_1.SUPPORTED_FRAMEWORKS.map((frameworkStr) => {
89
+ var _a, _b;
90
+ return ({
91
+ value: frameworkStr,
92
+ name: frameworkStr,
93
+ checked: (_b = (_a = newConnectorYaml === null || newConnectorYaml === void 0 ? void 0 : newConnectorYaml.generate) === null || _a === void 0 ? void 0 : _a.javascriptSdk) === null || _b === void 0 ? void 0 : _b[frameworkStr],
94
+ });
95
+ }),
94
96
  },
95
97
  ]);
96
98
  for (const framework of additionalFrameworks.fdcFrameworks) {
package/lib/logger.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.logger = exports.vsceLogEmitter = void 0;
3
+ exports.silenceStdout = exports.logger = exports.vsceLogEmitter = void 0;
4
4
  const winston = require("winston");
5
5
  const vsCodeUtils_1 = require("./vsCodeUtils");
6
6
  const events_1 = require("events");
@@ -46,6 +46,15 @@ function maybeUseVSCodeLogger(logger) {
46
46
  return logger;
47
47
  }
48
48
  const rawLogger = winston.createLogger();
49
- rawLogger.add(new winston.transports.Console({ silent: true }));
49
+ rawLogger.add(new winston.transports.Console({
50
+ silent: true,
51
+ consoleWarnLevels: ["debug", "warn"],
52
+ }));
50
53
  rawLogger.exitOnError = false;
51
54
  exports.logger = maybeUseVSCodeLogger(annotateDebugLines(expandErrors(rawLogger)));
55
+ function silenceStdout() {
56
+ exports.logger = winston.createLogger({
57
+ silent: true,
58
+ });
59
+ }
60
+ exports.silenceStdout = silenceStdout;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mcpAuthError = exports.NO_PROJECT_ERROR = void 0;
4
+ const util_1 = require("./util");
5
+ exports.NO_PROJECT_ERROR = (0, util_1.mcpError)('No active project was found. Use the `set_firebase_directory` tool to set the project directory to an absolute folder location containing a firebase.json config file. Alternatively, change the MCP server config to add [...,"--dir","/absolute/path/to/project/directory"] in its command-line arguments.', "PRECONDITION_FAILED");
6
+ function mcpAuthError() {
7
+ const cmd = (0, util_1.commandExistsSync)("firebase") ? "firebase" : "npx -y firebase-tools";
8
+ return (0, util_1.mcpError)(`The user is not currently logged into the Firebase CLI, which is required to use this tool. Please instruct the user to execute this shell command to sign in or to configure [Application Default Credentials][ADC] on their machine.
9
+ \`\`\`sh
10
+ ${cmd} login
11
+ \`\`\`
12
+
13
+ [ADC]: https://cloud.google.com/docs/authentication/application-default-credentials`);
14
+ }
15
+ exports.mcpAuthError = mcpAuthError;
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FirebaseMcpServer = void 0;
4
+ const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
5
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
6
+ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
7
+ const util_js_1 = require("./util.js");
8
+ const index_js_2 = require("./tools/index.js");
9
+ const configstore_js_1 = require("../configstore.js");
10
+ const index_js_3 = require("./tools/core/index.js");
11
+ const command_js_1 = require("../command.js");
12
+ const requireAuth_js_1 = require("../requireAuth.js");
13
+ const projectUtils_js_1 = require("../projectUtils.js");
14
+ const errors_js_1 = require("./errors.js");
15
+ const SERVER_VERSION = "0.0.1";
16
+ const PROJECT_ROOT_KEY = "mcp.projectRoot";
17
+ const cmd = new command_js_1.Command("experimental:mcp").before(requireAuth_js_1.requireAuth);
18
+ class FirebaseMcpServer {
19
+ constructor(options) {
20
+ var _a, _b, _c;
21
+ this.activeFeatures = options.activeFeatures;
22
+ this.server = new index_js_1.Server({ name: "firebase", version: SERVER_VERSION });
23
+ this.server.registerCapabilities({ tools: { listChanged: true } });
24
+ this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, this.mcpListTools.bind(this));
25
+ this.server.setRequestHandler(types_js_1.CallToolRequestSchema, this.mcpCallTool.bind(this));
26
+ this.projectRoot =
27
+ (_c = (_b = (_a = options.projectRoot) !== null && _a !== void 0 ? _a : configstore_js_1.configstore.get(PROJECT_ROOT_KEY)) !== null && _b !== void 0 ? _b : process.env.PROJECT_ROOT) !== null && _c !== void 0 ? _c : process.cwd();
28
+ if (options.projectRoot)
29
+ this.fixedRoot = true;
30
+ }
31
+ 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;
41
+ }
42
+ getTool(name) {
43
+ return this.availableTools.find((t) => t.mcp.name === name) || null;
44
+ }
45
+ async mcpListTools() {
46
+ const hasActiveProject = !!(await this.getProjectId());
47
+ return {
48
+ tools: this.availableTools.map((t) => t.mcp),
49
+ _meta: {
50
+ projectRoot: this.projectRoot,
51
+ projectDetected: hasActiveProject,
52
+ authenticated: await this.getAuthenticated(),
53
+ activeFeatures: this.activeFeatures,
54
+ },
55
+ };
56
+ }
57
+ setProjectRoot(newRoot) {
58
+ if (newRoot === null) {
59
+ configstore_js_1.configstore.delete(PROJECT_ROOT_KEY);
60
+ this.projectRoot = process.env.PROJECT_ROOT || process.cwd();
61
+ void this.server.sendToolListChanged();
62
+ return;
63
+ }
64
+ configstore_js_1.configstore.set(PROJECT_ROOT_KEY, newRoot);
65
+ this.projectRoot = newRoot;
66
+ void this.server.sendToolListChanged();
67
+ }
68
+ async resolveOptions() {
69
+ const options = { cwd: this.projectRoot };
70
+ await cmd.prepare(options);
71
+ return options;
72
+ }
73
+ async getProjectId() {
74
+ return (0, projectUtils_js_1.getProjectId)(await this.resolveOptions());
75
+ }
76
+ async getAuthenticated() {
77
+ try {
78
+ await (0, requireAuth_js_1.requireAuth)(await this.resolveOptions());
79
+ return true;
80
+ }
81
+ catch (e) {
82
+ return false;
83
+ }
84
+ }
85
+ async mcpCallTool(request) {
86
+ var _a, _b;
87
+ const toolName = request.params.name;
88
+ const toolArgs = request.params.arguments;
89
+ const tool = this.getTool(toolName);
90
+ if (!tool)
91
+ throw new Error(`Tool '${toolName}' could not be found.`);
92
+ const projectId = await this.getProjectId();
93
+ if (((_a = tool.mcp._meta) === null || _a === void 0 ? void 0 : _a.requiresAuth) && !(await this.getAuthenticated()))
94
+ return (0, errors_js_1.mcpAuthError)();
95
+ if (((_b = tool.mcp._meta) === null || _b === void 0 ? void 0 : _b.requiresProject) && !projectId)
96
+ return errors_js_1.NO_PROJECT_ERROR;
97
+ try {
98
+ return tool.fn(toolArgs, { projectId: await this.getProjectId(), host: this });
99
+ }
100
+ catch (err) {
101
+ return (0, util_js_1.mcpError)(err);
102
+ }
103
+ }
104
+ async start() {
105
+ const transport = new stdio_js_1.StdioServerTransport();
106
+ await this.server.connect(transport);
107
+ }
108
+ }
109
+ exports.FirebaseMcpServer = FirebaseMcpServer;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.tool = void 0;
4
+ const zod_to_json_schema_1 = require("zod-to-json-schema");
5
+ function tool(options, fn) {
6
+ return {
7
+ mcp: Object.assign(Object.assign({}, options), { inputSchema: (0, zod_to_json_schema_1.zodToJsonSchema)(options.inputSchema) }),
8
+ fn,
9
+ };
10
+ }
11
+ exports.tool = tool;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.disable_auth_user = 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.disable_auth_user = (0, tool_js_1.tool)({
9
+ name: "disable_auth_user",
10
+ description: "Disables or enables a user based on a UID.",
11
+ inputSchema: zod_1.z.object({
12
+ uid: zod_1.z.string().describe("The localId or UID of the user to disable or enable"),
13
+ disabled: zod_1.z.boolean().describe("true disables the user, false enables the user"),
14
+ }),
15
+ annotations: {
16
+ title: "Disable or enable a particular user",
17
+ destructiveHint: true,
18
+ idempotentHint: true,
19
+ },
20
+ _meta: {
21
+ requiresAuth: true,
22
+ requiresProject: true,
23
+ },
24
+ }, async ({ uid, disabled }, { projectId }) => {
25
+ const res = await (0, auth_js_1.disableUser)(projectId, uid, disabled);
26
+ if (res) {
27
+ return (0, util_js_1.toContent)(`User ${uid} as been ${disabled ? "disabled" : "enabled"}`);
28
+ }
29
+ return (0, util_js_1.toContent)(`Failed to ${disabled ? "disable" : "enable"} user ${uid}`);
30
+ });
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.get_auth_user = 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.get_auth_user = (0, tool_js_1.tool)({
9
+ name: "get_auth_user",
10
+ description: "Retrieves a user based on an email address, phone number, or UID.",
11
+ inputSchema: zod_1.z.object({
12
+ email: zod_1.z.string().optional(),
13
+ phoneNumber: zod_1.z.string().optional(),
14
+ uid: zod_1.z.string().optional(),
15
+ }),
16
+ annotations: {
17
+ title: "Get information about 1 user.",
18
+ readOnlyHint: true,
19
+ },
20
+ _meta: {
21
+ requiresAuth: true,
22
+ requiresProject: true,
23
+ },
24
+ }, async ({ email, phoneNumber, uid }, { projectId }) => {
25
+ if (email === undefined && phoneNumber === undefined && uid === undefined) {
26
+ return (0, util_js_1.mcpError)(`No user identifier supplied in get_auth_user tool`);
27
+ }
28
+ return (0, util_js_1.toContent)(await (0, auth_js_1.findUser)(projectId, email, phoneNumber, uid));
29
+ });
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
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];
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.set_auth_claim = 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_auth_claim = (0, tool_js_1.tool)({
9
+ name: "set_auth_claims",
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
+ inputSchema: zod_1.z.object({
12
+ uid: zod_1.z.string().describe("the UID of the user to update"),
13
+ claim: zod_1.z.string().describe("the name (key) of the claim to update, e.g. 'admin'"),
14
+ value: zod_1.z
15
+ .union([
16
+ zod_1.z.string(),
17
+ zod_1.z.number(),
18
+ zod_1.z.boolean(),
19
+ zod_1.z.record(zod_1.z.union([zod_1.z.string(), zod_1.z.number(), zod_1.z.boolean()])),
20
+ zod_1.z.array(zod_1.z.union([zod_1.z.string(), zod_1.z.number(), zod_1.z.boolean()])),
21
+ ])
22
+ .describe("the value of the custom claim"),
23
+ }),
24
+ annotations: {
25
+ title: "Set custom Firebase Auth claim",
26
+ idempotentHint: true,
27
+ },
28
+ _meta: {
29
+ requiresAuth: true,
30
+ requiresProject: true,
31
+ },
32
+ }, async ({ uid, claim, value }, { projectId }) => {
33
+ return (0, util_js_1.toContent)(await (0, auth_js_1.setCustomClaim)(projectId, uid, { [claim]: value }, { merge: true }));
34
+ });
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.get_firebase_directory = 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 detectProjectRoot_js_1 = require("../../../detectProjectRoot.js");
8
+ exports.get_firebase_directory = (0, tool_js_1.tool)({
9
+ name: "get_firebase_directory",
10
+ description: "Gets the current Firebase project directory. If this has been set using the `set_firebase_directory` tool it will return that, otherwise it will look for a PROJECT_ROOT environment variable or the current working directory of the running Firebase MCP server.",
11
+ inputSchema: zod_1.z.object({}),
12
+ annotations: {
13
+ title: "Get Firebase Project Directory",
14
+ readOnlyHint: true,
15
+ },
16
+ }, (_, { host }) => {
17
+ if (!(0, detectProjectRoot_js_1.detectProjectRoot)({ cwd: host.projectRoot }))
18
+ return Promise.resolve((0, util_js_1.mcpError)(`There is no detected 'firebase.json' in directory '${host.projectRoot}'. Please use the 'set_firebase_directory' tool to activate a Firebase project directory.`));
19
+ return Promise.resolve((0, util_js_1.toContent)(`The current Firebase project directory is '${host.projectRoot}'.`));
20
+ });
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.coreTools = void 0;
4
+ const get_firebase_directory_1 = require("./get_firebase_directory");
5
+ const set_firebase_directory_1 = require("./set_firebase_directory");
6
+ exports.coreTools = [get_firebase_directory_1.get_firebase_directory, set_firebase_directory_1.set_firebase_directory];
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.set_firebase_directory = 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 fs_1 = require("fs");
8
+ const path_1 = require("path");
9
+ exports.set_firebase_directory = (0, tool_js_1.tool)({
10
+ name: "set_firebase_directory",
11
+ description: "Sets the project directory for the Firebase MCP server to utilize for project detection and authentication. This should be a directory with a `firebase.json` file in it. This information is persisted between sessions.",
12
+ inputSchema: zod_1.z.object({
13
+ dir: zod_1.z
14
+ .string()
15
+ .nullable()
16
+ .describe("the absolute path of the directory. set to null to 'unset' the value and fall back to the working directory"),
17
+ }),
18
+ annotations: {
19
+ title: "Set Firebase Project Directory",
20
+ idempotentHint: true,
21
+ },
22
+ }, ({ dir }, { host }) => {
23
+ if (dir === null) {
24
+ host.setProjectRoot(null);
25
+ return Promise.resolve((0, util_js_1.toContent)(`Firebase MCP project directory setting deleted. New project root is: ${host.projectRoot || "unset"}`));
26
+ }
27
+ if (!(0, fs_1.existsSync)(dir))
28
+ return Promise.resolve((0, util_js_1.mcpError)(`Directory '${dir}' does not exist.`));
29
+ if (!(0, fs_1.existsSync)((0, path_1.join)(dir, "firebase.json")))
30
+ return Promise.resolve((0, util_js_1.mcpError)(`Directory '${dir}' does not contain a 'firebase.json' file.`));
31
+ host.setProjectRoot(dir);
32
+ return Promise.resolve((0, util_js_1.toContent)(`Firebase MCP project directory set to '${dir}'.`));
33
+ });
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dataconnectTools = void 0;
4
+ const list_dataconnect_services_js_1 = require("./list_dataconnect_services.js");
5
+ exports.dataconnectTools = [list_dataconnect_services_js_1.list_dataconnect_services];
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.list_dataconnect_services = 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 client = require("../../../dataconnect/client");
8
+ exports.list_dataconnect_services = (0, tool_js_1.tool)({
9
+ name: "list_dataconnect_services",
10
+ description: "List the Firebase Data Connect services available in the current project.",
11
+ inputSchema: zod_1.z.object({}),
12
+ annotations: {
13
+ title: "List the Firebase Data Connect Services that's available in the backend",
14
+ readOnlyHint: true,
15
+ },
16
+ _meta: {
17
+ requiresProject: true,
18
+ requiresAuth: true,
19
+ },
20
+ }, async (_, { projectId }) => {
21
+ const services = await client.listAllServices(projectId);
22
+ return (0, util_js_1.toContent)(services, { format: "yaml" });
23
+ });
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.firestoreDocumentToJson = void 0;
4
+ const logger_1 = require("../../../logger");
5
+ function firestoreValueToJson(firestoreValue) {
6
+ var _a, _b;
7
+ if ("nullValue" in firestoreValue)
8
+ return null;
9
+ if ("booleanValue" in firestoreValue)
10
+ return firestoreValue.booleanValue;
11
+ if ("integerValue" in firestoreValue) {
12
+ const num = Number(firestoreValue.integerValue);
13
+ if (num > Number.MAX_SAFE_INTEGER || num < Number.MIN_SAFE_INTEGER) {
14
+ return firestoreValue.integerValue;
15
+ }
16
+ return num;
17
+ }
18
+ if ("doubleValue" in firestoreValue)
19
+ return firestoreValue.doubleValue;
20
+ if ("timestampValue" in firestoreValue)
21
+ return { __type__: "Timestamp", value: firestoreValue.timestampValue };
22
+ if ("stringValue" in firestoreValue)
23
+ return firestoreValue.stringValue;
24
+ if ("bytesValue" in firestoreValue)
25
+ return firestoreValue.bytesValue;
26
+ if ("referenceValue" in firestoreValue)
27
+ return { __type__: "Reference", value: firestoreValue.referenceValue };
28
+ if ("geoPointValue" in firestoreValue)
29
+ return {
30
+ __type__: "GeoPoint",
31
+ value: [firestoreValue.geoPointValue.latitude, firestoreValue.geoPointValue.longitude],
32
+ };
33
+ if ("arrayValue" in firestoreValue)
34
+ return (_b = (_a = firestoreValue.arrayValue.values) === null || _a === void 0 ? void 0 : _a.map((v) => firestoreValueToJson(v))) !== null && _b !== void 0 ? _b : [];
35
+ if ("mapValue" in firestoreValue) {
36
+ const map = firestoreValue.mapValue.fields || {};
37
+ const obj = {};
38
+ for (const key of Object.keys(map)) {
39
+ obj[key] = firestoreValueToJson(map[key]);
40
+ }
41
+ return obj;
42
+ }
43
+ logger_1.logger.warn("Unhandled Firestore Value type encountered:", firestoreValue);
44
+ return undefined;
45
+ }
46
+ function firestoreDocumentToJson(firestoreDoc) {
47
+ const nameParts = firestoreDoc.name.split("/documents/");
48
+ const path = nameParts.length > 1 ? nameParts[nameParts.length - 1] : "";
49
+ const result = { __path__: path };
50
+ if (firestoreDoc.fields) {
51
+ for (const key of Object.keys(firestoreDoc.fields)) {
52
+ result[key] = firestoreValueToJson(firestoreDoc.fields[key]);
53
+ }
54
+ }
55
+ return result;
56
+ }
57
+ exports.firestoreDocumentToJson = firestoreDocumentToJson;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.get_documents = 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 firestore_js_1 = require("../../../gcp/firestore.js");
8
+ const converter_js_1 = require("./converter.js");
9
+ exports.get_documents = (0, tool_js_1.tool)({
10
+ name: "get_documents",
11
+ description: "Retrieves one or more Firestore documents from a database in the current project by full document paths. Use this if you know the exact path of a document.",
12
+ inputSchema: zod_1.z.object({
13
+ paths: zod_1.z
14
+ .array(zod_1.z.string())
15
+ .describe("One or more document paths (e.g. `collectionName/documentId` or `parentCollection/parentDocument/collectionName/documentId`)"),
16
+ }),
17
+ annotations: {
18
+ title: "Get Firestore documents",
19
+ readOnlyHint: true,
20
+ },
21
+ _meta: {
22
+ requiresAuth: true,
23
+ requiresProject: true,
24
+ },
25
+ }, async ({ paths }, { projectId }) => {
26
+ if (!paths.length)
27
+ return (0, util_js_1.mcpError)("Must supply at least one document path.");
28
+ const { documents, missing } = await (0, firestore_js_1.getDocuments)(projectId, paths);
29
+ if (missing.length > 0 && documents.length === 0) {
30
+ return (0, util_js_1.mcpError)(`None of the specified documents were found in project '${projectId}'`);
31
+ }
32
+ const docs = documents.map(converter_js_1.firestoreDocumentToJson);
33
+ if (documents.length === 1 && missing.length === 0) {
34
+ return (0, util_js_1.toContent)(docs[0]);
35
+ }
36
+ const docsContent = (0, util_js_1.toContent)(docs);
37
+ if (missing.length) {
38
+ docsContent.content = [
39
+ { type: "text", text: "Retrieved documents:\n\n" },
40
+ ...docsContent.content,
41
+ {
42
+ type: "text",
43
+ text: `The following documents do not exist: ${missing.join(", ")}`,
44
+ },
45
+ ];
46
+ }
47
+ return docsContent;
48
+ });
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.get_firestore_rules = 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 rules_1 = require("../../../gcp/rules");
8
+ exports.get_firestore_rules = (0, tool_js_1.tool)({
9
+ name: "get_firestore_rules",
10
+ description: "Retrieves the active Firestore security rules for the current project.",
11
+ inputSchema: zod_1.z.object({}),
12
+ annotations: {
13
+ title: "Get Current Firestore Rules",
14
+ readOnlyHint: true,
15
+ },
16
+ _meta: {
17
+ requiresProject: true,
18
+ requiresAuth: true,
19
+ },
20
+ }, async (_, { projectId }) => {
21
+ const rulesetName = await (0, rules_1.getLatestRulesetName)(projectId, "cloud.firestore");
22
+ if (!rulesetName)
23
+ return (0, util_js_1.mcpError)(`No active Firestore rules were found in project '${projectId}'`);
24
+ const rules = await (0, rules_1.getRulesetContent)(rulesetName);
25
+ return (0, util_js_1.toContent)(rules[0].content);
26
+ });
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.firestoreTools = void 0;
4
+ const get_documents_1 = require("./get_documents");
5
+ const get_firestore_rules_1 = require("./get_firestore_rules");
6
+ const list_collections_1 = require("./list_collections");
7
+ exports.firestoreTools = [list_collections_1.list_collections, get_documents_1.get_documents, get_firestore_rules_1.get_firestore_rules];
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.list_collections = 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 firestore_js_1 = require("../../../gcp/firestore.js");
8
+ const errors_js_1 = require("../../errors.js");
9
+ exports.list_collections = (0, tool_js_1.tool)({
10
+ name: "list_collections",
11
+ description: "Retrieves a list of collections from a Firestore database in the current project.",
12
+ inputSchema: zod_1.z.object({
13
+ document_path: zod_1.z
14
+ .string()
15
+ .nullish()
16
+ .describe("a parent document to list subcollections under. only needed for subcollections, omit to list top-level collections"),
17
+ }),
18
+ annotations: {
19
+ title: "List Firestore collections",
20
+ readOnlyHint: true,
21
+ },
22
+ _meta: {
23
+ requiresAuth: true,
24
+ requiresProject: true,
25
+ },
26
+ }, async (_, { projectId }) => {
27
+ if (!projectId)
28
+ return errors_js_1.NO_PROJECT_ERROR;
29
+ return (0, util_js_1.toContent)(await (0, firestore_js_1.listCollectionIds)(projectId));
30
+ });
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.tools = void 0;
4
+ const index_js_1 = require("./auth/index.js");
5
+ const index_js_2 = require("./dataconnect/index.js");
6
+ const index_js_3 = require("./firestore/index.js");
7
+ const index_js_4 = require("./project/index.js");
8
+ exports.tools = {
9
+ project: index_js_4.projectTools,
10
+ firestore: index_js_3.firestoreTools,
11
+ auth: index_js_1.authTools,
12
+ dataconnect: index_js_2.dataconnectTools,
13
+ storage: [],
14
+ };
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.get_project = void 0;
4
+ const zod_1 = require("zod");
5
+ const tool_js_1 = require("../../tool.js");
6
+ const projects_js_1 = require("../../../management/projects.js");
7
+ const util_js_1 = require("../../util.js");
8
+ exports.get_project = (0, tool_js_1.tool)({
9
+ name: "get_project",
10
+ description: "Retrieves information about the currently active Firebase project.",
11
+ inputSchema: zod_1.z.object({}),
12
+ annotations: {
13
+ title: "Get Current Firebase Project",
14
+ readOnlyHint: true,
15
+ },
16
+ _meta: {
17
+ requiresAuth: true,
18
+ requiresProject: true,
19
+ },
20
+ }, async (_, { projectId }) => {
21
+ return (0, util_js_1.toContent)(await (0, projects_js_1.getProject)(projectId));
22
+ });
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.get_sdk_config = 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 apps_js_1 = require("../../../management/apps.js");
8
+ exports.get_sdk_config = (0, tool_js_1.tool)({
9
+ name: "get_sdk_config",
10
+ description: "Retrieves the Firebase SDK configuration information for the specified platform. You must specify either a platform or an app_id.",
11
+ inputSchema: zod_1.z.object({
12
+ platform: zod_1.z
13
+ .enum(["ios", "android", "web"])
14
+ .nullish()
15
+ .describe("the platform for which you want config"),
16
+ app_id: zod_1.z.string().nullish().describe("the specific app id to fetch"),
17
+ }),
18
+ annotations: {
19
+ title: "Get Firebase SDK Config",
20
+ readOnlyHint: true,
21
+ },
22
+ _meta: {
23
+ requiresProject: true,
24
+ requiresAuth: true,
25
+ },
26
+ }, async ({ platform: inputPlatform, app_id: appId }, { projectId }) => {
27
+ var _a, _b;
28
+ let platform = inputPlatform === null || inputPlatform === void 0 ? void 0 : inputPlatform.toUpperCase();
29
+ if (!platform && !appId)
30
+ return (0, util_js_1.mcpError)("Must specify one of 'web', 'ios', or 'android' for platform or an app_id for get_sdk_config tool.");
31
+ const apps = await (0, apps_js_1.listFirebaseApps)(projectId, platform !== null && platform !== void 0 ? platform : apps_js_1.AppPlatform.ANY);
32
+ platform = platform || ((_a = apps.find((app) => app.appId === appId)) === null || _a === void 0 ? void 0 : _a.platform);
33
+ appId = appId || ((_b = apps.find((app) => app.platform === platform)) === null || _b === void 0 ? void 0 : _b.appId);
34
+ if (!appId)
35
+ return (0, util_js_1.mcpError)(`Could not find an app for platform '${inputPlatform}' in project '${projectId}'`);
36
+ const sdkConfig = await (0, apps_js_1.getAppConfig)(appId, platform);
37
+ return (0, util_js_1.toContent)(sdkConfig, { format: "json" });
38
+ });
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.projectTools = void 0;
4
+ const get_project_js_1 = require("./get_project.js");
5
+ const get_sdk_config_js_1 = require("./get_sdk_config.js");
6
+ const list_apps_js_1 = require("./list_apps.js");
7
+ exports.projectTools = [get_project_js_1.get_project, list_apps_js_1.list_apps, get_sdk_config_js_1.get_sdk_config];