@cloudbase/cloudbase-mcp 2.32.1 → 2.32.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.cjs CHANGED
@@ -4220,6 +4220,31 @@ function applyBoundEnvRegion(server, region) {
4220
4220
  server.cloudBaseOptions.region = region;
4221
4221
  }
4222
4222
  }
4223
+ /**
4224
+ * 绑定环境后固化站点:region 歧义(如 ap-singapore 同时属于 domestic 与 intl)且
4225
+ * 未显式指定 site 时,若仅一个凭证槽位可用,则将 TCB_SITE 固定为该站点,
4226
+ * 避免后续资源调用再次落入歧义默认分支(issue #960)。
4227
+ */
4228
+ async function applyBoundEnvSite(server, region) {
4229
+ if (!region || (0, site_map_js_1.getSite)(region) !== "ambiguous") {
4230
+ return;
4231
+ }
4232
+ if ((0, site_map_js_1.normalizeSite)(process.env.TCB_SITE)) {
4233
+ return; // 已显式指定 site,不覆盖
4234
+ }
4235
+ const usableSites = await (0, auth_js_1.listUsableCredentialSites)();
4236
+ if (usableSites.length !== 1) {
4237
+ return; // 无法唯一判定时不猜测,保持既有歧义回退逻辑
4238
+ }
4239
+ process.env.TCB_SITE = usableSites[0];
4240
+ if (server.cloudBaseOptions) {
4241
+ server.cloudBaseOptions.site = usableSites[0];
4242
+ }
4243
+ (0, logger_js_1.debug)("applyBoundEnvSite: pinned TCB_SITE from the only usable credential slot", {
4244
+ region,
4245
+ site: usableSites[0],
4246
+ });
4247
+ }
4223
4248
  const CODEBUDDY_AUTH_ACTIONS = ["status", "set_env", "login_by_api_key"];
4224
4249
  const DEFAULT_AUTH_ACTIONS = [
4225
4250
  "status",
@@ -5494,7 +5519,17 @@ function registerEnvTools(server) {
5494
5519
  : {}),
5495
5520
  onDeviceCode: deviceOnCode,
5496
5521
  })
