@cloudbase/cloudbase-mcp 2.32.2 → 2.32.4

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"],
@@ -33409,8 +33460,9 @@ const SUPABASE_LIKE_SDK_HINTS = {
33409
33460
  emailOtp: "auth.signInWithOtp({ email })",
33410
33461
  password: "auth.signInWithPassword({ username|email|phone, password })",
33411
33462
  signup: "auth.signUp({ phone|email, ... })",
33412
- verifyOtp: "verifyOtp({ token })",
33463
+ verifyOtp: "const { data, error } = await auth.signInWithOtp({ email })\nconst { data: loginData, error: loginError } = await data.verifyOtp({ token })",
33413
33464
  anonymous: "auth.signInAnonymously()",
33465
+ caution: "verifyOtp is a callback on the signInWithOtp / signUp result: call data.verifyOtp({ token }) with only the token. Do NOT call a standalone auth.verifyOtp({ token }) — it requires an extra messageId and fails with 'messageId is required' when the messageId is missing. Error 'messageId is required' means you used the standalone call instead of the callback.",
33414
33466
  };
33415
33467
  function buildErrorEnvelope(error) {
33416
33468
  return {
@@ -129453,9 +129505,55 @@ function buildUploadErrorMessage(error, localPath) {
129453
129505
  }
129454
129506
  return `[manageHosting(upload)] ${baseMessage}\n建议:${suggestions.join(' ')}`;
129455
129507
  }
129508
+ /**
129509
+ * TCB 管控面 DescribeStaticStore 接口 QPS 限流(20 次/秒)。
129510
+ * Manager SDK 的 hosting.deleteFiles / uploadFiles / findFiles 等每次调用
129511
+ * 都会先 checkStatus() → getInfo() → DescribeStaticStore(无缓存),
129512
+ * AI 连续快速删除多个文件或失败后立即重试时极易触发该限流。
129513
+ */
129514
+ const DESCRIBE_STATIC_STORE_RATE_LIMIT_RE = /\[DescribeStaticStore\][\s\S]*exceeds the frequency limit `20` for a second/i;
129515
+ function buildDeleteErrorMessage(error) {
129516
+ const baseMessage = error instanceof Error ? error.message : String(error);
129517
+ if (DESCRIBE_STATIC_STORE_RATE_LIMIT_RE.test(baseMessage)) {
129518
+ return (`[manageHosting(delete)] ${baseMessage}\n` +
129519
+ '原因:静态托管底层 DescribeStaticStore 管控接口有 20 次/秒的 QPS 限制,' +
129520
+ '连续快速删除多个文件(或失败后立即重试)容易触发限流,删除操作本身可能已经部分生效。\n' +
129521
+ '处理建议:\n' +
129522
+ '1) 等待 1-2 秒后重试本次删除,不要立即连续重试;\n' +
129523
+ '2) 批量删除多个文件时,逐次调用并保持间隔(建议每秒不超过 10 次),不要并发或循环快速重试;\n' +
129524
+ '3) 同一目录下的多个文件可改用 isDir=true 一次删除整个目录,减少调用次数;\n' +
129525
+ '4) 若不确定删除是否已生效,可调用 queryHosting(action="findFiles") 核对。');
129526
+ }
129527
+ return `[manageHosting(delete)] ${baseMessage}`;
129528
+ }
129456
129529
  function isRecord(value) {
129457
129530
  return typeof value === 'object' && value !== null && !Array.isArray(value);
129458
129531
  }
129532
+ /**
129533
+ * Manager SDK hosting.deleteFiles 对 COS 删除失败不抛异常,返回
129534
+ * { Deleted: [], Error: [e] };此处提取 Error 数组为可读字符串列表,
129535
+ * 供 delete 路径做失败判定与引导。
129536
+ */
129537
+ function extractDeleteErrors(result) {
129538
+ if (!isRecord(result)) {
129539
+ return [];
129540
+ }
129541
+ const rawErrors = result.Error ?? result.error;
129542
+ if (!Array.isArray(rawErrors)) {
129543
+ return [];
129544
+ }
129545
+ return rawErrors
129546
+ .map((item) => {
129547
+ if (typeof item === 'string' && item.trim()) {
129548
+ return item.trim();
129549
+ }
129550
+ if (isRecord(item) && typeof item.message === 'string') {
129551
+ return item.message;
129552
+ }
129553
+ return JSON.stringify(item);
129554
+ })
129555
+ .filter(Boolean);
129556
+ }
129459
129557
  function getRecordString(record, keys) {
129460
129558
  for (const key of keys) {
129461
129559
  const value = record[key];
@@ -129680,11 +129778,24 @@ async function getHostingStoreOrThrow(cloudbase, cloudBaseOptions, logger) {
129680
129778
  }
129681
129779
  throw new Error(`当前环境 ${envId} 未发现静态托管资源配置,无法上传文件。请先确认 MCP 已绑定到已开通静态托管的 CloudBase 环境;如需切换环境,请调用 auth(action="set_env", envId="目标环境ID") 后重试。`);
129682
129780
  }
129781
+ function enrichRateLimitMessage(message) {
129782
+ // delete 路径已由 buildDeleteErrorMessage 提供完整引导,避免重复追加
129783
+ if (message.includes('QPS 限制') || message.includes('等待 1-2 秒后重试')) {
129784
+ return message;
129785
+ }
129786
+ if (DESCRIBE_STATIC_STORE_RATE_LIMIT_RE.test(message)) {
129787
+ return (`${message}\n` +
129788
+ '原因:静态托管底层 DescribeStaticStore 管控接口有 20 次/秒的 QPS 限制,' +
129789
+ '连续快速调用(或失败后立即重试)容易触发限流,本次查询/操作可能没有完整生效。\n' +
129790
+ '处理建议:等待 1-2 秒后重试,不要连续快速重试;多个文件操作请逐次调用并保持间隔。');
129791
+ }
129792
+ return message;
129793
+ }
129683
129794
  function buildFailureResult(action, error) {
129684
129795
  return (0, tool_result_js_1.buildJsonToolResult)({
129685
129796
  success: false,
129686
129797
  errorCode: `HOSTING_${action.toUpperCase()}_FAILED`,
129687
- message: error instanceof Error ? error.message : String(error),
129798
+ message: enrichRateLimitMessage(error instanceof Error ? error.message : String(error)),
129688
129799
  });
129689
129800
  }
129690
129801
  function normalizeFileFields(files) {
@@ -129864,7 +129975,7 @@ function registerHostingTools(server) {
129864
129975
  });
129865
129976
  server.registerTool('manageHosting', {
129866
129977
  title: '管理 CloudBase 静态托管',
129867
- 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。',
129978
+ 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。',
129868
129979
  inputSchema: manageHostingInputSchema,
129869
129980
  annotations: {
129870
129981
  readOnlyHint: false,
@@ -130034,14 +130145,27 @@ function registerHostingTools(server) {
130034
130145
  if (!input.confirm) {
130035
130146
  throw new Error('manageHosting(action="delete") 是破坏性操作,必须显式传 confirm=true。');
130036
130147
  }
130037
- const result = await cloudbase.hosting.deleteFiles({
130038
- cloudPath: input.cloudPath,
130039
- isDir: input.isDir ?? false,
130040
- });
130148
+ let result;
130149
+ try {
130150
+ result = await cloudbase.hosting.deleteFiles({
130151
+ cloudPath: input.cloudPath,
130152
+ isDir: input.isDir ?? false,
130153
+ });
130154
+ }
130155
+ catch (error) {
130156
+ throw new Error(buildDeleteErrorMessage(error));
130157
+ }
130041
130158
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
130159
+ // Manager SDK 的 deleteFiles 对 COS 单文件删除失败不抛异常,
130160
+ // 而是返回 { Deleted: [], Error: [e] },需要显式检查 Error 数组。
130161
+ const deleteErrors = extractDeleteErrors(result);
130042
130162
  // Post-validation: verify deletion was successful
130043
130163
  let deleteVerified = true;
130044
130164
  let verificationError;
130165
+ if (deleteErrors.length > 0) {
130166
+ deleteVerified = false;
130167
+ verificationError = `删除请求未生效:${deleteErrors.join(';')}`;
130168
+ }
130045
130169
  try {
130046
130170
  const checkResult = await cloudbase.hosting.findFiles({
130047
130171
  prefix: input.cloudPath,
@@ -130049,7 +130173,7 @@ function registerHostingTools(server) {
130049
130173
  });
130050
130174
  if (Array.isArray(checkResult) && checkResult.length > 0) {
130051
130175
  deleteVerified = false;
130052
- verificationError = `删除后验证失败:文件仍在静态托管中`;
130176
+ verificationError = verificationError ?? `删除后验证失败:文件仍在静态托管中`;
130053
130177
  }
130054
130178
  }
130055
130179
  catch (error) {
@@ -130068,7 +130192,11 @@ function registerHostingTools(server) {
130068
130192
  },
130069
130193
  message: deleteVerified
130070
130194
  ? `已删除静态托管${input.isDir ? '目录' : '文件'} \`${input.cloudPath}\`。`
130071
- : `删除操作已提交,但验证发现文件可能未完全删除,请手动确认。`,
130195
+ : `删除操作已提交,但验证发现文件可能未完全删除。` +
130196
+ `可能原因:底层 COS 删除请求失败(如文件不存在、存储桶权限不足),或触发 DescribeStaticStore 限流后删除未生效。` +
130197
+ `建议:1) 等待 1-2 秒后用 queryHosting(action="findFiles", prefix="${input.cloudPath}") 核对文件状态;` +
130198
+ `2) 若确认文件仍存在,重新调用 manageHosting(action="delete", confirm=true) 重试;` +
130199
+ `3) 批量删除多个文件请逐次调用并保持间隔(DescribeStaticStore 有 20 次/秒 QPS 限制)。`,
130072
130200
  });
130073
130201
  }
130074
130202
  case 'setWebsiteDocument': {
@@ -138368,6 +138496,11 @@ function buildCapiErrorMessage(service, action, error) {
138368
138496
  function registerCapiTools(server) {
138369
138497
  const cloudBaseOptions = server.cloudBaseOptions;
138370
138498
  const logger = server.logger;
138499
+ // commonService 透传调用只依赖凭据上下文(secretId/secretKey/token/region),
138500
+ // EnvId 由 params 携带,不依赖 MCP 绑定的环境。强制绑定会在「已登录但未绑环境」
138501
+ // 场景把 DescribeEnvs/CreateEnv/CAM 等无 env 依赖的 Action 全部挡在 ENV_REQUIRED,
138502
+ // 并诱发无头客户端原样重试风暴(2026-08-20 单日 15 万次报错),故豁免环境绑定。
138503
+ const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions, requireEnvId: false });
138371
138504
  server.registerTool?.("callCloudApi", {
138372
138505
  title: "调用云API",
138373
138506
  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 分类如下:
@@ -149415,7 +149548,7 @@ class TelemetryReporter {
149415
149548
  const arch = os_1.default.arch(); // 系统架构
149416
149549
  // Build-time injected version; guard for vitest / non-webpack runtimes.
149417
149550
  const mcpVersion = process.env.npm_package_version ||
149418
- ( true ? "2.32.2" : 0);
149551
+ ( true ? "2.32.4" : 0);
149419
149552
  return {
149420
149553
  userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
149421
149554
  deviceId: this.deviceId,
@@ -177126,20 +177259,26 @@ async function resolvePgDbContext(cloudBaseOptions, args) {
177126
177259
  envId,
177127
177260
  instanceId: args?.instanceId ?? "cloudbase-pg",
177128
177261
  defaultSchema: args?.defaultSchema ?? "public",
177129
- role: args?.role ?? "cloudbase_admin",
177262
+ role: args?.role ?? PG_DEFAULT_ROLE,
177130
177263
  };
177131
177264
  }
177132
177265
  /** Default / recommended roles for CloudBase PG ExecutePGSql.Role */
177133
- const PG_DEFAULT_ROLE = "cloudbase_admin";
177266
+ const PG_DEFAULT_ROLE = "cloudbase_postgres";
177134
177267
  const PG_RECOMMENDED_ROLES = [
177135
- "cloudbase_admin",
177268
+ "cloudbase_postgres",
177136
177269
  "anon",
177137
177270
  "authenticated",
177138
177271
  "service_role",
177139
177272
  ];
177273
+ /**
177274
+ * Platform-owned admin roles. cloudbase_admin is the platform management
177275
+ * account and must not be used by user-facing ExecutePGSql calls.
177276
+ */
177277
+ const PG_PLATFORM_RESERVED_ROLES = ["cloudbase_admin"];
177140
177278
  /**
177141
177279
  * Roles that look like login/instance principals and routinely fail SET ROLE
177142
177280
  * in beacon data (e.g. postgres, postgres_pgdb_<id>). Reject before the API call.
177281
+ * Also rejects platform-reserved roles (cloudbase_admin) that are not for user calls.
177143
177282
  */
177144
177283
  function isLikelyInvalidPgExecuteRole(role) {
177145
177284
  const normalized = role.trim();
@@ -177149,6 +177288,10 @@ function isLikelyInvalidPgExecuteRole(role) {
177149
177288
  if (normalized === "postgres") {
177150
177289
  return true;
177151
177290
  }
177291
+ // Platform management account, reserved for the platform itself.
177292
+ if (PG_PLATFORM_RESERVED_ROLES.includes(normalized)) {
177293
+ return true;
177294
+ }
177152
177295
  // Instance-derived login principals, not valid ExecutePGSql Role targets
177153
177296
  if (/^postgres_pgdb_[a-z0-9]+$/i.test(normalized)) {
177154
177297
  return true;
@@ -177185,8 +177328,10 @@ function isPgRoleExecutionError(message) {
177185
177328
  }
177186
177329
  function buildPgRoleGuidanceMessage(attemptedRole) {
177187
177330
  const recommended = PG_RECOMMENDED_ROLES.join(", ");
177331
+ const reserved = PG_PLATFORM_RESERVED_ROLES.join(", ");
177188
177332
  return (`PostgreSQL role "${attemptedRole}" cannot be used with ExecutePGSql (SET ROLE failed or role does not exist). ` +
177189
177333
  `Do not invent roles like postgres / postgres_pgdb_* from env or instance names. ` +
177334
+ `Platform-reserved roles (${reserved}) are the platform management account and must not be used by user calls. ` +
177190
177335
  `Omit role (defaults to ${PG_DEFAULT_ROLE}) or pass one of: ${recommended}. ` +
177191
177336
  `To list roles that exist in this database, retry with role=${PG_DEFAULT_ROLE} and SQL: SELECT rolname FROM pg_roles ORDER BY rolname;`);
177192
177337
  }
@@ -179455,7 +179600,7 @@ function registerPGDatabaseTools(server, providedDeps) {
179455
179600
  .optional()
179456
179601
  .describe(`可选的 PostgreSQL role,传给 Manager SDK executePGSql 的 Role(平台会 SET ROLE)。默认 ${PG_DEFAULT_ROLE}。` +
179457
179602
  `推荐取值:${PG_RECOMMENDED_ROLES.join(" / ")}。` +
179458
- `不要传 postgres、postgres_pgdb_* 或从环境名臆造的角色;不确定时省略本字段,或先用 ${PG_DEFAULT_ROLE} 执行 SELECT rolname FROM pg_roles。`),
179603
+ `不要传 postgres、postgres_pgdb_*、平台保留角色(${PG_PLATFORM_RESERVED_ROLES.join("、")},为平台管理账号不对用户开放)或从环境名臆造的角色;不确定时省略本字段,或先用 ${PG_DEFAULT_ROLE} 执行 SELECT rolname FROM pg_roles。`),
179459
179604
  objectName: zod_1.z
179460
179605
  .string()
179461
179606
  .optional()
@@ -192960,6 +193105,87 @@ module.exports = Tar;
192960
193105
  */
192961
193106
 
192962
193107
 
193108
+ /***/ }),
193109
+
193110
+ /***/ 60827:
193111
+ /***/ ((__unused_webpack_module, exports) => {
193112
+
193113
+ "use strict";
193114
+
193115
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
193116
+ exports.REPEAT_GUARD_THRESHOLD = void 0;
193117
+ exports.applyRepeatGuardToPayload = applyRepeatGuardToPayload;
193118
+ exports.resetRepeatGuard = resetRepeatGuard;
193119
+ exports.__resetRepeatGuardForTests = __resetRepeatGuardForTests;
193120
+ /**
193121
+ * 连续重复错误熔断(repeat guard)。
193122
+ *
193123
+ * 背景:无头自动化客户端收到结构化引导错误(如 ENV_REQUIRED)后,可能不执行
193124
+ * 引导动作而是原样重试,形成重试风暴(2026-08-20 单日 15 万次同文案报错)。
193125
+ * 本模块在同一进程(≈同一 MCP session)内跟踪连续相同错误:
193126
+ * - 连续次数达到阈值后,在 payload 注入 repeat_guard 升级字段,提醒模型
193127
+ * 「原样重试无效,必须先完成 next_step 指向的修复动作」;
193128
+ * - 不改写 message 本身,保持灯塔侧按错误文案聚合的稳定性;
193129
+ * - 任一工具调用成功即整体清零(说明循环已被打破)。
193130
+ */
193131
+ exports.REPEAT_GUARD_THRESHOLD = 3;
193132
+ const REPEAT_GUARD_KEY_MAX_LENGTH = 200;
193133
+ let lastKey = null;
193134
+ let consecutiveCount = 0;
193135
+ function buildRepeatGuardKey(payload) {
193136
+ const code = typeof payload.code === "string" ? payload.code : "";
193137
+ const message = typeof payload.message === "string" ? payload.message : "";
193138
+ return `${code}|${message.slice(0, REPEAT_GUARD_KEY_MAX_LENGTH)}`;
193139
+ }
193140
+ function buildRepeatGuardNotice(count) {
193141
+ return (`当前会话已连续 ${count} 次返回相同错误。原样重试不会成功:` +
193142
+ "请停止重试当前调用,先按 payload 中的 message / next_step 完成修复动作" +
193143
+ "(如完成 auth 登录或绑定环境),或调整参数后再继续。");
193144
+ }
193145
+ /**
193146
+ * 记录一次结构化工具错误;达到阈值时返回注入 repeat_guard 的新 payload。
193147
+ */
193148
+ function applyRepeatGuardToPayload(payload) {
193149
+ if (!payload || typeof payload !== "object") {
193150
+ return payload;
193151
+ }
193152
+ const key = buildRepeatGuardKey(payload);
193153
+ if (key === lastKey) {
193154
+ consecutiveCount += 1;
193155
+ }
193156
+ else {
193157
+ lastKey = key;
193158
+ consecutiveCount = 1;
193159
+ }
193160
+ if (consecutiveCount < exports.REPEAT_GUARD_THRESHOLD) {
193161
+ return payload;
193162
+ }
193163
+ const code = typeof payload.code === "string" && payload.code.length > 0
193164
+ ? payload.code
193165
+ : "UNKNOWN";
193166
+ const repeatGuard = {
193167
+ code,
193168
+ consecutive_count: consecutiveCount,
193169
+ threshold: exports.REPEAT_GUARD_THRESHOLD,
193170
+ notice: buildRepeatGuardNotice(consecutiveCount),
193171
+ };
193172
+ return { ...payload, repeat_guard: repeatGuard };
193173
+ }
193174
+ /**
193175
+ * 工具调用成功时清零计数。
193176
+ */
193177
+ function resetRepeatGuard() {
193178
+ lastKey = null;
193179
+ consecutiveCount = 0;
193180
+ }
193181
+ /**
193182
+ * 测试辅助:恢复初始状态。
193183
+ */
193184
+ function __resetRepeatGuardForTests() {
193185
+ resetRepeatGuard();
193186
+ }
193187
+
193188
+
192963
193189
  /***/ }),
192964
193190
 
192965
193191
  /***/ 60890:
@@ -233116,6 +233342,7 @@ const cloud_mode_js_1 = __webpack_require__(89684);
233116
233342
  const logger_js_1 = __webpack_require__(13039);
233117
233343
  const telemetry_js_1 = __webpack_require__(45880);
233118
233344
  const tool_result_js_1 = __webpack_require__(9835);
233345
+ const repeat_error_guard_js_1 = __webpack_require__(60827);
233119
233346
  /**
233120
233347
  * Mirror annotations.category into _meta.category.
233121
233348
  *
@@ -233187,7 +233414,7 @@ ${envIdSection}
233187
233414
  ## 环境信息
233188
233415
  - 操作系统: ${os_1.default.type()} ${os_1.default.release()}
233189
233416
  - Node.js版本: ${process.version}
233190
- - MCP 版本:${process.env.npm_package_version || ( true ? "2.32.2" : 0)}
233417
+ - MCP 版本:${process.env.npm_package_version || ( true ? "2.32.4" : 0)}
233191
233418
  - 系统架构: ${os_1.default.arch()}
233192
233419
  - 时间: ${new Date().toISOString()}
233193
233420
  - 请求ID: ${requestId}
@@ -233233,6 +233460,8 @@ function createWrappedHandler(name, handler, server) {
233233
233460
  // 执行原始处理函数
233234
233461
  const result = await handler(args);
233235
233462
  success = true;
233463
+ // 任一工具成功说明重复错误循环已被打破,清零连续错误计数
233464
+ (0, repeat_error_guard_js_1.resetRepeatGuard)();
233236
233465
  requestId = extractRequestIdFromToolResult(result);
233237
233466
  const duration = Date.now() - startTime;
233238
233467
  (0, logger_js_1.debug)(`工具执行成功: ${name}`, { duration, requestId: requestId || undefined });
@@ -233257,7 +233486,9 @@ function createWrappedHandler(name, handler, server) {
233257
233486
  // Preserve structured tool guidance such as next_step.
233258
233487
  // These errors are expected control flow and should be serialized by the outer server wrapper.
233259
233488
  if ((0, tool_result_js_1.isToolPayloadError)(error)) {
233260
- throw error;
233489
+ // 连续相同结构化错误达到阈值时注入 repeat_guard 升级提示,
233490
+ // 打断无头客户端的原样重试循环(不改写 message,保持遥测聚合稳定)
233491
+ throw new tool_result_js_1.ToolPayloadError((0, repeat_error_guard_js_1.applyRepeatGuardToPayload)(error.payload));
233261
233492
  }
233262
233493
  // In tests, avoid any extra work that may block (envId lookup, issue link generation, etc.)
233263
233494
  if (isTestEnvironment) {
@@ -233268,7 +233499,7 @@ function createWrappedHandler(name, handler, server) {
233268
233499
  requestId: (typeof error === 'object' && error && 'requestId' in error) ? error.requestId : '',
233269
233500
  ide: server.ide || process.env.INTEGRATION_IDE || ''
233270
233501
  });
233271
- const mcpVersion = true ? "2.32.2" : 0;
233502
+ const mcpVersion = true ? "2.32.4" : 0;
233272
233503
  const requestIdSuffix = requestId ? `\n🆔 RequestId: ${requestId}` : '';
233273
233504
  const enhancedErrorMessage = `${errorMessage}${requestIdSuffix}\n\n📦 CloudBase MCP v${mcpVersion}\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
233274
233505
  // 创建新的错误对象,保持原有的错误类型但更新消息
@@ -252657,7 +252888,7 @@ function registerSetupTools(server) {
252657
252888
  server.registerTool("downloadTemplate", {
252658
252889
  title: "下载项目模板",
252659
252890
  description: `自动下载并部署CloudBase项目模板。
252660
- **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.2" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
252891
+ **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.4" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
252661
252892
  inputSchema: {
252662
252893
  template: zod_1.z
252663
252894
  .enum(["react", "vue", "miniprogram", "uniapp", "rules"])
@@ -254695,6 +254926,8 @@ exports.setPendingAuthProgressState = setPendingAuthProgressState;
254695
254926
  exports.resolveAuthProgressState = resolveAuthProgressState;
254696
254927
  exports.rejectAuthProgressState = rejectAuthProgressState;
254697
254928
  exports.resetAuthProgressState = resetAuthProgressState;
254929
+ exports.ensureSlottedCredential = ensureSlottedCredential;
254930
+ exports.listUsableCredentialSites = listUsableCredentialSites;
254698
254931
  exports.peekLoginState = peekLoginState;
254699
254932
  exports.ensureLogin = ensureLogin;
254700
254933
  exports.getLoginState = getLoginState;
@@ -255042,6 +255275,20 @@ function isUsableCredential(value) {
255042
255275
  typeof value === "object" &&
255043
255276
  Object.keys(value).length > 0);
255044
255277
  }
255278
+ /**
255279
+ * 返回当前存在可用凭证的 site 槽位列表(凭证续期失败视为不可用)。
255280
+ * 用于歧义 region(如 ap-singapore)时判定用户实际所属站点。
255281
+ */
255282
+ async function listUsableCredentialSites() {
255283
+ const usable = [];
255284
+ for (const site of ["domestic", "intl"]) {
255285
+ const state = await resolveSiteLoginState(site).catch(() => null);
255286
+ if (state) {
255287
+ usable.push(site);
255288
+ }
255289
+ }
255290
+ return usable;
255291
+ }
255045
255292
  async function peekLoginState(options) {
255046
255293
  const envVarLoginState = normalizeLoginStateFromEnvVars(options);
255047
255294
  if (envVarLoginState) {
@@ -255064,8 +255311,23 @@ async function peekLoginState(options) {
255064
255311
  return envVarLoginState;
255065
255312
  }
255066
255313
  // 按 site 读取分槽凭证(缺省按 TCB_SITE/region 解析站点,默认 domestic)
255067
- const site = (0, site_map_js_1.normalizeSite)(options?.site) ?? (0, site_map_js_1.resolveSite)(options?.region, process.env.TCB_SITE);
255068
- const slotLoginState = await resolveSiteLoginState(site);
255314
+ const explicitSite = (0, site_map_js_1.normalizeSite)(options?.site) ?? (0, site_map_js_1.normalizeSite)(process.env.TCB_SITE);
255315
+ const site = explicitSite ?? (0, site_map_js_1.resolveSite)(options?.region);
255316
+ let slotLoginState = await resolveSiteLoginState(site);
255317
+ // 歧义 region(如 ap-singapore 同时属于 domestic 与 intl)且未显式指定 site 时,
255318
+ // 解析槽位为空则回退检查另一槽位:仅另一槽位有可用凭证时直接使用,
255319
+ // 避免对单站点用户误报 AUTH_REQUIRED(如国内站 ap-singapore 环境场景)。
255320
+ if (!slotLoginState && !explicitSite && (0, site_map_js_1.getSite)(options?.region) === "ambiguous") {
255321
+ const fallbackSite = site === "domestic" ? "intl" : "domestic";
255322
+ const fallbackLoginState = await resolveSiteLoginState(fallbackSite);
255323
+ if (fallbackLoginState) {
255324
+ (0, logger_js_1.debug)("peekLoginState: ambiguous region, using the only usable site slot", {
255325
+ resolvedSite: site,
255326
+ fallbackSite,
255327
+ });
255328
+ return fallbackLoginState;
255329
+ }
255330
+ }
255069
255331
  if (slotLoginState) {
255070
255332
  return slotLoginState;
255071
255333
  }