@cloudbase/cloudbase-mcp 2.23.11 → 2.24.1

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.d.ts CHANGED
@@ -330,6 +330,13 @@ declare interface LoginState {
330
330
 
331
331
  export declare function logout(): Promise<void>;
332
332
 
333
+ /** MCP initialize clientInfo fields available after handshake via Server.getClientVersion(). */
334
+ declare type McpClientInfo = {
335
+ name?: string;
336
+ version?: string;
337
+ title?: string;
338
+ };
339
+
333
340
  export { McpServer }
334
341
 
335
342
  /** 各插件的可选配置 */
@@ -357,6 +364,7 @@ export declare const reportToolCall: (params: {
357
364
  inputParams?: any;
358
365
  cloudBaseOptions?: CloudBaseOptions;
359
366
  ide?: string;
367
+ mcpClientInfo?: McpClientInfo;
360
368
  }) => Promise<void>;
361
369
 
362
370
  export declare const reportToolkitLifecycle: (params: {
@@ -366,6 +374,7 @@ export declare const reportToolkitLifecycle: (params: {
366
374
  error?: string;
367
375
  cloudBaseOptions?: CloudBaseOptions;
368
376
  ide?: string;
377
+ mcpClientInfo?: McpClientInfo;
369
378
  }) => Promise<void>;
370
379
 
371
380
  export declare function resetCloudBaseManagerCache(): void;
package/dist/index.js CHANGED
@@ -30,6 +30,8 @@ import * as __WEBPACK_EXTERNAL_MODULE_winston__ from "winston";
30
30
 
31
31
 
32
32
  Object.defineProperty(exports, "__esModule", ({ value: true }));
33
+ exports.DEFAULT_CREATE_ENV_RESOURCES = exports.CREATE_ENV_RESOURCE_VALUES = void 0;
34
+ exports.resolveCreateEnvResources = resolveCreateEnvResources;
33
35
  exports.simplifyEnvList = simplifyEnvList;
34
36
  exports.registerEnvTools = registerEnvTools;
35
37
  const toolbox_1 = __webpack_require__(2090);
@@ -39,6 +41,26 @@ const cloudbase_manager_js_1 = __webpack_require__(3431);
39
41
  const logger_js_1 = __webpack_require__(3039);
40
42
  const tool_result_js_1 = __webpack_require__(9835);
41
43
  const env_setup_js_1 = __webpack_require__(6978);
44
+ /**
45
+ * Resources accepted by CreateEnv. Matches Cloud API / Manager SDK contract.
46
+ * `postgresql` enables CloudBase PostgreSQL (PG mode) when supported by the package.
47
+ */
48
+ exports.CREATE_ENV_RESOURCE_VALUES = [
49
+ "flexdb",
50
+ "storage",
51
+ "function",
52
+ "postgresql",
53
+ ];
54
+ /** Default Resources when callers omit the field (schema promises "all four"). */
55
+ exports.DEFAULT_CREATE_ENV_RESOURCES = [
56
+ ...exports.CREATE_ENV_RESOURCE_VALUES,
57
+ ];
58
+ function resolveCreateEnvResources(resources) {
59
+ if (Array.isArray(resources) && resources.length > 0) {
60
+ return [...resources];
61
+ }
62
+ return [...exports.DEFAULT_CREATE_ENV_RESOURCES];
63
+ }
42
64
  /**
43
65
  * Simplify environment list data by keeping only essential fields for AI assistant
44
66
  * This reduces token consumption when returning environment lists via MCP tools
@@ -804,7 +826,13 @@ function resolveToolAuthOptions(server, overrides) {
804
826
  function registerEnvTools(server) {
805
827
  // 获取 cloudBaseOptions,如果没有则为 undefined
806
828
  const cloudBaseOptions = server.cloudBaseOptions;
807
- const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions, mcpServer: server });
829
+ // Default: env-scoped tools need a bound envId (domains / domain management use SDK this.envId).
830
+ // manageEnv create/listPackages are account-level — use requireEnvId: false there only.
831
+ const getManager = (options) => (0, cloudbase_manager_js_1.getCloudBaseManager)({
832
+ cloudBaseOptions,
833
+ requireEnvId: options?.requireEnvId ?? true,
834
+ mcpServer: server,
835
+ });
808
836
  const getManagerForEnvQuery = (targetEnvId, requireEnvId = true) => (0, cloudbase_manager_js_1.getCloudBaseManager)({
809
837
  cloudBaseOptions: targetEnvId && targetEnvId !== cloudBaseOptions?.envId
810
838
  ? {
@@ -1685,14 +1713,10 @@ function registerEnvTools(server) {
1685
1713
  .string()
1686
1714
  .optional()
1687
1715
  .describe("套餐 ID(action=create/modifyPlan 时必填)。可选值如 baas_personal(个人版)、baas_pf_standard(标准版)、baas_pf_enterprise(企业版)"),
1688
- region: zod_1.z
1689
- .string()
1690
- .optional()
1691
- .describe("环境地域(action=create 时可选)。默认 ap-shanghai,可选 ap-guangzhou、ap-beijing 等"),
1692
1716
  resources: zod_1.z
1693
- .array(zod_1.z.enum(["flexdb", "storage", "function", "postgresql"]))
1717
+ .array(zod_1.z.enum(exports.CREATE_ENV_RESOURCE_VALUES))
1694
1718
  .optional()
1695
- .describe("启用的资源类型(action=create 时可选)。默认启用全部四项:flexdb(文档数据库)、storage(存储)、function(云函数)、postgresql(PostgreSQL 数据库)"),
1719
+ .describe("启用的资源类型(action=create 时可选)。省略时默认全部四项:flexdb(文档数据库)、storage(存储)、function(云函数)、postgresql(PostgreSQL)。CreateEnv 要求 Resources 非空,MCP 会始终下发该字段。"),
1696
1720
  duration: zod_1.z
1697
1721
  .number()
1698
1722
  .int()
@@ -1720,17 +1744,19 @@ function registerEnvTools(server) {
1720
1744
  const action = rawArgs.action ?? "";
1721
1745
  const alias = normalizeOptionalToolString(rawArgs.alias);
1722
1746
  const packageId = normalizeOptionalToolString(rawArgs.packageId);
1723
- const region = normalizeOptionalToolString(rawArgs.region) ?? "ap-shanghai";
1724
- const resources = rawArgs.resources;
1747
+ const resolvedResources = resolveCreateEnvResources(rawArgs.resources);
1725
1748
  const duration = rawArgs.duration ?? 1;
1726
1749
  const envId = normalizeOptionalToolString(rawArgs.envId);
1727
1750
  const confirmed = rawArgs.confirm === "yes";
1728
1751
  try {
1729
- const cloudbase = await getManager();
1752
+ const cloudbase = await getManager({ requireEnvId: false });
1730
1753
  switch (action) {
1731
1754
  case "listPackages": {
1732
- // 查询可选套餐列表(新购场景)
1733
- const result = await cloudbase.env.describeBaasPackageList({ TargetAction: "new" });
1755
+ // Query packages for new purchase. Source aligns with billing-side Baas package APIs.
1756
+ const result = await cloudbase.env.describeBaasPackageList({
1757
+ TargetAction: "new",
1758
+ Source: "qcloud",
1759
+ });
1734
1760
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
1735
1761
  return (0, tool_result_js_1.buildJsonToolResult)({
1736
1762
  ok: true,
@@ -1740,12 +1766,13 @@ function registerEnvTools(server) {
1740
1766
  });
1741
1767
  }
1742
1768
  case "create": {
1743
- // 创建环境需要 confirm
1769
+ // CreateEnv (Manager SDK / Cloud API) accepts Alias, PackageId, Resources, Period, etc.
1770
+ // It does NOT accept Region — region is determined by account/package, not this call.
1744
1771
  if (!confirmed) {
1745
1772
  return (0, tool_result_js_1.buildJsonToolResult)({
1746
1773
  ok: false,
1747
1774
  code: "CONFIRM_REQUIRED",
1748
- message: `创建环境需要您确认。请确认以下配置信息后传入 confirm="yes":\n别名: ${alias}\n套餐: ${packageId}\n地域: ${region}\n资源类型: ${resources?.join(", ") ?? "flexdb, storage, function, postgresql"}\n时长: ${duration} 个月`,
1775
+ message: `创建环境需要您确认。请确认以下配置信息后传入 confirm="yes":\n别名: ${alias}\n套餐: ${packageId}\n资源类型: ${resolvedResources.join(", ")}\n时长: ${duration} 个月\n说明: CreateEnv 不接受 Region;环境地域由账号与套餐侧决定。`,
1749
1776
  next_step: {
1750
1777
  tool: "manageEnv",
1751
1778
  action: "create",
@@ -1762,12 +1789,9 @@ function registerEnvTools(server) {
1762
1789
  const createParams = {
1763
1790
  Alias: alias,
1764
1791
  PackageId: packageId,
1765
- Region: region,
1792
+ Resources: resolvedResources,
1766
1793
  Period: duration,
1767
1794
  };
1768
- if (resources && resources.length > 0) {
1769
- createParams.Resources = resources;
1770
- }
1771
1795
  const result = await cloudbase.env.createEnv(createParams);
1772
1796
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
1773
1797
  return (0, tool_result_js_1.buildJsonToolResult)({
@@ -1775,6 +1799,7 @@ function registerEnvTools(server) {
1775
1799
  code: "ENV_CREATED",
1776
1800
  message: `环境创建成功!新环境 ID: ${result.EnvId}。环境初始化可能需要几分钟,请通过 queryEnv(action="info", envId="${result.EnvId}") 轮询直到 Status 为正常。`,
1777
1801
  envId: result.EnvId,
1802
+ resources: resolvedResources,
1778
1803
  });
1779
1804
  }
1780
1805
  case "modifyPlan": {
@@ -1852,11 +1877,19 @@ function registerEnvTools(server) {
1852
1877
  if (toolPayloadResult) {
1853
1878
  return toolPayloadResult;
1854
1879
  }
1880
+ const errorMessage = error instanceof Error ? error.message : String(error);
1881
+ if (action === "listPackages" && /billTags/i.test(errorMessage)) {
1882
+ return (0, tool_result_js_1.buildJsonToolResult)({
1883
+ ok: false,
1884
+ code: "PACKAGE_LIST_FAILED",
1885
+ message: `查询套餐列表失败(计费标签 BillTags):${errorMessage}。可改用控制台查看套餐,或直接使用已知 packageId(如 baas_personal)调用 manageEnv(action="create")。`,
1886
+ });
1887
+ }
1855
1888
  return {
1856
1889
  content: [
1857
1890
  {
1858
1891
  type: "text",
1859
- text: `环境管理操作失败: ${error instanceof Error ? error.message : String(error)}`,
1892
+ text: `环境管理操作失败: ${errorMessage}`,
1860
1893
  },
1861
1894
  ],
1862
1895
  };
@@ -2200,7 +2233,7 @@ ${envIdSection}
2200
2233
  ## 环境信息
2201
2234
  - 操作系统: ${os_1.default.type()} ${os_1.default.release()}
2202
2235
  - Node.js版本: ${process.version}
2203
- - MCP 版本:${process.env.npm_package_version || "2.23.11" || 0}
2236
+ - MCP 版本:${process.env.npm_package_version || "2.24.1" || 0}
2204
2237
  - 系统架构: ${os_1.default.arch()}
2205
2238
  - 时间: ${new Date().toISOString()}
2206
2239
  - 请求ID: ${requestId}
@@ -2281,7 +2314,7 @@ function createWrappedHandler(name, handler, server) {
2281
2314
  requestId: (typeof error === 'object' && error && 'requestId' in error) ? error.requestId : '',
2282
2315
  ide: server.ide || process.env.INTEGRATION_IDE || ''
2283
2316
  });
2284
- const mcpVersion = true ? "2.23.11" : 0;
2317
+ const mcpVersion = true ? "2.24.1" : 0;
2285
2318
  const requestIdSuffix = requestId ? `\n🆔 RequestId: ${requestId}` : '';
2286
2319
  const enhancedErrorMessage = `${errorMessage}${requestIdSuffix}\n\n📦 CloudBase MCP v${mcpVersion}\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
2287
2320
  // 创建新的错误对象,保持原有的错误类型但更新消息
@@ -2318,7 +2351,8 @@ function createWrappedHandler(name, handler, server) {
2318
2351
  error: errorMessage,
2319
2352
  inputParams: sanitizeArgs(args), // 添加入参上报
2320
2353
  cloudBaseOptions: cloudBaseOptions, // 传递 CloudBase 配置(可能已更新)
2321
- ide: server.ide || process.env.INTEGRATION_IDE // 传递集成IDE信息
2354
+ ide: server.ide || process.env.INTEGRATION_IDE, // 传递集成IDE信息
2355
+ mcpClientInfo: (0, telemetry_js_1.readMcpClientInfoFromServer)(server),
2322
2356
  }).catch(err => {
2323
2357
  // 静默处理上报错误,不影响主要功能
2324
2358
  (0, logger_js_1.debug)('遥测上报失败', { toolName: name, error: err instanceof Error ? err.message : String(err) });
@@ -9886,6 +9920,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
9886
9920
  };
9887
9921
  Object.defineProperty(exports, "__esModule", ({ value: true }));
9888
9922
  exports.reportToolkitLifecycle = exports.reportToolCall = exports.telemetryReporter = void 0;
9923
+ exports.extractMcpClientInfo = extractMcpClientInfo;
9924
+ exports.readMcpClientInfoFromServer = readMcpClientInfoFromServer;
9889
9925
  const crypto_1 = __importDefault(__webpack_require__(7823));
9890
9926
  const http_1 = __importDefault(__webpack_require__(3782));
9891
9927
  const https_1 = __importDefault(__webpack_require__(3871));
@@ -9937,7 +9973,7 @@ class TelemetryReporter {
9937
9973
  const nodeVersion = process.version; // Node.js版本
9938
9974
  const arch = os_1.default.arch(); // 系统架构
9939
9975
  // 从构建时注入的版本号获取MCP版本信息
9940
- const mcpVersion = process.env.npm_package_version || "2.23.11" || 0;
9976
+ const mcpVersion = process.env.npm_package_version || "2.24.1" || 0;
9941
9977
  return {
9942
9978
  userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
9943
9979
  deviceId: this.deviceId,
@@ -10119,6 +10155,49 @@ function resolveEnvId(cloudBaseOptions) {
10119
10155
  }
10120
10156
  return { envId, envIdSource };
10121
10157
  }
10158
+ /**
10159
+ * Normalize SDK Implementation / clientInfo into telemetry-safe fields.
10160
+ */
10161
+ function extractMcpClientInfo(clientVersion) {
10162
+ if (!clientVersion || typeof clientVersion !== "object") {
10163
+ return {};
10164
+ }
10165
+ const info = clientVersion;
10166
+ const name = typeof info.name === "string" ? info.name.trim() : "";
10167
+ const version = typeof info.version === "string" ? info.version.trim() : "";
10168
+ const title = typeof info.title === "string" ? info.title.trim() : "";
10169
+ return {
10170
+ ...(name ? { name } : {}),
10171
+ ...(version ? { version } : {}),
10172
+ ...(title ? { title } : {}),
10173
+ };
10174
+ }
10175
+ /**
10176
+ * Read MCP clientInfo from an McpServer after initialize.
10177
+ * Returns empty object when handshake has not completed or the accessor is unavailable.
10178
+ */
10179
+ function readMcpClientInfoFromServer(server) {
10180
+ try {
10181
+ return extractMcpClientInfo(server?.server?.getClientVersion?.());
10182
+ }
10183
+ catch {
10184
+ return {};
10185
+ }
10186
+ }
10187
+ function appendMcpClientInfoFields(eventData, clientInfo) {
10188
+ if (!clientInfo) {
10189
+ return;
10190
+ }
10191
+ if (clientInfo.name) {
10192
+ eventData.mcpClientName = clientInfo.name;
10193
+ }
10194
+ if (clientInfo.version) {
10195
+ eventData.mcpClientVersion = clientInfo.version;
10196
+ }
10197
+ if (clientInfo.title) {
10198
+ eventData.mcpClientTitle = clientInfo.title;
10199
+ }
10200
+ }
10122
10201
  // 便捷方法
10123
10202
  const reportToolCall = async (params) => {
10124
10203
  const { nodeVersion, osType, osRelease, arch, mcpVersion } = exports.telemetryReporter.getUserAgent();
@@ -10163,6 +10242,7 @@ const reportToolCall = async (params) => {
10163
10242
  if (params.ide) {
10164
10243
  eventData.ide = params.ide;
10165
10244
  }
10245
+ appendMcpClientInfoFields(eventData, params.mcpClientInfo);
10166
10246
  // Debug: 打印最终上报参数
10167
10247
  (0, logger_js_1.debug)('[telemetry] 工具调用上报参数', {
10168
10248
  toolName: params.toolName,
@@ -10205,6 +10285,7 @@ const reportToolkitLifecycle = async (params) => {
10205
10285
  if (params.ide) {
10206
10286
  eventData.ide = params.ide;
10207
10287
  }
10288
+ appendMcpClientInfoFields(eventData, params.mcpClientInfo);
10208
10289
  // Debug: 打印最终上报参数
10209
10290
  (0, logger_js_1.debug)('[telemetry] 生命周期事件上报参数', {
10210
10291
  event: params.event,
@@ -11778,6 +11859,38 @@ function classifySqlRisk(sql) {
11778
11859
  requiresConfirm: true,
11779
11860
  };
11780
11861
  }
11862
+ /**
11863
+ * Schema DDL that must go through applyMigration by default.
11864
+ * Includes CREATE/ALTER/COMMENT (schema_change) and DROP/TRUNCATE / destructive ALTER.
11865
+ * Excludes DML DELETE and security_change (GRANT/POLICY), which may still use execute.
11866
+ */
11867
+ function isSchemaDdlRisk(risk, sql) {
11868
+ if (risk === "schema_change") {
11869
+ return true;
11870
+ }
11871
+ if (risk !== "destructive") {
11872
+ return false;
11873
+ }
11874
+ const verb = getSqlVerb(stripLeadingSqlComments(sql));
11875
+ return ["DROP", "TRUNCATE", "ALTER"].includes(verb);
11876
+ }
11877
+ function buildLocalMigrationFileHint(version, name) {
11878
+ return `migrations/${version}_${name}.sql`;
11879
+ }
11880
+ const MIGRATION_VERSION_REQUIRED_MESSAGE = "migrationVersion is required (14-digit UTC timestamp YYYYMMDDHHMMSS). " +
11881
+ "Decide the version first, write local file migrations/<version>_<migrationName>.sql, " +
11882
+ "then call planMigration/applyMigration with the same migrationVersion and migrationName.";
11883
+ function requireExplicitMigrationVersion(args, actionLabel) {
11884
+ const version = args.migrationVersion?.trim();
11885
+ if (!version) {
11886
+ return buildPgToolResult({
11887
+ success: false,
11888
+ errorCode: "MIGRATION_VERSION_REQUIRED",
11889
+ message: `Provide migrationVersion when action=${actionLabel}. ${MIGRATION_VERSION_REQUIRED_MESSAGE}`,
11890
+ });
11891
+ }
11892
+ return version;
11893
+ }
11781
11894
  function quoteIdentifier(identifier) {
11782
11895
  return `"${identifier.replace(/"/g, '""')}"`;
11783
11896
  }
@@ -12142,12 +12255,6 @@ async function executeManagerPGSql(context, sql, cloudBaseOptions) {
12142
12255
  }
12143
12256
  throw new Error("Current @cloudbase/manager-node runtime does not expose database.executePGSql or commonService fallback. Upgrade to @cloudbase/manager-node >= 5.4.0.");
12144
12257
  }
12145
- /** Generate a 14-digit migration version (YYYYMMDDHHMMSS) from current time */
12146
- function generateMigrationVersion() {
12147
- const d = new Date();
12148
- const p = (n) => String(n).padStart(2, "0");
12149
- return `${d.getFullYear()}${p(d.getMonth() + 1)}${p(d.getDate())}${p(d.getHours())}${p(d.getMinutes())}${p(d.getSeconds())}`;
12150
- }
12151
12258
  /** Call a CloudBase PG migration API via commonService fallback */
12152
12259
  async function callPgMigrationApi(context, action, params, cloudBaseOptions) {
12153
12260
  const manager = await (0, cloudbase_manager_js_1.getCloudBaseManager)({
@@ -12390,6 +12497,34 @@ async function handleExecuteSql(args, context, deps) {
12390
12497
  });
12391
12498
  }
12392
12499
  const classification = classifySqlRisk(args.sql);
12500
+ if (isSchemaDdlRisk(classification.risk, args.sql) && args.allowDdlViaExecute !== true) {
12501
+ return buildPgToolResult({
12502
+ success: false,
12503
+ errorCode: "DDL_USE_APPLY_MIGRATION",
12504
+ message: "Schema DDL (CREATE/ALTER/DROP/TRUNCATE/...) must use applyMigration with an explicit migrationVersion, " +
12505
+ "not execute. Write local migrations/<version>_<name>.sql first, then call applyMigration. " +
12506
+ "Set allowDdlViaExecute=true only for exceptional one-off ops that intentionally bypass migration history.",
12507
+ data: {
12508
+ classification,
12509
+ localFileHintPattern: "migrations/<migrationVersion>_<migrationName>.sql",
12510
+ },
12511
+ nextActions: [
12512
+ buildNextAction(MANAGE_PG_DATABASE, "planMigration", "Preview the schema change as a versioned migration (provide migrationVersion + migrationName).", {
12513
+ action: "planMigration",
12514
+ migrationName: "describe_your_change",
12515
+ migrationVersion: "YYYYMMDDHHMMSS",
12516
+ sql: args.sql,
12517
+ }),
12518
+ buildNextAction(MANAGE_PG_DATABASE, "applyMigration", "Apply the schema change via PushPGUserMigrations with the same explicit migrationVersion.", {
12519
+ action: "applyMigration",
12520
+ migrationName: "describe_your_change",
12521
+ migrationVersion: "YYYYMMDDHHMMSS",
12522
+ sql: args.sql,
12523
+ confirm: true,
12524
+ }),
12525
+ ],
12526
+ });
12527
+ }
12393
12528
  if (!classification.readOnly && !args.confirm) {
12394
12529
  return buildPgToolResult({
12395
12530
  success: false,
@@ -12399,7 +12534,12 @@ async function handleExecuteSql(args, context, deps) {
12399
12534
  classification,
12400
12535
  },
12401
12536
  nextActions: [
12402
- buildNextAction(MANAGE_PG_DATABASE, "execute", "Re-issue with confirm=true to actually run the SQL.", { action: "execute", sql: args.sql, confirm: true }),
12537
+ buildNextAction(MANAGE_PG_DATABASE, "execute", "Re-issue with confirm=true to actually run the SQL.", {
12538
+ action: "execute",
12539
+ sql: args.sql,
12540
+ confirm: true,
12541
+ ...(args.allowDdlViaExecute === true ? { allowDdlViaExecute: true } : {}),
12542
+ }),
12403
12543
  buildNextAction(QUERY_PG_DATABASE, "metadata", "Inspect table size and shape before destructive changes.", { action: "metadata", limit: 20 }),
12404
12544
  ],
12405
12545
  });
@@ -12416,8 +12556,15 @@ async function handleExecuteSql(args, context, deps) {
12416
12556
  .slice(0, 5)
12417
12557
  .map((row) => serializeValue(row)),
12418
12558
  targetTable: targetTable ?? null,
12559
+ ...(args.allowDdlViaExecute === true && isSchemaDdlRisk(classification.risk, args.sql)
12560
+ ? {
12561
+ warning: "DDL executed via allowDdlViaExecute bypasses migration history. Prefer applyMigration for reproducible schema changes.",
12562
+ }
12563
+ : {}),
12419
12564
  },
12420
- message: "Write SQL executed successfully.",
12565
+ message: args.allowDdlViaExecute === true && isSchemaDdlRisk(classification.risk, args.sql)
12566
+ ? "Write SQL executed successfully (DDL via allowDdlViaExecute; migration history was bypassed)."
12567
+ : "Write SQL executed successfully.",
12421
12568
  nextActions: classification.risk === "schema_change" && targetTable
12422
12569
  ? [
12423
12570
  buildNextAction(QUERY_PG_DATABASE, "schema", "Inspect the table schema, columns, indexes, and RLS policies after schema changes.", {
@@ -12445,6 +12592,43 @@ async function handleDryRun(args) {
12445
12592
  });
12446
12593
  }
12447
12594
  const classification = classifySqlRisk(args.sql);
12595
+ const schemaDdl = isSchemaDdlRisk(classification.risk, args.sql);
12596
+ if (classification.readOnly) {
12597
+ return buildPgToolResult({
12598
+ success: true,
12599
+ data: {
12600
+ classification,
12601
+ wouldExecute: false,
12602
+ sqlPreview: args.sql.trim().slice(0, 500),
12603
+ },
12604
+ message: "SQL dry run completed. This statement is read-only; use queryPgDatabase(action=sql) to execute it.",
12605
+ nextActions: [
12606
+ buildNextAction(QUERY_PG_DATABASE, "sql", "Execute the read-only SQL through queryPgDatabase.", { action: "sql", sql: args.sql, limit: 20 }),
12607
+ ],
12608
+ });
12609
+ }
12610
+ if (schemaDdl) {
12611
+ return buildPgToolResult({
12612
+ success: true,
12613
+ data: {
12614
+ classification,
12615
+ wouldExecute: false,
12616
+ sqlPreview: args.sql.trim().slice(0, 500),
12617
+ preferredAction: "applyMigration",
12618
+ localFileHintPattern: "migrations/<migrationVersion>_<migrationName>.sql",
12619
+ },
12620
+ message: "SQL dry run completed. Schema DDL should use applyMigration with an explicit migrationVersion, not execute.",
12621
+ nextActions: [
12622
+ buildNextAction(MANAGE_PG_DATABASE, "applyMigration", "Apply schema DDL via versioned migration (provide migrationVersion + migrationName + confirm=true).", {
12623
+ action: "applyMigration",
12624
+ migrationName: "describe_your_change",
12625
+ migrationVersion: "YYYYMMDDHHMMSS",
12626
+ sql: args.sql,
12627
+ confirm: true,
12628
+ }),
12629
+ ],
12630
+ });
12631
+ }
12448
12632
  return buildPgToolResult({
12449
12633
  success: true,
12450
12634
  data: {
@@ -12452,15 +12636,9 @@ async function handleDryRun(args) {
12452
12636
  wouldExecute: false,
12453
12637
  sqlPreview: args.sql.trim().slice(0, 500),
12454
12638
  },
12455
- message: classification.readOnly
12456
- ? "SQL dry run completed. This statement is read-only; use queryPgDatabase(action=sql) to execute it."
12457
- : "SQL dry run completed. Write SQL requires managePgDatabase(action=execute, confirm=true).",
12639
+ message: "SQL dry run completed. Write SQL requires managePgDatabase(action=execute, confirm=true).",
12458
12640
  nextActions: [
12459
- buildNextAction(classification.readOnly ? QUERY_PG_DATABASE : MANAGE_PG_DATABASE, classification.readOnly ? "sql" : "execute", classification.readOnly
12460
- ? "Execute the read-only SQL through queryPgDatabase."
12461
- : "Execute the write SQL only after explicit confirmation.", classification.readOnly
12462
- ? { action: "sql", sql: args.sql, limit: 20 }
12463
- : { action: "execute", sql: args.sql, confirm: true }),
12641
+ buildNextAction(MANAGE_PG_DATABASE, "execute", "Execute the write SQL only after explicit confirmation.", { action: "execute", sql: args.sql, confirm: true }),
12464
12642
  ],
12465
12643
  });
12466
12644
  }
@@ -12479,7 +12657,12 @@ async function handlePlanMigration(args, context, deps, cloudBaseOptions) {
12479
12657
  message: "Provide migrationName (lowercase letters, digits and underscores, starting with a letter) when action=planMigration.",
12480
12658
  });
12481
12659
  }
12482
- const version = args.migrationVersion?.trim() || generateMigrationVersion();
12660
+ const versionOrError = requireExplicitMigrationVersion(args, "planMigration");
12661
+ if (typeof versionOrError !== "string") {
12662
+ return versionOrError;
12663
+ }
12664
+ const version = versionOrError;
12665
+ const localFileHint = buildLocalMigrationFileHint(version, args.migrationName);
12483
12666
  const migration = {
12484
12667
  Version: version,
12485
12668
  Name: args.migrationName,
@@ -12494,10 +12677,22 @@ async function handlePlanMigration(args, context, deps, cloudBaseOptions) {
12494
12677
  }, cloudBaseOptions);
12495
12678
  return buildPgToolResult({
12496
12679
  success: true,
12497
- data: result,
12498
- message: "Migration plan generated via PreviewPGUserMigrations.",
12680
+ data: {
12681
+ migrationVersion: version,
12682
+ migrationName: args.migrationName,
12683
+ localFileHint,
12684
+ apiResult: result,
12685
+ },
12686
+ message: `Migration plan generated via PreviewPGUserMigrations. Reuse migrationVersion=${version} on applyMigration. Ensure local file ${localFileHint} exists and matches.`,
12499
12687
  nextActions: [
12500
- buildNextAction(MANAGE_PG_DATABASE, "applyMigration", "Review the plan above. If it looks correct, call applyMigration to execute.", { action: "applyMigration", migrationName: args.migrationName, sql: args.sql, confirm: true, ...(args.rollbackSql ? { rollbackSql: args.rollbackSql } : {}) }),
12688
+ buildNextAction(MANAGE_PG_DATABASE, "applyMigration", "Review the plan above. If it looks correct, call applyMigration with the same migrationVersion.", {
12689
+ action: "applyMigration",
12690
+ migrationName: args.migrationName,
12691
+ migrationVersion: version,
12692
+ sql: args.sql,
12693
+ confirm: true,
12694
+ ...(args.rollbackSql ? { rollbackSql: args.rollbackSql } : {}),
12695
+ }),
12501
12696
  ],
12502
12697
  });
12503
12698
  }
@@ -12531,7 +12726,12 @@ async function handleApplyMigration(args, context, deps, cloudBaseOptions) {
12531
12726
  message: "PushPGUserMigrations requires confirm=true. Run with confirm=true to proceed.",
12532
12727
  });
12533
12728
  }
12534
- const version = args.migrationVersion?.trim() || generateMigrationVersion();
12729
+ const versionOrError = requireExplicitMigrationVersion(args, "applyMigration");
12730
+ if (typeof versionOrError !== "string") {
12731
+ return versionOrError;
12732
+ }
12733
+ const version = versionOrError;
12734
+ const localFileHint = buildLocalMigrationFileHint(version, args.migrationName);
12535
12735
  const migration = {
12536
12736
  Version: version,
12537
12737
  Name: args.migrationName,
@@ -12551,8 +12751,16 @@ async function handleApplyMigration(args, context, deps, cloudBaseOptions) {
12551
12751
  const result = await callPgMigrationApi(context, "PushPGUserMigrations", params, cloudBaseOptions);
12552
12752
  return buildPgToolResult({
12553
12753
  success: true,
12554
- data: result,
12555
- message: "Migrations applied via PushPGUserMigrations.",
12754
+ data: {
12755
+ migrationVersion: version,
12756
+ migrationName: args.migrationName,
12757
+ localFileHint,
12758
+ apiResult: result,
12759
+ },
12760
+ message: `Migrations applied via PushPGUserMigrations. Ensure local file ${localFileHint} exists and matches.`,
12761
+ nextActions: [
12762
+ buildNextAction(MANAGE_PG_DATABASE, "listMigrations", "Verify the remote migration history records the same migrationVersion.", { action: "listMigrations", limit: 20 }),
12763
+ ],
12556
12764
  });
12557
12765
  }
12558
12766
  catch (error) {
@@ -12827,11 +13035,11 @@ function registerPGDatabaseTools(server, providedDeps) {
12827
13035
  });
12828
13036
  server.registerTool?.(MANAGE_PG_DATABASE, {
12829
13037
  title: "管理 PostgreSQL 上下文或执行写入 SQL",
12830
- description: "管理 CloudBase PostgreSQL:执行已确认的写入 SQL 或 DDL、SQL 风险预检、迁移管理。建表、改 schema、数据结构变更优先使用 applyMigration(带版本管理与回滚),而非 execute。",
13038
+ description: "管理 CloudBase PostgreSQL:执行已确认的写入 SQL、SQL 风险预检、迁移管理。建表/ALTER/DROP 等 schema 变更必须使用 applyMigration(显式 migrationVersion + 本地 migrations/ 留档),不要默认用 execute。execute 主要用于 DML 与 GRANT/RLS 等运维 SQL。",
12831
13039
  inputSchema: {
12832
13040
  action: zod_1.z
12833
13041
  .enum(MANAGE_ACTIONS)
12834
- .describe("操作类型:execute=执行已确认的写入 SQL 或 DDL;dryRun=只分析 SQL 风险不执行;planMigration=预览迁移计划(需 migrationName + sql);applyMigration=应用迁移,建表/改 schema 首选(需 migrationName + sql + confirm=true);listMigrations=查询已应用的 Migration 列表(可传 limit/offset 分页);migrationDetail=查看单条 Migration 详情(需 migrationVersion);rollbackMigration=回滚最近 N 条 Migration(需 lastN + confirm=true);repairMigration=修复 Migration 历史记录(需 migrationVersion + migrationName + repairStatus + repairReason)"),
13042
+ .describe("操作类型:execute=执行已确认的写入 SQL(DML/GRANT/RLS;schema DDL 默认拒绝,需 allowDdlViaExecute=true);dryRun=只分析 SQL 风险不执行;planMigration=预览迁移计划(需 migrationName + migrationVersion + sql);applyMigration=应用迁移,建表/改 schema 首选(需 migrationName + migrationVersion + sql + confirm=true);listMigrations=查询已应用的 Migration 列表(可传 limit/offset 分页);migrationDetail=查看单条 Migration 详情(需 migrationVersion);rollbackMigration=回滚最近 N 条 Migration(需 lastN + confirm=true);repairMigration=修复 Migration 历史记录(需 migrationVersion + migrationName + repairStatus + repairReason)"),
12835
13043
  sql: zod_1.z
12836
13044
  .string()
12837
13045
  .optional()
@@ -12869,7 +13077,7 @@ function registerPGDatabaseTools(server, providedDeps) {
12869
13077
  .string()
12870
13078
  .regex(/^\d{14}$/)
12871
13079
  .optional()
12872
- .describe("14 位时间戳 YYYYMMDDHHMMSS。detail/repair 必填;plan/apply 不传时自动生成。"),
13080
+ .describe("14 位时间戳 YYYYMMDDHHMMSS。plan/apply/detail/repair 必填;禁止由服务端静默生成,避免与本地 migrations/<version>_<name>.sql 分叉。"),
12873
13081
  rollbackSql: zod_1.z
12874
13082
  .string()
12875
13083
  .optional()
@@ -12911,6 +13119,10 @@ function registerPGDatabaseTools(server, providedDeps) {
12911
13119
  .string()
12912
13120
  .optional()
12913
13121
  .describe("repair 必填:修复原因。"),
13122
+ allowDdlViaExecute: zod_1.z
13123
+ .boolean()
13124
+ .optional()
13125
+ .describe("可选,默认 false。仅当需要故意绕过 migration history 时设为 true,才允许 schema DDL 走 execute;正常建表/改 schema 必须用 applyMigration。"),
12914
13126
  },
12915
13127
  annotations: {
12916
13128
  readOnlyHint: false,
@@ -12924,6 +13136,14 @@ function registerPGDatabaseTools(server, providedDeps) {
12924
13136
  const cbOpts = server.cloudBaseOptions;
12925
13137
  switch (args.action) {
12926
13138
  case "execute": {
13139
+ // Soft-block schema DDL before readiness probe so agents get migration guidance immediately.
13140
+ if (args.sql?.trim()) {
13141
+ const classification = classifySqlRisk(args.sql);
13142
+ if (isSchemaDdlRisk(classification.risk, args.sql) &&
13143
+ args.allowDdlViaExecute !== true) {
13144
+ return handleExecuteSql(args, context, deps);
13145
+ }
13146
+ }
12927
13147
  try {
12928
13148
  await ensurePgReadyOnce(cbOpts, deps);
12929
13149
  }
@@ -14055,7 +14275,7 @@ function registerSetupTools(server) {
14055
14275
  server.registerTool("downloadTemplate", {
14056
14276
  title: "下载项目模板",
14057
14277
  description: `自动下载并部署CloudBase项目模板。
14058
- **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.23.11" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
14278
+ **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.24.1" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
14059
14279
  inputSchema: {
14060
14280
  template: zod_1.z
14061
14281
  .enum(["react", "vue", "miniprogram", "uniapp", "rules"])
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudbase/cloudbase-mcp",
3
- "version": "2.23.11",
3
+ "version": "2.24.1",
4
4
  "packageManager": "pnpm@10.8.0",
5
5
  "description": "腾讯云开发 MCP Server,通过AI提示词和MCP协议+云开发,让开发更智能、更高效,当你在Cursor/ VSCode GitHub Copilot/WinSurf/CodeBuddy/Augment Code/Claude Code等AI编程工具里写代码时,它能自动帮你生成可直接部署的前后端应用+小程序,并一键发布到腾讯云开发 CloudBase。",
6
6
  "main": "./dist/index.cjs",
@@ -100,6 +100,12 @@
100
100
  "webpack-obfuscator": "^3.6.0"
101
101
  },
102
102
  "overrides": {
103
- "tough-cookie": "^4.1.3"
103
+ "tough-cookie": "^4.1.3",
104
+ "js-yaml": "4.3.0",
105
+ "brace-expansion@1": "1.1.16",
106
+ "brace-expansion@2": "2.1.2",
107
+ "brace-expansion@3": "5.0.7",
108
+ "brace-expansion@4": "5.0.7",
109
+ "brace-expansion@5": "5.0.7"
104
110
  }
105
111
  }