firebase-tools 14.12.0 → 14.13.0
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.
- package/README.md +1 -1
- package/lib/commands/dataconnect-services-list.js +5 -5
- package/lib/commands/dataconnect-sql-grant.js +5 -0
- package/lib/commands/dataconnect-sql-setup.js +1 -3
- package/lib/commands/firestore-databases-create.js +11 -0
- package/lib/crashlytics/buildToolsJarHelper.js +1 -2
- package/lib/crashlytics/getIssueDetails.js +41 -0
- package/lib/crashlytics/getSampleCrash.js +48 -0
- package/lib/dataconnect/client.js +23 -15
- package/lib/dataconnect/ensureApis.js +5 -9
- package/lib/dataconnect/fileUtils.js +5 -6
- package/lib/dataconnect/freeTrial.js +16 -39
- package/lib/dataconnect/provisionCloudSql.js +67 -70
- package/lib/dataconnect/schemaMigration.js +75 -47
- package/lib/deploy/dataconnect/deploy.js +9 -11
- package/lib/deploy/dataconnect/prepare.js +9 -12
- package/lib/deploy/dataconnect/release.js +13 -7
- package/lib/deploy/firestore/deploy.js +10 -0
- package/lib/deploy/functions/backend.js +8 -2
- package/lib/deploy/functions/build.js +23 -1
- package/lib/deploy/functions/ensure.js +1 -1
- package/lib/deploy/functions/functionsDeployHelper.js +8 -1
- package/lib/deploy/functions/prepare.js +6 -4
- package/lib/deploy/functions/prepareFunctionsUpload.js +3 -1
- package/lib/deploy/functions/pricing.js +12 -5
- package/lib/deploy/functions/release/fabricator.js +25 -3
- package/lib/emulator/controller.js +2 -1
- package/lib/emulator/downloadableEmulatorInfo.json +18 -18
- package/lib/emulator/functionsEmulator.js +9 -1
- package/lib/experiments.js +4 -0
- package/lib/extensions/extensionsHelper.js +4 -15
- package/lib/extensions/utils.js +1 -12
- package/lib/firestore/api-sort.js +96 -3
- package/lib/firestore/api-types.js +14 -1
- package/lib/firestore/api.js +85 -4
- package/lib/firestore/pretty-print.js +7 -0
- package/lib/firestore/validator.js +1 -1
- package/lib/functional.js +7 -1
- package/lib/functions/deprecationWarnings.js +4 -4
- package/lib/functions/projectConfig.js +25 -2
- package/lib/functions/secrets.js +3 -0
- package/lib/gcp/cloudfunctionsv2.js +3 -31
- package/lib/gcp/cloudscheduler.js +1 -1
- package/lib/gcp/cloudsql/cloudsqladmin.js +2 -14
- package/lib/gcp/cloudsql/connect.js +2 -2
- package/lib/gcp/cloudsql/permissionsSetup.js +13 -15
- package/lib/gcp/k8s.js +32 -0
- package/lib/gcp/runv2.js +178 -0
- package/lib/gemini/fdcExperience.js +5 -3
- package/lib/init/features/dataconnect/index.js +266 -162
- package/lib/init/features/dataconnect/sdk.js +32 -17
- package/lib/init/features/project.js +4 -0
- package/lib/management/studio.js +1 -1
- package/lib/mcp/index.js +75 -2
- package/lib/mcp/prompt.js +10 -0
- package/lib/mcp/prompts/core/deploy.js +58 -0
- package/lib/mcp/prompts/core/index.js +5 -0
- package/lib/mcp/prompts/index.js +45 -0
- package/lib/mcp/tools/core/get_sdk_config.js +10 -0
- package/lib/mcp/tools/core/init.js +7 -6
- package/lib/mcp/tools/crashlytics/get_issue_details.js +33 -0
- package/lib/mcp/tools/crashlytics/get_sample_crash.js +43 -0
- package/lib/mcp/tools/crashlytics/index.js +7 -1
- package/lib/mcp/tools/crashlytics/list_top_issues.js +2 -1
- package/lib/mcp/tools/database/get_data.js +49 -0
- package/lib/mcp/tools/database/get_rules.js +39 -0
- package/lib/mcp/tools/database/index.js +8 -0
- package/lib/mcp/tools/database/set_data.js +57 -0
- package/lib/mcp/tools/database/set_rules.js +41 -0
- package/lib/mcp/tools/database/validate_rules.js +41 -0
- package/lib/mcp/tools/index.js +4 -1
- package/lib/mcp/tools/rules/get_rules.js +1 -1
- package/lib/mcp/types.js +2 -0
- package/lib/mcp/util.js +2 -0
- package/lib/rtdb.js +10 -6
- package/lib/utils.js +24 -1
- package/package.json +1 -1
- package/schema/firebase-config.json +6 -0
- package/templates/init/firestore/firestore.indexes.json +26 -1
- package/lib/extensions/resolveSource.js +0 -24
package/lib/management/studio.js
CHANGED
|
@@ -110,7 +110,7 @@ async function updateStudioFirebaseProject(projectId) {
|
|
|
110
110
|
if (err.original) {
|
|
111
111
|
message += ` (original: ${err.original.message})`;
|
|
112
112
|
}
|
|
113
|
-
logger_1.logger.
|
|
113
|
+
logger_1.logger.debug(`Failed to update active Firebase Project for current Studio Workspace: ${message}`);
|
|
114
114
|
}
|
|
115
115
|
recordStudioProjectSyncTime();
|
|
116
116
|
}
|
package/lib/mcp/index.js
CHANGED
|
@@ -7,6 +7,7 @@ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
|
7
7
|
const util_1 = require("./util");
|
|
8
8
|
const types_1 = require("./types");
|
|
9
9
|
const index_1 = require("./tools/index");
|
|
10
|
+
const index_2 = require("./prompts/index");
|
|
10
11
|
const configstore_1 = require("../configstore");
|
|
11
12
|
const command_1 = require("../command");
|
|
12
13
|
const requireAuth_1 = require("../requireAuth");
|
|
@@ -22,7 +23,7 @@ const api = require("../api");
|
|
|
22
23
|
const logging_transport_1 = require("./logging-transport");
|
|
23
24
|
const env_1 = require("../env");
|
|
24
25
|
const timeout_1 = require("../timeout");
|
|
25
|
-
const SERVER_VERSION = "0.
|
|
26
|
+
const SERVER_VERSION = "0.3.0";
|
|
26
27
|
const cmd = new command_1.Command("experimental:mcp");
|
|
27
28
|
const orderedLogLevels = [
|
|
28
29
|
"debug",
|
|
@@ -56,9 +57,15 @@ class FirebaseMcpServer {
|
|
|
56
57
|
this.activeFeatures = options.activeFeatures;
|
|
57
58
|
this.startupRoot = options.projectRoot || process.env.PROJECT_ROOT;
|
|
58
59
|
this.server = new index_js_1.Server({ name: "firebase", version: SERVER_VERSION });
|
|
59
|
-
this.server.registerCapabilities({
|
|
60
|
+
this.server.registerCapabilities({
|
|
61
|
+
tools: { listChanged: true },
|
|
62
|
+
logging: {},
|
|
63
|
+
prompts: { listChanged: true },
|
|
64
|
+
});
|
|
60
65
|
this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, this.mcpListTools.bind(this));
|
|
61
66
|
this.server.setRequestHandler(types_js_1.CallToolRequestSchema, this.mcpCallTool.bind(this));
|
|
67
|
+
this.server.setRequestHandler(types_js_1.ListPromptsRequestSchema, this.mcpListPrompts.bind(this));
|
|
68
|
+
this.server.setRequestHandler(types_js_1.GetPromptRequestSchema, this.mcpGetPrompt.bind(this));
|
|
62
69
|
this.server.oninitialized = async () => {
|
|
63
70
|
var _a, _b;
|
|
64
71
|
const clientInfo = this.server.getClientVersion();
|
|
@@ -159,11 +166,19 @@ class FirebaseMcpServer {
|
|
|
159
166
|
getTool(name) {
|
|
160
167
|
return this.availableTools.find((t) => t.mcp.name === name) || null;
|
|
161
168
|
}
|
|
169
|
+
get availablePrompts() {
|
|
170
|
+
var _a;
|
|
171
|
+
return (0, index_2.availablePrompts)(((_a = this.activeFeatures) === null || _a === void 0 ? void 0 : _a.length) ? this.activeFeatures : this.detectedFeatures);
|
|
172
|
+
}
|
|
173
|
+
getPrompt(name) {
|
|
174
|
+
return this.availablePrompts.find((p) => p.mcp.name === name) || null;
|
|
175
|
+
}
|
|
162
176
|
setProjectRoot(newRoot) {
|
|
163
177
|
this.updateStoredClientConfig({ projectRoot: newRoot });
|
|
164
178
|
this.cachedProjectRoot = newRoot || undefined;
|
|
165
179
|
this.detectedFeatures = undefined;
|
|
166
180
|
void this.server.sendToolListChanged();
|
|
181
|
+
void this.server.sendPromptListChanged();
|
|
167
182
|
}
|
|
168
183
|
async resolveOptions() {
|
|
169
184
|
const options = { cwd: this.cachedProjectRoot, isMCP: true };
|
|
@@ -258,6 +273,64 @@ class FirebaseMcpServer {
|
|
|
258
273
|
return (0, util_1.mcpError)(err);
|
|
259
274
|
}
|
|
260
275
|
}
|
|
276
|
+
async mcpListPrompts() {
|
|
277
|
+
await Promise.all([this.detectActiveFeatures(), this.detectProjectRoot()]);
|
|
278
|
+
const hasActiveProject = !!(await this.getProjectId());
|
|
279
|
+
await this.trackGA4("mcp_list_prompts");
|
|
280
|
+
const skipAutoAuthForStudio = (0, env_1.isFirebaseStudio)();
|
|
281
|
+
return {
|
|
282
|
+
prompts: this.availablePrompts.map((p) => ({
|
|
283
|
+
name: p.mcp.name,
|
|
284
|
+
description: p.mcp.description,
|
|
285
|
+
annotations: p.mcp.annotations,
|
|
286
|
+
arguments: p.mcp.arguments,
|
|
287
|
+
})),
|
|
288
|
+
_meta: {
|
|
289
|
+
projectRoot: this.cachedProjectRoot,
|
|
290
|
+
projectDetected: hasActiveProject,
|
|
291
|
+
authenticatedUser: await this.getAuthenticatedUser(skipAutoAuthForStudio),
|
|
292
|
+
activeFeatures: this.activeFeatures,
|
|
293
|
+
detectedFeatures: this.detectedFeatures,
|
|
294
|
+
},
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
async mcpGetPrompt(req) {
|
|
298
|
+
await this.detectProjectRoot();
|
|
299
|
+
const promptName = req.params.name;
|
|
300
|
+
const promptArgs = req.params.arguments || {};
|
|
301
|
+
const prompt = this.getPrompt(promptName);
|
|
302
|
+
if (!prompt) {
|
|
303
|
+
throw new Error(`Prompt '${promptName}' could not be found.`);
|
|
304
|
+
}
|
|
305
|
+
let projectId = await this.getProjectId();
|
|
306
|
+
projectId = projectId || "";
|
|
307
|
+
const skipAutoAuthForStudio = (0, env_1.isFirebaseStudio)();
|
|
308
|
+
const accountEmail = await this.getAuthenticatedUser(skipAutoAuthForStudio);
|
|
309
|
+
const options = { projectDir: this.cachedProjectRoot, cwd: this.cachedProjectRoot };
|
|
310
|
+
const promptsCtx = {
|
|
311
|
+
projectId: projectId,
|
|
312
|
+
host: this,
|
|
313
|
+
config: config_1.Config.load(options, true) || new config_1.Config({}, options),
|
|
314
|
+
rc: (0, rc_1.loadRC)(options),
|
|
315
|
+
accountEmail,
|
|
316
|
+
};
|
|
317
|
+
try {
|
|
318
|
+
const messages = await prompt.fn(promptArgs, promptsCtx);
|
|
319
|
+
await this.trackGA4("mcp_get_prompt", {
|
|
320
|
+
tool_name: promptName,
|
|
321
|
+
});
|
|
322
|
+
return {
|
|
323
|
+
messages,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
catch (err) {
|
|
327
|
+
await this.trackGA4("mcp_get_prompt", {
|
|
328
|
+
tool_name: promptName,
|
|
329
|
+
error: 1,
|
|
330
|
+
});
|
|
331
|
+
throw err;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
261
334
|
async start() {
|
|
262
335
|
const transport = process.env.FIREBASE_MCP_DEBUG_LOG
|
|
263
336
|
? new logging_transport_1.LoggingStdioServerTransport(process.env.FIREBASE_MCP_DEBUG_LOG)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deploy = void 0;
|
|
4
|
+
const prompt_1 = require("../../prompt");
|
|
5
|
+
exports.deploy = (0, prompt_1.prompt)({
|
|
6
|
+
name: "deploy",
|
|
7
|
+
omitPrefix: true,
|
|
8
|
+
description: "Use this command to deploy resources to Firebase.",
|
|
9
|
+
arguments: [
|
|
10
|
+
{
|
|
11
|
+
name: "prompt",
|
|
12
|
+
description: "any specific instructions you wish to provide about deploying",
|
|
13
|
+
required: false,
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
annotations: {
|
|
17
|
+
title: "Deploy to Firebase",
|
|
18
|
+
},
|
|
19
|
+
}, async ({ prompt }, { config, projectId, accountEmail }) => {
|
|
20
|
+
return [
|
|
21
|
+
{
|
|
22
|
+
role: "user",
|
|
23
|
+
content: {
|
|
24
|
+
type: "text",
|
|
25
|
+
text: `
|
|
26
|
+
Your goal is to deploy resources from the current project to Firebase.
|
|
27
|
+
|
|
28
|
+
Active user: ${accountEmail || "<NONE>"}
|
|
29
|
+
Active project: ${projectId || "<NONE>"}
|
|
30
|
+
|
|
31
|
+
Contents of \`firebase.json\` config file:
|
|
32
|
+
|
|
33
|
+
\`\`\`json
|
|
34
|
+
${config.readProjectFile("firebase.json", { fallback: "<FILE DOES NOT EXIST>" })}
|
|
35
|
+
\`\`\`
|
|
36
|
+
|
|
37
|
+
## User Instructions
|
|
38
|
+
|
|
39
|
+
${prompt || "<the user didn't supply specific instructions>"}
|
|
40
|
+
|
|
41
|
+
## Steps
|
|
42
|
+
|
|
43
|
+
Follow the steps below taking note of any user instructions provided above.
|
|
44
|
+
|
|
45
|
+
1. If there is no active user, prompt the user to run \`firebase login\` in an interactive terminal before continuing.
|
|
46
|
+
2. If there is no \`firebase.json\` file and the current workspace is a static web application, manually create a \`firebase.json\` with \`"hosting"\` configuration based on the current directory's web app configuration. Add a \`{"hosting": {"predeploy": "<build_script>"}}\` config to build before deploying.
|
|
47
|
+
3. If there is no active project, ask the user if they want to use an existing project or create a new one.
|
|
48
|
+
3a. If create a new one, use the \`firebase_create_project\` tool.
|
|
49
|
+
3b. If they want to use an existing one, ask them for a project id (the \`firebase_list_projects\` tool may be helpful).
|
|
50
|
+
4. Only after making sure Firebase has been initialized, run the \`firebase deploy\` shell command to perform the deploy. This may take a few minutes.
|
|
51
|
+
5. If the deploy has errors, attempt to fix them and ask the user clarifying questions as needed.
|
|
52
|
+
6. If the deploy needs \`--force\` to run successfully, ALWAYS prompt the user before running \`firebase deploy --force\`.
|
|
53
|
+
7. If only one specific feature is failing, use command \`firebase deploy --only <feature>\` as you debug.
|
|
54
|
+
`.trim(),
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.availablePrompts = void 0;
|
|
4
|
+
const core_1 = require("./core");
|
|
5
|
+
const prompts = {
|
|
6
|
+
core: core_1.corePrompts,
|
|
7
|
+
firestore: [],
|
|
8
|
+
storage: [],
|
|
9
|
+
dataconnect: [],
|
|
10
|
+
auth: [],
|
|
11
|
+
messaging: [],
|
|
12
|
+
remoteconfig: [],
|
|
13
|
+
crashlytics: [],
|
|
14
|
+
apphosting: [],
|
|
15
|
+
database: [],
|
|
16
|
+
};
|
|
17
|
+
function namespacePrompts(promptsToNamespace, feature) {
|
|
18
|
+
return promptsToNamespace.map((p) => {
|
|
19
|
+
const newPrompt = Object.assign({}, p);
|
|
20
|
+
newPrompt.mcp = Object.assign({}, p.mcp);
|
|
21
|
+
if (newPrompt.mcp.omitPrefix) {
|
|
22
|
+
}
|
|
23
|
+
else if (feature === "core") {
|
|
24
|
+
newPrompt.mcp.name = `firebase:${p.mcp.name}`;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
newPrompt.mcp.name = `firebase:${feature}:${p.mcp.name}`;
|
|
28
|
+
}
|
|
29
|
+
newPrompt.mcp._meta = Object.assign(Object.assign({}, p.mcp._meta), { feature });
|
|
30
|
+
return newPrompt;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
function availablePrompts(features) {
|
|
34
|
+
const allPrompts = namespacePrompts(prompts["core"], "core");
|
|
35
|
+
if (!features) {
|
|
36
|
+
features = Object.keys(prompts).filter((f) => f !== "core");
|
|
37
|
+
}
|
|
38
|
+
for (const feature of features) {
|
|
39
|
+
if (prompts[feature] && feature !== "core") {
|
|
40
|
+
allPrompts.push(...namespacePrompts(prompts[feature], feature));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return allPrompts;
|
|
44
|
+
}
|
|
45
|
+
exports.availablePrompts = availablePrompts;
|
|
@@ -37,5 +37,15 @@ exports.get_sdk_config = (0, tool_1.tool)({
|
|
|
37
37
|
if (!appId)
|
|
38
38
|
return (0, util_1.mcpError)(`Could not find an app for platform '${inputPlatform}' in project '${projectId}'`);
|
|
39
39
|
const sdkConfig = await (0, apps_1.getAppConfig)(appId, platform);
|
|
40
|
+
if ("configFilename" in sdkConfig) {
|
|
41
|
+
return {
|
|
42
|
+
content: [
|
|
43
|
+
{
|
|
44
|
+
type: "text",
|
|
45
|
+
text: `SDK config content for \`${sdkConfig.configFilename}\`:\n\n\`\`\`\n${Buffer.from(sdkConfig.configFileContents, "base64").toString("utf-8")}\n\`\`\``,
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
40
50
|
return (0, util_1.toContent)(sdkConfig, { format: "json" });
|
|
41
51
|
});
|
|
@@ -54,6 +54,10 @@ exports.init = (0, tool_1.tool)({
|
|
|
54
54
|
.describe("Provide this object to initialize Cloud Firestore in this project directory."),
|
|
55
55
|
dataconnect: zod_1.z
|
|
56
56
|
.object({
|
|
57
|
+
app_description: zod_1.z
|
|
58
|
+
.string()
|
|
59
|
+
.optional()
|
|
60
|
+
.describe("Provide a description of the app you are trying to build. If present, Gemini will help generate Data Connect Schema, Connector and seed data"),
|
|
57
61
|
service_id: zod_1.z
|
|
58
62
|
.string()
|
|
59
63
|
.optional()
|
|
@@ -74,7 +78,7 @@ exports.init = (0, tool_1.tool)({
|
|
|
74
78
|
.describe("The Postgres database ID to use in the Firebase Data Connect service."),
|
|
75
79
|
})
|
|
76
80
|
.optional()
|
|
77
|
-
.describe("Provide this object to initialize Firebase Data Connect in this project directory."),
|
|
81
|
+
.describe("Provide this object to initialize Firebase Data Connect with Cloud SQL Postgres in this project directory."),
|
|
78
82
|
storage: zod_1.z
|
|
79
83
|
.object({
|
|
80
84
|
rules_filename: zod_1.z
|
|
@@ -127,15 +131,12 @@ exports.init = (0, tool_1.tool)({
|
|
|
127
131
|
if (features.dataconnect) {
|
|
128
132
|
featuresList.push("dataconnect");
|
|
129
133
|
featureInfo.dataconnect = {
|
|
134
|
+
analyticsFlow: "mcp",
|
|
135
|
+
appDescription: features.dataconnect.app_description || "",
|
|
130
136
|
serviceId: features.dataconnect.service_id || "",
|
|
131
137
|
locationId: features.dataconnect.location_id || "",
|
|
132
138
|
cloudSqlInstanceId: features.dataconnect.cloudsql_instance_id || "",
|
|
133
139
|
cloudSqlDatabase: features.dataconnect.cloudsql_database || "",
|
|
134
|
-
connectors: [],
|
|
135
|
-
isNewInstance: false,
|
|
136
|
-
isNewDatabase: false,
|
|
137
|
-
schemaGql: [],
|
|
138
|
-
shouldProvisionCSQL: true,
|
|
139
140
|
};
|
|
140
141
|
}
|
|
141
142
|
const setup = {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.get_issue_details = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const tool_1 = require("../../tool");
|
|
6
|
+
const util_1 = require("../../util");
|
|
7
|
+
const getIssueDetails_1 = require("../../../crashlytics/getIssueDetails");
|
|
8
|
+
exports.get_issue_details = (0, tool_1.tool)({
|
|
9
|
+
name: "get_issue_details",
|
|
10
|
+
description: "Gets the details about a specific crashlytics issue.",
|
|
11
|
+
inputSchema: zod_1.z.object({
|
|
12
|
+
app_id: zod_1.z
|
|
13
|
+
.string()
|
|
14
|
+
.describe("The AppID for which the issues list should be fetched. For an Android application, read the mobilesdk_app_id value specified in the google-services.json file for the current package name. For an iOS Application, read the GOOGLE_APP_ID from GoogleService-Info.plist. If neither is available, use the `firebase_list_apps` tool to find an app_id to pass to this tool."),
|
|
15
|
+
issue_id: zod_1.z
|
|
16
|
+
.string()
|
|
17
|
+
.describe("The issue ID for which the details needs to be fetched. This is the value of the field `id` in the list of issues. Defaults to the first id in the list of issues."),
|
|
18
|
+
}),
|
|
19
|
+
annotations: {
|
|
20
|
+
title: "Gets the details of a specific issue.",
|
|
21
|
+
readOnlyHint: true,
|
|
22
|
+
},
|
|
23
|
+
_meta: {
|
|
24
|
+
requiresAuth: true,
|
|
25
|
+
requiresProject: false,
|
|
26
|
+
},
|
|
27
|
+
}, async ({ app_id, issue_id }) => {
|
|
28
|
+
if (!app_id)
|
|
29
|
+
return (0, util_1.mcpError)(`Must specify 'app_id' parameter.`);
|
|
30
|
+
if (!issue_id)
|
|
31
|
+
return (0, util_1.mcpError)(`Must specify 'issue_id' parameter.`);
|
|
32
|
+
return (0, util_1.toContent)(await (0, getIssueDetails_1.getIssueDetails)(app_id, issue_id));
|
|
33
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.get_sample_crash = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const tool_1 = require("../../tool");
|
|
6
|
+
const util_1 = require("../../util");
|
|
7
|
+
const getSampleCrash_1 = require("../../../crashlytics/getSampleCrash");
|
|
8
|
+
exports.get_sample_crash = (0, tool_1.tool)({
|
|
9
|
+
name: "get_sample_crash_for_issue",
|
|
10
|
+
description: "Gets the sample crash for an issue.",
|
|
11
|
+
inputSchema: zod_1.z.object({
|
|
12
|
+
app_id: zod_1.z
|
|
13
|
+
.string()
|
|
14
|
+
.describe("AppId for which the issues list should be fetched. For an Android application, read the mobilesdk_app_id value specified in the google-services.json file for the current package name. For an iOS Application, read the GOOGLE_APP_ID from GoogleService-Info.plist. If neither is available, use the `firebase_list_apps` tool to find an app_id to pass to this tool."),
|
|
15
|
+
issue_id: zod_1.z
|
|
16
|
+
.string()
|
|
17
|
+
.describe("The issue Id for which the sample crash needs to be fetched. This is the value of the field `id` in the list of issues. Defaults to the first id in the list of issues."),
|
|
18
|
+
variant_id: zod_1.z
|
|
19
|
+
.string()
|
|
20
|
+
.optional()
|
|
21
|
+
.describe("The issue variant Id used as a filter to get sample issues."),
|
|
22
|
+
sample_count: zod_1.z
|
|
23
|
+
.number()
|
|
24
|
+
.describe("Number of samples that needs to be fetched. Maximum value is 3. Defaults to 1.")
|
|
25
|
+
.default(1),
|
|
26
|
+
}),
|
|
27
|
+
annotations: {
|
|
28
|
+
title: "Gets a sample of a crash for a specific issue.",
|
|
29
|
+
readOnlyHint: true,
|
|
30
|
+
},
|
|
31
|
+
_meta: {
|
|
32
|
+
requiresAuth: true,
|
|
33
|
+
requiresProject: false,
|
|
34
|
+
},
|
|
35
|
+
}, async ({ app_id, issue_id, variant_id, sample_count }) => {
|
|
36
|
+
if (!app_id)
|
|
37
|
+
return (0, util_1.mcpError)(`Must specify 'app_id' parameter.`);
|
|
38
|
+
if (!issue_id)
|
|
39
|
+
return (0, util_1.mcpError)(`Must specify 'issue_id' parameter.`);
|
|
40
|
+
if (sample_count > 3)
|
|
41
|
+
sample_count = 3;
|
|
42
|
+
return (0, util_1.toContent)(await (0, getSampleCrash_1.getSampleCrash)(app_id, issue_id, sample_count, variant_id));
|
|
43
|
+
});
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.crashlyticsTools = void 0;
|
|
4
|
+
const get_issue_details_1 = require("./get_issue_details");
|
|
4
5
|
const list_top_issues_1 = require("./list_top_issues");
|
|
5
|
-
|
|
6
|
+
const get_sample_crash_1 = require("./get_sample_crash");
|
|
7
|
+
exports.crashlyticsTools = [
|
|
8
|
+
list_top_issues_1.list_top_issues,
|
|
9
|
+
get_issue_details_1.get_issue_details,
|
|
10
|
+
get_sample_crash_1.get_sample_crash,
|
|
11
|
+
];
|
|
@@ -16,7 +16,8 @@ exports.list_top_issues = (0, tool_1.tool)({
|
|
|
16
16
|
issue_count: zod_1.z
|
|
17
17
|
.number()
|
|
18
18
|
.optional()
|
|
19
|
-
.describe("Number of issues that needs to be fetched. Defaults to 10 if unspecified.")
|
|
19
|
+
.describe("Number of issues that needs to be fetched. Defaults to 10 if unspecified.")
|
|
20
|
+
.default(10),
|
|
20
21
|
issue_type: zod_1.z
|
|
21
22
|
.enum(["FATAL", "NON-FATAL", "ANR"])
|
|
22
23
|
.optional()
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.get_data = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const tool_1 = require("../../tool");
|
|
6
|
+
const util_1 = require("../../util");
|
|
7
|
+
const url = require("node:url");
|
|
8
|
+
const apiv2_1 = require("../../../apiv2");
|
|
9
|
+
const consumers_1 = require("node:stream/consumers");
|
|
10
|
+
const node_path_1 = require("node:path");
|
|
11
|
+
exports.get_data = (0, tool_1.tool)({
|
|
12
|
+
name: "get_data",
|
|
13
|
+
description: "Returns RTDB data from the specified location",
|
|
14
|
+
inputSchema: zod_1.z.object({
|
|
15
|
+
databaseUrl: zod_1.z
|
|
16
|
+
.string()
|
|
17
|
+
.optional()
|
|
18
|
+
.describe("connect to the database at url. If omitted, use default database instance <project>-default-rtdb.firebasedatabase.app. Can point to emulator URL (e.g. localhost:6000/<instance>)"),
|
|
19
|
+
path: zod_1.z.string().describe("The path to the data to read. (ex: /my/cool/path)"),
|
|
20
|
+
}),
|
|
21
|
+
annotations: {
|
|
22
|
+
title: "Get Realtime Database data",
|
|
23
|
+
readOnlyHint: true,
|
|
24
|
+
},
|
|
25
|
+
_meta: {
|
|
26
|
+
requiresAuth: false,
|
|
27
|
+
requiresProject: false,
|
|
28
|
+
},
|
|
29
|
+
}, async ({ path: getPath, databaseUrl }, { projectId, host }) => {
|
|
30
|
+
if (!getPath.startsWith("/")) {
|
|
31
|
+
return (0, util_1.mcpError)(`paths must start with '/' (you passed ''${getPath}')`);
|
|
32
|
+
}
|
|
33
|
+
const dbUrl = new url.URL(databaseUrl
|
|
34
|
+
? `${databaseUrl}/${getPath}.json`
|
|
35
|
+
: node_path_1.default.join(`https://${projectId}-default-rtdb.us-central1.firebasedatabase.app`, `${getPath}.json`));
|
|
36
|
+
const client = new apiv2_1.Client({
|
|
37
|
+
urlPrefix: dbUrl.origin,
|
|
38
|
+
auth: true,
|
|
39
|
+
});
|
|
40
|
+
host.logger.debug(`sending read request to path '${getPath}' for url '${dbUrl.toString()}'`);
|
|
41
|
+
const res = await client.request({
|
|
42
|
+
method: "GET",
|
|
43
|
+
path: dbUrl.pathname,
|
|
44
|
+
responseType: "stream",
|
|
45
|
+
resolveOnHTTPError: true,
|
|
46
|
+
});
|
|
47
|
+
const content = await (0, consumers_1.text)(res.body);
|
|
48
|
+
return (0, util_1.toContent)(content);
|
|
49
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.get_rules = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const apiv2_1 = require("../../../apiv2");
|
|
6
|
+
const tool_1 = require("../../tool");
|
|
7
|
+
const util_1 = require("../../util");
|
|
8
|
+
exports.get_rules = (0, tool_1.tool)({
|
|
9
|
+
name: "get_rules",
|
|
10
|
+
description: "Get an RTDB database's rules",
|
|
11
|
+
inputSchema: zod_1.z.object({
|
|
12
|
+
databaseUrl: zod_1.z
|
|
13
|
+
.string()
|
|
14
|
+
.optional()
|
|
15
|
+
.describe("connect to the database at url. If omitted, use default database instance <project>-default-rtdb.firebaseio.com. Can point to emulator URL (e.g. localhost:6000/<instance>)"),
|
|
16
|
+
}),
|
|
17
|
+
annotations: {
|
|
18
|
+
title: "Get Realtime Database rules",
|
|
19
|
+
readOnlyHint: true,
|
|
20
|
+
},
|
|
21
|
+
_meta: {
|
|
22
|
+
requiresAuth: false,
|
|
23
|
+
requiresProject: false,
|
|
24
|
+
},
|
|
25
|
+
}, async ({ databaseUrl }, { projectId }) => {
|
|
26
|
+
const dbUrl = databaseUrl !== null && databaseUrl !== void 0 ? databaseUrl : `https://${projectId}-default-rtdb.us-central1.firebasedatabase.app`;
|
|
27
|
+
const client = new apiv2_1.Client({ urlPrefix: dbUrl });
|
|
28
|
+
const response = await client.request({
|
|
29
|
+
method: "GET",
|
|
30
|
+
path: "/.settings/rules.json",
|
|
31
|
+
responseType: "stream",
|
|
32
|
+
resolveOnHTTPError: true,
|
|
33
|
+
});
|
|
34
|
+
if (response.status !== 200) {
|
|
35
|
+
return (0, util_1.mcpError)(`Failed to fetch current rules. Code: ${response.status}`);
|
|
36
|
+
}
|
|
37
|
+
const rules = await response.response.text();
|
|
38
|
+
return (0, util_1.toContent)(rules);
|
|
39
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.realtimeDatabaseTools = void 0;
|
|
4
|
+
const get_rules_1 = require("./get_rules");
|
|
5
|
+
const get_data_1 = require("./get_data");
|
|
6
|
+
const set_data_1 = require("./set_data");
|
|
7
|
+
const validate_rules_1 = require("./validate_rules");
|
|
8
|
+
exports.realtimeDatabaseTools = [get_data_1.get_data, set_data_1.set_data, get_rules_1.get_rules, validate_rules_1.validate_rules];
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.set_data = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const tool_1 = require("../../tool");
|
|
6
|
+
const util_1 = require("../../util");
|
|
7
|
+
const url = require("node:url");
|
|
8
|
+
const utils_1 = require("../../../utils");
|
|
9
|
+
const apiv2_1 = require("../../../apiv2");
|
|
10
|
+
const error_1 = require("../../../error");
|
|
11
|
+
const node_path_1 = require("node:path");
|
|
12
|
+
exports.set_data = (0, tool_1.tool)({
|
|
13
|
+
name: "set_data",
|
|
14
|
+
description: "Writes RTDB data to the specified location",
|
|
15
|
+
inputSchema: zod_1.z.object({
|
|
16
|
+
databaseUrl: zod_1.z
|
|
17
|
+
.string()
|
|
18
|
+
.optional()
|
|
19
|
+
.describe("connect to the database at url. If omitted, use default database instance <project>-default-rtdb.us-central1.firebasedatabase.app. Can point to emulator URL (e.g. localhost:6000/<instance>)"),
|
|
20
|
+
path: zod_1.z.string().describe("The path to the data to read. (ex: /my/cool/path)"),
|
|
21
|
+
data: zod_1.z.string().describe('The JSON to write. (ex: {"alphabet": ["a", "b", "c"]})'),
|
|
22
|
+
}),
|
|
23
|
+
annotations: {
|
|
24
|
+
title: "Set Realtime Database data",
|
|
25
|
+
readOnlyHint: false,
|
|
26
|
+
idempotentHint: true,
|
|
27
|
+
},
|
|
28
|
+
_meta: {
|
|
29
|
+
requiresAuth: false,
|
|
30
|
+
requiresProject: false,
|
|
31
|
+
},
|
|
32
|
+
}, async ({ path: setPath, databaseUrl, data }, { projectId, host }) => {
|
|
33
|
+
if (!setPath.startsWith("/")) {
|
|
34
|
+
return (0, util_1.mcpError)(`paths must start with '/' (you passed ''${setPath}')`);
|
|
35
|
+
}
|
|
36
|
+
const dbUrl = new url.URL(databaseUrl
|
|
37
|
+
? `${databaseUrl}/${setPath}.json`
|
|
38
|
+
: node_path_1.default.join(`https://${projectId}-default-rtdb.us-central1.firebasedatabase.app`, `${setPath}.json`));
|
|
39
|
+
const client = new apiv2_1.Client({
|
|
40
|
+
urlPrefix: dbUrl.origin,
|
|
41
|
+
auth: true,
|
|
42
|
+
});
|
|
43
|
+
const inStream = (0, utils_1.stringToStream)(data);
|
|
44
|
+
host.logger.debug(`sending write request to path '${setPath}' for url '${dbUrl.toString()}'`);
|
|
45
|
+
try {
|
|
46
|
+
await client.request({
|
|
47
|
+
method: "PUT",
|
|
48
|
+
path: dbUrl.pathname,
|
|
49
|
+
body: inStream,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
host.logger.debug((0, error_1.getErrMsg)(err));
|
|
54
|
+
return (0, util_1.mcpError)(`Unexpected error while setting data: ${(0, error_1.getErrMsg)(err)}`);
|
|
55
|
+
}
|
|
56
|
+
return (0, util_1.toContent)("write successful!");
|
|
57
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validate_rules = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const apiv2_1 = require("../../../apiv2");
|
|
6
|
+
const tool_1 = require("../../tool");
|
|
7
|
+
const util_1 = require("../../util");
|
|
8
|
+
const rtdb_1 = require("../../../rtdb");
|
|
9
|
+
const error_1 = require("../../../error");
|
|
10
|
+
exports.validate_rules = (0, tool_1.tool)({
|
|
11
|
+
name: "validate_rules",
|
|
12
|
+
description: "Validates an RTDB database's rules",
|
|
13
|
+
inputSchema: zod_1.z.object({
|
|
14
|
+
databaseUrl: zod_1.z
|
|
15
|
+
.string()
|
|
16
|
+
.optional()
|
|
17
|
+
.describe("connect to the database at url. If omitted, use default database instance <project>-default-rtdb.firebaseio.com. Can point to emulator URL (e.g. localhost:6000/<instance>)"),
|
|
18
|
+
rules: zod_1.z
|
|
19
|
+
.string()
|
|
20
|
+
.describe('The rules object, as a string (ex: {".read": false, ".write": false})'),
|
|
21
|
+
}),
|
|
22
|
+
annotations: {
|
|
23
|
+
title: "Validate Realtime Database rules",
|
|
24
|
+
idempotentHint: true,
|
|
25
|
+
},
|
|
26
|
+
_meta: {
|
|
27
|
+
requiresAuth: true,
|
|
28
|
+
requiresProject: false,
|
|
29
|
+
},
|
|
30
|
+
}, async ({ databaseUrl, rules }, { projectId, host }) => {
|
|
31
|
+
const dbUrl = databaseUrl !== null && databaseUrl !== void 0 ? databaseUrl : `https://${projectId}-default-rtdb.us-central1.firebasedatabase.app`;
|
|
32
|
+
const client = new apiv2_1.Client({ urlPrefix: dbUrl });
|
|
33
|
+
try {
|
|
34
|
+
await (0, rtdb_1.updateRulesWithClient)(client, rules, { dryRun: true });
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
host.logger.debug(`failed to update rules at url ${dbUrl}`);
|
|
38
|
+
return (0, util_1.mcpError)((0, error_1.getErrMsg)(e));
|
|
39
|
+
}
|
|
40
|
+
return (0, util_1.toContent)("the inputted rules are valid!");
|
|
41
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validate_rules = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const apiv2_1 = require("../../../apiv2");
|
|
6
|
+
const tool_1 = require("../../tool");
|
|
7
|
+
const util_1 = require("../../util");
|
|
8
|
+
const rtdb_1 = require("../../../rtdb");
|
|
9
|
+
const error_1 = require("../../../error");
|
|
10
|
+
exports.validate_rules = (0, tool_1.tool)({
|
|
11
|
+
name: "validate_rules",
|
|
12
|
+
description: "Validates an RTDB database's rules",
|
|
13
|
+
inputSchema: zod_1.z.object({
|
|
14
|
+
databaseUrl: zod_1.z
|
|
15
|
+
.string()
|
|
16
|
+
.optional()
|
|
17
|
+
.describe("connect to the database at url. If omitted, use default database instance <project>-default-rtdb.firebaseio.com. Can point to emulator URL (e.g. localhost:6000/<instance>)"),
|
|
18
|
+
rules: zod_1.z
|
|
19
|
+
.string()
|
|
20
|
+
.describe('The rules object, as a string (ex: {"rules": {".read": false, ".write": false}})'),
|
|
21
|
+
}),
|
|
22
|
+
annotations: {
|
|
23
|
+
title: "Validate Realtime Database rules",
|
|
24
|
+
idempotentHint: true,
|
|
25
|
+
},
|
|
26
|
+
_meta: {
|
|
27
|
+
requiresAuth: true,
|
|
28
|
+
requiresProject: false,
|
|
29
|
+
},
|
|
30
|
+
}, async ({ databaseUrl, rules }, { projectId, host }) => {
|
|
31
|
+
const dbUrl = databaseUrl !== null && databaseUrl !== void 0 ? databaseUrl : `https://${projectId}-default-rtdb.us-central1.firebasedatabase.app`;
|
|
32
|
+
const client = new apiv2_1.Client({ urlPrefix: dbUrl });
|
|
33
|
+
try {
|
|
34
|
+
await (0, rtdb_1.updateRulesWithClient)(client, rules, { dryRun: true });
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
host.logger.debug(`failed to validate rules at url ${dbUrl}`);
|
|
38
|
+
return (0, util_1.mcpError)((0, error_1.getErrMsg)(e));
|
|
39
|
+
}
|
|
40
|
+
return (0, util_1.toContent)("the inputted rules are valid!");
|
|
41
|
+
});
|