@cloudbase/cloudbase-mcp 2.15.2-beta.1 → 2.15.2-beta.3

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/dist/index.js CHANGED
@@ -62,6 +62,8 @@ function simplifyEnvList(envList) {
62
62
  simplified.EnvType = env.EnvType;
63
63
  if (env.Region !== undefined)
64
64
  simplified.Region = env.Region;
65
+ if (env.PackageId !== undefined)
66
+ simplified.PackageId = env.PackageId;
65
67
  if (env.PackageName !== undefined)
66
68
  simplified.PackageName = env.PackageName;
67
69
  if (env.IsDefault !== undefined)
@@ -76,6 +78,7 @@ const DEFAULT_ENV_FIELDS = [
76
78
  "Status",
77
79
  "EnvType",
78
80
  "Region",
81
+ "PackageId",
79
82
  "PackageName",
80
83
  "IsDefault",
81
84
  ];
@@ -91,10 +94,14 @@ function selectEnvFields(env, fields) {
91
94
  }
92
95
  function filterEnvList(envList, filters) {
93
96
  const alias = filters.alias?.trim().toLowerCase();
97
+ const aliasExact = filters.aliasExact === true;
94
98
  const envId = filters.envId?.trim().toLowerCase();
95
99
  return envList.filter((env) => {
100
+ const normalizedAlias = String(env.Alias ?? "").toLowerCase();
96
101
  const matchesAlias = alias
97
- ? String(env.Alias ?? "").toLowerCase().includes(alias)
102
+ ? aliasExact
103
+ ? normalizedAlias === alias
104
+ : normalizedAlias.includes(alias)
98
105
  : true;
99
106
  const matchesEnvId = envId
100
107
  ? String(env.EnvId ?? "").toLowerCase() === envId
@@ -198,9 +205,17 @@ function buildEnvQueryListResult(params) {
198
205
  : envList;
199
206
  const filteredList = filterEnvList(baseList, {
200
207
  alias: params.filters.alias,
208
+ aliasExact: params.filters.aliasExact,
201
209
  envId: params.filters.envId,
202
210
  });
203
211
  const paginated = paginateEnvList(filteredList, params.filters.offset, params.filters.limit);
212
+ const exactEnvIdSummaryHint = params.filters.envId
213
+ ? {
214
+ tool: "envQuery",
215
+ action: "info",
216
+ reason: "action=list with envId only returns a concise summary. Use action=info to fetch detailed environment information such as full resource metadata and additional environment details.",
217
+ }
218
+ : undefined;
204
219
  return {
205
220
  EnvList: paginated.items.map((env) => selectEnvFields(env, params.filters.fields)),
206
221
  TotalCount: paginated.total,
@@ -209,12 +224,58 @@ function buildEnvQueryListResult(params) {
209
224
  HasMore: paginated.offset + paginated.items.length < paginated.total,
210
225
  AppliedFilters: {
211
226
  alias: params.filters.alias ?? null,
227
+ aliasExact: params.filters.aliasExact ?? null,
212
228
  envId: params.filters.envId ?? null,
213
229
  fields: params.filters.fields ?? [...DEFAULT_ENV_FIELDS],
214
230
  currentEnvOnly: shouldRestrictToCurrentEnv,
215
231
  },
232
+ ...(exactEnvIdSummaryHint
233
+ ? {
234
+ RecommendedNextAction: exactEnvIdSummaryHint,
235
+ }
236
+ : {}),
216
237
  };
217
238
  }
239
+ async function enrichEnvInfoWithBilling(params) {
240
+ const targetEnvId = params.envId ||
241
+ params.result?.EnvInfo?.EnvId;
242
+ if (!targetEnvId || !params.result?.EnvInfo) {
243
+ return params.result;
244
+ }
245
+ try {
246
+ const billingResult = await params.manager.commonService("tcb", "2018-06-08").call({
247
+ Action: "DescribeBillingInfo",
248
+ Param: {
249
+ EnvId: targetEnvId,
250
+ },
251
+ });
252
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(params.logger, billingResult);
253
+ const billingList = billingResult?.EnvBillingInfoList ||
254
+ billingResult?.Response?.EnvBillingInfoList ||
255
+ billingResult?.Data?.EnvBillingInfoList ||
256
+ [];
257
+ const matchedBillingInfo = Array.isArray(billingList)
258
+ ? billingList.find((item) => item?.EnvId === targetEnvId) ?? billingList[0]
259
+ : undefined;
260
+ if (!matchedBillingInfo) {
261
+ return params.result;
262
+ }
263
+ return {
264
+ ...params.result,
265
+ EnvInfo: {
266
+ ...params.result.EnvInfo,
267
+ BillingInfo: matchedBillingInfo,
268
+ },
269
+ };
270
+ }
271
+ catch (billingError) {
272
+ (0, logger_js_1.debug)("DescribeBillingInfo enrichment failed, continuing without billing info", {
273
+ error: billingError,
274
+ envId: targetEnvId,
275
+ });
276
+ return params.result;
277
+ }
278
+ }
218
279
  async function getGuidePrompt(server) {
219
280
  if (getCurrentIde(server) === "CodeBuddy" ||
220
281
  process.env.CLOUDBASE_GUIDE_PROMPT === "false") {
@@ -232,6 +293,16 @@ function registerEnvTools(server) {
232
293
  // 获取 cloudBaseOptions,如果没有则为 undefined
233
294
  const cloudBaseOptions = server.cloudBaseOptions;
234
295
  const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions, mcpServer: server });
296
+ const getManagerForEnvQuery = (targetEnvId, requireEnvId = true) => (0, cloudbase_manager_js_1.getCloudBaseManager)({
297
+ cloudBaseOptions: targetEnvId && targetEnvId !== cloudBaseOptions?.envId
298
+ ? {
299
+ ...cloudBaseOptions,
300
+ envId: targetEnvId,
301
+ }
302
+ : cloudBaseOptions,
303
+ requireEnvId,
304
+ mcpServer: server,
305
+ });
235
306
  const hasEnvId = typeof cloudBaseOptions?.envId === 'string' && cloudBaseOptions?.envId.length > 0;
236
307
  const supportedAuthActions = getSupportedAuthActions(server);
237
308
  const authActionEnum = [...supportedAuthActions];
@@ -597,26 +668,27 @@ function registerEnvTools(server) {
597
668
  // envQuery - 环境查询(合并 listEnvs + getEnvInfo + getEnvAuthDomains + getWebsiteConfig)
598
669
  server.registerTool?.("envQuery", {
599
670
  title: "环境查询",
600
- description: "查询云开发环境相关信息,支持查询环境列表、当前环境信息、安全域名和静态网站托管配置。(原工具名:listEnvs/getEnvInfo/getEnvAuthDomains/getWebsiteConfig,为兼容旧AI规则可继续使用这些名称)当 action=list 时,标准返回字段为 EnvId、Alias、Status、EnvType、Region、PackageName、IsDefault,并支持通过 fields 白名单裁剪这些字段。当前 MCP 输出不支持套餐/计划到期时间(expiry / expire time)查询。",
671
+ description: "查询云开发环境相关信息,支持查询环境列表、当前环境信息、安全域名和静态网站托管配置。(原工具名:listEnvs/getEnvInfo/getEnvAuthDomains/getWebsiteConfig,为兼容旧AI规则可继续使用这些名称)当 action=list 时,标准返回字段为 EnvId、Alias、Status、EnvType、Region、PackageId、PackageName、IsDefault,并支持通过 fields 白名单裁剪这些字段;aliasExact=true 时会按别名精确筛选,避免把前缀相近的环境误当作候选;即使传入 envId,action=list 也只返回摘要,不会返回完整资源明细或 expiry。如需查询某个已知环境的详细信息,请使用 action=info。action=info 会在可用时补充 BillingInfo(如 ExpireTime、PayMode、IsAutoRenew 等计费字段)。",
601
672
  inputSchema: {
602
673
  action: zod_1.z
603
674
  .enum(["list", "info", "domains", "hosting"])
604
- .describe("查询类型:list=环境列表(标准字段:EnvId、Alias、Status、EnvType、Region、PackageName、IsDefault;不支持 expiry),info=当前环境信息,domains=安全域名列表,hosting=静态网站托管配置"),
675
+ .describe("查询类型:list=环境列表/摘要筛选(即使传 envId 也只返回 EnvId、Alias、Status、EnvType、Region、PackageId、PackageName、IsDefault,不支持 expiry),info=当前环境详细信息(详情中可查看更完整资源字段),domains=安全域名列表,hosting=静态网站托管配置"),
605
676
  alias: zod_1.z.string().optional().describe("按环境别名筛选。action=list 时可选"),
606
- envId: zod_1.z.string().optional().describe("按环境 ID 精确筛选。action=list 时可选"),
677
+ aliasExact: zod_1.z.boolean().optional().describe("按环境别名精确筛选。action=list 时可选;与 alias 配合使用"),
678
+ envId: zod_1.z.string().optional().describe("按环境 ID 精确筛选。action=list 时可选;注意 list + envId 仍只返回摘要,如需该环境详情请改用 action=info"),
607
679
  limit: zod_1.z.number().int().positive().optional().describe("返回数量上限。action=list 时可选"),
608
680
  offset: zod_1.z.number().int().min(0).optional().describe("分页偏移。action=list 时可选"),
609
681
  fields: zod_1.z
610
682
  .array(zod_1.z.enum(DEFAULT_ENV_FIELDS))
611
683
  .optional()
612
- .describe("返回字段白名单。仅支持 EnvId、Alias、Status、EnvType、Region、PackageName、IsDefault。action=list 时可选"),
684
+ .describe("返回字段白名单。仅支持 EnvId、Alias、Status、EnvType、Region、PackageId、PackageName、IsDefault。action=list 时可选"),
613
685
  },
614
686
  annotations: {
615
687
  readOnlyHint: true,
616
688
  openWorldHint: true,
617
689
  category: "env",
618
690
  },
619
- }, async ({ action, alias, envId, limit, offset, fields, }) => {
691
+ }, async ({ action, alias, aliasExact, envId, limit, offset, fields, }) => {
620
692
  try {
621
693
  let result;
622
694
  switch (action) {
@@ -689,6 +761,7 @@ function registerEnvTools(server) {
689
761
  hasEnvId,
690
762
  filters: {
691
763
  alias,
764
+ aliasExact,
692
765
  envId,
693
766
  limit,
694
767
  offset,
@@ -697,20 +770,44 @@ function registerEnvTools(server) {
697
770
  });
698
771
  break;
699
772
  case "info":
700
- const cloudbaseInfo = await getManager();
773
+ const cloudbaseInfo = await getManagerForEnvQuery(envId);
701
774
  result = await cloudbaseInfo.env.getEnvInfo();
702
775
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
776
+ result = await enrichEnvInfoWithBilling({
777
+ manager: cloudbaseInfo,
778
+ result,
779
+ envId,
780
+ logger: server.logger,
781
+ });
703
782
  break;
704
783
  case "domains":
705
784
  const cloudbaseDomains = await getManager();
706
785
  result = await cloudbaseDomains.env.getEnvAuthDomains();
707
786
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
708
787
  break;
709
- case "hosting":
788
+ case "hosting": {
710
789
  const cloudbaseHosting = await getManager();
711
- result = await cloudbaseHosting.hosting.getWebsiteConfig();
712
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
790
+ const websiteConfig = await cloudbaseHosting.hosting.getWebsiteConfig();
791
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, websiteConfig);
792
+ const hostingResult = {
793
+ ...websiteConfig,
794
+ CdnDomain: websiteConfig.CdnDomain ?? null,
795
+ Bucket: websiteConfig.Bucket ?? null,
796
+ };
797
+ try {
798
+ const envInfo = await cloudbaseHosting.env.getEnvInfo();
799
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, envInfo);
800
+ hostingResult.CdnDomain = envInfo.EnvInfo?.StaticStorages?.[0]?.StaticDomain ?? hostingResult.CdnDomain;
801
+ hostingResult.Bucket = envInfo.EnvInfo?.StaticStorages?.[0]?.Bucket ?? hostingResult.Bucket;
802
+ }
803
+ catch (hostingInfoError) {
804
+ (0, logger_js_1.debug)("Failed to enrich hosting envQuery result with env info", {
805
+ error: hostingInfoError,
806
+ });
807
+ }
808
+ result = hostingResult;
713
809
  break;
810
+ }
714
811
  default:
715
812
  throw new Error(`不支持的查询类型: ${action}`);
716
813
  }
@@ -968,7 +1065,7 @@ function registerCapiTools(server) {
968
1065
  const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions });
969
1066
  server.registerTool?.("callCloudApi", {
970
1067
  title: "调用云API",
971
- description: `通用的云 API 调用工具,主要用于 CloudBase / 腾讯云管控面与依赖资源相关 API 调用。调用前请先确认 service、Action 与 Param,避免猜测 Action 名称。如果你的目标是通过 HTTP 协议直接集成 auth/functions/cloudrun/storage/mysqldb 等 CloudBase 业务 API,请不要优先使用 callCloudApi,而应优先查看对应 OpenAPI / Swagger。现有 OpenAPI / Swagger 能力不是通用的管控面 Action 集合;管控面 API 请优先参考 CloudBase API 概览 ${CLOUDBASE_CONTROL_PLANE_DOC_URL} 与云开发依赖资源接口指引 ${CLOUDBASE_DEPENDENCY_API_DOC_URL}。对于 tcb,可参考类似 \`CreateEnv\`、\`ModifyEnv\`、\`DestroyEnv\` 这类真实 Action 组织请求。`,
1068
+ description: `通用的云 API 调用工具,主要用于 CloudBase / 腾讯云管控面与依赖资源相关 API 调用。调用前请先确认 service、Action 与 Param,避免猜测 Action 名称。如果你的目标是通过 HTTP 协议直接集成 auth/functions/cloudrun/storage/mysqldb 等 CloudBase 业务 API,请不要优先使用 callCloudApi,而应优先查看对应 OpenAPI / Swagger。现有 OpenAPI / Swagger 能力不是通用的管控面 Action 集合;管控面 API 请优先参考 CloudBase API 概览 ${CLOUDBASE_CONTROL_PLANE_DOC_URL} 与云开发依赖资源接口指引 ${CLOUDBASE_DEPENDENCY_API_DOC_URL}。对于 tcb,可参考类似 \`CreateEnv\`、\`ModifyEnv\`、\`DestroyEnv\` 这类真实 Action 组织请求;销毁环境时,常见做法是至少带上 \`EnvId\` 和 \`BypassCheck: true\`,如果环境已经处于隔离期再按文档补 \`IsForce: true\`。`,
972
1069
  inputSchema: {
973
1070
  service: zod_1.z
974
1071
  .enum(ALLOWED_SERVICES)
@@ -980,7 +1077,7 @@ function registerCapiTools(server) {
980
1077
  params: zod_1.z
981
1078
  .record(zod_1.z.any())
982
1079
  .optional()
983
- .describe("Action 对应的参数对象,键名需与官方 API 定义一致。某些 Action 需要携带 EnvId 等信息;如不确定参数结构,请先查官方文档。tcb 示例:`{ \"service\": \"tcb\", \"action\": \"DestroyEnv\", \"params\": { \"EnvId\": \"env-xxx\" } }` 或 `{ \"service\": \"tcb\", \"action\": \"ModifyEnv\", \"params\": { \"EnvId\": \"env-xxx\", \"Alias\": \"demo\" } }`。若你的场景是通过 HTTP 协议直接集成 auth/functions/cloudrun/storage/mysqldb 等 CloudBase 业务 API,请优先使用 OpenAPI / Swagger 或 searchKnowledgeBase(mode=\"openapi\"),而不是优先使用 callCloudApi。"),
1080
+ .describe("Action 对应的参数对象,键名需与官方 API 定义一致。某些 Action 需要携带 EnvId 等信息;如不确定参数结构,请先查官方文档。tcb 示例:`{ \"service\": \"tcb\", \"action\": \"DestroyEnv\", \"params\": { \"EnvId\": \"env-xxx\", \"BypassCheck\": true } }`,如果环境已经处于隔离期,可再补 `IsForce: true`;更新环境别名则可用 `{ \"service\": \"tcb\", \"action\": \"ModifyEnv\", \"params\": { \"EnvId\": \"env-xxx\", \"Alias\": \"demo\" } }`。若你的场景是通过 HTTP 协议直接集成 auth/functions/cloudrun/storage/mysqldb 等 CloudBase 业务 API,请优先使用 OpenAPI / Swagger 或 searchKnowledgeBase(mode=\"openapi\"),而不是优先使用 callCloudApi。"),
984
1081
  },
985
1082
  annotations: {
986
1083
  readOnlyHint: false,
@@ -1112,7 +1209,7 @@ ${envIdSection}
1112
1209
  ## 环境信息
1113
1210
  - 操作系统: ${os_1.default.type()} ${os_1.default.release()}
1114
1211
  - Node.js版本: ${process.version}
1115
- - MCP 版本:${process.env.npm_package_version || "2.15.2-beta.1" || 0}
1212
+ - MCP 版本:${process.env.npm_package_version || "2.15.2-beta.3" || 0}
1116
1213
  - 系统架构: ${os_1.default.arch()}
1117
1214
  - 时间: ${new Date().toISOString()}
1118
1215
  - 请求ID: ${requestId}
@@ -5738,9 +5835,9 @@ async function getCloudBaseManager(options = {}) {
5738
5835
  }
5739
5836
  try {
5740
5837
  // Get region from environment variable for auth URL
5741
- const region = cloudBaseOptions?.region ?? process.env.TCB_REGION;
5838
+ const fallbackRegion = cloudBaseOptions?.region ?? process.env.TCB_REGION;
5742
5839
  const loginState = authStrategy === 'ensure'
5743
- ? await (0, auth_js_1.getLoginState)({ region })
5840
+ ? await (0, auth_js_1.getLoginState)({ region: fallbackRegion })
5744
5841
  : await (0, auth_js_1.peekLoginState)();
5745
5842
  if (!loginState) {
5746
5843
  const authState = await (0, auth_js_1.getAuthProgressState)();
@@ -5788,13 +5885,29 @@ async function getCloudBaseManager(options = {}) {
5788
5885
  }
5789
5886
  // envId priority: envManager.cachedEnvId > envManager.getEnvId() > loginState.envId > undefined
5790
5887
  // Note: envManager.cachedEnvId has highest priority as it reflects user's latest environment switch
5791
- // Region priority: process.env.TCB_REGION > undefined (use SDK default)
5792
- // At this point, cloudBaseOptions is undefined (checked above), so only use env var if present
5793
- // Reuse region variable declared above (line 259) for CloudBase initialization
5888
+ // Region priority: envCandidates[].region (actual env region) > process.env.TCB_REGION > undefined
5889
+ // Resolving the environment's own region avoids INVALID_REGION errors when TCB_REGION
5890
+ // is set to a different region than the target environment.
5891
+ const resolvedEnvId = finalEnvId || loginEnvId;
5892
+ let region = fallbackRegion;
5893
+ if (resolvedEnvId && !cloudBaseOptions?.region) {
5894
+ try {
5895
+ const envCandidates = await listAvailableEnvCandidates({ loginState });
5896
+ const matchedEnv = envCandidates.find(c => c.envId === resolvedEnvId);
5897
+ if (matchedEnv?.region) {
5898
+ region = matchedEnv.region;
5899
+ (0, logger_js_1.debug)('使用环境实际 region:', { envId: resolvedEnvId, region });
5900
+ }
5901
+ }
5902
+ catch {
5903
+ // If env listing fails, fall back to TCB_REGION / undefined
5904
+ (0, logger_js_1.debug)('无法获取环境列表,使用 fallback region');
5905
+ }
5906
+ }
5794
5907
  const manager = new manager_node_1.default({
5795
5908
  secretId,
5796
5909
  secretKey,
5797
- envId: finalEnvId || loginEnvId,
5910
+ envId: resolvedEnvId,
5798
5911
  token,
5799
5912
  proxy: process.env.http_proxy,
5800
5913
  region,
@@ -6647,6 +6760,31 @@ async function getDatabaseInstanceId(getManager) {
6647
6760
  function delay(ms) {
6648
6761
  return new Promise((resolve) => setTimeout(resolve, ms));
6649
6762
  }
6763
+ function parseNoSqlDocument(value) {
6764
+ if (typeof value !== "string") {
6765
+ return value;
6766
+ }
6767
+ try {
6768
+ const parsed = JSON.parse(value);
6769
+ return parsed !== null && typeof parsed === "object" ? parsed : value;
6770
+ }
6771
+ catch {
6772
+ return value;
6773
+ }
6774
+ }
6775
+ function normalizeNoSqlDocuments(data) {
6776
+ if (!Array.isArray(data)) {
6777
+ return [];
6778
+ }
6779
+ return data.map((item) => parseNoSqlDocument(item));
6780
+ }
6781
+ function withCollectionName(collectionName, payload) {
6782
+ return {
6783
+ ...payload,
6784
+ collection: collectionName,
6785
+ collectionName,
6786
+ };
6787
+ }
6650
6788
  async function waitForCollectionReady({ cloudbase, collectionName, logger, timeoutMs = COLLECTION_READY_TIMEOUT_MS, pollIntervalMs = COLLECTION_READY_POLL_INTERVAL_MS, }) {
6651
6789
  const startedAt = Date.now();
6652
6790
  const deadline = startedAt + timeoutMs;
@@ -6772,14 +6910,14 @@ checkIndex: 检查索引是否存在`),
6772
6910
  content: [
6773
6911
  {
6774
6912
  type: "text",
6775
- text: JSON.stringify({
6913
+ text: JSON.stringify(withCollectionName(collectionName, {
6776
6914
  success: true,
6777
6915
  exists: result.Exists,
6778
6916
  requestId: result.RequestId,
6779
6917
  message: result.Exists
6780
6918
  ? "云开发数据库集合已存在"
6781
6919
  : "云开发数据库集合不存在",
6782
- }, null, 2),
6920
+ }), null, 2),
6783
6921
  },
6784
6922
  ],
6785
6923
  };
@@ -6794,13 +6932,13 @@ checkIndex: 检查索引是否存在`),
6794
6932
  content: [
6795
6933
  {
6796
6934
  type: "text",
6797
- text: JSON.stringify({
6935
+ text: JSON.stringify(withCollectionName(collectionName, {
6798
6936
  success: true,
6799
6937
  requestId: result.RequestId,
6800
6938
  indexNum: result.IndexNum,
6801
6939
  indexes: result.Indexes,
6802
6940
  message: "获取云开发数据库集合信息成功",
6803
- }, null, 2),
6941
+ }), null, 2),
6804
6942
  },
6805
6943
  ],
6806
6944
  };
@@ -6815,13 +6953,13 @@ checkIndex: 检查索引是否存在`),
6815
6953
  content: [
6816
6954
  {
6817
6955
  type: "text",
6818
- text: JSON.stringify({
6956
+ text: JSON.stringify(withCollectionName(collectionName, {
6819
6957
  success: true,
6820
6958
  requestId: result.RequestId,
6821
6959
  indexNum: result.IndexNum,
6822
6960
  indexes: result.Indexes,
6823
6961
  message: "获取索引列表成功",
6824
- }, null, 2),
6962
+ }), null, 2),
6825
6963
  },
6826
6964
  ],
6827
6965
  };
@@ -6836,12 +6974,13 @@ checkIndex: 检查索引是否存在`),
6836
6974
  content: [
6837
6975
  {
6838
6976
  type: "text",
6839
- text: JSON.stringify({
6977
+ text: JSON.stringify(withCollectionName(collectionName, {
6840
6978
  success: true,
6979
+ indexName,
6841
6980
  exists: result.Exists,
6842
6981
  requestId: result.RequestId,
6843
6982
  message: result.Exists ? "索引已存在" : "索引不存在",
6844
- }, null, 2),
6983
+ }), null, 2),
6845
6984
  },
6846
6985
  ],
6847
6986
  };
@@ -6905,12 +7044,12 @@ deleteCollection: 删除集合`),
6905
7044
  content: [
6906
7045
  {
6907
7046
  type: "text",
6908
- text: JSON.stringify({
7047
+ text: JSON.stringify(withCollectionName(collectionName, {
6909
7048
  success: true,
6910
7049
  requestId: result.RequestId,
6911
7050
  action,
6912
7051
  message: "云开发数据库集合创建成功",
6913
- }, null, 2),
7052
+ }), null, 2),
6914
7053
  },
6915
7054
  ],
6916
7055
  };
@@ -6925,12 +7064,12 @@ deleteCollection: 删除集合`),
6925
7064
  content: [
6926
7065
  {
6927
7066
  type: "text",
6928
- text: JSON.stringify({
7067
+ text: JSON.stringify(withCollectionName(collectionName, {
6929
7068
  success: true,
6930
7069
  requestId: result.RequestId,
6931
7070
  action,
6932
7071
  message: "云开发数据库集合更新成功",
6933
- }, null, 2),
7072
+ }), null, 2),
6934
7073
  },
6935
7074
  ],
6936
7075
  };
@@ -6938,12 +7077,12 @@ deleteCollection: 删除集合`),
6938
7077
  if (action === "deleteCollection") {
6939
7078
  const result = await cloudbase.database.deleteCollection(collectionName);
6940
7079
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
6941
- const body = {
7080
+ const body = withCollectionName(collectionName, {
6942
7081
  success: true,
6943
7082
  requestId: result.RequestId,
6944
7083
  action,
6945
7084
  message: result.Exists === false ? "集合不存在" : "云开发数据库集合删除成功",
6946
- };
7085
+ });
6947
7086
  if (result.Exists === false) {
6948
7087
  body.exists = false;
6949
7088
  }
@@ -7009,17 +7148,21 @@ deleteCollection: 删除集合`),
7009
7148
  },
7010
7149
  });
7011
7150
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
7151
+ const documents = normalizeNoSqlDocuments(result.Data);
7012
7152
  return {
7013
7153
  content: [
7014
7154
  {
7015
7155
  type: "text",
7016
- text: JSON.stringify({
7156
+ text: JSON.stringify(withCollectionName(collectionName, {
7017
7157
  success: true,
7018
7158
  requestId: result.RequestId,
7019
- data: result.Data,
7159
+ data: documents,
7160
+ total: typeof result.Pager?.Total === "number"
7161
+ ? result.Pager.Total
7162
+ : documents.length,
7020
7163
  pager: result.Pager,
7021
7164
  message: "文档查询成功",
7022
- }, null, 2),
7165
+ }), null, 2),
7023
7166
  },
7024
7167
  ],
7025
7168
  };
@@ -7027,7 +7170,7 @@ deleteCollection: 删除集合`),
7027
7170
  // writeNoSqlDatabaseContent
7028
7171
  server.registerTool?.("writeNoSqlDatabaseContent", {
7029
7172
  title: "修改 NoSQL 数据库数据记录",
7030
- description: "修改 NoSQL 数据库数据记录",
7173
+ description: "修改 NoSQL 数据库数据记录。可按 MongoDB updateOne/updateMany 的心智模型理解:部分更新必须使用 $set/$inc/$push 等更新操作符;如果直接传 { field: value } 这类普通对象,底层会把它当作替换内容,存在覆盖整条文档的风险。",
7031
7174
  inputSchema: {
7032
7175
  action: zod_1.z
7033
7176
  .enum(["insert", "update", "delete"])
@@ -7044,7 +7187,7 @@ deleteCollection: 删除集合`),
7044
7187
  update: zod_1.z
7045
7188
  .union([zod_1.z.object({}).passthrough(), zod_1.z.string()])
7046
7189
  .optional()
7047
- .describe("更新内容(对象或字符串,推荐对象)(update 操作必填)"),
7190
+ .describe("更新内容(对象或字符串,推荐对象)(update 操作必填)。按 MongoDB 更新语义传入 MgoUpdate:部分更新请使用 $set/$inc/$unset/$push 等操作符,例如 { \"$set\": { \"status\": \"pending\" } };不要直接传 { \"status\": \"pending\" },否则可能替换整条文档。"),
7048
7191
  isMulti: zod_1.z
7049
7192
  .boolean()
7050
7193
  .optional()
@@ -7142,12 +7285,15 @@ async function insertDocuments({ collectionName, documents, getManager, logger,
7142
7285
  },
7143
7286
  });
7144
7287
  (0, cloudbase_manager_js_1.logCloudBaseResult)(logger, result);
7145
- return JSON.stringify({
7288
+ return JSON.stringify(withCollectionName(collectionName, {
7146
7289
  success: true,
7147
7290
  requestId: result.RequestId,
7148
7291
  insertedIds: result.InsertedIds,
7292
+ insertedCount: Array.isArray(result.InsertedIds)
7293
+ ? result.InsertedIds.length
7294
+ : undefined,
7149
7295
  message: "文档插入成功",
7150
- }, null, 2);
7296
+ }), null, 2);
7151
7297
  }
7152
7298
  async function updateDocuments({ collectionName, query, update, isMulti, upsert, getManager, logger, }) {
7153
7299
  const cloudbase = await getManager();
@@ -7165,14 +7311,14 @@ async function updateDocuments({ collectionName, query, update, isMulti, upsert,
7165
7311
  },
7166
7312
  });
7167
7313
  (0, cloudbase_manager_js_1.logCloudBaseResult)(logger, result);
7168
- return JSON.stringify({
7314
+ return JSON.stringify(withCollectionName(collectionName, {
7169
7315
  success: true,
7170
7316
  requestId: result.RequestId,
7171
7317
  modifiedCount: result.ModifiedNum,
7172
7318
  matchedCount: result.MatchedNum,
7173
7319
  upsertedId: result.UpsertedId,
7174
7320
  message: "文档更新成功",
7175
- }, null, 2);
7321
+ }), null, 2);
7176
7322
  }
7177
7323
  async function deleteDocuments({ collectionName, query, isMulti, getManager, logger, }) {
7178
7324
  const cloudbase = await getManager();
@@ -7188,12 +7334,12 @@ async function deleteDocuments({ collectionName, query, isMulti, getManager, log
7188
7334
  },
7189
7335
  });
7190
7336
  (0, cloudbase_manager_js_1.logCloudBaseResult)(logger, result);
7191
- return JSON.stringify({
7337
+ return JSON.stringify(withCollectionName(collectionName, {
7192
7338
  success: true,
7193
7339
  requestId: result.RequestId,
7194
7340
  deleted: result.Deleted,
7195
7341
  message: "文档删除成功",
7196
- }, null, 2);
7342
+ }), null, 2);
7197
7343
  }
7198
7344
 
7199
7345
 
@@ -9056,11 +9202,11 @@ function registerGatewayTools(server) {
9056
9202
  targetName: input.targetName,
9057
9203
  path: accessPath,
9058
9204
  raw: result,
9059
- }, `已为目标 ${input.targetName} 创建网关访问路径`, [
9205
+ }, `已为目标 ${input.targetName} 创建网关访问路径。注意:路由配置传播通常需要等待 30 秒到 3 分钟,请勿立即访问。`, [
9060
9206
  {
9061
9207
  tool: "queryGateway",
9062
9208
  action: "getAccess",
9063
- reason: "确认访问入口是否已生效",
9209
+ reason: "等待 30 秒到 3 分钟后再确认访问入口是否已生效",
9064
9210
  },
9065
9211
  ]);
9066
9212
  }
@@ -9316,7 +9462,7 @@ function registerStorageTools(server) {
9316
9462
  // Tool 2: manageStorage - 管理存储文件(写操作)
9317
9463
  server.registerTool("manageStorage", {
9318
9464
  title: "管理存储文件",
9319
- description: "管理云存储文件,支持上传文件/目录、下载文件/目录、删除文件/目录等操作。删除操作需要设置force=true进行确认,防止误删除重要文件。",
9465
+ description: "管理云存储文件,仅用于 COS/Storage 对象,不用于静态网站托管。支持上传文件/目录、下载文件/目录、删除文件/目录等操作。删除操作需要设置force=true进行确认,防止误删除重要文件。",
9320
9466
  inputSchema: manageStorageInputSchema,
9321
9467
  annotations: {
9322
9468
  readOnlyHint: false,
@@ -10385,7 +10531,7 @@ class TelemetryReporter {
10385
10531
  const nodeVersion = process.version; // Node.js版本
10386
10532
  const arch = os_1.default.arch(); // 系统架构
10387
10533
  // 从构建时注入的版本号获取MCP版本信息
10388
- const mcpVersion = process.env.npm_package_version || "2.15.2-beta.1" || 0;
10534
+ const mcpVersion = process.env.npm_package_version || "2.15.2-beta.3" || 0;
10389
10535
  return {
10390
10536
  userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
10391
10537
  deviceId: this.deviceId,
@@ -10699,11 +10845,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
10699
10845
  Object.defineProperty(exports, "__esModule", ({ value: true }));
10700
10846
  exports.MANAGE_FUNCTION_ACTIONS = exports.QUERY_FUNCTION_ACTIONS = exports.TRIGGER_CONFIG_EXAMPLES = exports.SUPPORTED_TRIGGER_TYPES = exports.DEFAULT_NODEJS_RUNTIME = exports.SUPPORTED_NODEJS_RUNTIMES = exports.RECOMMENDED_RUNTIMES = exports.DEFAULT_RUNTIME = exports.ALL_SUPPORTED_RUNTIMES = exports.SUPPORTED_RUNTIMES = void 0;
10701
10847
  exports.formatRuntimeList = formatRuntimeList;
10848
+ exports.shouldInstallDependencyForFunction = shouldInstallDependencyForFunction;
10849
+ exports.buildFunctionOperationErrorMessage = buildFunctionOperationErrorMessage;
10702
10850
  exports.registerFunctionTools = registerFunctionTools;
10703
10851
  const zod_1 = __webpack_require__(2971);
10704
10852
  const cloudbase_manager_js_1 = __webpack_require__(3431);
10705
10853
  const cloud_mode_js_1 = __webpack_require__(9684);
10706
10854
  const logger_js_1 = __webpack_require__(3039);
10855
+ const fs_1 = __webpack_require__(4421);
10707
10856
  const path_1 = __importDefault(__webpack_require__(2521));
10708
10857
  exports.SUPPORTED_RUNTIMES = {
10709
10858
  nodejs: [
@@ -10892,6 +11041,12 @@ function getExpectedFunctionPath(functionRootPath, functionName) {
10892
11041
  return undefined;
10893
11042
  return path_1.default.join(path_1.default.normalize(functionRootPath), functionName);
10894
11043
  }
11044
+ function shouldInstallDependencyForFunction(functionType, hasPackageJson) {
11045
+ if (functionType === "HTTP") {
11046
+ return hasPackageJson;
11047
+ }
11048
+ return true;
11049
+ }
10895
11050
  function buildFunctionOperationErrorMessage(operation, functionName, functionRootPath, error) {
10896
11051
  const baseMessage = error instanceof Error ? error.message : String(error);
10897
11052
  const suggestions = [];
@@ -10903,6 +11058,13 @@ function buildFunctionOperationErrorMessage(operation, functionName, functionRoo
10903
11058
  suggestions.push(`当前工具会从 \`functionRootPath + 函数名\` 查找代码目录,期望目录是 \`${expectedFunctionPath}\`。`);
10904
11059
  suggestions.push("如果你传入的已经是函数目录本身,请改为传它的父目录。");
10905
11060
  }
11061
+ if (/paths\[0\].*undefined/i.test(baseMessage)) {
11062
+ suggestions.push("HTTP 函数创建时需要提供 functionRootPath(指向 cloudfunctions 父目录)或 zipFile,否则 SDK 无法定位函数目录。");
11063
+ }
11064
+ if (/依赖安装失败|package\.json/i.test(baseMessage)) {
11065
+ suggestions.push("如果 HTTP 函数只使用原生 Node.js API 且没有第三方依赖,可以保留函数目录中的 index.js 和 scf_bootstrap,工具会跳过依赖安装。");
11066
+ suggestions.push("如果你确实依赖 npm 包,请在函数目录下补充 package.json 后重试。");
11067
+ }
10906
11068
  if (suggestions.length === 0) {
10907
11069
  suggestions.push("请检查函数名、目录结构和环境中的函数状态后重试。");
10908
11070
  }
@@ -11278,8 +11440,21 @@ function registerFunctionTools(server) {
11278
11440
  throw new Error(`不支持的运行时环境: "${String(func.runtime)}"\n\n支持的运行时:\n${formatRuntimeList()}`);
11279
11441
  }
11280
11442
  }
11281
- func.installDependency = true;
11282
11443
  const processedRootPath = processFunctionRootPath(input.functionRootPath, functionName);
11444
+ const functionType = typeof func.type === "string" ? func.type : undefined;
11445
+ const expectedFunctionPath = getExpectedFunctionPath(processedRootPath, functionName);
11446
+ if (functionType === "HTTP" && !processedRootPath && !input.zipFile) {
11447
+ throw new Error("createFunction 创建 HTTP 函数时,需要提供 functionRootPath(指向 cloudfunctions 父目录)或 zipFile。");
11448
+ }
11449
+ const hasPackageJson = expectedFunctionPath !== undefined
11450
+ ? (0, fs_1.existsSync)(path_1.default.join(expectedFunctionPath, "package.json"))
11451
+ : false;
11452
+ func.installDependency = input.zipFile
11453
+ ? true
11454
+ : shouldInstallDependencyForFunction(functionType, hasPackageJson);
11455
+ if (functionType === "HTTP" && processedRootPath && !hasPackageJson) {
11456
+ console.warn(`检测到 HTTP 函数 ${functionName} 目录下没有 package.json,已跳过依赖安装;如果你需要第三方依赖,请补充 package.json 后重试。`);
11457
+ }
11283
11458
  let result;
11284
11459
  try {
11285
11460
  result = await cloudbase.functions.createFunction({
@@ -11292,11 +11467,7 @@ function registerFunctionTools(server) {
11292
11467
  throw wrapFunctionOperationError("createFunction", functionName, processedRootPath, error);
11293
11468
  }
11294
11469
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
11295
- return buildEnvelope({
11296
- action: input.action,
11297
- functionName,
11298
- raw: result,
11299
- }, `已创建函数 ${functionName}`, [
11470
+ const nextActions = [
11300
11471
  {
11301
11472
  tool: "queryFunctions",
11302
11473
  action: "getFunctionDetail",
@@ -11307,7 +11478,37 @@ function registerFunctionTools(server) {
11307
11478
  action: "listFunctionTriggers",
11308
11479
  reason: "检查函数触发器",
11309
11480
  },
11310
- ]);
11481
+ ];
11482
+ if (func.type === "HTTP") {
11483
+ nextActions.push({
11484
+ tool: "manageGateway",
11485
+ action: "createAccess",
11486
+ reason: "如果需要通过 URL 访问 HTTP 函数,请按实际路径和鉴权需求显式创建访问入口,不要默认假设 /函数名 已存在",
11487
+ });
11488
+ nextActions.push({
11489
+ tool: "queryGateway",
11490
+ action: "getAccess",
11491
+ reason: "交付前确认 HTTP 访问路径是否已存在并已生效",
11492
+ });
11493
+ nextActions.push({
11494
+ tool: "readSecurityRule",
11495
+ action: "读取安全规则",
11496
+ reason: "评测、浏览器或其他外部调用方可能以匿名身份访问;若直接报 EXCEED_AUTHORITY,应先读取当前函数安全规则",
11497
+ });
11498
+ nextActions.push({
11499
+ tool: "writeSecurityRule",
11500
+ action: "写入安全规则",
11501
+ reason: "只有在确认需要匿名访问时,才按实际安全要求调整函数安全规则,例如处理 EXCEED_AUTHORITY",
11502
+ });
11503
+ }
11504
+ const message = func.type === "HTTP"
11505
+ ? `已创建 HTTP 函数 ${functionName}。如果后续需要通过 URL 访问,请显式调用 manageGateway(action="createAccess") 按实际路径和鉴权需求创建访问入口。评测或其他外部调用方可能会以匿名身份访问,而且失败后不一定会把 EXCEED_AUTHORITY 再反馈给 AI;交付前请主动确认访问路径和函数安全规则,若已出现 EXCEED_AUTHORITY,请先调用 readSecurityRule(resourceType="function") 查看当前规则,再按需要使用 writeSecurityRule 调整权限。`
11506
+ : `已创建函数 ${functionName}`;
11507
+ return buildEnvelope({
11508
+ action: input.action,
11509
+ functionName,
11510
+ raw: result,
11511
+ }, message, nextActions);
11311
11512
  }
11312
11513
  case "updateFunctionCode": {
11313
11514
  if (!input.functionName) {
@@ -11352,6 +11553,9 @@ function registerFunctionTools(server) {
11352
11553
  }
11353
11554
  const cloudbase = await getManager();
11354
11555
  const functionDetail = await cloudbase.functions.getFunctionDetail(input.functionName);
11556
+ if (!functionDetail) {
11557
+ throw new Error(`函数 ${input.functionName} 不存在或无法获取详情`);
11558
+ }
11355
11559
  const currentVpc = typeof functionDetail.VpcConfig === "object" &&
11356
11560
  functionDetail.VpcConfig !== null &&
11357
11561
  functionDetail.VpcConfig.SubnetId &&
@@ -12159,7 +12363,7 @@ function registerSetupTools(server) {
12159
12363
  title: "下载项目模板",
12160
12364
  description: `自动下载并部署CloudBase项目模板。⚠️ **MANDATORY FOR NEW PROJECTS** ⚠️
12161
12365
 
12162
- **CRITICAL**: This tool MUST be called FIRST when starting a new project.\n\n支持的模板:\n- react: React + CloudBase 全栈应用模板\n- vue: Vue + CloudBase 全栈应用模板\n- miniprogram: 微信小程序 + 云开发模板 \n- uniapp: UniApp + CloudBase 跨端应用模板\n- rules: 只包含AI编辑器配置文件(包含Cursor、WindSurf、CodeBuddy等所有主流编辑器配置),适合在已有项目中补充AI编辑器配置\n\n支持的IDE类型:\n- all: 下载所有IDE配置\n- cursor: Cursor AI编辑器\n- 其他IDE类型见下方列表\n\n注意:如果未传入 ide 参数且无法从环境变量检测到 IDE,将提示错误并要求传入 ide 参数\n- windsurf: WindSurf AI编辑器\n- codebuddy: CodeBuddy AI编辑器\n- claude-code: Claude Code AI编辑器\n- cline: Cline AI编辑器\n- gemini-cli: Gemini CLI\n- opencode: OpenCode AI编辑器\n- qwen-code: 通义灵码\n- baidu-comate: 百度Comate\n- openai-codex-cli: OpenAI Codex CLI\n- augment-code: Augment Code\n- github-copilot: GitHub Copilot\n- roocode: RooCode AI编辑器\n- tongyi-lingma: 通义灵码\n- trae: Trae AI编辑器\n- qoder: Qoder AI编辑器\n- antigravity: Google Antigravity AI编辑器\n- vscode: Visual Studio Code\n- kiro: Kiro AI编辑器\n- aider: Aider AI编辑器\n\n特别说明:\n- rules 模板会自动包含当前 mcp 版本号信息(版本号:${ true ? "2.15.2-beta.1" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
12366
+ **CRITICAL**: This tool MUST be called FIRST when starting a new project.\n\n支持的模板:\n- react: React + CloudBase 全栈应用模板\n- vue: Vue + CloudBase 全栈应用模板\n- miniprogram: 微信小程序 + 云开发模板 \n- uniapp: UniApp + CloudBase 跨端应用模板\n- rules: 只包含AI编辑器配置文件(包含Cursor、WindSurf、CodeBuddy等所有主流编辑器配置),适合在已有项目中补充AI编辑器配置\n\n支持的IDE类型:\n- all: 下载所有IDE配置\n- cursor: Cursor AI编辑器\n- 其他IDE类型见下方列表\n\n注意:如果未传入 ide 参数且无法从环境变量检测到 IDE,将提示错误并要求传入 ide 参数\n- windsurf: WindSurf AI编辑器\n- codebuddy: CodeBuddy AI编辑器\n- claude-code: Claude Code AI编辑器\n- cline: Cline AI编辑器\n- gemini-cli: Gemini CLI\n- opencode: OpenCode AI编辑器\n- qwen-code: 通义灵码\n- baidu-comate: 百度Comate\n- openai-codex-cli: OpenAI Codex CLI\n- augment-code: Augment Code\n- github-copilot: GitHub Copilot\n- roocode: RooCode AI编辑器\n- tongyi-lingma: 通义灵码\n- trae: Trae AI编辑器\n- qoder: Qoder AI编辑器\n- antigravity: Google Antigravity AI编辑器\n- vscode: Visual Studio Code\n- kiro: Kiro AI编辑器\n- aider: Aider AI编辑器\n\n特别说明:\n- rules 模板会自动包含当前 mcp 版本号信息(版本号:${ true ? "2.15.2-beta.3" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
12163
12367
  inputSchema: {
12164
12368
  template: zod_1.z
12165
12369
  .enum(["react", "vue", "miniprogram", "uniapp", "rules"])
@@ -13846,10 +14050,10 @@ function registerHostingTools(server) {
13846
14050
  // uploadFiles - 上传文件到静态网站托管 (cloud-incompatible)
13847
14051
  server.registerTool("uploadFiles", {
13848
14052
  title: "上传静态文件",
13849
- description: "上传文件到静态网站托管。部署前请先完成构建;如果站点会部署到子路径,请检查构建配置中的 publicPath、base、assetPrefix 等是否使用相对路径,避免静态资源加载失败。",
14053
+ description: "上传文件到静态网站托管,仅用于 Web 站点部署,不用于云存储对象上传。部署前请先完成构建;如果站点会部署到子路径,请检查构建配置中的 publicPath、base、assetPrefix 等是否使用相对路径,避免静态资源加载失败。若需要上传 COS 云存储文件,请使用 manageStorage。",
13850
14054
  inputSchema: {
13851
14055
  localPath: zod_1.z.string().optional().describe("本地文件或文件夹路径,需要是绝对路径,例如 /tmp/files/data.txt。"),
13852
- cloudPath: zod_1.z.string().optional().describe("云端文件或文件夹路径,例如 files/data.txt。若部署到子路径,请同时检查构建配置中的 publicPath、base、assetPrefix 等是否为相对路径。"),
14056
+ cloudPath: zod_1.z.string().optional().describe("静态托管云端文件或文件夹路径,例如 files/data.txt。若部署到子路径,请同时检查构建配置中的 publicPath、base、assetPrefix 等是否为相对路径。云存储对象路径请改用 manageStorage。"),
13853
14057
  files: zod_1.z.array(zod_1.z.object({
13854
14058
  localPath: zod_1.z.string(),
13855
14059
  cloudPath: zod_1.z.string()
@@ -14579,11 +14783,68 @@ module.exports = __WEBPACK_EXTERNAL_MODULE_crypto__;
14579
14783
 
14580
14784
  Object.defineProperty(exports, "__esModule", ({ value: true }));
14581
14785
  exports.WRITE_SECURITY_RULE = exports.READ_SECURITY_RULE = void 0;
14786
+ exports.normalizeCustomSecurityRule = normalizeCustomSecurityRule;
14787
+ exports.buildSecurityRuleErrorMessage = buildSecurityRuleErrorMessage;
14582
14788
  exports.registerSecurityRuleTools = registerSecurityRuleTools;
14583
14789
  const zod_1 = __webpack_require__(2971);
14584
14790
  const cloudbase_manager_js_1 = __webpack_require__(3431);
14585
14791
  exports.READ_SECURITY_RULE = "readSecurityRule";
14586
14792
  exports.WRITE_SECURITY_RULE = "writeSecurityRule";
14793
+ const FUNCTION_CUSTOM_RULE_EXAMPLE = JSON.stringify({
14794
+ "*": {
14795
+ invoke: true,
14796
+ },
14797
+ }, null, 2);
14798
+ function isPlainObject(value) {
14799
+ return (typeof value === "object" &&
14800
+ value !== null &&
14801
+ !Array.isArray(value));
14802
+ }
14803
+ function formatSecurityRuleInputHint(resourceType) {
14804
+ if (resourceType === "function") {
14805
+ return `函数自定义安全规则应传 JSON 对象或 JSON 字符串,例如 ${FUNCTION_CUSTOM_RULE_EXAMPLE}。`;
14806
+ }
14807
+ if (resourceType === "noSqlDatabase") {
14808
+ return "NoSQL 自定义安全规则应传 JSON 对象或 JSON 字符串,例如 {\"read\":\"true\",\"write\":\"true\"}。";
14809
+ }
14810
+ return "自定义安全规则应传 JSON 对象或 JSON 字符串,并与 readSecurityRule 返回的规则结构保持一致。";
14811
+ }
14812
+ function normalizeCustomSecurityRule(resourceType, rule) {
14813
+ if (rule === undefined) {
14814
+ throw new Error(`resourceType=${resourceType} 且 aclTag=CUSTOM 时必须提供 rule 字段`);
14815
+ }
14816
+ if (typeof rule === "string") {
14817
+ try {
14818
+ const parsed = JSON.parse(rule);
14819
+ if (!isPlainObject(parsed)) {
14820
+ throw new Error(`rule 必须是 JSON 对象,不支持 ${Array.isArray(parsed) ? "数组" : typeof parsed}。`);
14821
+ }
14822
+ return JSON.stringify(parsed);
14823
+ }
14824
+ catch (error) {
14825
+ const baseMessage = error instanceof Error ? error.message : "rule 不是合法的 JSON 字符串";
14826
+ throw new Error(`${baseMessage} ${formatSecurityRuleInputHint(resourceType)}`);
14827
+ }
14828
+ }
14829
+ if (!isPlainObject(rule)) {
14830
+ throw new Error(`rule 必须是 JSON 对象或 JSON 字符串。${formatSecurityRuleInputHint(resourceType)}`);
14831
+ }
14832
+ return JSON.stringify(rule);
14833
+ }
14834
+ function buildSecurityRuleErrorMessage(operation, resourceType, aclTag, error) {
14835
+ const baseMessage = error instanceof Error ? error.message : String(error);
14836
+ const suggestions = [];
14837
+ if (operation === "writeSecurityRule" && aclTag === "CUSTOM") {
14838
+ suggestions.push(formatSecurityRuleInputHint(resourceType));
14839
+ }
14840
+ if (/INVALID_VALUE|ModifySecurityRule|错误的值|JSON/i.test(baseMessage)) {
14841
+ suggestions.push("请先把 CUSTOM 规则整理成 JSON 对象,再传给 writeSecurityRule。");
14842
+ }
14843
+ if (suggestions.length === 0) {
14844
+ suggestions.push("请检查 resourceType、aclTag 和 rule 是否与官方安全规则格式一致后重试。");
14845
+ }
14846
+ return `[${operation}] 调用失败: ${baseMessage}\n建议:${suggestions.join(" ")}`;
14847
+ }
14587
14848
  /**
14588
14849
  * 注册安全规则相关 Tool
14589
14850
  * @param server MCP Server 实例
@@ -14594,7 +14855,7 @@ function registerSecurityRuleTools(server) {
14594
14855
  // 读取安全规则 Tool
14595
14856
  server.registerTool?.(exports.READ_SECURITY_RULE, {
14596
14857
  title: "读取安全规则",
14597
- description: `读取指定资源(noSQL 数据库、SQL 数据库、云函数、存储桶)的安全规则和权限类别。`,
14858
+ description: `读取指定资源(noSQL 数据库、SQL 数据库、云函数、存储桶)的安全规则和权限类别。\n\n安全规则文档:\n- 云函数安全规则:https://docs.cloudbase.net/cloud-function/security-rules\n- 存储安全规则:https://docs.cloudbase.net/storage/security-rules\n- 文档型数据库安全规则:https://docs.cloudbase.net/database/security-rules`,
14598
14859
  inputSchema: {
14599
14860
  resourceType: zod_1.z
14600
14861
  .enum(["noSqlDatabase", "sqlDatabase", "function", "storage"])
@@ -14680,7 +14941,7 @@ function registerSecurityRuleTools(server) {
14680
14941
  // 写入安全规则 Tool
14681
14942
  server.registerTool?.(exports.WRITE_SECURITY_RULE, {
14682
14943
  title: "写入安全规则",
14683
- description: `设置指定资源(数据库集合、云函数、存储桶)的安全规则。`,
14944
+ description: `设置指定资源(数据库集合、云函数、存储桶)的安全规则。\n\n安全规则文档:\n- 云函数安全规则:https://docs.cloudbase.net/cloud-function/security-rules\n- 存储安全规则:https://docs.cloudbase.net/storage/security-rules\n- 数据库安全规则:https://docs.cloudbase.net/database/security-rules\n\n云函数安全规则仅支持 aclTag="CUSTOM",rule 字段为 JSON 字符串。例如放开所有访问可设置 rule="true"。`,
14684
14945
  inputSchema: {
14685
14946
  resourceType: zod_1.z
14686
14947
  .enum(["sqlDatabase", "noSqlDatabase", "function", "storage"])
@@ -14692,9 +14953,9 @@ function registerSecurityRuleTools(server) {
14692
14953
  .enum(["READONLY", "PRIVATE", "ADMINWRITE", "ADMINONLY", "CUSTOM"])
14693
14954
  .describe("权限类别"),
14694
14955
  rule: zod_1.z
14695
- .string()
14956
+ .union([zod_1.z.string(), zod_1.z.record(zod_1.z.any())])
14696
14957
  .optional()
14697
- .describe("自定义安全规则内容,仅当 aclTag 为 CUSTOM 时必填"),
14958
+ .describe(`自定义安全规则内容,仅当 aclTag 为 CUSTOM 时必填。可以直接传 JSON 对象,或者传可解析为 JSON 对象的字符串。函数示例:${FUNCTION_CUSTOM_RULE_EXAMPLE}`),
14698
14959
  },
14699
14960
  annotations: {
14700
14961
  readOnlyHint: false,
@@ -14702,138 +14963,140 @@ function registerSecurityRuleTools(server) {
14702
14963
  category: "security-rule",
14703
14964
  },
14704
14965
  }, async ({ resourceType, resourceId, aclTag, rule }) => {
14705
- const cloudbase = await getManager();
14706
- const envId = await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
14707
- let result;
14708
- if (resourceType === "noSqlDatabase") {
14709
- if (aclTag === "CUSTOM") {
14710
- if (!rule)
14711
- throw new Error("noSQL 数据库自定义安全规则(CUSTOM)必须提供 rule 字段");
14712
- result = await cloudbase.commonService().call({
14713
- Action: "ModifySafeRule",
14714
- Param: {
14715
- CollectionName: resourceId,
14716
- EnvId: envId,
14717
- AclTag: aclTag,
14718
- Rule: rule,
14719
- },
14720
- });
14721
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
14966
+ try {
14967
+ const cloudbase = await getManager();
14968
+ const envId = await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
14969
+ let result;
14970
+ if (resourceType === "noSqlDatabase") {
14971
+ if (aclTag === "CUSTOM") {
14972
+ const normalizedRule = normalizeCustomSecurityRule(resourceType, rule);
14973
+ result = await cloudbase.commonService().call({
14974
+ Action: "ModifySafeRule",
14975
+ Param: {
14976
+ CollectionName: resourceId,
14977
+ EnvId: envId,
14978
+ AclTag: aclTag,
14979
+ Rule: normalizedRule,
14980
+ },
14981
+ });
14982
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
14983
+ }
14984
+ else {
14985
+ result = await cloudbase.commonService().call({
14986
+ Action: "ModifyDatabaseACL",
14987
+ Param: {
14988
+ CollectionName: resourceId,
14989
+ EnvId: envId,
14990
+ AclTag: aclTag,
14991
+ },
14992
+ });
14993
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
14994
+ }
14722
14995
  }
14723
- else {
14996
+ else if (resourceType === "function") {
14997
+ if (aclTag !== "CUSTOM")
14998
+ throw new Error("云函数安全规则仅支持 CUSTOM 权限类别");
14999
+ const normalizedRule = normalizeCustomSecurityRule(resourceType, rule);
14724
15000
  result = await cloudbase.commonService().call({
14725
- Action: "ModifyDatabaseACL",
15001
+ Action: "ModifySecurityRule",
14726
15002
  Param: {
14727
- CollectionName: resourceId,
14728
- EnvId: envId,
14729
15003
  AclTag: aclTag,
14730
- },
14731
- });
14732
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
14733
- }
14734
- }
14735
- else if (resourceType === "function") {
14736
- if (aclTag !== "CUSTOM")
14737
- throw new Error("云函数安全规则仅支持 CUSTOM 权限类别");
14738
- if (!rule)
14739
- throw new Error("云函数自定义安全规则(CUSTOM)必须提供 rule 字段");
14740
- result = await cloudbase.commonService().call({
14741
- Action: "ModifySecurityRule",
14742
- Param: {
14743
- AclTag: aclTag,
14744
- EnvId: envId,
14745
- ResourceType: "FUNCTION",
14746
- Rule: rule,
14747
- },
14748
- });
14749
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
14750
- }
14751
- else if (resourceType === "storage") {
14752
- if (aclTag === "CUSTOM") {
14753
- if (!rule)
14754
- throw new Error("存储自定义安全规则(CUSTOM)必须提供 rule 字段");
14755
- result = await cloudbase.commonService().call({
14756
- Action: "ModifyStorageSafeRule",
14757
- Param: {
14758
- Bucket: resourceId,
14759
15004
  EnvId: envId,
14760
- AclTag: aclTag,
14761
- Rule: rule,
15005
+ ResourceType: "FUNCTION",
15006
+ Rule: normalizedRule,
14762
15007
  },
14763
15008
  });
14764
15009
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
14765
15010
  }
14766
- else {
14767
- result = await cloudbase.commonService().call({
14768
- Action: "ModifyStorageSafeRule",
15011
+ else if (resourceType === "storage") {
15012
+ if (aclTag === "CUSTOM") {
15013
+ const normalizedRule = normalizeCustomSecurityRule(resourceType, rule);
15014
+ result = await cloudbase.commonService().call({
15015
+ Action: "ModifyStorageSafeRule",
15016
+ Param: {
15017
+ Bucket: resourceId,
15018
+ EnvId: envId,
15019
+ AclTag: aclTag,
15020
+ Rule: normalizedRule,
15021
+ },
15022
+ });
15023
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
15024
+ }
15025
+ else {
15026
+ result = await cloudbase.commonService().call({
15027
+ Action: "ModifyStorageSafeRule",
15028
+ Param: {
15029
+ Bucket: resourceId,
15030
+ EnvId: envId,
15031
+ AclTag: aclTag,
15032
+ },
15033
+ });
15034
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
15035
+ }
15036
+ }
15037
+ else if (resourceType === "sqlDatabase") {
15038
+ if (aclTag === "CUSTOM") {
15039
+ throw new Error("SQL 数据库不支持自定义安全规则(CUSTOM)");
15040
+ }
15041
+ const schema = envId;
15042
+ const tableName = resourceId;
15043
+ const instanceId = "default";
15044
+ const resource = `${instanceId}#${schema}#${tableName}`;
15045
+ const resourceType = "table";
15046
+ const effect = "allow";
15047
+ const policyList = [
15048
+ "allUser",
15049
+ "anonymousUser",
15050
+ "externalUser",
15051
+ "internalUser",
15052
+ ].map((roleIdentity) => ({
15053
+ RoleIdentity: roleIdentity,
15054
+ ResourceType: resourceType,
15055
+ ResourceId: resource,
15056
+ RowPermission: [],
15057
+ Effect: effect,
15058
+ }));
15059
+ policyList[0].RowPermission = getRowPermission(aclTag);
15060
+ result = await cloudbase.commonService("lowcode").call({
15061
+ Action: "BatchCreateResourcePolicy",
14769
15062
  Param: {
14770
- Bucket: resourceId,
14771
15063
  EnvId: envId,
14772
- AclTag: aclTag,
15064
+ PolicyList: policyList,
14773
15065
  },
14774
15066
  });
14775
15067
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
15068
+ function getRowPermission(policy) {
15069
+ return {
15070
+ READONLY: [
15071
+ { Key: "all", Value: "r" },
15072
+ { Key: "me", Value: "rw" },
15073
+ ],
15074
+ PRIVATE: [{ Key: "me", Value: "rw" }],
15075
+ ADMINWRITE: [{ Key: "all", Value: "r" }],
15076
+ ADMINONLY: [],
15077
+ }[policy];
15078
+ }
14776
15079
  }
14777
- }
14778
- else if (resourceType === "sqlDatabase") {
14779
- if (aclTag === "CUSTOM") {
14780
- throw new Error("SQL 数据库不支持自定义安全规则(CUSTOM)");
14781
- }
14782
- const schema = envId;
14783
- const tableName = resourceId;
14784
- const instanceId = "default";
14785
- const resource = `${instanceId}#${schema}#${tableName}`;
14786
- const resourceType = "table";
14787
- const effect = "allow";
14788
- const policyList = [
14789
- "allUser",
14790
- "anonymousUser",
14791
- "externalUser",
14792
- "internalUser",
14793
- ].map((roleIdentity) => ({
14794
- RoleIdentity: roleIdentity,
14795
- ResourceType: resourceType,
14796
- ResourceId: resource,
14797
- RowPermission: [],
14798
- Effect: effect,
14799
- }));
14800
- policyList[0].RowPermission = getRowPermission(aclTag);
14801
- result = await cloudbase.commonService("lowcode").call({
14802
- Action: "BatchCreateResourcePolicy",
14803
- Param: {
14804
- EnvId: envId,
14805
- PolicyList: policyList,
14806
- },
14807
- });
14808
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
14809
- function getRowPermission(policy) {
14810
- return {
14811
- READONLY: [
14812
- { Key: "all", Value: "r" },
14813
- { Key: "me", Value: "rw" },
14814
- ],
14815
- PRIVATE: [{ Key: "me", Value: "rw" }],
14816
- ADMINWRITE: [{ Key: "all", Value: "r" }],
14817
- ADMINONLY: [],
14818
- }[policy];
15080
+ else {
15081
+ throw new Error(`不支持的资源类型: ${resourceType}`);
14819
15082
  }
15083
+ return {
15084
+ content: [
15085
+ {
15086
+ type: "text",
15087
+ text: JSON.stringify({
15088
+ success: true,
15089
+ requestId: result.RequestId,
15090
+ raw: result,
15091
+ message: "安全规则写入成功",
15092
+ }, null, 2),
15093
+ },
15094
+ ],
15095
+ };
14820
15096
  }
14821
- else {
14822
- throw new Error(`不支持的资源类型: ${resourceType}`);
15097
+ catch (error) {
15098
+ throw new Error(buildSecurityRuleErrorMessage("writeSecurityRule", resourceType, aclTag, error));
14823
15099
  }
14824
- return {
14825
- content: [
14826
- {
14827
- type: "text",
14828
- text: JSON.stringify({
14829
- success: true,
14830
- requestId: result.RequestId,
14831
- raw: result,
14832
- message: "安全规则写入成功",
14833
- }, null, 2),
14834
- },
14835
- ],
14836
- };
14837
15100
  });
14838
15101
  }
14839
15102
 
@@ -14954,12 +15217,15 @@ As the most important part of application development, the following four core c
14954
15217
  **Refer to deployment process in \`rules/web-development/rule.md\`**
14955
15218
  - Use CloudBase static hosting after build completion
14956
15219
  - Deploy using \`uploadFiles\` tool
15220
+ - \`uploadFiles\` is only for static hosting deployment; do not use it for COS / cloud storage uploads. For storage objects, use \`manageStorage(action="upload")\`
14957
15221
  - Remind users that CDN has a few minutes of cache after deployment
14958
15222
  - Generate markdown format access links with random queryString
14959
15223
 
14960
15224
  ### 4. Backend Deployment (Cloud Functions or CloudRun)
14961
15225
  **Refer to \`rules/cloudrun-development/rule.md\`**
14962
- - **Cloud Function Deployment**: Prefer \`queryFunctions\` to inspect current state, then use \`manageFunctions\` with \`action="createFunction"\` or \`action="updateFunctionCode"\`
15226
+ - **Cloud Function Deployment**: Prefer \`queryFunctions\` to inspect current state, then use \`manageFunctions\` with \`action="createFunction"\` or \`action="updateFunctionCode"\`.
15227
+ - In agent / non-interactive runs, treat \`manageFunctions\` as the default deployment path for both Event and HTTP functions; do not fall back to CLI login flows unless MCP tools are unavailable.
15228
+ - For HTTP functions, create or update them through \`manageFunctions\` with \`func.type="HTTP"\` instead of relying on \`tcb fn deploy\` as the primary path.
14963
15229
  - **CloudRun Deployment**: Use \`manageCloudRun\` tool for containerized deployment
14964
15230
  - Ensure backend code supports CORS, prepare Dockerfile (for container type)
14965
15231
 
@@ -15058,11 +15324,11 @@ If remote links are needed in the application, can continue to call uploadFile t
15058
15324
 
15059
15325
  ### Deployment Process
15060
15326
 
15061
- 1. **Cloud Function Deployment Process**: Prefer \`queryFunctions\` to query current functions, then call \`manageFunctions\` with \`action="createFunction"\` or \`action="updateFunctionCode"\` to deploy cloud function code. Only need to point \`functionRootPath\` to the parent directory of the cloud function directory (for example, the absolute path of the \`cloudfunctions\` directory). No need for code compression or other preprocessing.
15327
+ 1. **Cloud Function Deployment Process**: Prefer \`queryFunctions\` to query current functions, then call \`manageFunctions\` with \`action="createFunction"\` or \`action="updateFunctionCode"\` to deploy cloud function code. In agent / non-interactive runs, keep deployment on MCP tools instead of CLI login flows. For HTTP functions, set \`func.type="HTTP"\` when creating them. Only need to point \`functionRootPath\` to the parent directory of the cloud function directory (for example, the absolute path of the \`cloudfunctions\` directory). No need for code compression or other preprocessing.
15062
15328
 
15063
15329
  2. **CloudRun Deployment Process**: For non-cloud function backend services (Java, Go, PHP, Python, Node.js, etc.), use manageCloudRun tool for deployment. Ensure backend code supports CORS, prepare Dockerfile, then call manageCloudRun for containerized deployment. For details, refer to \`rules/cloudrun-development/rule.md\`
15064
15330
 
15065
- 3. **Static Hosting Deployment Process**: Deploy using uploadFiles tool. After deployment, remind users that CDN has a few minutes of cache. Can generate markdown format access links with random queryString. For details, refer to \`rules/web-development/rule.md\`
15331
+ 3. **Static Hosting Deployment Process**: Deploy using uploadFiles tool for static hosting only. If the task needs a COS object that must be queried or polled with the storage SDK, use \`manageStorage\` / \`queryStorage\` instead. After deployment, remind users that CDN has a few minutes of cache. Can generate markdown format access links with random queryString. For details, refer to \`rules/web-development/rule.md\`
15066
15332
 
15067
15333
  ### Documentation Generation Rules
15068
15334
 
@@ -16066,7 +16332,9 @@ function buildProvisionNextActions(status, request) {
16066
16332
  return [];
16067
16333
  }
16068
16334
  return [
16069
- buildNextAction(QUERY_SQL_DATABASE, "describeTaskStatus", "MySQL provisioning is still running. Check task status before initializing schema.", request ? { action: "describeTaskStatus", request } : { action: "describeTaskStatus" }),
16335
+ buildNextAction(QUERY_SQL_DATABASE, "describeCreateResult", "MySQL provisioning is still running. Check the create result again before initializing schema.", request
16336
+ ? { action: "describeCreateResult", request }
16337
+ : { action: "describeCreateResult" }),
16070
16338
  ];
16071
16339
  }
16072
16340
  function inferTaskKind(request) {