@cloudbase/cloudbase-mcp 2.25.4 → 2.25.5
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/cli.cjs +2 -2
- package/dist/index.cjs +320 -38
- package/dist/index.js +320 -38
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2772,7 +2772,7 @@ ${envIdSection}
|
|
|
2772
2772
|
## 环境信息
|
|
2773
2773
|
- 操作系统: ${os_1.default.type()} ${os_1.default.release()}
|
|
2774
2774
|
- Node.js版本: ${process.version}
|
|
2775
|
-
- MCP 版本:${process.env.npm_package_version || "2.25.
|
|
2775
|
+
- MCP 版本:${process.env.npm_package_version || "2.25.5" || 0}
|
|
2776
2776
|
- 系统架构: ${os_1.default.arch()}
|
|
2777
2777
|
- 时间: ${new Date().toISOString()}
|
|
2778
2778
|
- 请求ID: ${requestId}
|
|
@@ -2853,7 +2853,7 @@ function createWrappedHandler(name, handler, server) {
|
|
|
2853
2853
|
requestId: (typeof error === 'object' && error && 'requestId' in error) ? error.requestId : '',
|
|
2854
2854
|
ide: server.ide || process.env.INTEGRATION_IDE || ''
|
|
2855
2855
|
});
|
|
2856
|
-
const mcpVersion = true ? "2.25.
|
|
2856
|
+
const mcpVersion = true ? "2.25.5" : 0;
|
|
2857
2857
|
const requestIdSuffix = requestId ? `\n🆔 RequestId: ${requestId}` : '';
|
|
2858
2858
|
const enhancedErrorMessage = `${errorMessage}${requestIdSuffix}\n\n📦 CloudBase MCP v${mcpVersion}\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
|
|
2859
2859
|
// 创建新的错误对象,保持原有的错误类型但更新消息
|
|
@@ -7960,7 +7960,11 @@ function registerGatewayTools(server) {
|
|
|
7960
7960
|
return result ?? {};
|
|
7961
7961
|
};
|
|
7962
7962
|
const buildPrivilegeDescription = (privilege) => {
|
|
7963
|
-
const serviceStatus = privilege.EnableService ===
|
|
7963
|
+
const serviceStatus = privilege.EnableService === undefined
|
|
7964
|
+
? "未知"
|
|
7965
|
+
: privilege.EnableService === true
|
|
7966
|
+
? "已开启"
|
|
7967
|
+
: "未开启";
|
|
7964
7968
|
const authStatus = privilege.EnableAuth === undefined
|
|
7965
7969
|
? "未知"
|
|
7966
7970
|
: privilege.EnableAuth === true
|
|
@@ -8300,12 +8304,28 @@ function registerGatewayTools(server) {
|
|
|
8300
8304
|
if (!input.domain || !input.certificateId) {
|
|
8301
8305
|
throw new Error("action=bindCustomDomain 时必须提供 domain 和 certificateId");
|
|
8302
8306
|
}
|
|
8307
|
+
const accessType = input.accessType ?? "DIRECT";
|
|
8308
|
+
if (accessType === "CUSTOM" && !input.customCname) {
|
|
8309
|
+
throw new Error("action=bindCustomDomain 且 accessType=CUSTOM 时必须提供 customCname(自有 CDN/WAF 的回源/回填地址,不是 DNS 解析目标)。" +
|
|
8310
|
+
"说明:https://docs.cloudbase.net/service/custom-domain");
|
|
8311
|
+
}
|
|
8312
|
+
if (accessType !== "CUSTOM" && input.customCname) {
|
|
8313
|
+
throw new Error("customCname 仅在 accessType=CUSTOM 时可用;普通绑定用默认 DIRECT,不要传 customCname。" +
|
|
8314
|
+
"说明:https://docs.cloudbase.net/service/custom-domain");
|
|
8315
|
+
}
|
|
8303
8316
|
const cloudbase = await getManager();
|
|
8304
8317
|
const result = await cloudbase.env.bindCustomDomain({
|
|
8305
8318
|
EnvId: await resolveEnvId(),
|
|
8306
8319
|
Domain: {
|
|
8307
8320
|
Domain: input.domain,
|
|
8308
8321
|
CertId: input.certificateId,
|
|
8322
|
+
AccessType: accessType,
|
|
8323
|
+
...(input.enable !== undefined
|
|
8324
|
+
? { Enable: input.enable }
|
|
8325
|
+
: {}),
|
|
8326
|
+
...(input.customCname
|
|
8327
|
+
? { CustomCname: input.customCname }
|
|
8328
|
+
: {}),
|
|
8309
8329
|
},
|
|
8310
8330
|
});
|
|
8311
8331
|
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
@@ -8313,24 +8333,64 @@ function registerGatewayTools(server) {
|
|
|
8313
8333
|
action: input.action,
|
|
8314
8334
|
domain: input.domain,
|
|
8315
8335
|
certificateId: input.certificateId,
|
|
8336
|
+
accessType,
|
|
8337
|
+
...(input.customCname
|
|
8338
|
+
? { customCname: input.customCname }
|
|
8339
|
+
: {}),
|
|
8340
|
+
...(input.enable !== undefined
|
|
8341
|
+
? { enable: input.enable }
|
|
8342
|
+
: {}),
|
|
8316
8343
|
raw: result,
|
|
8317
|
-
},
|
|
8344
|
+
}, `自定义域名绑定成功(${accessType})`, [
|
|
8345
|
+
{
|
|
8346
|
+
tool: "manageGateway",
|
|
8347
|
+
action: "createRoute",
|
|
8348
|
+
reason: "绑定后需 createRoute 添加访问路径,并完成 DNS CNAME 解析后才可访问",
|
|
8349
|
+
},
|
|
8350
|
+
]);
|
|
8318
8351
|
}
|
|
8319
8352
|
case "deleteCustomDomain": {
|
|
8320
8353
|
if (!input.domain) {
|
|
8321
8354
|
throw new Error("action=deleteCustomDomain 时必须提供 domain");
|
|
8322
8355
|
}
|
|
8323
8356
|
const cloudbase = await getManager();
|
|
8324
|
-
|
|
8325
|
-
|
|
8326
|
-
|
|
8327
|
-
|
|
8328
|
-
|
|
8329
|
-
|
|
8330
|
-
|
|
8331
|
-
|
|
8332
|
-
|
|
8333
|
-
|
|
8357
|
+
try {
|
|
8358
|
+
const result = await cloudbase.env.deleteCustomDomain({
|
|
8359
|
+
EnvId: await resolveEnvId(),
|
|
8360
|
+
Domain: input.domain,
|
|
8361
|
+
});
|
|
8362
|
+
(0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
|
|
8363
|
+
return buildEnvelope({
|
|
8364
|
+
action: input.action,
|
|
8365
|
+
domain: input.domain,
|
|
8366
|
+
raw: result,
|
|
8367
|
+
}, "自定义域名删除成功");
|
|
8368
|
+
}
|
|
8369
|
+
catch (error) {
|
|
8370
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
8371
|
+
// SDK 在域名下仍有路由绑定时抛错,需先删路由再删域名
|
|
8372
|
+
if (/route binding/i.test(message)) {
|
|
8373
|
+
return {
|
|
8374
|
+
success: false,
|
|
8375
|
+
data: { action: input.action, domain: input.domain },
|
|
8376
|
+
message: `域名 ${input.domain} 下仍有路由绑定,需先删除路由再删除域名。` +
|
|
8377
|
+
`原始错误:${message}`,
|
|
8378
|
+
nextActions: [
|
|
8379
|
+
{
|
|
8380
|
+
tool: "queryGateway",
|
|
8381
|
+
action: "listRoutes",
|
|
8382
|
+
reason: "查看该域名下的路由,确认需要删除的路径",
|
|
8383
|
+
},
|
|
8384
|
+
{
|
|
8385
|
+
tool: "manageGateway",
|
|
8386
|
+
action: "deleteRoute",
|
|
8387
|
+
reason: "先删除该域名下的全部路由(逐个 deleteRoute),再重试 deleteCustomDomain",
|
|
8388
|
+
},
|
|
8389
|
+
],
|
|
8390
|
+
};
|
|
8391
|
+
}
|
|
8392
|
+
throw error;
|
|
8393
|
+
}
|
|
8334
8394
|
}
|
|
8335
8395
|
case "enableService": {
|
|
8336
8396
|
if (typeof input.enable !== "boolean") {
|
|
@@ -8443,7 +8503,8 @@ function registerGatewayTools(server) {
|
|
|
8443
8503
|
"enableService/authSwitch 开关 HTTP 网关总开关与访问鉴权(需配合 enable 参数)。" +
|
|
8444
8504
|
"createRoute/updateRoute 必须提供 upstreamResourceType。" +
|
|
8445
8505
|
"已有自定义域名时优先 createRoute(domain=已有域名) 实现访问,不必再次 bindCustomDomain / 传入 certificateId;" +
|
|
8446
|
-
"bindCustomDomain
|
|
8506
|
+
"bindCustomDomain 仅用于首次绑定新域名(需 certificateId;可选 accessType=DIRECT|CDN|CUSTOM,CUSTOM 需 customCname;普通场景用默认 DIRECT)。" +
|
|
8507
|
+
"接入说明:https://docs.cloudbase.net/service/custom-domain"),
|
|
8447
8508
|
targetName: zod_1.z
|
|
8448
8509
|
.string()
|
|
8449
8510
|
.optional()
|
|
@@ -8506,11 +8567,24 @@ function registerGatewayTools(server) {
|
|
|
8506
8567
|
.optional()
|
|
8507
8568
|
.describe("证书 ID。仅首次 bindCustomDomain 必填。" +
|
|
8508
8569
|
"在已有自定义域名上 createRoute / updateRoute / deleteRoute 不需要 certificateId。"),
|
|
8570
|
+
accessType: zod_1.z
|
|
8571
|
+
.enum(["DIRECT", "CDN", "CUSTOM"])
|
|
8572
|
+
.optional()
|
|
8573
|
+
.describe("绑定类型(仅 bindCustomDomain,默认 DIRECT)。" +
|
|
8574
|
+
"DIRECT=直连(普通绑域名用这个);CDN=云开发 CDN;CUSTOM=自有 CDN/WAF(需 customCname)。" +
|
|
8575
|
+
"详见 https://docs.cloudbase.net/service/custom-domain"),
|
|
8576
|
+
customCname: zod_1.z
|
|
8577
|
+
.string()
|
|
8578
|
+
.optional()
|
|
8579
|
+
.describe("自有 CDN/WAF 的回源/回填地址(仅 bindCustomDomain 且 accessType=CUSTOM)。" +
|
|
8580
|
+
"不是 DNS 里用户域名要解析到的那个 CNAME;DIRECT/CDN 不要传。" +
|
|
8581
|
+
"详见 https://docs.cloudbase.net/service/custom-domain"),
|
|
8509
8582
|
enable: zod_1.z
|
|
8510
8583
|
.boolean()
|
|
8511
8584
|
.optional()
|
|
8512
|
-
.describe("
|
|
8513
|
-
"
|
|
8585
|
+
.describe("开关目标状态(enableService / authSwitch 必填):enable=true 开启,enable=false 关闭。" +
|
|
8586
|
+
"bindCustomDomain 可选:enable=false 表示绑定后禁用域名(默认启用)。" +
|
|
8587
|
+
"省略或非布尔值会返回参数错误。"),
|
|
8514
8588
|
},
|
|
8515
8589
|
annotations: {
|
|
8516
8590
|
readOnlyHint: false,
|
|
@@ -11134,7 +11208,7 @@ class TelemetryReporter {
|
|
|
11134
11208
|
const nodeVersion = process.version; // Node.js版本
|
|
11135
11209
|
const arch = os_1.default.arch(); // 系统架构
|
|
11136
11210
|
// 从构建时注入的版本号获取MCP版本信息
|
|
11137
|
-
const mcpVersion = process.env.npm_package_version || "2.25.
|
|
11211
|
+
const mcpVersion = process.env.npm_package_version || "2.25.5" || 0;
|
|
11138
11212
|
return {
|
|
11139
11213
|
userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
|
|
11140
11214
|
deviceId: this.deviceId,
|
|
@@ -13144,6 +13218,14 @@ function collectMigrationVersions(value, found = new Set()) {
|
|
|
13144
13218
|
/** Brief retries before concluding a pushed migration never landed (Push can be async). */
|
|
13145
13219
|
const MIGRATION_VERIFY_ATTEMPTS = 4;
|
|
13146
13220
|
const MIGRATION_VERIFY_DELAY_MS = 1500;
|
|
13221
|
+
/** Page size when hydrating remote migration history into a Push/Preview payload. */
|
|
13222
|
+
const MIGRATION_LIST_PAGE_SIZE = 100;
|
|
13223
|
+
/**
|
|
13224
|
+
* PushPGUserMigrations is async: poll DescribeTaskResult until Succeed/Failed.
|
|
13225
|
+
* Aligns with CLI `tcb db pg migration up` and Manager SDK docs.
|
|
13226
|
+
*/
|
|
13227
|
+
const MIGRATION_TASK_POLL_INTERVAL_MS = 1500;
|
|
13228
|
+
const MIGRATION_TASK_MAX_WAIT_MS = 90_000;
|
|
13147
13229
|
/**
|
|
13148
13230
|
* Confirm a pushed migration version is present in the remote migration history.
|
|
13149
13231
|
*
|
|
@@ -13177,6 +13259,99 @@ async function verifyMigrationApplied(context, version, cloudBaseOptions) {
|
|
|
13177
13259
|
}
|
|
13178
13260
|
return { applied: null, error: lastError };
|
|
13179
13261
|
}
|
|
13262
|
+
function extractMigrationSummaries(history) {
|
|
13263
|
+
const raw = history.Migrations;
|
|
13264
|
+
if (!Array.isArray(raw)) {
|
|
13265
|
+
return [];
|
|
13266
|
+
}
|
|
13267
|
+
const summaries = [];
|
|
13268
|
+
for (const item of raw) {
|
|
13269
|
+
if (!item || typeof item !== "object") {
|
|
13270
|
+
continue;
|
|
13271
|
+
}
|
|
13272
|
+
const row = item;
|
|
13273
|
+
const version = typeof row.Version === "string" ? row.Version.trim() : "";
|
|
13274
|
+
const name = typeof row.Name === "string" ? row.Name.trim() : "";
|
|
13275
|
+
if (version) {
|
|
13276
|
+
summaries.push({ Version: version, Name: name || version });
|
|
13277
|
+
}
|
|
13278
|
+
}
|
|
13279
|
+
return summaries;
|
|
13280
|
+
}
|
|
13281
|
+
/**
|
|
13282
|
+
* List every remote migration summary (paginated). Required before Push/Preview:
|
|
13283
|
+
* submitting only the pending migration while remote history exists yields
|
|
13284
|
+
* Executable=false with Conflicts reason remote_history_not_found_locally, and the
|
|
13285
|
+
* async task fails with "migration plan is not executable".
|
|
13286
|
+
*/
|
|
13287
|
+
async function listAllRemoteMigrationSummaries(context, cloudBaseOptions) {
|
|
13288
|
+
const summaries = [];
|
|
13289
|
+
let offset = 0;
|
|
13290
|
+
let latestVersion = "";
|
|
13291
|
+
let total = 0;
|
|
13292
|
+
while (true) {
|
|
13293
|
+
const page = await callPgMigrationApi(context, "ListPGUserMigrations", { Limit: MIGRATION_LIST_PAGE_SIZE, Offset: offset }, cloudBaseOptions);
|
|
13294
|
+
const pageItems = extractMigrationSummaries(page);
|
|
13295
|
+
summaries.push(...pageItems);
|
|
13296
|
+
if (typeof page.LatestVersion === "string" && page.LatestVersion.trim()) {
|
|
13297
|
+
latestVersion = page.LatestVersion.trim();
|
|
13298
|
+
}
|
|
13299
|
+
if (typeof page.Total === "number" && Number.isFinite(page.Total)) {
|
|
13300
|
+
total = page.Total;
|
|
13301
|
+
}
|
|
13302
|
+
else {
|
|
13303
|
+
total = summaries.length;
|
|
13304
|
+
}
|
|
13305
|
+
if (pageItems.length < MIGRATION_LIST_PAGE_SIZE) {
|
|
13306
|
+
break;
|
|
13307
|
+
}
|
|
13308
|
+
offset += MIGRATION_LIST_PAGE_SIZE;
|
|
13309
|
+
}
|
|
13310
|
+
return { summaries, latestVersion, total };
|
|
13311
|
+
}
|
|
13312
|
+
/**
|
|
13313
|
+
* Build Push/Preview Migrations = remote history Queries + the pending migration.
|
|
13314
|
+
* Matches CLI behavior (submit full set; server skips already-applied versions).
|
|
13315
|
+
*/
|
|
13316
|
+
async function buildMigrationsPayloadWithRemoteHistory(context, pending, cloudBaseOptions) {
|
|
13317
|
+
const { summaries, latestVersion } = await listAllRemoteMigrationSummaries(context, cloudBaseOptions);
|
|
13318
|
+
const migrations = [];
|
|
13319
|
+
for (const summary of summaries) {
|
|
13320
|
+
if (summary.Version === pending.Version) {
|
|
13321
|
+
// Caller supplies the authoritative SQL for this version.
|
|
13322
|
+
continue;
|
|
13323
|
+
}
|
|
13324
|
+
const detail = await callPgMigrationApi(context, "DescribePGUserMigration", { MigrationVersion: summary.Version }, cloudBaseOptions);
|
|
13325
|
+
const query = typeof detail.Query === "string" ? detail.Query : "";
|
|
13326
|
+
if (!query.trim()) {
|
|
13327
|
+
throw new Error(`DescribePGUserMigration returned empty Query for version=${summary.Version} (${summary.Name}). ` +
|
|
13328
|
+
"Cannot hydrate remote history for PushPGUserMigrations.");
|
|
13329
|
+
}
|
|
13330
|
+
migrations.push({
|
|
13331
|
+
Version: typeof detail.Version === "string" && detail.Version.trim() ? detail.Version.trim() : summary.Version,
|
|
13332
|
+
Name: typeof detail.Name === "string" && detail.Name.trim() ? detail.Name.trim() : summary.Name,
|
|
13333
|
+
Query: query,
|
|
13334
|
+
});
|
|
13335
|
+
}
|
|
13336
|
+
migrations.push(pending);
|
|
13337
|
+
return { migrations, remoteCount: summaries.length, latestVersion };
|
|
13338
|
+
}
|
|
13339
|
+
async function waitPgMigrationTask(context, taskId, cloudBaseOptions) {
|
|
13340
|
+
const deadline = Date.now() + MIGRATION_TASK_MAX_WAIT_MS;
|
|
13341
|
+
let last = { TaskId: taskId };
|
|
13342
|
+
while (Date.now() <= deadline) {
|
|
13343
|
+
last = (await callPgMigrationApi(context, "DescribeTaskResult", { TaskId: taskId }, cloudBaseOptions));
|
|
13344
|
+
const status = String(last.Status || "").toLowerCase();
|
|
13345
|
+
if (status === "succeed" || status === "failed") {
|
|
13346
|
+
return last;
|
|
13347
|
+
}
|
|
13348
|
+
await sleep(MIGRATION_TASK_POLL_INTERVAL_MS);
|
|
13349
|
+
}
|
|
13350
|
+
const error = new Error(`DescribeTaskResult timed out after ${Math.floor(MIGRATION_TASK_MAX_WAIT_MS / 1000)}s ` +
|
|
13351
|
+
`for TaskId=${taskId} (last Status=${last.Status || "-"}, Phase=${last.Phase || "-"}).`);
|
|
13352
|
+
error.lastTask = last;
|
|
13353
|
+
throw error;
|
|
13354
|
+
}
|
|
13180
13355
|
const MIGRATION_VERSION_REQUIRED_MESSAGE = "migrationVersion is required (14-digit UTC timestamp YYYYMMDDHHMMSS). " +
|
|
13181
13356
|
"Decide the version first, write local file migrations/<version>_<migrationName>.sql, " +
|
|
13182
13357
|
"then call planMigration/applyMigration with the same migrationVersion and migrationName.";
|
|
@@ -13963,29 +14138,38 @@ async function handlePlanMigration(args, context, deps, cloudBaseOptions) {
|
|
|
13963
14138
|
}
|
|
13964
14139
|
const version = versionOrError;
|
|
13965
14140
|
const localFileHint = buildLocalMigrationFileHint(version, args.migrationName);
|
|
13966
|
-
const
|
|
14141
|
+
const pending = {
|
|
13967
14142
|
Version: version,
|
|
13968
14143
|
Name: args.migrationName,
|
|
13969
14144
|
Query: args.sql,
|
|
13970
14145
|
};
|
|
13971
14146
|
if (args.rollbackSql?.trim()) {
|
|
13972
|
-
|
|
14147
|
+
pending.Rollback = args.rollbackSql;
|
|
13973
14148
|
}
|
|
13974
14149
|
try {
|
|
14150
|
+
const hydrated = await buildMigrationsPayloadWithRemoteHistory(context, pending, cloudBaseOptions);
|
|
13975
14151
|
const result = await callPgMigrationApi(context, "PreviewPGUserMigrations", {
|
|
13976
|
-
Migrations:
|
|
14152
|
+
Migrations: hydrated.migrations,
|
|
13977
14153
|
}, cloudBaseOptions);
|
|
14154
|
+
const executable = result.Executable === true;
|
|
13978
14155
|
return buildPgToolResult({
|
|
13979
14156
|
success: true,
|
|
13980
14157
|
data: {
|
|
13981
14158
|
migrationVersion: version,
|
|
13982
14159
|
migrationName: args.migrationName,
|
|
13983
14160
|
localFileHint,
|
|
14161
|
+
hydratedRemoteCount: hydrated.remoteCount,
|
|
14162
|
+
latestRemoteVersion: hydrated.latestVersion || null,
|
|
14163
|
+
executable,
|
|
13984
14164
|
apiResult: result,
|
|
13985
14165
|
},
|
|
13986
|
-
message:
|
|
14166
|
+
message: executable
|
|
14167
|
+
? `Migration plan generated via PreviewPGUserMigrations (hydrated ${hydrated.remoteCount} remote migration(s)). Reuse migrationVersion=${version} on applyMigration. Ensure local file ${localFileHint} exists and matches.`
|
|
14168
|
+
: `Migration plan is NOT executable (PreviewPGUserMigrations.Executable=false after hydrating ${hydrated.remoteCount} remote migration(s)). Inspect apiResult.Conflicts before applyMigration. Common causes: version older than latest remote (${hydrated.latestVersion || "unknown"}), or checksum mismatch.`,
|
|
13987
14169
|
nextActions: [
|
|
13988
|
-
buildNextAction(MANAGE_PG_DATABASE, "applyMigration",
|
|
14170
|
+
buildNextAction(MANAGE_PG_DATABASE, "applyMigration", executable
|
|
14171
|
+
? "Review the plan above. If it looks correct, call applyMigration with the same migrationVersion."
|
|
14172
|
+
: "Resolve Conflicts (often pick a migrationVersion newer than LatestVersion), then retry planMigration/applyMigration.", {
|
|
13989
14173
|
action: "applyMigration",
|
|
13990
14174
|
migrationName: args.migrationName,
|
|
13991
14175
|
migrationVersion: version,
|
|
@@ -14032,26 +14216,118 @@ async function handleApplyMigration(args, context, deps, cloudBaseOptions) {
|
|
|
14032
14216
|
}
|
|
14033
14217
|
const version = versionOrError;
|
|
14034
14218
|
const localFileHint = buildLocalMigrationFileHint(version, args.migrationName);
|
|
14035
|
-
const
|
|
14219
|
+
const pending = {
|
|
14036
14220
|
Version: version,
|
|
14037
14221
|
Name: args.migrationName,
|
|
14038
14222
|
Query: args.sql,
|
|
14039
14223
|
};
|
|
14040
14224
|
if (args.rollbackSql?.trim()) {
|
|
14041
|
-
|
|
14042
|
-
}
|
|
14043
|
-
const params = { Migrations: [migration] };
|
|
14044
|
-
if (args.lockTimeoutMs !== undefined) {
|
|
14045
|
-
params.LockTimeoutMs = args.lockTimeoutMs;
|
|
14046
|
-
}
|
|
14047
|
-
if (args.statementTimeoutMs !== undefined) {
|
|
14048
|
-
params.StatementTimeoutMs = args.statementTimeoutMs;
|
|
14225
|
+
pending.Rollback = args.rollbackSql;
|
|
14049
14226
|
}
|
|
14050
14227
|
try {
|
|
14228
|
+
// Pushing only the pending migration while remote history exists makes the plan
|
|
14229
|
+
// non-executable (remote_history_not_found_locally). Hydrate remote Queries first,
|
|
14230
|
+
// matching CLI `tcb db pg migration up` (full list; server skips applied).
|
|
14231
|
+
const hydrated = await buildMigrationsPayloadWithRemoteHistory(context, pending, cloudBaseOptions);
|
|
14232
|
+
const preview = await callPgMigrationApi(context, "PreviewPGUserMigrations", { Migrations: hydrated.migrations }, cloudBaseOptions);
|
|
14233
|
+
if (preview.Executable !== true) {
|
|
14234
|
+
return buildPgToolResult({
|
|
14235
|
+
success: false,
|
|
14236
|
+
errorCode: "MIGRATION_NOT_EXECUTABLE",
|
|
14237
|
+
data: {
|
|
14238
|
+
migrationVersion: version,
|
|
14239
|
+
migrationName: args.migrationName,
|
|
14240
|
+
localFileHint,
|
|
14241
|
+
hydratedRemoteCount: hydrated.remoteCount,
|
|
14242
|
+
latestRemoteVersion: hydrated.latestVersion || null,
|
|
14243
|
+
previewResult: preview,
|
|
14244
|
+
verified: false,
|
|
14245
|
+
},
|
|
14246
|
+
message: `PreviewPGUserMigrations reported Executable=false for migrationVersion=${version} ` +
|
|
14247
|
+
`(latest remote=${hydrated.latestVersion || "unknown"}, hydratedRemoteCount=${hydrated.remoteCount}). ` +
|
|
14248
|
+
"Push was NOT submitted. Inspect previewResult.Conflicts — common reasons: " +
|
|
14249
|
+
"local_migration_before_latest_remote (pick a newer 14-digit version) or checksum_mismatch. " +
|
|
14250
|
+
"If the schema change is urgent you may fall back to action=execute with allowDdlViaExecute=true, but that bypasses migration history.",
|
|
14251
|
+
nextActions: [
|
|
14252
|
+
buildNextAction(MANAGE_PG_DATABASE, "listMigrations", "Check LatestVersion and pick a migrationVersion strictly newer than it.", { action: "listMigrations", limit: 20 }),
|
|
14253
|
+
buildNextAction(MANAGE_PG_DATABASE, "planMigration", "Re-run planMigration after adjusting migrationVersion/SQL.", {
|
|
14254
|
+
action: "planMigration",
|
|
14255
|
+
migrationName: args.migrationName,
|
|
14256
|
+
migrationVersion: version,
|
|
14257
|
+
sql: args.sql,
|
|
14258
|
+
...(args.rollbackSql ? { rollbackSql: args.rollbackSql } : {}),
|
|
14259
|
+
}),
|
|
14260
|
+
],
|
|
14261
|
+
});
|
|
14262
|
+
}
|
|
14263
|
+
const params = { Migrations: hydrated.migrations };
|
|
14264
|
+
if (args.lockTimeoutMs !== undefined) {
|
|
14265
|
+
params.LockTimeoutMs = args.lockTimeoutMs;
|
|
14266
|
+
}
|
|
14267
|
+
if (args.statementTimeoutMs !== undefined) {
|
|
14268
|
+
params.StatementTimeoutMs = args.statementTimeoutMs;
|
|
14269
|
+
}
|
|
14051
14270
|
const result = await callPgMigrationApi(context, "PushPGUserMigrations", params, cloudBaseOptions);
|
|
14052
|
-
|
|
14053
|
-
|
|
14054
|
-
|
|
14271
|
+
const taskId = typeof result.TaskId === "string" ? result.TaskId.trim() : "";
|
|
14272
|
+
let taskResult = null;
|
|
14273
|
+
if (taskId) {
|
|
14274
|
+
try {
|
|
14275
|
+
taskResult = await waitPgMigrationTask(context, taskId, cloudBaseOptions);
|
|
14276
|
+
}
|
|
14277
|
+
catch (error) {
|
|
14278
|
+
const lastTask = error.lastTask;
|
|
14279
|
+
return buildPgToolResult({
|
|
14280
|
+
success: false,
|
|
14281
|
+
errorCode: "MIGRATION_TASK_TIMEOUT",
|
|
14282
|
+
data: {
|
|
14283
|
+
migrationVersion: version,
|
|
14284
|
+
migrationName: args.migrationName,
|
|
14285
|
+
localFileHint,
|
|
14286
|
+
hydratedRemoteCount: hydrated.remoteCount,
|
|
14287
|
+
apiResult: result,
|
|
14288
|
+
taskResult: lastTask ?? { TaskId: taskId },
|
|
14289
|
+
verified: null,
|
|
14290
|
+
},
|
|
14291
|
+
message: `PushPGUserMigrations returned TaskId=${taskId}, but waiting for DescribeTaskResult timed out: ` +
|
|
14292
|
+
`${error instanceof Error ? error.message : String(error)}. ` +
|
|
14293
|
+
"The migration may still be running — check action=listMigrations before retrying or falling back to execute.",
|
|
14294
|
+
nextActions: [
|
|
14295
|
+
buildNextAction(MANAGE_PG_DATABASE, "listMigrations", "Check whether the migrationVersion eventually landed.", { action: "listMigrations", limit: 20 }),
|
|
14296
|
+
],
|
|
14297
|
+
});
|
|
14298
|
+
}
|
|
14299
|
+
const status = String(taskResult.Status || "").toLowerCase();
|
|
14300
|
+
if (status === "failed") {
|
|
14301
|
+
return buildPgToolResult({
|
|
14302
|
+
success: false,
|
|
14303
|
+
errorCode: "MIGRATION_TASK_FAILED",
|
|
14304
|
+
data: {
|
|
14305
|
+
migrationVersion: version,
|
|
14306
|
+
migrationName: args.migrationName,
|
|
14307
|
+
localFileHint,
|
|
14308
|
+
hydratedRemoteCount: hydrated.remoteCount,
|
|
14309
|
+
apiResult: result,
|
|
14310
|
+
taskResult: taskResult,
|
|
14311
|
+
verified: false,
|
|
14312
|
+
},
|
|
14313
|
+
message: `PushPGUserMigrations TaskId=${taskId} failed at phase=${taskResult.Phase || "-"}: ` +
|
|
14314
|
+
`${taskResult.Reason || "unknown reason"}. ` +
|
|
14315
|
+
"The migration did NOT take effect. Fix Conflicts/SQL (or use a newer migrationVersion), then retry applyMigration. " +
|
|
14316
|
+
"Urgent bypass: action=execute with allowDdlViaExecute=true (skips migration history).",
|
|
14317
|
+
nextActions: [
|
|
14318
|
+
buildNextAction(MANAGE_PG_DATABASE, "planMigration", "Preview again to inspect Conflicts before retrying apply.", {
|
|
14319
|
+
action: "planMigration",
|
|
14320
|
+
migrationName: args.migrationName,
|
|
14321
|
+
migrationVersion: version,
|
|
14322
|
+
sql: args.sql,
|
|
14323
|
+
...(args.rollbackSql ? { rollbackSql: args.rollbackSql } : {}),
|
|
14324
|
+
}),
|
|
14325
|
+
buildNextAction(MANAGE_PG_DATABASE, "listMigrations", "Confirm the version is still absent from remote history.", { action: "listMigrations", limit: 20 }),
|
|
14326
|
+
],
|
|
14327
|
+
});
|
|
14328
|
+
}
|
|
14329
|
+
}
|
|
14330
|
+
// Task Succeed (or sync response without TaskId): confirm history records the version.
|
|
14055
14331
|
const verification = await verifyMigrationApplied(context, version, cloudBaseOptions);
|
|
14056
14332
|
if (verification.applied === false) {
|
|
14057
14333
|
return buildPgToolResult({
|
|
@@ -14061,10 +14337,12 @@ async function handleApplyMigration(args, context, deps, cloudBaseOptions) {
|
|
|
14061
14337
|
migrationVersion: version,
|
|
14062
14338
|
migrationName: args.migrationName,
|
|
14063
14339
|
localFileHint,
|
|
14340
|
+
hydratedRemoteCount: hydrated.remoteCount,
|
|
14064
14341
|
apiResult: result,
|
|
14342
|
+
taskResult: taskResult,
|
|
14065
14343
|
verified: false,
|
|
14066
14344
|
},
|
|
14067
|
-
message: `PushPGUserMigrations
|
|
14345
|
+
message: `PushPGUserMigrations completed (TaskId=${taskId || "none"}), but migrationVersion=${version} is not present in the remote migration history, ` +
|
|
14068
14346
|
"so the migration did NOT take effect. Do not assume the schema change is applied. " +
|
|
14069
14347
|
"Re-check the SQL and migrationVersion, inspect the migration with action=migrationDetail, and retry action=applyMigration. " +
|
|
14070
14348
|
"If the schema change is urgent you may fall back to action=execute with allowDdlViaExecute=true, but that bypasses migration history.",
|
|
@@ -14082,7 +14360,9 @@ async function handleApplyMigration(args, context, deps, cloudBaseOptions) {
|
|
|
14082
14360
|
migrationVersion: version,
|
|
14083
14361
|
migrationName: args.migrationName,
|
|
14084
14362
|
localFileHint,
|
|
14363
|
+
hydratedRemoteCount: hydrated.remoteCount,
|
|
14085
14364
|
apiResult: result,
|
|
14365
|
+
taskResult: taskResult,
|
|
14086
14366
|
verified: null,
|
|
14087
14367
|
},
|
|
14088
14368
|
message: `PushPGUserMigrations was submitted, but verifying migrationVersion=${version} against the remote history failed: ${verification.error}. ` +
|
|
@@ -14098,10 +14378,12 @@ async function handleApplyMigration(args, context, deps, cloudBaseOptions) {
|
|
|
14098
14378
|
migrationVersion: version,
|
|
14099
14379
|
migrationName: args.migrationName,
|
|
14100
14380
|
localFileHint,
|
|
14381
|
+
hydratedRemoteCount: hydrated.remoteCount,
|
|
14101
14382
|
apiResult: result,
|
|
14383
|
+
taskResult: taskResult,
|
|
14102
14384
|
verified: true,
|
|
14103
14385
|
},
|
|
14104
|
-
message: `Migrations applied via PushPGUserMigrations
|
|
14386
|
+
message: `Migrations applied via PushPGUserMigrations (hydrated ${hydrated.remoteCount} remote migration(s)), task ${taskId ? `TaskId=${taskId} ` : ""}verified present in the remote migration history. Ensure local file ${localFileHint} exists and matches.`,
|
|
14105
14387
|
});
|
|
14106
14388
|
}
|
|
14107
14389
|
catch (error) {
|
|
@@ -15616,7 +15898,7 @@ function registerSetupTools(server) {
|
|
|
15616
15898
|
server.registerTool("downloadTemplate", {
|
|
15617
15899
|
title: "下载项目模板",
|
|
15618
15900
|
description: `自动下载并部署CloudBase项目模板。
|
|
15619
|
-
**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.25.
|
|
15901
|
+
**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.25.5" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
|
|
15620
15902
|
inputSchema: {
|
|
15621
15903
|
template: zod_1.z
|
|
15622
15904
|
.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.25.
|
|
3
|
+
"version": "2.25.5",
|
|
4
4
|
"packageManager": "pnpm@10.8.0",
|
|
5
5
|
"description": "CloudBase MCP Server — operate Tencent CloudBase (database, auth, functions, storage, hosting) from AI coding tools via the Model Context Protocol. Part of CloudBase AI Toolkit.",
|
|
6
6
|
"main": "./dist/index.cjs",
|