firebase-tools 14.3.0 → 14.4.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 +10 -2
- package/lib/apphosting/backend.js +72 -18
- package/lib/apphosting/githubConnections.js +13 -19
- package/lib/apphosting/rollout.js +2 -2
- package/lib/apphosting/secrets/dialogs.js +4 -4
- package/lib/apphosting/secrets/index.js +2 -2
- package/lib/auth.js +22 -2
- package/lib/bin/cli.js +1 -33
- package/lib/bin/mcp.js +12 -2
- package/lib/checkValidTargetFilters.js +1 -0
- package/lib/command.js +3 -2
- package/lib/commands/apphosting-secrets-grantaccess.js +1 -1
- package/lib/commands/deploy.js +1 -0
- package/lib/commands/init.js +1 -1
- package/lib/commands/login-use.js +2 -11
- package/lib/commands/use.js +9 -8
- package/lib/config.js +1 -0
- package/lib/crashlytics/listTopIssues.js +44 -0
- package/lib/dataconnect/cloudAICompanionClient.js +67 -0
- package/lib/dataconnect/dataplaneClient.js +16 -1
- package/lib/dataconnect/types.js +5 -1
- package/lib/deploy/apphosting/args.js +2 -0
- package/lib/deploy/apphosting/deploy.js +74 -0
- package/lib/deploy/apphosting/index.js +9 -0
- package/lib/deploy/apphosting/prepare.js +141 -0
- package/lib/deploy/apphosting/release.js +53 -0
- package/lib/deploy/apphosting/util.js +65 -0
- package/lib/deploy/extensions/v2FunctionHelper.js +2 -1
- package/lib/deploy/functions/checkIam.js +3 -3
- package/lib/deploy/functions/ensure.js +2 -1
- package/lib/deploy/functions/prepare.js +23 -16
- package/lib/deploy/functions/release/fabricator.js +4 -4
- package/lib/deploy/functions/release/index.js +1 -1
- package/lib/deploy/functions/runtimes/python/index.js +3 -0
- package/lib/deploy/functions/runtimes/supported/types.js +17 -11
- package/lib/deploy/index.js +2 -0
- package/lib/emulator/apphosting/index.js +1 -0
- package/lib/emulator/apphosting/serve.js +77 -3
- package/lib/emulator/auth/widget_ui.js +2 -1
- package/lib/emulator/controller.js +18 -4
- package/lib/emulator/dataconnectEmulator.js +9 -2
- package/lib/emulator/downloadableEmulatorInfo.json +60 -0
- package/lib/emulator/downloadableEmulators.js +25 -61
- package/lib/ensureApiEnabled.js +11 -1
- package/lib/experiments.js +1 -1
- package/lib/extensions/manifest.js +2 -2
- package/lib/fsAsync.js +9 -2
- package/lib/gcp/auth.js +32 -2
- package/lib/gcp/cloudbilling.js +12 -1
- package/lib/gcp/cloudfunctions.js +1 -2
- package/lib/gcp/cloudfunctionsv2.js +1 -2
- package/lib/gcp/cloudscheduler.js +2 -2
- package/lib/gcp/computeEngine.js +19 -2
- package/lib/gcp/devConnect.js +6 -1
- package/lib/gcp/firestore.js +24 -1
- package/lib/gcp/iam.js +1 -5
- package/lib/gcp/storage.js +43 -1
- package/lib/index.js +1 -2
- package/lib/init/features/apphosting.js +84 -6
- package/lib/init/features/database.js +64 -45
- package/lib/init/features/dataconnect/index.js +51 -53
- package/lib/init/features/emulators.js +9 -5
- package/lib/init/features/firestore/index.js +54 -23
- package/lib/init/features/firestore/indexes.js +23 -23
- package/lib/init/features/firestore/rules.js +35 -40
- package/lib/init/features/functions/index.js +2 -0
- package/lib/init/features/functions/javascript.js +3 -2
- package/lib/init/features/functions/typescript.js +3 -2
- package/lib/init/features/genkit/index.js +2 -1
- package/lib/init/features/hosting/github.js +3 -2
- package/lib/init/features/index.js +8 -4
- package/lib/init/features/remoteconfig.js +3 -2
- package/lib/init/index.js +76 -24
- package/lib/logger.js +71 -7
- package/lib/management/projects.js +25 -2
- package/lib/mcp/errors.js +1 -1
- package/lib/mcp/index.js +134 -51
- package/lib/mcp/tools/auth/{disable_auth_user.js → disable_user.js} +3 -3
- package/lib/mcp/tools/auth/get_user.js +38 -0
- package/lib/mcp/tools/auth/index.js +8 -6
- package/lib/mcp/tools/auth/list_users.js +47 -0
- package/lib/mcp/tools/auth/set_claims.js +43 -0
- package/lib/mcp/tools/auth/set_sms_region_policy.js +1 -1
- package/lib/mcp/tools/core/{consult_firebase_assistant.js → consult_assistant.js} +4 -4
- package/lib/mcp/tools/core/create_android_sha.js +40 -0
- package/lib/mcp/tools/core/create_app.js +90 -0
- package/lib/mcp/tools/core/create_project.js +68 -0
- package/lib/mcp/tools/core/get_admin_sdk_config.js +26 -0
- package/lib/mcp/tools/core/get_environment.js +51 -0
- package/lib/mcp/tools/{project → core}/get_sdk_config.js +6 -3
- package/lib/mcp/tools/core/index.js +20 -6
- package/lib/mcp/tools/core/init.js +129 -0
- package/lib/mcp/tools/core/update_environment.js +55 -0
- package/lib/mcp/tools/crashlytics/index.js +5 -0
- package/lib/mcp/tools/crashlytics/list_top_issues.js +34 -0
- package/lib/mcp/tools/dataconnect/converter.js +30 -1
- package/lib/mcp/tools/dataconnect/emulator.js +32 -0
- package/lib/mcp/tools/dataconnect/execute_graphql.js +48 -0
- package/lib/mcp/tools/dataconnect/execute_graphql_read.js +48 -0
- package/lib/mcp/tools/dataconnect/execute_mutation.js +62 -0
- package/lib/mcp/tools/dataconnect/execute_query.js +62 -0
- package/lib/mcp/tools/dataconnect/{generate_dataconnect_operation.js → generate_operation.js} +9 -9
- package/lib/mcp/tools/dataconnect/{generate_dataconnect_schema.js → generate_schema.js} +4 -4
- package/lib/mcp/tools/dataconnect/{get_dataconnect_connector.js → get_connector.js} +9 -9
- package/lib/mcp/tools/dataconnect/{get_dataconnect_schema.js → get_schema.js} +10 -10
- package/lib/mcp/tools/dataconnect/index.js +14 -10
- package/lib/mcp/tools/dataconnect/{list_dataconnect_services.js → list_services.js} +5 -5
- package/lib/mcp/tools/firestore/converter.js +47 -1
- package/lib/mcp/tools/firestore/delete_document.js +37 -0
- package/lib/mcp/tools/firestore/{get_firestore_documents.js → get_documents.js} +3 -3
- package/lib/mcp/tools/firestore/index.js +12 -6
- package/lib/mcp/tools/firestore/{list_firestore_collections.js → list_collections.js} +4 -9
- package/lib/mcp/tools/firestore/query_collection.js +116 -0
- package/lib/mcp/tools/index.js +44 -8
- package/lib/mcp/tools/messaging/index.js +5 -0
- package/lib/mcp/tools/messaging/send_message.js +42 -0
- package/lib/mcp/tools/remoteconfig/get_template.js +27 -0
- package/lib/mcp/tools/remoteconfig/index.js +7 -0
- package/lib/mcp/tools/remoteconfig/publish_template.js +34 -0
- package/lib/mcp/tools/remoteconfig/rollback_template.js +29 -0
- package/lib/mcp/tools/rules/get_rules.js +29 -0
- package/lib/mcp/tools/rules/validate_rules.js +98 -0
- package/lib/mcp/tools/storage/get_download_url.js +34 -0
- package/lib/mcp/tools/storage/{get_storage_rules.js → get_rules.js} +6 -6
- package/lib/mcp/tools/storage/index.js +8 -2
- package/lib/mcp/types.js +9 -1
- package/lib/mcp/util.js +29 -2
- package/lib/messaging/interfaces.js +2 -0
- package/lib/messaging/sendMessage.js +48 -0
- package/lib/remoteconfig/publish.js +39 -0
- package/lib/requireAuth.js +2 -2
- package/lib/utils.js +2 -37
- package/package.json +2 -1
- package/schema/firebase-config.json +62 -10
- package/templates/init/functions/javascript/package.lint.json +1 -1
- package/templates/init/functions/javascript/package.nolint.json +1 -1
- package/templates/init/functions/typescript/package.lint.json +1 -1
- package/templates/init/functions/typescript/package.nolint.json +2 -2
- package/lib/mcp/tools/auth/get_auth_user.js +0 -29
- package/lib/mcp/tools/auth/set_auth_claims.js +0 -34
- package/lib/mcp/tools/core/get_firebase_directory.js +0 -20
- package/lib/mcp/tools/core/set_firebase_directory.js +0 -33
- package/lib/mcp/tools/firestore/get_firestore_rules.js +0 -26
- package/lib/mcp/tools/project/index.js +0 -7
- /package/lib/mcp/tools/{project → core}/get_project.js +0 -0
- /package/lib/mcp/tools/{project → core}/list_apps.js +0 -0
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.list_collections = 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 firestore_js_1 = require("../../../gcp/firestore.js");
|
|
8
8
|
const errors_js_1 = require("../../errors.js");
|
|
9
|
-
exports.
|
|
10
|
-
name: "
|
|
9
|
+
exports.list_collections = (0, tool_js_1.tool)({
|
|
10
|
+
name: "list_collections",
|
|
11
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
|
-
}),
|
|
12
|
+
inputSchema: zod_1.z.object({}),
|
|
18
13
|
annotations: {
|
|
19
14
|
title: "List Firestore collections",
|
|
20
15
|
readOnlyHint: true,
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.query_collection = 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.query_collection = (0, tool_js_1.tool)({
|
|
10
|
+
name: "query_collection",
|
|
11
|
+
description: "Retrieves one or more Firestore documents from a collection is a database in the current project by a collection with a full document path. Use this if you know the exact path of a collection and the filtering clause you would like for the document.",
|
|
12
|
+
inputSchema: zod_1.z.object({
|
|
13
|
+
collection_path: zod_1.z
|
|
14
|
+
.string()
|
|
15
|
+
.describe("A collection path (e.g. `collectionName/` or `parentCollection/parentDocument/collectionName`)"),
|
|
16
|
+
filters: zod_1.z
|
|
17
|
+
.object({
|
|
18
|
+
compare_value: zod_1.z
|
|
19
|
+
.object({
|
|
20
|
+
string_value: zod_1.z.string().nullish().describe("The string value to compare against."),
|
|
21
|
+
boolean_value: zod_1.z.string().nullish().describe("The boolean value to compare against."),
|
|
22
|
+
string_array_value: zod_1.z
|
|
23
|
+
.array(zod_1.z.string())
|
|
24
|
+
.nullish()
|
|
25
|
+
.describe("The string value to compare against."),
|
|
26
|
+
integer_value: zod_1.z.number().nullish().describe("The integer value to compare against."),
|
|
27
|
+
double_value: zod_1.z.number().nullish().describe("The double value to compare against."),
|
|
28
|
+
})
|
|
29
|
+
.describe("One and only one value may be specified per filters object."),
|
|
30
|
+
field: zod_1.z.string().describe("the field searching against"),
|
|
31
|
+
op: zod_1.z
|
|
32
|
+
.enum([
|
|
33
|
+
"OPERATOR_UNSPECIFIED",
|
|
34
|
+
"LESS_THAN",
|
|
35
|
+
"LESS_THAN_OR_EQUAL",
|
|
36
|
+
"GREATER_THAN",
|
|
37
|
+
"GREATER_THAN_OR_EQUAL",
|
|
38
|
+
"EQUAL",
|
|
39
|
+
"NOT_EQUAL",
|
|
40
|
+
"ARRAY_CONTAINS",
|
|
41
|
+
"ARRAY_CONTAINS_ANY",
|
|
42
|
+
"IN",
|
|
43
|
+
"NOT_IN",
|
|
44
|
+
])
|
|
45
|
+
.describe("the equality evaluator to use"),
|
|
46
|
+
})
|
|
47
|
+
.array()
|
|
48
|
+
.describe("the multiple filters to use in querying against the existing collection."),
|
|
49
|
+
order: zod_1.z
|
|
50
|
+
.object({
|
|
51
|
+
orderBy: zod_1.z.string().describe("the field to order by"),
|
|
52
|
+
orderByDirection: zod_1.z
|
|
53
|
+
.enum(["ASCENDING", "DESCENDING"])
|
|
54
|
+
.describe("the direction to order values"),
|
|
55
|
+
})
|
|
56
|
+
.nullish()
|
|
57
|
+
.describe("Specifies the field and direction to order the results. If not provided, the order is undefined."),
|
|
58
|
+
limit: zod_1.z
|
|
59
|
+
.number()
|
|
60
|
+
.describe("The maximum amount of records to return. Default is 10.")
|
|
61
|
+
.nullish(),
|
|
62
|
+
}),
|
|
63
|
+
annotations: {
|
|
64
|
+
title: "Query Firestore collection",
|
|
65
|
+
readOnlyHint: true,
|
|
66
|
+
},
|
|
67
|
+
_meta: {
|
|
68
|
+
requiresAuth: true,
|
|
69
|
+
requiresProject: true,
|
|
70
|
+
},
|
|
71
|
+
}, async ({ collection_path, filters, order, limit }, { projectId }) => {
|
|
72
|
+
if (!collection_path || !collection_path.length)
|
|
73
|
+
return (0, util_js_1.mcpError)("Must supply at least one collection path.");
|
|
74
|
+
const structuredQuery = {
|
|
75
|
+
from: [{ collectionId: collection_path, allDescendants: false }],
|
|
76
|
+
};
|
|
77
|
+
if (filters) {
|
|
78
|
+
structuredQuery.where = {
|
|
79
|
+
compositeFilter: {
|
|
80
|
+
op: "AND",
|
|
81
|
+
filters: filters.map((f) => {
|
|
82
|
+
if (f.compare_value.boolean_value &&
|
|
83
|
+
f.compare_value.double_value &&
|
|
84
|
+
f.compare_value.integer_value &&
|
|
85
|
+
f.compare_value.string_array_value &&
|
|
86
|
+
f.compare_value.string_value) {
|
|
87
|
+
throw (0, util_js_1.mcpError)("One and only one value may be specified per filters object.");
|
|
88
|
+
}
|
|
89
|
+
const out = Object.entries(f.compare_value).filter(([, value]) => {
|
|
90
|
+
return value !== null && value !== undefined;
|
|
91
|
+
});
|
|
92
|
+
return {
|
|
93
|
+
fieldFilter: {
|
|
94
|
+
field: { fieldPath: f.field },
|
|
95
|
+
op: f.op,
|
|
96
|
+
value: (0, converter_js_1.convertInputToValue)(out[0][1]),
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}),
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
if (order) {
|
|
104
|
+
structuredQuery.orderBy = [
|
|
105
|
+
{
|
|
106
|
+
field: { fieldPath: order.orderBy },
|
|
107
|
+
direction: order.orderByDirection,
|
|
108
|
+
},
|
|
109
|
+
];
|
|
110
|
+
}
|
|
111
|
+
structuredQuery.limit = limit ? limit : 10;
|
|
112
|
+
const { documents } = await (0, firestore_js_1.queryCollection)(projectId, structuredQuery);
|
|
113
|
+
const docs = documents.map(converter_js_1.firestoreDocumentToJson);
|
|
114
|
+
const docsContent = (0, util_js_1.toContent)(docs);
|
|
115
|
+
return docsContent;
|
|
116
|
+
});
|
package/lib/mcp/tools/index.js
CHANGED
|
@@ -1,15 +1,51 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.markdownDocsOfTools = exports.availableTools = void 0;
|
|
4
4
|
const index_js_1 = require("./auth/index.js");
|
|
5
5
|
const index_js_2 = require("./dataconnect/index.js");
|
|
6
6
|
const index_js_3 = require("./firestore/index.js");
|
|
7
|
-
const index_js_4 = require("./
|
|
7
|
+
const index_js_4 = require("./core/index.js");
|
|
8
8
|
const index_js_5 = require("./storage/index.js");
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
const index_js_6 = require("./messaging/index.js");
|
|
10
|
+
const index_js_7 = require("./remoteconfig/index.js");
|
|
11
|
+
const index_js_8 = require("./crashlytics/index.js");
|
|
12
|
+
function availableTools(activeFeatures) {
|
|
13
|
+
const toolDefs = addFeaturePrefix("firebase", index_js_4.coreTools);
|
|
14
|
+
if (!(activeFeatures === null || activeFeatures === void 0 ? void 0 : activeFeatures.length)) {
|
|
15
|
+
activeFeatures = Object.keys(tools);
|
|
16
|
+
}
|
|
17
|
+
for (const key of activeFeatures) {
|
|
18
|
+
toolDefs.push(...tools[key]);
|
|
19
|
+
}
|
|
20
|
+
return toolDefs;
|
|
21
|
+
}
|
|
22
|
+
exports.availableTools = availableTools;
|
|
23
|
+
const tools = {
|
|
24
|
+
firestore: addFeaturePrefix("firestore", index_js_3.firestoreTools),
|
|
25
|
+
auth: addFeaturePrefix("auth", index_js_1.authTools),
|
|
26
|
+
dataconnect: addFeaturePrefix("dataconnect", index_js_2.dataconnectTools),
|
|
27
|
+
storage: addFeaturePrefix("storage", index_js_5.storageTools),
|
|
28
|
+
messaging: addFeaturePrefix("messaging", index_js_6.messagingTools),
|
|
29
|
+
remoteconfig: addFeaturePrefix("remoteconfig", index_js_7.remoteConfigTools),
|
|
30
|
+
crashlytics: addFeaturePrefix("crashlytics", index_js_8.crashlyticsTools),
|
|
15
31
|
};
|
|
32
|
+
function addFeaturePrefix(feature, tools) {
|
|
33
|
+
return tools.map((tool) => (Object.assign(Object.assign({}, tool), { mcp: Object.assign(Object.assign({}, tool.mcp), { name: `${feature}_${tool.mcp.name}`, _meta: Object.assign(Object.assign({}, tool.mcp._meta), { feature }) }) })));
|
|
34
|
+
}
|
|
35
|
+
function markdownDocsOfTools() {
|
|
36
|
+
var _a, _b, _c;
|
|
37
|
+
const allTools = availableTools([]);
|
|
38
|
+
let doc = `
|
|
39
|
+
| Tool Name | Feature Group | Description |
|
|
40
|
+
| --------- | ------------- | ----------- |`;
|
|
41
|
+
for (const tool of allTools) {
|
|
42
|
+
let feature = ((_b = (_a = tool.mcp) === null || _a === void 0 ? void 0 : _a._meta) === null || _b === void 0 ? void 0 : _b.feature) || "";
|
|
43
|
+
if (feature === "firebase") {
|
|
44
|
+
feature = "core";
|
|
45
|
+
}
|
|
46
|
+
doc += `
|
|
47
|
+
| ${tool.mcp.name} | ${feature} | ${((_c = tool.mcp) === null || _c === void 0 ? void 0 : _c.description) || ""} |`;
|
|
48
|
+
}
|
|
49
|
+
return doc;
|
|
50
|
+
}
|
|
51
|
+
exports.markdownDocsOfTools = markdownDocsOfTools;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.send_message = 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 sendMessage_js_1 = require("../../../messaging/sendMessage.js");
|
|
8
|
+
exports.send_message = (0, tool_js_1.tool)({
|
|
9
|
+
name: "send_message",
|
|
10
|
+
description: "Sends a message to a Firebase Cloud Messaging registration token or topic. ONLY ONE of `registration_token` or `topic` may be supplied in a specific call.",
|
|
11
|
+
inputSchema: zod_1.z.object({
|
|
12
|
+
registration_token: zod_1.z
|
|
13
|
+
.string()
|
|
14
|
+
.optional()
|
|
15
|
+
.describe("A specific device registration token for delivery. Supply either this or topic."),
|
|
16
|
+
topic: zod_1.z
|
|
17
|
+
.string()
|
|
18
|
+
.optional()
|
|
19
|
+
.describe("A topic name for delivery. Supply either this or registration_token."),
|
|
20
|
+
title: zod_1.z.string().optional().describe("The title of the push notification message."),
|
|
21
|
+
body: zod_1.z.string().optional().describe("The body of the push notification message."),
|
|
22
|
+
image: zod_1.z
|
|
23
|
+
.string()
|
|
24
|
+
.optional()
|
|
25
|
+
.describe("The URL of an image that will be displayed with the notification. JPEG, PNG, BMP have full support across platforms. Animated GIF and video only work on iOS. WebP and HEIF have varying levels of support across platforms and platform versions."),
|
|
26
|
+
}),
|
|
27
|
+
annotations: {
|
|
28
|
+
title: "Send FCM Message",
|
|
29
|
+
},
|
|
30
|
+
_meta: {
|
|
31
|
+
requiresAuth: true,
|
|
32
|
+
requiresProject: true,
|
|
33
|
+
},
|
|
34
|
+
}, async ({ registration_token, topic, title, body }, { projectId }) => {
|
|
35
|
+
if (!registration_token && !topic) {
|
|
36
|
+
return (0, util_js_1.mcpError)("Must supply either a `registration_token` or `topic` parameter to `send_message`.");
|
|
37
|
+
}
|
|
38
|
+
if (registration_token && topic) {
|
|
39
|
+
return (0, util_js_1.mcpError)("Cannot supply both `registration_token` and `topic` in a single `send_message` request.");
|
|
40
|
+
}
|
|
41
|
+
return (0, util_js_1.toContent)(await (0, sendMessage_js_1.sendFcmMessage)(projectId, { token: registration_token, topic, title, body }));
|
|
42
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.get_template = 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 get_js_1 = require("../../../remoteconfig/get.js");
|
|
8
|
+
exports.get_template = (0, tool_js_1.tool)({
|
|
9
|
+
name: "get_template",
|
|
10
|
+
description: "Retrieves a remote config template for the project",
|
|
11
|
+
inputSchema: zod_1.z.object({
|
|
12
|
+
version_number: zod_1.z
|
|
13
|
+
.string()
|
|
14
|
+
.optional()
|
|
15
|
+
.describe("The version number of the template to retrieve. If not provided, retrieves the active template."),
|
|
16
|
+
}),
|
|
17
|
+
annotations: {
|
|
18
|
+
title: "Get Remote Config template",
|
|
19
|
+
readOnlyHint: true,
|
|
20
|
+
},
|
|
21
|
+
_meta: {
|
|
22
|
+
requiresAuth: true,
|
|
23
|
+
requiresProject: true,
|
|
24
|
+
},
|
|
25
|
+
}, async ({ version_number }, { projectId }) => {
|
|
26
|
+
return (0, util_js_1.toContent)(await (0, get_js_1.getTemplate)(projectId, version_number));
|
|
27
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.remoteConfigTools = void 0;
|
|
4
|
+
const get_template_js_1 = require("./get_template.js");
|
|
5
|
+
const rollback_template_js_1 = require("./rollback_template.js");
|
|
6
|
+
const publish_template_js_1 = require("./publish_template.js");
|
|
7
|
+
exports.remoteConfigTools = [get_template_js_1.get_template, publish_template_js_1.publish_template, rollback_template_js_1.rollback_template];
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.publish_template = 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 publish_js_1 = require("../../../remoteconfig/publish.js");
|
|
8
|
+
exports.publish_template = (0, tool_js_1.tool)({
|
|
9
|
+
name: "publish_template",
|
|
10
|
+
description: "Publishes a new remote config template for the project",
|
|
11
|
+
inputSchema: zod_1.z.object({
|
|
12
|
+
template: zod_1.z.object({}).describe("The Remote Config template object to publish."),
|
|
13
|
+
force: zod_1.z
|
|
14
|
+
.boolean()
|
|
15
|
+
.optional()
|
|
16
|
+
.describe("If true, the publish will bypass ETag validation and overwrite the current template. Defaults to false if not provided."),
|
|
17
|
+
}),
|
|
18
|
+
annotations: {
|
|
19
|
+
title: "Publish Remote Config template",
|
|
20
|
+
readOnlyHint: false,
|
|
21
|
+
},
|
|
22
|
+
_meta: {
|
|
23
|
+
requiresAuth: true,
|
|
24
|
+
requiresProject: true,
|
|
25
|
+
},
|
|
26
|
+
}, async ({ template, force }, { projectId }) => {
|
|
27
|
+
if (template === undefined) {
|
|
28
|
+
return (0, util_js_1.mcpError)(`No template specified in the publish requests`);
|
|
29
|
+
}
|
|
30
|
+
if (force === undefined) {
|
|
31
|
+
return (0, util_js_1.toContent)(await (0, publish_js_1.publishTemplate)(projectId, template));
|
|
32
|
+
}
|
|
33
|
+
return (0, util_js_1.toContent)(await (0, publish_js_1.publishTemplate)(projectId, template, { force }));
|
|
34
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.rollback_template = 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 rollback_js_1 = require("../../../remoteconfig/rollback.js");
|
|
8
|
+
exports.rollback_template = (0, tool_js_1.tool)({
|
|
9
|
+
name: "rollback_template",
|
|
10
|
+
description: "Rollback to a specific version of Remote Config template for a project",
|
|
11
|
+
inputSchema: zod_1.z.object({
|
|
12
|
+
version_number: zod_1.z
|
|
13
|
+
.number()
|
|
14
|
+
.describe("The version number to roll back to. This field is required."),
|
|
15
|
+
}),
|
|
16
|
+
annotations: {
|
|
17
|
+
title: "Rollback remote config template",
|
|
18
|
+
readOnlyHint: false,
|
|
19
|
+
},
|
|
20
|
+
_meta: {
|
|
21
|
+
requiresAuth: true,
|
|
22
|
+
requiresProject: true,
|
|
23
|
+
},
|
|
24
|
+
}, async ({ version_number }, { projectId }) => {
|
|
25
|
+
if (version_number === undefined) {
|
|
26
|
+
return (0, util_js_1.mcpError)(`No version number specified in the rollback requests`);
|
|
27
|
+
}
|
|
28
|
+
return (0, util_js_1.toContent)(await (0, rollback_js_1.rollbackTemplate)(projectId, version_number));
|
|
29
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getRulesTool = 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_js_1 = require("../../../gcp/rules.js");
|
|
8
|
+
function getRulesTool(productName, releaseName) {
|
|
9
|
+
return (0, tool_js_1.tool)({
|
|
10
|
+
name: "get_rules",
|
|
11
|
+
description: `Retrieves the active ${productName} security rules for the current project.`,
|
|
12
|
+
inputSchema: zod_1.z.object({}),
|
|
13
|
+
annotations: {
|
|
14
|
+
title: `Get Current ${productName} Rules`,
|
|
15
|
+
readOnlyHint: true,
|
|
16
|
+
},
|
|
17
|
+
_meta: {
|
|
18
|
+
requiresProject: true,
|
|
19
|
+
requiresAuth: true,
|
|
20
|
+
},
|
|
21
|
+
}, async (_, { projectId }) => {
|
|
22
|
+
const rulesetName = await (0, rules_js_1.getLatestRulesetName)(projectId, releaseName);
|
|
23
|
+
if (!rulesetName)
|
|
24
|
+
return (0, util_js_1.mcpError)(`No active Firestore rules were found in project '${projectId}'`);
|
|
25
|
+
const rules = await (0, rules_js_1.getRulesetContent)(rulesetName);
|
|
26
|
+
return (0, util_js_1.toContent)(rules[0].content);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
exports.getRulesTool = getRulesTool;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateRulesTool = 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_js_1 = require("../../../gcp/rules.js");
|
|
8
|
+
const path_1 = require("path");
|
|
9
|
+
function formatRulesetIssues(issues, rulesSource) {
|
|
10
|
+
const sourceLines = rulesSource.split("\n");
|
|
11
|
+
const formattedOutput = [];
|
|
12
|
+
for (const issue of issues) {
|
|
13
|
+
const { sourcePosition, description, severity } = issue;
|
|
14
|
+
let issueString = `${severity}: ${description} [Ln ${sourcePosition.line}, Col ${sourcePosition.column}]`;
|
|
15
|
+
if (sourcePosition.line) {
|
|
16
|
+
const lineIndex = sourcePosition.line - 1;
|
|
17
|
+
if (lineIndex >= 0 && lineIndex < sourceLines.length) {
|
|
18
|
+
const errorLine = sourceLines[lineIndex];
|
|
19
|
+
issueString += `\n\`\`\`\n${errorLine}`;
|
|
20
|
+
if (sourcePosition.column &&
|
|
21
|
+
sourcePosition.currentOffset &&
|
|
22
|
+
sourcePosition.endOffset &&
|
|
23
|
+
sourcePosition.column > 0 &&
|
|
24
|
+
sourcePosition.endOffset > sourcePosition.currentOffset) {
|
|
25
|
+
const startColumnOnLine = sourcePosition.column - 1;
|
|
26
|
+
const errorTokenLength = sourcePosition.endOffset - sourcePosition.currentOffset;
|
|
27
|
+
if (startColumnOnLine >= 0 &&
|
|
28
|
+
errorTokenLength > 0 &&
|
|
29
|
+
startColumnOnLine <= errorLine.length) {
|
|
30
|
+
const padding = " ".repeat(startColumnOnLine);
|
|
31
|
+
const carets = "^".repeat(errorTokenLength);
|
|
32
|
+
issueString += `\n${padding}${carets}\n\`\`\``;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
formattedOutput.push(issueString);
|
|
38
|
+
}
|
|
39
|
+
return formattedOutput.join("\n\n");
|
|
40
|
+
}
|
|
41
|
+
function validateRulesTool(productName) {
|
|
42
|
+
return (0, tool_js_1.tool)({
|
|
43
|
+
name: "validate_rules",
|
|
44
|
+
description: `Checks the provided ${productName} Rules source for syntax and validation errors. Provide EITHER the source code to validate OR a path to a source file.`,
|
|
45
|
+
inputSchema: zod_1.z.object({
|
|
46
|
+
source: zod_1.z
|
|
47
|
+
.string()
|
|
48
|
+
.optional()
|
|
49
|
+
.describe("the rules source code to check. provide either this OR a path"),
|
|
50
|
+
source_file: zod_1.z
|
|
51
|
+
.string()
|
|
52
|
+
.optional()
|
|
53
|
+
.describe("a file path, relative to the project root, to a file containing the rules source you want to validate. provide this OR source, not both"),
|
|
54
|
+
}),
|
|
55
|
+
annotations: {
|
|
56
|
+
title: `Validate ${productName} Rules`,
|
|
57
|
+
readOnlyHint: true,
|
|
58
|
+
},
|
|
59
|
+
_meta: {
|
|
60
|
+
requiresProject: true,
|
|
61
|
+
requiresAuth: true,
|
|
62
|
+
},
|
|
63
|
+
}, async ({ source, source_file }, { projectId, config, host }) => {
|
|
64
|
+
var _a, _b;
|
|
65
|
+
if (source && source_file) {
|
|
66
|
+
return (0, util_js_1.mcpError)("Must supply `source` or `source_file`, not both.");
|
|
67
|
+
}
|
|
68
|
+
let rulesSourceContent;
|
|
69
|
+
if (source_file) {
|
|
70
|
+
try {
|
|
71
|
+
const filePath = (0, path_1.resolve)(source_file, host.cachedProjectRoot);
|
|
72
|
+
if (filePath.includes("../"))
|
|
73
|
+
return (0, util_js_1.mcpError)("Cannot read files outside of the project directory.");
|
|
74
|
+
rulesSourceContent = config.readProjectFile(source_file);
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
return (0, util_js_1.mcpError)(`Failed to read source_file '${source_file}': ${e.message}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else if (source) {
|
|
81
|
+
rulesSourceContent = source;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
rulesSourceContent = "";
|
|
85
|
+
}
|
|
86
|
+
const result = await (0, rules_js_1.testRuleset)(projectId, [
|
|
87
|
+
{ name: "test.rules", content: rulesSourceContent },
|
|
88
|
+
]);
|
|
89
|
+
if ((_b = (_a = result.body) === null || _a === void 0 ? void 0 : _a.issues) === null || _b === void 0 ? void 0 : _b.length) {
|
|
90
|
+
const issues = result.body.issues;
|
|
91
|
+
let out = `Found ${issues.length} issues in rules source:\n\n`;
|
|
92
|
+
out += formatRulesetIssues(issues, rulesSourceContent);
|
|
93
|
+
return (0, util_js_1.toContent)(out);
|
|
94
|
+
}
|
|
95
|
+
return (0, util_js_1.toContent)("OK: No errors detected.");
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
exports.validateRulesTool = validateRulesTool;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.get_object_download_url = 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 storage_js_1 = require("../../../gcp/storage.js");
|
|
8
|
+
exports.get_object_download_url = (0, tool_js_1.tool)({
|
|
9
|
+
name: "get_object_download_url",
|
|
10
|
+
description: "Retrieves the download URL for an object in Firebase Storage.",
|
|
11
|
+
inputSchema: zod_1.z.object({
|
|
12
|
+
bucket: zod_1.z
|
|
13
|
+
.string()
|
|
14
|
+
.nullish()
|
|
15
|
+
.describe("The bucket name in Firebase Storage. If not provided, defaults to the project's default bucket (e.g., `{projectid}.firebasestorage.app`)."),
|
|
16
|
+
object_path: zod_1.z
|
|
17
|
+
.string()
|
|
18
|
+
.describe("The path to the object in Firebase storage without the bucket name attached"),
|
|
19
|
+
}),
|
|
20
|
+
annotations: {
|
|
21
|
+
title: "Get Storage Object Download URL",
|
|
22
|
+
readOnlyHint: true,
|
|
23
|
+
},
|
|
24
|
+
_meta: {
|
|
25
|
+
requiresProject: true,
|
|
26
|
+
requiresAuth: true,
|
|
27
|
+
},
|
|
28
|
+
}, async ({ bucket, object_path }, { projectId }) => {
|
|
29
|
+
if (!bucket) {
|
|
30
|
+
bucket = `${projectId}.firebasestorage.app`;
|
|
31
|
+
}
|
|
32
|
+
const downloadUrl = await (0, storage_js_1.getDownloadUrl)(bucket, object_path);
|
|
33
|
+
return (0, util_js_1.toContent)(downloadUrl);
|
|
34
|
+
});
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.get_rules = 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
|
-
const
|
|
8
|
-
exports.
|
|
9
|
-
name: "
|
|
7
|
+
const rules_js_1 = require("../../../gcp/rules.js");
|
|
8
|
+
exports.get_rules = (0, tool_js_1.tool)({
|
|
9
|
+
name: "get_rules",
|
|
10
10
|
description: "Retrieves the Firebase Cloud Storage Rules for the default bucket.",
|
|
11
11
|
inputSchema: zod_1.z.object({}),
|
|
12
12
|
annotations: {
|
|
@@ -18,9 +18,9 @@ exports.get_storage_rules = (0, tool_js_1.tool)({
|
|
|
18
18
|
requiresAuth: true,
|
|
19
19
|
},
|
|
20
20
|
}, async (_, { projectId }) => {
|
|
21
|
-
const rulesetName = await (0,
|
|
21
|
+
const rulesetName = await (0, rules_js_1.getLatestRulesetName)(projectId, "firebase.storage");
|
|
22
22
|
if (!rulesetName)
|
|
23
23
|
return (0, util_js_1.mcpError)(`No active Firebase Storage rules were found in project '${projectId}'`);
|
|
24
|
-
const rules = await (0,
|
|
24
|
+
const rules = await (0, rules_js_1.getRulesetContent)(rulesetName);
|
|
25
25
|
return (0, util_js_1.toContent)(rules[0].content);
|
|
26
26
|
});
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.storageTools = void 0;
|
|
4
|
-
const
|
|
5
|
-
|
|
4
|
+
const get_rules_1 = require("../rules/get_rules");
|
|
5
|
+
const validate_rules_1 = require("../rules/validate_rules");
|
|
6
|
+
const get_download_url_1 = require("./get_download_url");
|
|
7
|
+
exports.storageTools = [
|
|
8
|
+
(0, get_rules_1.getRulesTool)("Storage", "firebase.storage"),
|
|
9
|
+
(0, validate_rules_1.validateRulesTool)("Storage"),
|
|
10
|
+
get_download_url_1.get_object_download_url,
|
|
11
|
+
];
|
package/lib/mcp/types.js
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SERVER_FEATURES = void 0;
|
|
4
|
-
exports.SERVER_FEATURES = [
|
|
4
|
+
exports.SERVER_FEATURES = [
|
|
5
|
+
"firestore",
|
|
6
|
+
"storage",
|
|
7
|
+
"dataconnect",
|
|
8
|
+
"auth",
|
|
9
|
+
"messaging",
|
|
10
|
+
"remoteconfig",
|
|
11
|
+
"crashlytics",
|
|
12
|
+
];
|
package/lib/mcp/util.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.commandExistsSync = exports.mcpError = exports.toContent = void 0;
|
|
3
|
+
exports.checkFeatureActive = exports.commandExistsSync = exports.mcpError = exports.toContent = void 0;
|
|
4
4
|
const child_process_1 = require("child_process");
|
|
5
5
|
const js_yaml_1 = require("js-yaml");
|
|
6
6
|
const os_1 = require("os");
|
|
7
|
+
const api_1 = require("../api");
|
|
8
|
+
const ensureApiEnabled_1 = require("../ensureApiEnabled");
|
|
7
9
|
function toContent(data, options) {
|
|
8
10
|
if (typeof data === "string")
|
|
9
11
|
return { content: [{ type: "text", text: data }] };
|
|
@@ -17,8 +19,10 @@ function toContent(data, options) {
|
|
|
17
19
|
text = (0, js_yaml_1.dump)(data);
|
|
18
20
|
break;
|
|
19
21
|
}
|
|
22
|
+
const prefix = (options === null || options === void 0 ? void 0 : options.contentPrefix) || "";
|
|
23
|
+
const suffix = (options === null || options === void 0 ? void 0 : options.contentSuffix) || "";
|
|
20
24
|
return {
|
|
21
|
-
content: [{ type: "text", text }],
|
|
25
|
+
content: [{ type: "text", text: `${prefix}${text}${suffix}` }],
|
|
22
26
|
};
|
|
23
27
|
}
|
|
24
28
|
exports.toContent = toContent;
|
|
@@ -50,3 +54,26 @@ function commandExistsSync(command) {
|
|
|
50
54
|
}
|
|
51
55
|
}
|
|
52
56
|
exports.commandExistsSync = commandExistsSync;
|
|
57
|
+
const SERVER_FEATURE_APIS = {
|
|
58
|
+
firestore: (0, api_1.firestoreOrigin)(),
|
|
59
|
+
storage: (0, api_1.storageOrigin)(),
|
|
60
|
+
dataconnect: (0, api_1.dataconnectOrigin)(),
|
|
61
|
+
auth: (0, api_1.authManagementOrigin)(),
|
|
62
|
+
messaging: (0, api_1.messagingApiOrigin)(),
|
|
63
|
+
remoteconfig: (0, api_1.remoteConfigApiOrigin)(),
|
|
64
|
+
crashlytics: (0, api_1.crashlyticsApiOrigin)(),
|
|
65
|
+
};
|
|
66
|
+
async function checkFeatureActive(feature, projectId, options) {
|
|
67
|
+
var _a;
|
|
68
|
+
if (feature in (((_a = options === null || options === void 0 ? void 0 : options.config) === null || _a === void 0 ? void 0 : _a.data) || {}))
|
|
69
|
+
return true;
|
|
70
|
+
try {
|
|
71
|
+
if (projectId)
|
|
72
|
+
return await (0, ensureApiEnabled_1.check)(projectId, SERVER_FEATURE_APIS[feature], "", true);
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
exports.checkFeatureActive = checkFeatureActive;
|