firebase-tools 15.16.0 → 15.17.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/lib/api.js +1 -1
- package/lib/deploy/apphosting/release.js +1 -1
- package/lib/deploy/functions/prepare.js +113 -3
- package/lib/deploy/functions/services/ailogic.js +7 -0
- package/lib/deploy/functions/services/database.js +16 -0
- package/lib/deploy/functions/services/firestore.js +1 -0
- package/lib/deploy/functions/services/storage.js +15 -1
- package/lib/deploy/functions/triggerRegionHelper.js +111 -2
- package/lib/emulator/downloadableEmulatorInfo.json +23 -23
- package/lib/emulator/functionsEmulatorShared.js +2 -1
- package/lib/env.js +5 -1
- package/lib/firestore/api-sort.js +22 -0
- package/lib/firestore/api-types.js +11 -1
- package/lib/firestore/api.js +21 -1
- package/lib/firestore/fsConfig.js +8 -0
- package/lib/firestore/pretty-print.js +26 -8
- package/lib/frameworks/next/index.js +1 -1
- package/lib/mcp/apps/deploy/mcp-app.js +120 -0
- package/lib/mcp/apps/deploy/vite.config.js +16 -0
- package/lib/mcp/apps/init/mcp-app.js +230 -0
- package/lib/mcp/apps/init/vite.config.js +16 -0
- package/lib/mcp/apps/update_environment/mcp-app.js +38 -36
- package/lib/mcp/apps/update_environment/vite.config.js +16 -0
- package/lib/mcp/index.js +16 -5
- package/lib/mcp/resources/deploy_ui.js +31 -0
- package/lib/mcp/resources/index.js +4 -0
- package/lib/mcp/resources/init_ui.js +31 -0
- package/lib/mcp/resources/update_environment_ui.js +3 -3
- package/lib/mcp/tools/auth/get_users.js +1 -1
- package/lib/mcp/tools/core/deploy.js +87 -0
- package/lib/mcp/tools/core/deploy_status.js +32 -0
- package/lib/mcp/tools/core/index.js +4 -0
- package/lib/mcp/tools/core/init.js +3 -0
- package/lib/mcp/tools/core/update_environment.js +3 -0
- package/lib/mcp/tools/firestore/query_collection.js +1 -1
- package/lib/mcp/tools/functions/list_functions.js +2 -2
- package/lib/mcp/util/jobs.js +31 -0
- package/lib/mcp/util.js +5 -4
- package/lib/tsconfig.compile.tsbuildinfo +1 -1
- package/lib/tsconfig.publish.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/templates/init/functions/dart/pubspec.yaml +1 -1
- package/templates/init/functions/dart/server.dart +2 -2
|
@@ -16,6 +16,8 @@ const track_1 = require("../../track");
|
|
|
16
16
|
const crashlytics_issues_1 = require("./guides/crashlytics_issues");
|
|
17
17
|
const crashlytics_reports_1 = require("./guides/crashlytics_reports");
|
|
18
18
|
const update_environment_ui_1 = require("./update_environment_ui");
|
|
19
|
+
const deploy_ui_1 = require("./deploy_ui");
|
|
20
|
+
const init_ui_1 = require("./init_ui");
|
|
19
21
|
exports.resources = [
|
|
20
22
|
app_id_1.app_id,
|
|
21
23
|
crashlytics_investigations_1.crashlytics_investigations,
|
|
@@ -28,6 +30,8 @@ exports.resources = [
|
|
|
28
30
|
init_auth_1.init_auth,
|
|
29
31
|
init_hosting_1.init_hosting,
|
|
30
32
|
update_environment_ui_1.update_environment_ui,
|
|
33
|
+
deploy_ui_1.deploy_ui,
|
|
34
|
+
init_ui_1.init_ui,
|
|
31
35
|
];
|
|
32
36
|
exports.resourceTemplates = [docs_1.docs];
|
|
33
37
|
async function resolveResource(uri, ctx, track = true) {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.init_ui = exports.RESOURCE_MIME_TYPE = void 0;
|
|
4
|
+
const resource_1 = require("../resource");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const fs = require("fs/promises");
|
|
7
|
+
exports.RESOURCE_MIME_TYPE = "text/html;profile=mcp-app";
|
|
8
|
+
const resourceUri = "ui://core/init/mcp-app.html";
|
|
9
|
+
exports.init_ui = (0, resource_1.resource)({
|
|
10
|
+
uri: resourceUri,
|
|
11
|
+
name: "Init UI",
|
|
12
|
+
description: "Visual interface for Firebase Init",
|
|
13
|
+
mimeType: exports.RESOURCE_MIME_TYPE,
|
|
14
|
+
}, async (_uri, _ctx) => {
|
|
15
|
+
try {
|
|
16
|
+
const htmlPath = path.join(__dirname, "../apps/init/mcp-app.html");
|
|
17
|
+
const html = await fs.readFile(htmlPath, "utf-8");
|
|
18
|
+
return {
|
|
19
|
+
contents: [
|
|
20
|
+
{
|
|
21
|
+
uri: resourceUri,
|
|
22
|
+
mimeType: exports.RESOURCE_MIME_TYPE,
|
|
23
|
+
text: html,
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
throw new Error(`Failed to load Init UI: ${e.message}`);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
@@ -4,13 +4,13 @@ exports.update_environment_ui = void 0;
|
|
|
4
4
|
const resource_1 = require("../resource");
|
|
5
5
|
const path = require("path");
|
|
6
6
|
const fs = require("fs/promises");
|
|
7
|
-
const
|
|
7
|
+
const RESOURCE_MIME_TYPE = "text/html;profile=mcp-app";
|
|
8
8
|
const resourceUri = "ui://core/update_environment/mcp-app.html";
|
|
9
9
|
exports.update_environment_ui = (0, resource_1.resource)({
|
|
10
10
|
uri: resourceUri,
|
|
11
11
|
name: "Update Environment UI",
|
|
12
12
|
description: "Visual interface for selecting active Firebase project",
|
|
13
|
-
mimeType:
|
|
13
|
+
mimeType: RESOURCE_MIME_TYPE,
|
|
14
14
|
}, async () => {
|
|
15
15
|
try {
|
|
16
16
|
const htmlPath = path.join(__dirname, "../apps/update_environment/mcp-app.html");
|
|
@@ -19,7 +19,7 @@ exports.update_environment_ui = (0, resource_1.resource)({
|
|
|
19
19
|
contents: [
|
|
20
20
|
{
|
|
21
21
|
uri: resourceUri,
|
|
22
|
-
mimeType:
|
|
22
|
+
mimeType: RESOURCE_MIME_TYPE,
|
|
23
23
|
text: html,
|
|
24
24
|
},
|
|
25
25
|
],
|
|
@@ -50,5 +50,5 @@ exports.get_users = (0, tool_1.tool)("auth", {
|
|
|
50
50
|
if (!uids?.length && !emails?.length && !phone_numbers?.length) {
|
|
51
51
|
users = await (0, auth_1.listUsers)(projectId, limit || 100);
|
|
52
52
|
}
|
|
53
|
-
return (0, util_1.toContent)(users.map(prune));
|
|
53
|
+
return (0, util_1.toContent)({ users: users.map(prune) });
|
|
54
54
|
});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deploy = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const tool_1 = require("../../tool");
|
|
6
|
+
const deploy_1 = require("../../../deploy");
|
|
7
|
+
const util_1 = require("../../util");
|
|
8
|
+
const jobs_1 = require("../../util/jobs");
|
|
9
|
+
const TARGETS = {
|
|
10
|
+
hosting: null,
|
|
11
|
+
database: null,
|
|
12
|
+
firestore: null,
|
|
13
|
+
functions: null,
|
|
14
|
+
storage: null,
|
|
15
|
+
remoteconfig: null,
|
|
16
|
+
extensions: null,
|
|
17
|
+
dataconnect: null,
|
|
18
|
+
apphosting: null,
|
|
19
|
+
auth: null,
|
|
20
|
+
};
|
|
21
|
+
exports.deploy = (0, tool_1.tool)("core", {
|
|
22
|
+
name: "deploy",
|
|
23
|
+
description: "Deploy resources to your Firebase project, based on the contents of firebase.json.",
|
|
24
|
+
inputSchema: zod_1.z.object({
|
|
25
|
+
only: zod_1.z
|
|
26
|
+
.string()
|
|
27
|
+
.optional()
|
|
28
|
+
.describe("Comma-separated list of services to deploy. Valid targets are: database, storage, firestore, functions, hosting, remoteconfig, extensions, dataconnect, apphosting, auth."),
|
|
29
|
+
}),
|
|
30
|
+
annotations: {
|
|
31
|
+
title: "Deploy Firebase Services",
|
|
32
|
+
readOnlyHint: false,
|
|
33
|
+
},
|
|
34
|
+
_meta: {
|
|
35
|
+
requiresAuth: true,
|
|
36
|
+
requiresProject: true,
|
|
37
|
+
ui: {
|
|
38
|
+
resourceUri: "ui://core/deploy/mcp-app.html",
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
}, async ({ only }, ctx) => {
|
|
42
|
+
const validTargets = Object.keys(TARGETS);
|
|
43
|
+
let targets = validTargets;
|
|
44
|
+
if (only) {
|
|
45
|
+
const parts = only.split(",").map((p) => p.trim());
|
|
46
|
+
targets = parts.filter((p) => validTargets.includes(p));
|
|
47
|
+
}
|
|
48
|
+
const jobId = Date.now().toString();
|
|
49
|
+
jobs_1.jobTracker.createJob(jobId);
|
|
50
|
+
const options = {
|
|
51
|
+
only: only || "",
|
|
52
|
+
except: "",
|
|
53
|
+
filteredTargets: targets,
|
|
54
|
+
project: ctx.projectId,
|
|
55
|
+
projectId: ctx.projectId,
|
|
56
|
+
rc: ctx.rc,
|
|
57
|
+
config: ctx.config,
|
|
58
|
+
nonInteractive: true,
|
|
59
|
+
onProgress: (progress) => {
|
|
60
|
+
const phaseNumbers = {
|
|
61
|
+
predeploy: 10,
|
|
62
|
+
prepare: 30,
|
|
63
|
+
deploy: 60,
|
|
64
|
+
release: 80,
|
|
65
|
+
postdeploy: 100,
|
|
66
|
+
};
|
|
67
|
+
const percentage = phaseNumbers[progress.phase] || 0;
|
|
68
|
+
jobs_1.jobTracker.updateJob(jobId, { progress: percentage });
|
|
69
|
+
jobs_1.jobTracker.addLog(jobId, `Deploy [${progress.phase}]: Complete for targets ${(progress.targets || []).join(",")}`);
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
void (async () => {
|
|
73
|
+
try {
|
|
74
|
+
const res = await (0, deploy_1.deploy)(targets, options);
|
|
75
|
+
jobs_1.jobTracker.updateJob(jobId, { status: "success", progress: 100, result: res });
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
79
|
+
jobs_1.jobTracker.updateJob(jobId, { status: "failed", error: message });
|
|
80
|
+
}
|
|
81
|
+
})();
|
|
82
|
+
const contentRes = (0, util_1.toContent)(`Deployment started with Job ID: ${jobId}. Use deploy_status tool to track.`);
|
|
83
|
+
return {
|
|
84
|
+
...contentRes,
|
|
85
|
+
structuredContent: { jobId, message: "Deployment started" },
|
|
86
|
+
};
|
|
87
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deploy_status = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const tool_1 = require("../../tool");
|
|
6
|
+
const util_1 = require("../../util");
|
|
7
|
+
const jobs_1 = require("../../util/jobs");
|
|
8
|
+
exports.deploy_status = (0, tool_1.tool)("core", {
|
|
9
|
+
name: "deploy_status",
|
|
10
|
+
description: "Check the status of a background deployment job using its Job ID.",
|
|
11
|
+
inputSchema: zod_1.z.object({
|
|
12
|
+
jobId: zod_1.z.string().describe("The Job ID returned by the deploy tool"),
|
|
13
|
+
}),
|
|
14
|
+
annotations: {
|
|
15
|
+
title: "Check Deployment Status",
|
|
16
|
+
readOnlyHint: true,
|
|
17
|
+
},
|
|
18
|
+
_meta: {
|
|
19
|
+
requiresAuth: true,
|
|
20
|
+
requiresProject: true,
|
|
21
|
+
},
|
|
22
|
+
}, async ({ jobId }) => {
|
|
23
|
+
const job = jobs_1.jobTracker.getJob(jobId);
|
|
24
|
+
if (!job) {
|
|
25
|
+
return (0, util_1.mcpError)(`Job not found: ${jobId}`);
|
|
26
|
+
}
|
|
27
|
+
const contentRes = (0, util_1.toContent)(`Job ID: ${jobId}\nStatus: ${job.status}\nProgress: ${job.progress}%\n\nLogs:\n${job.logs.join("\n")}`);
|
|
28
|
+
return {
|
|
29
|
+
...contentRes,
|
|
30
|
+
structuredContent: job,
|
|
31
|
+
};
|
|
32
|
+
});
|
|
@@ -16,6 +16,8 @@ const logout_1 = require("./logout");
|
|
|
16
16
|
const get_security_rules_1 = require("./get_security_rules");
|
|
17
17
|
const validate_security_rules_1 = require("./validate_security_rules");
|
|
18
18
|
const read_resources_1 = require("./read_resources");
|
|
19
|
+
const deploy_1 = require("./deploy");
|
|
20
|
+
const deploy_status_1 = require("./deploy_status");
|
|
19
21
|
exports.coreTools = [
|
|
20
22
|
login_1.login,
|
|
21
23
|
logout_1.logout,
|
|
@@ -32,4 +34,6 @@ exports.coreTools = [
|
|
|
32
34
|
init_1.init,
|
|
33
35
|
get_security_rules_1.get_security_rules,
|
|
34
36
|
read_resources_1.read_resources,
|
|
37
|
+
deploy_1.deploy,
|
|
38
|
+
deploy_status_1.deploy_status,
|
|
35
39
|
];
|
|
@@ -160,6 +160,9 @@ exports.init = (0, tool_1.tool)("core", {
|
|
|
160
160
|
_meta: {
|
|
161
161
|
requiresProject: false,
|
|
162
162
|
requiresAuth: false,
|
|
163
|
+
ui: {
|
|
164
|
+
resourceUri: "ui://core/init/mcp-app.html",
|
|
165
|
+
},
|
|
163
166
|
},
|
|
164
167
|
}, async ({ features }, { projectId, config, rc }) => {
|
|
165
168
|
const featuresList = [];
|
|
@@ -32,6 +32,9 @@ exports.update_environment = (0, tool_1.tool)("core", {
|
|
|
32
32
|
optionalProjectDir: true,
|
|
33
33
|
requiresAuth: false,
|
|
34
34
|
requiresProject: false,
|
|
35
|
+
ui: {
|
|
36
|
+
resourceUri: "ui://core/update_environment/mcp-app.html",
|
|
37
|
+
},
|
|
35
38
|
},
|
|
36
39
|
}, async ({ project_dir, active_project, active_user_account }, { config, rc, host }) => {
|
|
37
40
|
let output = "";
|
|
@@ -127,6 +127,6 @@ exports.query_collection = (0, tool_1.tool)("firestore", {
|
|
|
127
127
|
}
|
|
128
128
|
const { documents } = await (0, firestore_1.queryCollection)(projectId, structuredQuery, database, emulatorUrl);
|
|
129
129
|
const docs = documents.map(converter_1.firestoreDocumentToJson);
|
|
130
|
-
const docsContent = (0, util_1.toContent)(docs);
|
|
130
|
+
const docsContent = (0, util_1.toContent)({ documents: docs });
|
|
131
131
|
return docsContent;
|
|
132
132
|
});
|
|
@@ -35,11 +35,11 @@ exports.list_functions = (0, tool_1.tool)("functions", {
|
|
|
35
35
|
runtime: endpoint.runtime,
|
|
36
36
|
}));
|
|
37
37
|
if (!formattedList.length) {
|
|
38
|
-
return (0, util_1.toContent)([], {
|
|
38
|
+
return (0, util_1.toContent)({ functions: [] }, {
|
|
39
39
|
contentPrefix: "No functions found in this project.\n\n",
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
|
-
return (0, util_1.toContent)(formattedList);
|
|
42
|
+
return (0, util_1.toContent)({ functions: formattedList });
|
|
43
43
|
}
|
|
44
44
|
catch (err) {
|
|
45
45
|
const errMsg = (0, error_1.getErrMsg)(err?.original || err, "Failed to list functions.");
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.jobTracker = void 0;
|
|
4
|
+
class JobTracker {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.jobs = new Map();
|
|
7
|
+
}
|
|
8
|
+
createJob(id) {
|
|
9
|
+
this.jobs.set(id, {
|
|
10
|
+
status: "running",
|
|
11
|
+
progress: 0,
|
|
12
|
+
logs: [],
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
updateJob(id, updates) {
|
|
16
|
+
const job = this.jobs.get(id);
|
|
17
|
+
if (job) {
|
|
18
|
+
Object.assign(job, updates);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
addLog(id, log) {
|
|
22
|
+
const job = this.jobs.get(id);
|
|
23
|
+
if (job) {
|
|
24
|
+
job.logs.push(log);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
getJob(id) {
|
|
28
|
+
return this.jobs.get(id);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
exports.jobTracker = new JobTracker();
|
package/lib/mcp/util.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RESOURCE_MIME_TYPE = void 0;
|
|
4
3
|
exports.toContent = toContent;
|
|
5
4
|
exports.applyAppMeta = applyAppMeta;
|
|
6
5
|
exports.mcpError = mcpError;
|
|
@@ -26,10 +25,13 @@ function toContent(data, options) {
|
|
|
26
25
|
}
|
|
27
26
|
const prefix = options?.contentPrefix || "";
|
|
28
27
|
const suffix = options?.contentSuffix || "";
|
|
29
|
-
|
|
28
|
+
const result = {
|
|
30
29
|
content: [{ type: "text", text: `${prefix}${text}${suffix}` }],
|
|
31
|
-
structuredContent: data,
|
|
32
30
|
};
|
|
31
|
+
if (typeof data === "object" && data !== null && !Array.isArray(data)) {
|
|
32
|
+
result.structuredContent = data;
|
|
33
|
+
}
|
|
34
|
+
return result;
|
|
33
35
|
}
|
|
34
36
|
function applyAppMeta(result, resourceUri) {
|
|
35
37
|
if (experiments.isEnabled("mcpapps")) {
|
|
@@ -241,4 +243,3 @@ function cleanSchema(schema) {
|
|
|
241
243
|
const result = deepClean(schema, true);
|
|
242
244
|
return result === null ? {} : result;
|
|
243
245
|
}
|
|
244
|
-
exports.RESOURCE_MIME_TYPE = "application/vnd.mcp.ext-app+html";
|