5497
- .then(() => {
5522
+ .then(async () => {
5523
+ // toolbox 将新凭证写入 flat 'credential',这里迁移为分槽格式
5524
+ //(legacy flat 等价 domestic 槽位),对齐 ensureLogin() 的分槽行为
5525
+ try {
5526
+ await (0, auth_js_1.ensureSlottedCredential)();
5527
+ }
5528
+ catch (err) {
5529
+ (0, logger_js_1.debug)("device auth: slotted credential migration failed", {
5530
+ error: err instanceof Error ? err.message : String(err),
5531
+ });
5532
+ }
5498
5533
  (0, auth_js_1.resolveAuthProgressState)();
5499
5534
  })
5500
5535
  .catch((err) => {
@@ -5706,7 +5741,17 @@ function registerEnvTools(server) {
5706
5741
  });
5707
5742
  }
5708
5743
  await cloudbase_manager_js_1.envManager.setEnvId(envId);
5744
+ // 跨客户端通用事件推送(MCP 协议 notification,无 id):任何客户端
5745
+ // (如 dsh-plugin 面板)都能通过 notifications/cloudbase/env_changed
5746
+ // 感知环境变更,替代客户端本地推断与兜底轮询。通知失败不影响主流程。
5747
+ void server.server
5748
+ .notification({
5749
+ method: "notifications/cloudbase/env_changed",
5750
+ params: { envId },
5751
+ })
5752
+ .catch(() => { });
5709
5753
  applyBoundEnvRegion(server, target?.region);
5754
+ await applyBoundEnvSite(server, target?.region);
5710
5755
  const regionHint = target?.region
5711
5756
  ? `,地域: ${target.region}`
5712
5757
  : target
@@ -29542,14 +29587,20 @@ async function throwPendingAuthError() {
29542
29587
  async function throwEnvRequiredError(options) {
29543
29588
  const envCandidates = options?.envCandidates ?? (await listAvailableEnvCandidates(options));
29544
29589
  const singleEnvId = envCandidates.length === 1 ? envCandidates[0].envId : undefined;
29590
+ // 终止性引导:明确「重试当前工具不会成功」,给出可直接执行的替代动作与
29591
+ // 无头/无人值守环境的 fallback(CLOUDBASE_ENV_ID 或宿主配置传入 envId),
29592
+ // 避免客户端把本错误当作可重试错误陷入死循环(2026-08-20 单日 15 万次重试风暴)。
29593
+ const retryHint = "重试当前工具不会成功,请停止原样重试。";
29594
+ const headlessHint = "若运行在无浏览器/无人值守环境而无法交互完成登录绑定,可在启动 MCP 前设置 CLOUDBASE_ENV_ID 环境变量指定默认环境。";
29595
+ const message = envCandidates.length === 0
29596
+ ? `当前已登录,但还没有可用环境。${retryHint}请改为在腾讯云控制台创建 CloudBase 环境后重新绑定;${headlessHint}`
29597
+ : envCandidates.length === 1
29598
+ ? `当前已登录,但尚未绑定环境。${retryHint}请先调用 auth(action="set_env", envId="${singleEnvId}") 完成绑定后再继续;${headlessHint}`
29599
+ : `当前已登录,但尚未绑定环境。${retryHint}请先调用 auth(action="set_env", envId=候选环境之一) 完成绑定后再继续;${headlessHint}`;
29545
29600
  (0, tool_result_js_1.throwToolPayloadError)({
29546
29601
  ok: false,
29547
29602
  code: "ENV_REQUIRED",
29548
- message: envCandidates.length === 0
29549
- ? "当前已登录,但还没有可用环境,请先调用 auth 工具完成环境选择或创建环境。"
29550
- : envCandidates.length === 1
29551
- ? `当前已登录,但尚未绑定环境。可直接选择环境 ${singleEnvId}。`
29552
- : "当前已登录,但尚未绑定环境,请先调用 auth 工具选择环境。",
29603
+ message,
29553
29604
  env_candidates: envCandidates,
29554
29605
  next_step: (0, tool_result_js_1.buildAuthNextStep)("set_env", {
29555
29606
  requiredParams: singleEnvId ? undefined : ["envId"],
@@ -73039,10 +73090,16 @@ function registerMsgPushTools(server) {
73039
73090
  return buildTransportUnavailablePayload("queryMessagePush", action);
73040
73091
  }
73041
73092
  if (action === "list") {
73042
- const [state, container] = await Promise.all([
73093
+ const [state, containerRaw] = await Promise.all([
73043
73094
  readCallbackConfig(server, appid),
73044
- readContainerConfig(server, appid),
73095
+ // 兼容性降级:微信侧 apihttpagent 通道不支持 getcontainercallbackconfig
73096
+ // (ret=-9991)时,容器配置读取失败不阻断 list(pushMode 默认 cloudfunction)
73097
+ readContainerConfig(server, appid).catch((e) => {
73098
+ console.warn(`[msg-push] 读取云托管容器配置失败,降级为云函数模式推断: ${e instanceof Error ? e.message : String(e)}`);
73099
+ return null;
73100
+ }),
73045
73101
  ]);
73102
+ const container = containerRaw;
73046
73103
  const pushMode = resolvePushMode(container);
73047
73104
  const callbacks = env
73048
73105
  ? state.list.filter((c) => c.env === env)
@@ -129447,9 +129504,55 @@ function buildUploadErrorMessage(error, localPath) {
129447
129504
  }
129448
129505
  return `[manageHosting(upload)] ${baseMessage}\n建议:${suggestions.join(' ')}`;
129449
129506
  }
129507
+ /**
129508
+ * TCB 管控面 DescribeStaticStore 接口 QPS 限流(20 次/秒)。
129509
+ * Manager SDK 的 hosting.deleteFiles / uploadFiles / findFiles 等每次调用
129510
+ * 都会先 checkStatus() → getInfo() → DescribeStaticStore(无缓存),
129511
+ * AI 连续快速删除多个文件或失败后立即重试时极易触发该限流。
129512
+ */
129513
+ const DESCRIBE_STATIC_STORE_RATE_LIMIT_RE = /\[DescribeStaticStore\][\s\S]*exceeds the frequency limit `20` for a second/i;
129514
+ function buildDeleteErrorMessage(error) {
129515
+ const baseMessage = error instanceof Error ? error.message : String(error);
129516
+ if (DESCRIBE_STATIC_STORE_RATE_LIMIT_RE.test(baseMessage)) {
129517
+ return (`[manageHosting(delete)] ${baseMessage}\n` +
129518
+ '原因:静态托管底层 DescribeStaticStore 管控接口有 20 次/秒的 QPS 限制,' +
129519
+ '连续快速删除多个文件(或失败后立即重试)容易触发限流,删除操作本身可能已经部分生效。\n' +
129520
+ '处理建议:\n' +
129521
+ '1) 等待 1-2 秒后重试本次删除,不要立即连续重试;\n' +
129522
+ '2) 批量删除多个文件时,逐次调用并保持间隔(建议每秒不超过 10 次),不要并发或循环快速重试;\n' +
129523
+ '3) 同一目录下的多个文件可改用 isDir=true 一次删除整个目录,减少调用次数;\n' +
129524
+ '4) 若不确定删除是否已生效,可调用 queryHosting(action="findFiles") 核对。');
129525
+ }
129526
+ return `[manageHosting(delete)] ${baseMessage}`;
129527
+ }
129450
129528
  function isRecord(value) {
129451
129529
  return typeof value === 'object' && value !== null && !Array.isArray(value);
129452
129530
  }
129531
+ /**
129532
+ * Manager SDK hosting.deleteFiles 对 COS 删除失败不抛异常,返回
129533
+ * { Deleted: [], Error: [e] };此处提取 Error 数组为可读字符串列表,
129534
+ * 供 delete 路径做失败判定与引导。
129535
+ */
129536
+ function extractDeleteErrors(result) {
129537
+ if (!isRecord(result)) {
129538
+ return [];
129539
+ }
129540
+ const rawErrors = result.Error ?? result.error;
129541
+ if (!Array.isArray(rawErrors)) {
129542
+ return [];
129543
+ }
129544
+ return rawErrors
129545
+ .map((item) => {
129546
+ if (typeof item === 'string' && item.trim()) {
129547
+ return item.trim();
129548
+ }
129549
+ if (isRecord(item) && typeof item.message === 'string') {
129550
+ return item.message;
129551
+ }
129552
+ return JSON.stringify(item);
129553
+ })
129554
+ .filter(Boolean);
129555
+ }
129453
129556
  function getRecordString(record, keys) {
129454
129557
  for (const key of keys) {
129455
129558
  const value = record[key];
@@ -129674,11 +129777,24 @@ async function getHostingStoreOrThrow(cloudbase, cloudBaseOptions, logger) {
129674
129777
  }
129675
129778
  throw new Error(`当前环境 ${envId} 未发现静态托管资源配置,无法上传文件。请先确认 MCP 已绑定到已开通静态托管的 CloudBase 环境;如需切换环境,请调用 auth(action="set_env", envId="目标环境ID") 后重试。`);
129676
129779
  }
129780
+ function enrichRateLimitMessage(message) {
129781
+ // delete 路径已由 buildDeleteErrorMessage 提供完整引导,避免重复追加
129782
+ if (message.includes('QPS 限制') || message.includes('等待 1-2 秒后重试')) {
129783
+ return message;
129784
+ }
129785
+ if (DESCRIBE_STATIC_STORE_RATE_LIMIT_RE.test(message)) {
129786
+ return (`${message}\n` +
129787
+ '原因:静态托管底层 DescribeStaticStore 管控接口有 20 次/秒的 QPS 限制,' +
129788
+ '连续快速调用(或失败后立即重试)容易触发限流,本次查询/操作可能没有完整生效。\n' +
129789
+ '处理建议:等待 1-2 秒后重试,不要连续快速重试;多个文件操作请逐次调用并保持间隔。');
129790
+ }
129791
+ return message;
129792
+ }
129677
129793
  function buildFailureResult(action, error) {
129678
129794
  return (0, tool_result_js_1.buildJsonToolResult)({
129679
129795
  success: false,
129680
129796
  errorCode: `HOSTING_${action.toUpperCase()}_FAILED`,
129681
- message: error instanceof Error ? error.message : String(error),
129797
+ message: enrichRateLimitMessage(error instanceof Error ? error.message : String(error)),
129682
129798
  });
129683
129799
  }
129684
129800
  function normalizeFileFields(files) {
@@ -129858,7 +129974,7 @@ function registerHostingTools(server) {
129858
129974
  });
129859
129975
  server.registerTool('manageHosting', {
129860
129976
  title: '管理 CloudBase 静态托管',
129861
- description: '管理 CloudBase 静态托管的变更操作。action=upload 上传本地构建产物到共享域名(域名格式:<envId>-<appId>.tcloudbaseapp.com/<cloudPath>);action=delete 删除托管文件或目录(必须 confirm=true);action=setWebsiteDocument 设置首页/错误页/路由规则;action=enableService 开通静态托管;action=bindDomain / unbindDomain / updateDomain 管理自定义域名;action=downloadFile / downloadDirectory 下载托管内容到本地。⚠️ 本工具没有关闭默认域名(*.tcloudbaseapp.com)的 action;要禁用该默认公网域名,请用 manageGateway(action="disableRoute", domain=该 STATIC_STORE IsDefault 域名, path="/")(底层 ModifyHTTPServiceRoute,不是 ModifyGatewayRoute)。⚠️ 新项目部署优先使用 manageApps(部署到独立子域名),本工具适合已有老项目继续使用或作为 manageApps 的 fallback。manageApps 与 manageHosting 域名不同,切换会导致老链接失效。若任务只是查看配置、文件或域名状态,请改用 queryHosting。',
129977
+ description: '管理 CloudBase 静态托管的变更操作。action=upload 上传本地构建产物到共享域名(域名格式:<envId>-<appId>.tcloudbaseapp.com/<cloudPath>);action=delete 删除托管文件或目录(必须 confirm=true);action=setWebsiteDocument 设置首页/错误页/路由规则;action=enableService 开通静态托管;action=bindDomain / unbindDomain / updateDomain 管理自定义域名;action=downloadFile / downloadDirectory 下载托管内容到本地。⚠️ 底层每次托管操作都会请求 DescribeStaticStore 管控接口(20 次/秒 QPS 限制):批量删除多个文件请逐次调用并保持间隔(建议每秒不超过 10 次),同一目录下多个文件可优先用 isDir=true 一次删除整个目录;若报错含 "frequency limit" 说明触发了限流,请等待 1-2 秒后重试,不要连续快速重试。⚠️ 本工具没有关闭默认域名(*.tcloudbaseapp.com)的 action;要禁用该默认公网域名,请用 manageGateway(action="disableRoute", domain=该 STATIC_STORE IsDefault 域名, path="/")(底层 ModifyHTTPServiceRoute,不是 ModifyGatewayRoute)。⚠️ 新项目部署优先使用 manageApps(部署到独立子域名),本工具适合已有老项目继续使用或作为 manageApps 的 fallback。manageApps 与 manageHosting 域名不同,切换会导致老链接失效。若任务只是查看配置、文件或域名状态,请改用 queryHosting。',
129862
129978
  inputSchema: manageHostingInputSchema,
129863
129979
  annotations: {
129864
129980
  readOnlyHint: false,
@@ -130028,14 +130144,27 @@ function registerHostingTools(server) {
130028
130144
  if (!input.confirm) {
130029
130145
  throw new Error('manageHosting(action="delete") 是破坏性操作,必须显式传 confirm=true。');
130030
130146
  }
130031
- const result = await cloudbase.hosting.deleteFiles({
130032
- cloudPath: input.cloudPath,
130033
- isDir: input.isDir ?? false,
130034
- });
130147
+ let result;
130148
+ try {
130149
+ result = await cloudbase.hosting.deleteFiles({
130150
+ cloudPath: input.cloudPath,
130151
+ isDir: input.isDir ?? false,
130152
+ });
130153
+ }
130154
+ catch (error) {
130155
+ throw new Error(buildDeleteErrorMessage(error));
130156
+ }
130035
130157
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
130158
+ // Manager SDK 的 deleteFiles 对 COS 单文件删除失败不抛异常,
130159
+ // 而是返回 { Deleted: [], Error: [e] },需要显式检查 Error 数组。
130160
+ const deleteErrors = extractDeleteErrors(result);
130036
130161
  // Post-validation: verify deletion was successful
130037
130162
  let deleteVerified = true;
130038
130163
  let verificationError;
130164
+ if (deleteErrors.length > 0) {
130165
+ deleteVerified = false;
130166
+ verificationError = `删除请求未生效:${deleteErrors.join(';')}`;
130167
+ }
130039
130168
  try {
130040
130169
  const checkResult = await cloudbase.hosting.findFiles({
130041
130170
  prefix: input.cloudPath,
@@ -130043,7 +130172,7 @@ function registerHostingTools(server) {
130043
130172
  });
130044
130173
  if (Array.isArray(checkResult) && checkResult.length > 0) {
130045
130174
  deleteVerified = false;
130046
- verificationError = `删除后验证失败:文件仍在静态托管中`;
130175
+ verificationError = verificationError ?? `删除后验证失败:文件仍在静态托管中`;
130047
130176
  }
130048
130177
  }
130049
130178
  catch (error) {
@@ -130062,7 +130191,11 @@ function registerHostingTools(server) {
130062
130191
  },
130063
130192
  message: deleteVerified
130064
130193
  ? `已删除静态托管${input.isDir ? '目录' : '文件'} \`${input.cloudPath}\`。`
130065
- : `删除操作已提交,但验证发现文件可能未完全删除,请手动确认。`,
130194
+ : `删除操作已提交,但验证发现文件可能未完全删除。` +
130195
+ `可能原因:底层 COS 删除请求失败(如文件不存在、存储桶权限不足),或触发 DescribeStaticStore 限流后删除未生效。` +
130196
+ `建议:1) 等待 1-2 秒后用 queryHosting(action="findFiles", prefix="${input.cloudPath}") 核对文件状态;` +
130197
+ `2) 若确认文件仍存在,重新调用 manageHosting(action="delete", confirm=true) 重试;` +
130198
+ `3) 批量删除多个文件请逐次调用并保持间隔(DescribeStaticStore 有 20 次/秒 QPS 限制)。`,
130066
130199
  });
130067
130200
  }
130068
130201
  case 'setWebsiteDocument': {
@@ -138362,6 +138495,11 @@ function buildCapiErrorMessage(service, action, error) {
138362
138495
  function registerCapiTools(server) {
138363
138496
  const cloudBaseOptions = server.cloudBaseOptions;
138364
138497
  const logger = server.logger;
138498
+ // commonService 透传调用只依赖凭据上下文(secretId/secretKey/token/region),
138499
+ // EnvId 由 params 携带,不依赖 MCP 绑定的环境。强制绑定会在「已登录但未绑环境」
138500
+ // 场景把 DescribeEnvs/CreateEnv/CAM 等无 env 依赖的 Action 全部挡在 ENV_REQUIRED,
138501
+ // 并诱发无头客户端原样重试风暴(2026-08-20 单日 15 万次报错),故豁免环境绑定。
138502
+ const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions, requireEnvId: false });
138365
138503
  server.registerTool?.("callCloudApi", {
138366
138504
  title: "调用云API",
138367
138505
  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 service,常用 Action 分类如下:
@@ -149409,7 +149547,7 @@ class TelemetryReporter {
149409
149547
  const arch = os_1.default.arch(); // 系统架构
149410
149548
  // Build-time injected version; guard for vitest / non-webpack runtimes.
149411
149549
  const mcpVersion = process.env.npm_package_version ||
149412
- ( true ? "2.32.1" : 0);
149550
+ ( true ? "2.32.3" : 0);
149413
149551
  return {
149414
149552
  userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
149415
149553
  deviceId: this.deviceId,
@@ -177120,20 +177258,26 @@ async function resolvePgDbContext(cloudBaseOptions, args) {
177120
177258
  envId,
177121
177259
  instanceId: args?.instanceId ?? "cloudbase-pg",
177122
177260
  defaultSchema: args?.defaultSchema ?? "public",
177123
- role: args?.role ?? "cloudbase_admin",
177261
+ role: args?.role ?? PG_DEFAULT_ROLE,
177124
177262
  };
177125
177263
  }
177126
177264
  /** Default / recommended roles for CloudBase PG ExecutePGSql.Role */
177127
- const PG_DEFAULT_ROLE = "cloudbase_admin";
177265
+ const PG_DEFAULT_ROLE = "cloudbase_postgres";
177128
177266
  const PG_RECOMMENDED_ROLES = [
177129
- "cloudbase_admin",
177267
+ "cloudbase_postgres",
177130
177268
  "anon",
177131
177269
  "authenticated",
177132
177270
  "service_role",
177133
177271
  ];
177272
+ /**
177273
+ * Platform-owned admin roles. cloudbase_admin is the platform management
177274
+ * account and must not be used by user-facing ExecutePGSql calls.
177275
+ */
177276
+ const PG_PLATFORM_RESERVED_ROLES = ["cloudbase_admin"];
177134
177277
  /**
177135
177278
  * Roles that look like login/instance principals and routinely fail SET ROLE
177136
177279
  * in beacon data (e.g. postgres, postgres_pgdb_<id>). Reject before the API call.
177280
+ * Also rejects platform-reserved roles (cloudbase_admin) that are not for user calls.
177137
177281
  */
177138
177282
  function isLikelyInvalidPgExecuteRole(role) {
177139
177283
  const normalized = role.trim();
@@ -177143,6 +177287,10 @@ function isLikelyInvalidPgExecuteRole(role) {
177143
177287
  if (normalized === "postgres") {
177144
177288
  return true;
177145
177289
  }
177290
+ // Platform management account, reserved for the platform itself.
177291
+ if (PG_PLATFORM_RESERVED_ROLES.includes(normalized)) {
177292
+ return true;
177293
+ }
177146
177294
  // Instance-derived login principals, not valid ExecutePGSql Role targets
177147
177295
  if (/^postgres_pgdb_[a-z0-9]+$/i.test(normalized)) {
177148
177296
  return true;
@@ -177179,8 +177327,10 @@ function isPgRoleExecutionError(message) {
177179
177327
  }
177180
177328
  function buildPgRoleGuidanceMessage(attemptedRole) {
177181
177329
  const recommended = PG_RECOMMENDED_ROLES.join(", ");
177330
+ const reserved = PG_PLATFORM_RESERVED_ROLES.join(", ");
177182
177331
  return (`PostgreSQL role "${attemptedRole}" cannot be used with ExecutePGSql (SET ROLE failed or role does not exist). ` +
177183
177332
  `Do not invent roles like postgres / postgres_pgdb_* from env or instance names. ` +
177333
+ `Platform-reserved roles (${reserved}) are the platform management account and must not be used by user calls. ` +
177184
177334
  `Omit role (defaults to ${PG_DEFAULT_ROLE}) or pass one of: ${recommended}. ` +
177185
177335
  `To list roles that exist in this database, retry with role=${PG_DEFAULT_ROLE} and SQL: SELECT rolname FROM pg_roles ORDER BY rolname;`);
177186
177336
  }
@@ -179449,7 +179599,7 @@ function registerPGDatabaseTools(server, providedDeps) {
179449
179599
  .optional()
179450
179600
  .describe(`可选的 PostgreSQL role,传给 Manager SDK executePGSql 的 Role(平台会 SET ROLE)。默认 ${PG_DEFAULT_ROLE}。` +
179451
179601
  `推荐取值:${PG_RECOMMENDED_ROLES.join(" / ")}。` +
179452
- `不要传 postgres、postgres_pgdb_* 或从环境名臆造的角色;不确定时省略本字段,或先用 ${PG_DEFAULT_ROLE} 执行 SELECT rolname FROM pg_roles。`),
179602
+ `不要传 postgres、postgres_pgdb_*、平台保留角色(${PG_PLATFORM_RESERVED_ROLES.join("、")},为平台管理账号不对用户开放)或从环境名臆造的角色;不确定时省略本字段,或先用 ${PG_DEFAULT_ROLE} 执行 SELECT rolname FROM pg_roles。`),
179453
179603
  objectName: zod_1.z
179454
179604
  .string()
179455
179605
  .optional()
@@ -192954,6 +193104,87 @@ module.exports = Tar;
192954
193104
  */
192955
193105
 
192956
193106
 
193107
+ /***/ }),
193108
+
193109
+ /***/ 60827:
193110
+ /***/ ((__unused_webpack_module, exports) => {
193111
+
193112
+ "use strict";
193113
+
193114
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
193115
+ exports.REPEAT_GUARD_THRESHOLD = void 0;
193116
+ exports.applyRepeatGuardToPayload = applyRepeatGuardToPayload;
193117
+ exports.resetRepeatGuard = resetRepeatGuard;
193118
+ exports.__resetRepeatGuardForTests = __resetRepeatGuardForTests;
193119
+ /**
193120
+ * 连续重复错误熔断(repeat guard)。
193121
+ *
193122
+ * 背景:无头自动化客户端收到结构化引导错误(如 ENV_REQUIRED)后,可能不执行
193123
+ * 引导动作而是原样重试,形成重试风暴(2026-08-20 单日 15 万次同文案报错)。
193124
+ * 本模块在同一进程(≈同一 MCP session)内跟踪连续相同错误:
193125
+ * - 连续次数达到阈值后,在 payload 注入 repeat_guard 升级字段,提醒模型
193126
+ * 「原样重试无效,必须先完成 next_step 指向的修复动作」;
193127
+ * - 不改写 message 本身,保持灯塔侧按错误文案聚合的稳定性;
193128
+ * - 任一工具调用成功即整体清零(说明循环已被打破)。
193129
+ */
193130
+ exports.REPEAT_GUARD_THRESHOLD = 3;
193131
+ const REPEAT_GUARD_KEY_MAX_LENGTH = 200;
193132
+ let lastKey = null;
193133
+ let consecutiveCount = 0;
193134
+ function buildRepeatGuardKey(payload) {
193135
+ const code = typeof payload.code === "string" ? payload.code : "";
193136
+ const message = typeof payload.message === "string" ? payload.message : "";
193137
+ return `${code}|${message.slice(0, REPEAT_GUARD_KEY_MAX_LENGTH)}`;
193138
+ }
193139
+ function buildRepeatGuardNotice(count) {
193140
+ return (`当前会话已连续 ${count} 次返回相同错误。原样重试不会成功:` +
193141
+ "请停止重试当前调用,先按 payload 中的 message / next_step 完成修复动作" +
193142
+ "(如完成 auth 登录或绑定环境),或调整参数后再继续。");
193143
+ }
193144
+ /**
193145
+ * 记录一次结构化工具错误;达到阈值时返回注入 repeat_guard 的新 payload。
193146
+ */
193147
+ function applyRepeatGuardToPayload(payload) {
193148
+ if (!payload || typeof payload !== "object") {
193149
+ return payload;
193150
+ }
193151
+ const key = buildRepeatGuardKey(payload);
193152
+ if (key === lastKey) {
193153
+ consecutiveCount += 1;
193154
+ }
193155
+ else {
193156
+ lastKey = key;
193157
+ consecutiveCount = 1;
193158
+ }
193159
+ if (consecutiveCount < exports.REPEAT_GUARD_THRESHOLD) {
193160
+ return payload;
193161
+ }
193162
+ const code = typeof payload.code === "string" && payload.code.length > 0
193163
+ ? payload.code
193164
+ : "UNKNOWN";
193165
+ const repeatGuard = {
193166
+ code,
193167
+ consecutive_count: consecutiveCount,
193168
+ threshold: exports.REPEAT_GUARD_THRESHOLD,
193169
+ notice: buildRepeatGuardNotice(consecutiveCount),
193170
+ };
193171
+ return { ...payload, repeat_guard: repeatGuard };
193172
+ }
193173
+ /**
193174
+ * 工具调用成功时清零计数。
193175
+ */
193176
+ function resetRepeatGuard() {
193177
+ lastKey = null;
193178
+ consecutiveCount = 0;
193179
+ }
193180
+ /**
193181
+ * 测试辅助:恢复初始状态。
193182
+ */
193183
+ function __resetRepeatGuardForTests() {
193184
+ resetRepeatGuard();
193185
+ }
193186
+
193187
+
192957
193188
  /***/ }),
192958
193189
 
192959
193190
  /***/ 60890:
@@ -222687,7 +222918,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
222687
222918
  exports.CloudBaseContext = void 0;
222688
222919
  const constant_1 = __webpack_require__(40762);
222689
222920
  class CloudBaseContext {
222690
- constructor({ secretId = '', secretKey = '', token = '', proxy = '', region = '', envType = '', useInternalEndpoint = undefined, internalEndpointRegion = undefined }) {
222921
+ constructor({ secretId = '', secretKey = '', token = '', proxy = '', region = '', envType = '', useInternalEndpoint = undefined, internalEndpointRegion = undefined, requestFn = undefined }) {
222691
222922
  this.secretId = secretId;
222692
222923
  this.secretKey = secretKey;
222693
222924
  this.token = token;
@@ -222696,6 +222927,7 @@ class CloudBaseContext {
222696
222927
  this.envType = envType;
222697
222928
  this.useInternalEndpoint = useInternalEndpoint;
222698
222929
  this.internalEndpointRegion = internalEndpointRegion;
222930
+ this.requestFn = requestFn;
222699
222931
  }
222700
222932
  isInternalEndpoint() {
222701
222933
  return this.useInternalEndpoint !== undefined ? this.useInternalEndpoint : constant_1.USE_INTERNAL_ENDPOINT;
@@ -233109,6 +233341,7 @@ const cloud_mode_js_1 = __webpack_require__(89684);
233109
233341
  const logger_js_1 = __webpack_require__(13039);
233110
233342
  const telemetry_js_1 = __webpack_require__(45880);
233111
233343
  const tool_result_js_1 = __webpack_require__(9835);
233344
+ const repeat_error_guard_js_1 = __webpack_require__(60827);
233112
233345
  /**
233113
233346
  * Mirror annotations.category into _meta.category.
233114
233347
  *
@@ -233180,7 +233413,7 @@ ${envIdSection}
233180
233413
  ## 环境信息
233181
233414
  - 操作系统: ${os_1.default.type()} ${os_1.default.release()}
233182
233415
  - Node.js版本: ${process.version}
233183
- - MCP 版本:${process.env.npm_package_version || ( true ? "2.32.1" : 0)}
233416
+ - MCP 版本:${process.env.npm_package_version || ( true ? "2.32.3" : 0)}
233184
233417
  - 系统架构: ${os_1.default.arch()}
233185
233418
  - 时间: ${new Date().toISOString()}
233186
233419
  - 请求ID: ${requestId}
@@ -233226,6 +233459,8 @@ function createWrappedHandler(name, handler, server) {
233226
233459
  // 执行原始处理函数
233227
233460
  const result = await handler(args);
233228
233461
  success = true;
233462
+ // 任一工具成功说明重复错误循环已被打破,清零连续错误计数
233463
+ (0, repeat_error_guard_js_1.resetRepeatGuard)();
233229
233464
  requestId = extractRequestIdFromToolResult(result);
233230
233465
  const duration = Date.now() - startTime;
233231
233466
  (0, logger_js_1.debug)(`工具执行成功: ${name}`, { duration, requestId: requestId || undefined });
@@ -233250,7 +233485,9 @@ function createWrappedHandler(name, handler, server) {
233250
233485
  // Preserve structured tool guidance such as next_step.
233251
233486
  // These errors are expected control flow and should be serialized by the outer server wrapper.
233252
233487
  if ((0, tool_result_js_1.isToolPayloadError)(error)) {
233253
- throw error;
233488
+ // 连续相同结构化错误达到阈值时注入 repeat_guard 升级提示,
233489
+ // 打断无头客户端的原样重试循环(不改写 message,保持遥测聚合稳定)
233490
+ throw new tool_result_js_1.ToolPayloadError((0, repeat_error_guard_js_1.applyRepeatGuardToPayload)(error.payload));
233254
233491
  }
233255
233492
  // In tests, avoid any extra work that may block (envId lookup, issue link generation, etc.)
233256
233493
  if (isTestEnvironment) {
@@ -233261,7 +233498,7 @@ function createWrappedHandler(name, handler, server) {
233261
233498
  requestId: (typeof error === 'object' && error && 'requestId' in error) ? error.requestId : '',
233262
233499
  ide: server.ide || process.env.INTEGRATION_IDE || ''
233263
233500
  });
233264
- const mcpVersion = true ? "2.32.1" : 0;
233501
+ const mcpVersion = true ? "2.32.3" : 0;
233265
233502
  const requestIdSuffix = requestId ? `\n🆔 RequestId: ${requestId}` : '';
233266
233503
  const enhancedErrorMessage = `${errorMessage}${requestIdSuffix}\n\n📦 CloudBase MCP v${mcpVersion}\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
233267
233504
  // 创建新的错误对象,保持原有的错误类型但更新消息
@@ -252650,7 +252887,7 @@ function registerSetupTools(server) {
252650
252887
  server.registerTool("downloadTemplate", {
252651
252888
  title: "下载项目模板",
252652
252889
  description: `自动下载并部署CloudBase项目模板。
252653
- **Note**: Call this tool when the user requests to create a new project using a CloudBase template.\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.32.1" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
252890
+ **Note**: Call this tool when the user requests to create a new project using a CloudBase template.\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.32.3" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
252654
252891
  inputSchema: {
252655
252892
  template: zod_1.z
252656
252893
  .enum(["react", "vue", "miniprogram", "uniapp", "rules"])
@@ -254688,6 +254925,8 @@ exports.setPendingAuthProgressState = setPendingAuthProgressState;
254688
254925
  exports.resolveAuthProgressState = resolveAuthProgressState;
254689
254926
  exports.rejectAuthProgressState = rejectAuthProgressState;
254690
254927
  exports.resetAuthProgressState = resetAuthProgressState;
254928
+ exports.ensureSlottedCredential = ensureSlottedCredential;
254929
+ exports.listUsableCredentialSites = listUsableCredentialSites;
254691
254930
  exports.peekLoginState = peekLoginState;
254692
254931
  exports.ensureLogin = ensureLogin;
254693
254932
  exports.getLoginState = getLoginState;
@@ -255035,6 +255274,20 @@ function isUsableCredential(value) {
255035
255274
  typeof value === "object" &&
255036
255275
  Object.keys(value).length > 0);
255037
255276
  }
255277
+ /**
255278
+ * 返回当前存在可用凭证的 site 槽位列表(凭证续期失败视为不可用)。
255279
+ * 用于歧义 region(如 ap-singapore)时判定用户实际所属站点。
255280
+ */
255281
+ async function listUsableCredentialSites() {
255282
+ const usable = [];
255283
+ for (const site of ["domestic", "intl"]) {
255284
+ const state = await resolveSiteLoginState(site).catch(() => null);
255285
+ if (state) {
255286
+ usable.push(site);
255287
+ }
255288
+ }
255289
+ return usable;
255290
+ }
255038
255291
  async function peekLoginState(options) {
255039
255292
  const envVarLoginState = normalizeLoginStateFromEnvVars(options);
255040
255293
  if (envVarLoginState) {
@@ -255057,8 +255310,23 @@ async function peekLoginState(options) {
255057
255310
  return envVarLoginState;
255058
255311
  }
255059
255312
  // 按 site 读取分槽凭证(缺省按 TCB_SITE/region 解析站点,默认 domestic)
255060
- const site = (0, site_map_js_1.normalizeSite)(options?.site) ?? (0, site_map_js_1.resolveSite)(options?.region, process.env.TCB_SITE);
255061
- const slotLoginState = await resolveSiteLoginState(site);
255313
+ const explicitSite = (0, site_map_js_1.normalizeSite)(options?.site) ?? (0, site_map_js_1.normalizeSite)(process.env.TCB_SITE);
255314
+ const site = explicitSite ?? (0, site_map_js_1.resolveSite)(options?.region);
255315
+ let slotLoginState = await resolveSiteLoginState(site);
255316
+ // 歧义 region(如 ap-singapore 同时属于 domestic 与 intl)且未显式指定 site 时,
255317
+ // 解析槽位为空则回退检查另一槽位:仅另一槽位有可用凭证时直接使用,
255318
+ // 避免对单站点用户误报 AUTH_REQUIRED(如国内站 ap-singapore 环境场景)。
255319
+ if (!slotLoginState && !explicitSite && (0, site_map_js_1.getSite)(options?.region) === "ambiguous") {
255320
+ const fallbackSite = site === "domestic" ? "intl" : "domestic";
255321
+ const fallbackLoginState = await resolveSiteLoginState(fallbackSite);
255322
+ if (fallbackLoginState) {
255323
+ (0, logger_js_1.debug)("peekLoginState: ambiguous region, using the only usable site slot", {
255324
+ resolvedSite: site,
255325
+ fallbackSite,
255326
+ });
255327
+ return fallbackLoginState;
255328
+ }
255329
+ }
255062
255330
  if (slotLoginState) {
255063
255331
  return slotLoginState;
255064
255332
  }
@@ -270513,12 +270781,13 @@ function getDate(timestamp) {
270513
270781
  }
270514
270782
  class CloudService {
270515
270783
  /* eslint-disable-next-line */
270516
- constructor(context, service, version, baseParams) {
270784
+ constructor(context, service, version, baseParams, requestFn) {
270517
270785
  this.service = service;
270518
270786
  this.version = version;
270519
270787
  this.timeout = 60000;
270520
270788
  this.baseParams = baseParams || {};
270521
270789
  this.cloudBaseContext = context;
270790
+ this.requestFn = requestFn !== null && requestFn !== void 0 ? requestFn : context === null || context === void 0 ? void 0 : context.requestFn;
270522
270791
  }
270523
270792
  get baseUrl() {
270524
270793
  const internalEndpoint = this.cloudBaseContext.isInternalEndpoint();
@@ -270550,9 +270819,32 @@ class CloudService {
270550
270819
  }
270551
270820
  }
270552
270821
  async request(action, data = {}, method = 'POST') {
270822
+ var _a;
270553
270823
  this.action = action;
270554
- this.data = deepRemoveVoid(Object.assign(Object.assign({}, data), this.baseParams));
270555
270824
  this.method = method;
270825
+ this.data = deepRemoveVoid(Object.assign(Object.assign({}, data), this.baseParams));
270826
+ // 如果注入了 requestFn,直接委托,跳过 TC3 签名逻辑
270827
+ if (this.requestFn) {
270828
+ try {
270829
+ const result = await this.requestFn({
270830
+ service: this.service,
270831
+ action,
270832
+ version: this.version,
270833
+ region: (_a = this.cloudBaseContext.region) !== null && _a !== void 0 ? _a : '',
270834
+ payload: this.data,
270835
+ });
270836
+ return result;
270837
+ }
270838
+ catch (e) {
270839
+ if (e.name === 'CloudBaseError') {
270840
+ throw e;
270841
+ }
270842
+ throw new error_1.CloudBaseError(e.message, {
270843
+ action,
270844
+ code: e.code
270845
+ });
270846
+ }
270847
+ }
270556
270848
  this.url = this.baseUrl;
270557
270849
  let { secretId, secretKey, token } = this.cloudBaseContext;
270558
270850
  // 当在云函数环境下执行时,可init时不传入密钥,取环境变量中密钥使用
@@ -315767,9 +316059,9 @@ class CloudBase {
315767
316059
  }
315768
316060
  constructor(config = {}) {
315769
316061
  this.cloudBaseConfig = {};
315770
- let { secretId, secretKey, token, envId, proxy, region, envType, useInternalEndpoint, internalEndpointRegion } = config;
315771
- // config 中传入的 secretId secretkey 必须同时存在
315772
- if ((secretId && !secretKey) || (!secretId && secretKey)) {
316062
+ let { secretId, secretKey, token, envId, proxy, region, envType, useInternalEndpoint, internalEndpointRegion, requestFn } = config;
316063
+ // config 中传入的 secretId secretkey 必须同时存在(有 requestFn 时可以不传)
316064
+ if (!requestFn && ((secretId && !secretKey) || (!secretId && secretKey))) {
315773
316065
  throw new Error('secretId and secretKey must be a pair');
315774
316066
  }
315775
316067
  this.cloudBaseConfig = {
@@ -315782,6 +316074,7 @@ class CloudBase {
315782
316074
  region,
315783
316075
  useInternalEndpoint,
315784
316076
  internalEndpointRegion,
316077
+ requestFn,
315785
316078
  };
315786
316079
  // 初始化 context
315787
316080
  this.context = new context_1.CloudBaseContext(this.cloudBaseConfig);