@cloudbase/cloudbase-mcp 2.19.5 → 2.20.0

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
@@ -1,5 +1,4 @@
1
1
  import CloudBase from '@cloudbase/manager-node';
2
- import { Credential as Credential_2 } from '@cloudbase/toolbox';
3
2
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
4
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
5
4
 
@@ -176,12 +175,7 @@ export declare interface GetFileInfoParams {
176
175
  */
177
176
  export declare function getInteractiveServerAsync(): Promise<InteractiveServer>;
178
177
 
179
- export declare function getLoginState(options?: EnsureLoginOptions): Promise<{
180
- secretId: string;
181
- secretKey: string;
182
- token: string | undefined;
183
- envId: string | undefined;
184
- } | Credential_2>;
178
+ export declare function getLoginState(options?: EnsureLoginOptions): Promise<LoginState | null>;
185
179
 
186
180
  declare interface GetManagerOptions {
187
181
  requireEnvId?: boolean;
@@ -271,6 +265,13 @@ declare type Logger = (data: {
271
265
  [key: string]: any;
272
266
  }) => void;
273
267
 
268
+ declare interface LoginState {
269
+ secretId: string;
270
+ secretKey: string;
271
+ token?: string;
272
+ envId?: string;
273
+ }
274
+
274
275
  export declare function logout(): Promise<void>;
275
276
 
276
277
  export { McpServer }
package/dist/index.js CHANGED
@@ -727,6 +727,8 @@ function registerEnvTools(server) {
727
727
  if (action === "status") {
728
728
  const loginState = await (0, auth_js_1.peekLoginState)();
729
729
  const authFlowState = await (0, auth_js_1.getAuthProgressState)();
730
+ // 判断是否为 API Key 模式
731
+ const isApiKeyMode = !!(process.env.CLOUDBASE_API_KEY && process.env.CLOUDBASE_ENV_ID);
730
732
  const authStatus = loginState
731
733
  ? "READY"
732
734
  : authFlowState.status === "PENDING"
@@ -751,6 +753,7 @@ function registerEnvTools(server) {
751
753
  ok: true,
752
754
  code: "STATUS",
753
755
  auth_status: authStatus,
756
+ ...(isApiKeyMode ? { auth_mode: "api_key" } : {}),
754
757
  auth_config: authConfigSummary,
755
758
  ...(envPreparation
756
759
  ? buildAuthEnvSetupPayload(envPreparation)
@@ -773,6 +776,46 @@ function registerEnvTools(server) {
773
776
  });
774
777
  }
775
778
  if (action === "start_auth") {
779
+ // API Key 模式:尝试换取临时密钥
780
+ if (process.env.CLOUDBASE_API_KEY && process.env.CLOUDBASE_ENV_ID) {
781
+ try {
782
+ const existingLoginState = await (0, auth_js_1.peekLoginState)();
783
+ if (existingLoginState) {
784
+ return (0, tool_result_js_1.buildJsonToolResult)({
785
+ ok: true,
786
+ code: "AUTH_READY",
787
+ message: "当前使用 API Key 认证模式,已自动完成登录,无需手动授权。",
788
+ auth_mode: "api_key",
789
+ envId: process.env.CLOUDBASE_ENV_ID,
790
+ });
791
+ }
792
+ }
793
+ catch (e) {
794
+ // peekLoginState 内部异常,记录后 fall through
795
+ (0, logger_js_1.debug)("start_auth: peekLoginState threw", { error: e instanceof Error ? e.message : String(e) });
796
+ }
797
+ // API Key 换取失败:返回详细诊断信息
798
+ // 尝试获取更详细的错误信息
799
+ let diagMessage = "当前配置了 API Key 认证模式,但换取临时密钥失败。";
800
+ const endpoint = process.env.CLOUDBASE_API_ENDPOINT || `https://${process.env.CLOUDBASE_ENV_ID}.ap-shanghai.tcb-api.tencentcloudapi.com`;
801
+ diagMessage += `\n\n诊断信息:`;
802
+ diagMessage += `\n- CLOUDBASE_ENV_ID: ${process.env.CLOUDBASE_ENV_ID}`;
803
+ diagMessage += `\n- CLOUDBASE_API_KEY: ${process.env.CLOUDBASE_API_KEY.slice(0, 20)}...(已截断)`;
804
+ diagMessage += `\n- Endpoint: ${endpoint}`;
805
+ diagMessage += `\n\n可能原因:`;
806
+ diagMessage += `\n1. API Key 已过期或被删除`;
807
+ diagMessage += `\n2. Endpoint 不可达(网络/DNS 问题)`;
808
+ diagMessage += `\n3. CLOUDBASE_ENV_ID 与 API Key 所属环境不匹配`;
809
+ diagMessage += `\n\n建议:检查 MCP 配置中的 CLOUDBASE_API_KEY 和 CLOUDBASE_ENV_ID 环境变量是否正确。`;
810
+ return (0, tool_result_js_1.buildJsonToolResult)({
811
+ ok: false,
812
+ code: "API_KEY_AUTH_FAILED",
813
+ message: diagMessage,
814
+ auth_mode: "api_key",
815
+ envId: process.env.CLOUDBASE_ENV_ID,
816
+ endpoint,
817
+ });
818
+ }
776
819
  const region = server.cloudBaseOptions?.region || process.env.TCB_REGION;
777
820
  const auth = toolbox_1.AuthSupervisor.getInstance({});
778
821
  const authFlowState = await (0, auth_js_1.getAuthProgressState)();
@@ -977,6 +1020,15 @@ function registerEnvTools(server) {
977
1020
  });
978
1021
  }
979
1022
  if (action === "logout") {
1023
+ // API Key 模式下不允许 logout
1024
+ if (process.env.CLOUDBASE_API_KEY && process.env.CLOUDBASE_ENV_ID) {
1025
+ return (0, tool_result_js_1.buildJsonToolResult)({
1026
+ ok: false,
1027
+ code: "LOGOUT_NOT_ALLOWED",
1028
+ message: "当前使用 API Key 认证模式,不支持退出登录。如需切换认证方式,请移除 CLOUDBASE_API_KEY 环境变量后重启。",
1029
+ auth_mode: "api_key",
1030
+ });
1031
+ }
980
1032
  if (confirm !== "yes") {
981
1033
  return (0, tool_result_js_1.buildJsonToolResult)({
982
1034
  ok: false,
@@ -1103,29 +1155,58 @@ function registerEnvTools(server) {
1103
1155
  requireEnvId: true,
1104
1156
  mcpServer: server, // Pass server for IDE detection
1105
1157
  });
1106
- // Use commonService to call DescribeEnvs with filter parameters
1107
- // Filter parameters match the reference conditions provided by user
1108
- result = await cloudbaseList.commonService("tcb", "2018-06-08").call({
1109
- Action: "DescribeEnvs",
1110
- Param: {
1111
- EnvTypes: ["weda", "baas"], // Include weda and baas (normal) environments
1112
- IsVisible: false, // Filter out invisible environments
1113
- Channels: ["dcloud", "iotenable", "tem", "scene_module"], // Filter special channels
1114
- },
1115
- });
1116
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
1117
- // Transform response format to match original listEnvs() format
1118
- if (result && result.EnvList) {
1119
- result = { EnvList: result.EnvList };
1120
- }
1121
- else if (result && result.Data && result.Data.EnvList) {
1122
- result = { EnvList: result.Data.EnvList };
1158
+ // 当环境变量设置了 CLOUDBASE_ENV_ID 时(如 API Key 模式),
1159
+ // 避免调用 DescribeEnvs(临时密钥可能不支持该接口),
1160
+ // 改为通过 DescribeEnvInfo 获取单环境信息
1161
+ const envIdFromEnv = process.env.CLOUDBASE_ENV_ID;
1162
+ if (envIdFromEnv) {
1163
+ try {
1164
+ const envInfo = await cloudbaseList.env.describeEnvInfo({ EnvId: envIdFromEnv });
1165
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, envInfo);
1166
+ // DescribeEnvInfo 返回结构: { EnvInfo: { EnvBaseInfo: { EnvId, Alias, ... }, BillingInfo, ... } }
1167
+ // 需要提取 EnvBaseInfo 作为扁平 env 对象以匹配 DescribeEnvs 返回格式
1168
+ const baseInfo = envInfo?.EnvInfo?.EnvBaseInfo;
1169
+ if (baseInfo) {
1170
+ result = { EnvList: [baseInfo] };
1171
+ }
1172
+ else if (envInfo?.EnvInfo) {
1173
+ // 兼容: 如果没有 EnvBaseInfo 但有 EnvInfo,尝试直接使用
1174
+ result = { EnvList: [{ EnvId: envIdFromEnv, ...envInfo.EnvInfo }] };
1175
+ }
1176
+ else {
1177
+ result = { EnvList: [{ EnvId: envIdFromEnv }] };
1178
+ }
1179
+ }
1180
+ catch (envInfoError) {
1181
+ (0, logger_js_1.debug)("DescribeEnvInfo 失败,返回基础环境信息:", envInfoError instanceof Error ? envInfoError : new Error(String(envInfoError)));
1182
+ result = { EnvList: [{ EnvId: envIdFromEnv }] };
1183
+ }
1123
1184
  }
1124
1185
  else {
1125
- // Fallback to original method if format is unexpected
1126
- (0, logger_js_1.debug)("Unexpected response format, falling back to listEnvs()");
1127
- result = await cloudbaseList.env.listEnvs();
1186
+ // Use commonService to call DescribeEnvs with filter parameters
1187
+ // Filter parameters match the reference conditions provided by user
1188
+ result = await cloudbaseList.commonService("tcb", "2018-06-08").call({
1189
+ Action: "DescribeEnvs",
1190
+ Param: {
1191
+ EnvTypes: ["weda", "baas"], // Include weda and baas (normal) environments
1192
+ IsVisible: false, // Filter out invisible environments
1193
+ Channels: ["dcloud", "iotenable", "tem", "scene_module"], // Filter special channels
1194
+ },
1195
+ });
1128
1196
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
1197
+ // Transform response format to match original listEnvs() format
1198
+ if (result && result.EnvList) {
1199
+ result = { EnvList: result.EnvList };
1200
+ }
1201
+ else if (result && result.Data && result.Data.EnvList) {
1202
+ result = { EnvList: result.Data.EnvList };
1203
+ }
1204
+ else {
1205
+ // Fallback to original method if format is unexpected
1206
+ (0, logger_js_1.debug)("Unexpected response format, falling back to listEnvs()");
1207
+ result = await cloudbaseList.env.listEnvs();
1208
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
1209
+ }
1129
1210
  }
1130
1211
  }
1131
1212
  catch (error) {
@@ -1622,7 +1703,7 @@ ${envIdSection}
1622
1703
  ## 环境信息
1623
1704
  - 操作系统: ${os_1.default.type()} ${os_1.default.release()}
1624
1705
  - Node.js版本: ${process.version}
1625
- - MCP 版本:${process.env.npm_package_version || "2.19.5" || 0}
1706
+ - MCP 版本:${process.env.npm_package_version || "2.20.0" || 0}
1626
1707
  - 系统架构: ${os_1.default.arch()}
1627
1708
  - 时间: ${new Date().toISOString()}
1628
1709
  - 请求ID: ${requestId}
@@ -1670,7 +1751,7 @@ function createWrappedHandler(name, handler, server) {
1670
1751
  success = true;
1671
1752
  requestId = extractRequestIdFromToolResult(result);
1672
1753
  const duration = Date.now() - startTime;
1673
- (0, logger_js_1.debug)(`工具执行成功: ${name}`, { duration });
1754
+ (0, logger_js_1.debug)(`工具执行成功: ${name}`, { duration, requestId: requestId || undefined });
1674
1755
  if (!isTestEnvironment) {
1675
1756
  server.logger?.({ type: 'afterToolCall', toolName: name, args: sanitizeArgs(args), result: result, duration });
1676
1757
  }
@@ -1682,6 +1763,7 @@ function createWrappedHandler(name, handler, server) {
1682
1763
  requestId = (typeof error === 'object' && error && 'requestId' in error) ? error.requestId : '';
1683
1764
  (0, logger_js_1.debug)(`工具执行失败: ${name}`, {
1684
1765
  error: errorMessage,
1766
+ requestId: requestId || undefined,
1685
1767
  duration: Date.now() - startTime
1686
1768
  });
1687
1769
  const isTestEnvironment = false || process.env.VITEST === "true";
@@ -1702,8 +1784,9 @@ function createWrappedHandler(name, handler, server) {
1702
1784
  requestId: (typeof error === 'object' && error && 'requestId' in error) ? error.requestId : '',
1703
1785
  ide: server.ide || process.env.INTEGRATION_IDE || ''
1704
1786
  });
1705
- const mcpVersion = true ? "2.19.5" : 0;
1706
- const enhancedErrorMessage = `${errorMessage}\n\n📦 CloudBase MCP v${mcpVersion}\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
1787
+ const mcpVersion = true ? "2.20.0" : 0;
1788
+ const requestIdSuffix = requestId ? `\n🆔 RequestId: ${requestId}` : '';
1789
+ const enhancedErrorMessage = `${errorMessage}${requestIdSuffix}\n\n📦 CloudBase MCP v${mcpVersion}\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
1707
1790
  // 创建新的错误对象,保持原有的错误类型但更新消息
1708
1791
  const enhancedError = error instanceof Error
1709
1792
  ? new Error(enhancedErrorMessage)
@@ -3325,11 +3408,20 @@ function createManagerFromLoginState(loginState, region) {
3325
3408
  }
3326
3409
  async function listAvailableEnvCandidates(options) {
3327
3410
  const { cloudBaseOptions, loginState: providedLoginState } = options ?? {};
3411
+ // 优先使用显式传入的 envId
3328
3412
  if (cloudBaseOptions?.envId) {
3329
3413
  return [{
3330
3414
  envId: cloudBaseOptions.envId,
3331
3415
  }];
3332
3416
  }
3417
+ // 当环境变量设置了 CLOUDBASE_ENV_ID 且没有显式 credentials 时,直接返回(避免调 DescribeEnvs)
3418
+ // 有显式 credentials 时需要重新查询,因为可能是不同账号
3419
+ const hasExplicitCredentials = !!(cloudBaseOptions?.secretId && cloudBaseOptions?.secretKey);
3420
+ if (process.env.CLOUDBASE_ENV_ID && !hasExplicitCredentials) {
3421
+ return [{
3422
+ envId: process.env.CLOUDBASE_ENV_ID,
3423
+ }];
3424
+ }
3333
3425
  let cloudbase;
3334
3426
  if (cloudBaseOptions?.secretId && cloudBaseOptions?.secretKey) {
3335
3427
  cloudbase = createCloudBaseManagerWithOptions(cloudBaseOptions);
@@ -3656,6 +3748,7 @@ async function getCloudBaseManager(options = {}) {
3656
3748
  await throwPendingAuthError();
3657
3749
  }
3658
3750
  throwAuthRequiredError();
3751
+ return undefined; // unreachable, helps TypeScript narrow
3659
3752
  }
3660
3753
  const { envId: loginEnvId, secretId, secretKey, token } = loginState;
3661
3754
  let finalEnvId = cloudBaseOptions?.envId;
@@ -4199,7 +4292,11 @@ checkIndex: 检查指定索引是否存在`),
4199
4292
  ]).describe(`createCollection: 创建集合
4200
4293
  updateCollection: 更新集合配置;添加索引请传 updateOptions.CreateIndexes,删除索引请传 updateOptions.DropIndexes
4201
4294
  deleteCollection: 删除集合`),
4202
- collectionName: zod_1.z.string().describe("集合名称"),
4295
+ collectionName: zod_1.z
4296
+ .string()
4297
+ .trim()
4298
+ .min(1, "collectionName 不能为空")
4299
+ .describe("集合名称"),
4203
4300
  updateOptions: zod_1.z
4204
4301
  .object({
4205
4302
  CreateIndexes: zod_1.z
@@ -4323,31 +4420,40 @@ deleteCollection: 删除集合`),
4323
4420
  };
4324
4421
  }
4325
4422
  if (action === "deleteCollection") {
4326
- const result = await cloudbase.commonService("tcb", "2018-06-08").call({
4327
- Action: "DeleteTable",
4328
- Param: {
4329
- EnvId: await (0, cloudbase_manager_js_1.getEnvId)(server.cloudBaseOptions),
4330
- TableName: collectionName,
4331
- },
4332
- });
4333
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
4334
- const body = withCollectionName(collectionName, {
4335
- success: true,
4336
- requestId: result.RequestId,
4337
- action,
4338
- message: result.Exists === false ? "集合不存在" : "云开发数据库集合删除成功",
4339
- });
4340
- if (result.Exists === false) {
4341
- body.exists = false;
4342
- }
4343
- return {
4344
- content: [
4345
- {
4346
- type: "text",
4347
- text: JSON.stringify(body, null, 2),
4423
+ try {
4424
+ const result = await cloudbase.commonService("tcb", "2018-06-08").call({
4425
+ Action: "DeleteTable",
4426
+ Param: {
4427
+ EnvId: await (0, cloudbase_manager_js_1.getEnvId)(server.cloudBaseOptions),
4428
+ TableName: collectionName,
4348
4429
  },
4349
- ],
4350
- };
4430
+ });
4431
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
4432
+ const body = withCollectionName(collectionName, {
4433
+ success: true,
4434
+ requestId: result.RequestId,
4435
+ action,
4436
+ message: result.Exists === false ? "集合不存在" : "云开发数据库集合删除成功",
4437
+ });
4438
+ if (result.Exists === false) {
4439
+ body.exists = false;
4440
+ }
4441
+ return {
4442
+ content: [
4443
+ {
4444
+ type: "text",
4445
+ text: JSON.stringify(body, null, 2),
4446
+ },
4447
+ ],
4448
+ };
4449
+ }
4450
+ catch (error) {
4451
+ const message = error instanceof Error ? error.message : String(error);
4452
+ if (/parameter invalid|invalid parameter|param(eter)?\s+invalid/i.test(message)) {
4453
+ throw new Error(`deleteCollection 参数校验失败,请确认 collectionName 合法且已存在。当前 collectionName=\"${collectionName}\"。原始错误:${message}`);
4454
+ }
4455
+ throw error;
4456
+ }
4351
4457
  }
4352
4458
  throw new Error(`不支持的操作类型: ${action}`);
4353
4459
  });
@@ -6861,15 +6967,42 @@ function registerGatewayTools(server) {
6861
6967
  case "deleteAccess": {
6862
6968
  const cloudbase = await getManager();
6863
6969
  const accessPath = input.path ? normalizeAccessPath(input.path) : undefined;
6970
+ let resolvedAccessId = input.accessId;
6971
+ if (!resolvedAccessId) {
6972
+ if (!input.targetName && !accessPath) {
6973
+ throw new Error("action=deleteAccess 时至少需要提供 accessId,或提供 targetName/path 用于定位入口");
6974
+ }
6975
+ const accessList = await cloudbase.access.getAccessList({
6976
+ ...(input.targetName ? { name: input.targetName } : {}),
6977
+ ...(accessPath ? { path: accessPath } : {}),
6978
+ });
6979
+ const candidates = (accessList.APISet ?? []).filter((item) => {
6980
+ if (!accessPath) {
6981
+ return true;
6982
+ }
6983
+ return normalizeAccessPath(item.Path) === accessPath;
6984
+ });
6985
+ if (candidates.length === 0) {
6986
+ throw new Error("未找到可删除的访问入口,请确认 targetName/path 或直接传 accessId");
6987
+ }
6988
+ if (candidates.length > 1 && !accessPath) {
6989
+ throw new Error("匹配到多个访问入口,请补充 path 或直接传 accessId");
6990
+ }
6991
+ resolvedAccessId = candidates[0]?.APIId;
6992
+ }
6993
+ if (!resolvedAccessId) {
6994
+ throw new Error("无法解析访问入口 accessId,请改为显式传入 accessId");
6995
+ }
6864
6996
  const result = await cloudbase.access.deleteAccess({
6865
6997
  ...(input.targetName ? { name: input.targetName } : {}),
6866
- ...(accessPath ? { path: accessPath } : {}),
6998
+ apiId: resolvedAccessId,
6867
6999
  });
6868
7000
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
6869
7001
  return buildEnvelope({
6870
7002
  action: input.action,
6871
7003
  targetName: input.targetName ?? null,
6872
7004
  path: accessPath ?? null,
7005
+ accessId: resolvedAccessId,
6873
7006
  raw: result,
6874
7007
  }, "网关访问入口删除成功");
6875
7008
  }
@@ -6971,6 +7104,7 @@ function registerGatewayTools(server) {
6971
7104
  domain: zod_1.z.string().optional().describe("自定义域名"),
6972
7105
  certificateId: zod_1.z.string().optional().describe("证书 ID"),
6973
7106
  accessName: zod_1.z.string().optional().describe("访问入口名称,保留字段"),
7107
+ accessId: zod_1.z.string().optional().describe("访问入口 ID。deleteAccess 时可直接传入,避免参数歧义"),
6974
7108
  },
6975
7109
  annotations: {
6976
7110
  readOnlyHint: false,
@@ -9122,7 +9256,7 @@ class TelemetryReporter {
9122
9256
  const nodeVersion = process.version; // Node.js版本
9123
9257
  const arch = os_1.default.arch(); // 系统架构
9124
9258
  // 从构建时注入的版本号获取MCP版本信息
9125
- const mcpVersion = process.env.npm_package_version || "2.19.5" || 0;
9259
+ const mcpVersion = process.env.npm_package_version || "2.20.0" || 0;
9126
9260
  return {
9127
9261
  userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
9128
9262
  deviceId: this.deviceId,
@@ -11023,10 +11157,11 @@ function registerPermissionTools(server) {
11023
11157
  roleId: zod_1.z.string().optional(),
11024
11158
  roleIds: zod_1.z.array(zod_1.z.string()).optional(),
11025
11159
  roleName: zod_1.z.string().optional(),
11026
- roleIdentity: zod_1.z.string().optional(),
11160
+ roleIdentity: zod_1.z.string().optional().describe("角色标识符(字母/数字/_-:@.),action=createRole 时必填,用于程序化引用角色"),
11027
11161
  description: zod_1.z.string().optional(),
11028
11162
  memberUids: zod_1.z.array(zod_1.z.string()).optional(),
11029
11163
  policies: zod_1.z.array(zod_1.z.record(zod_1.z.any())).optional(),
11164
+ policyIds: zod_1.z.array(zod_1.z.string()).optional().describe("策略 ID 列表(当前不支持直接按 ID 绑定,请改传 policies 详情对象)"),
11030
11165
  uid: zod_1.z.string().optional(),
11031
11166
  uids: zod_1.z.array(zod_1.z.string()).optional(),
11032
11167
  username: zod_1.z.string().optional(),
@@ -11040,7 +11175,7 @@ function registerPermissionTools(server) {
11040
11175
  openWorldHint: true,
11041
11176
  category: "permissions",
11042
11177
  },
11043
- }, async ({ action, resourceType, resourceId, permission, securityRule, roleId, roleIds, roleName, roleIdentity, description, memberUids, policies, uid, uids, username, password, userStatus, }) => withEnvelope(async () => {
11178
+ }, async ({ action, resourceType, resourceId, permission, securityRule, roleId, roleIds, roleName, roleIdentity, description, memberUids, policies, policyIds, uid, uids, username, password, userStatus, }) => withEnvelope(async () => {
11044
11179
  const envId = await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
11045
11180
  const cloudbase = await getManager();
11046
11181
  const normalizedPolicies = normalizeRecordArray(policies, "policies");
@@ -11100,6 +11235,14 @@ function registerPermissionTools(server) {
11100
11235
  if (!roleId) {
11101
11236
  throw new Error(`action=${action} 时必须提供 roleId`);
11102
11237
  }
11238
+ if (action === "addRolePolicies" || action === "removeRolePolicies") {
11239
+ if (policyIds?.length) {
11240
+ throw new Error(`action=${action} 暂不支持 policyIds。请改传 policies,且每项至少包含 ResourceType 和 Resource。`);
11241
+ }
11242
+ if (!normalizedPolicies?.length) {
11243
+ throw new Error(`action=${action} 时必须提供 policies,且每项至少包含 ResourceType 和 Resource。`);
11244
+ }
11245
+ }
11103
11246
  const result = await cloudbase.permission.modifyRole({
11104
11247
  roleId,
11105
11248
  ...(action === "updateRole"
@@ -11669,7 +11812,7 @@ function registerSetupTools(server) {
11669
11812
  server.registerTool("downloadTemplate", {
11670
11813
  title: "下载项目模板",
11671
11814
  description: `自动下载并部署CloudBase项目模板。
11672
- **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.19.5" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
11815
+ **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.20.0" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
11673
11816
  inputSchema: {
11674
11817
  template: zod_1.z
11675
11818
  .enum(["react", "vue", "miniprogram", "uniapp", "rules"])
@@ -13467,8 +13610,21 @@ function updateAuthProgressState(partial) {
13467
13610
  return getAuthProgressStateSync();
13468
13611
  }
13469
13612
  function normalizeLoginStateFromEnvVars(options) {
13613
+ if (options?.ignoreEnvVars) {
13614
+ return null;
13615
+ }
13616
+ // 优先检查 CloudBase API Key(优先级高于 TENCENTCLOUD_*)
13617
+ const { CLOUDBASE_API_KEY, CLOUDBASE_ENV_ID: CLOUDBASE_ENV, } = process.env;
13618
+ if (CLOUDBASE_API_KEY && CLOUDBASE_ENV) {
13619
+ return {
13620
+ _type: 'api_key',
13621
+ apiKey: CLOUDBASE_API_KEY,
13622
+ envId: CLOUDBASE_ENV,
13623
+ };
13624
+ }
13625
+ // 然后检查腾讯云密钥环境变量
13470
13626
  const { TENCENTCLOUD_SECRETID, TENCENTCLOUD_SECRETKEY, TENCENTCLOUD_SESSIONTOKEN, } = process.env;
13471
- if (!options?.ignoreEnvVars && TENCENTCLOUD_SECRETID && TENCENTCLOUD_SECRETKEY) {
13627
+ if (TENCENTCLOUD_SECRETID && TENCENTCLOUD_SECRETKEY) {
13472
13628
  return {
13473
13629
  secretId: TENCENTCLOUD_SECRETID,
13474
13630
  secretKey: TENCENTCLOUD_SECRETKEY,
@@ -13611,13 +13767,30 @@ function resetAuthProgressState() {
13611
13767
  async function peekLoginState(options) {
13612
13768
  const envVarLoginState = normalizeLoginStateFromEnvVars(options);
13613
13769
  if (envVarLoginState) {
13770
+ // API Key 模式:需要先用 API Key 换取临时密钥
13771
+ if ('_type' in envVarLoginState && envVarLoginState._type === 'api_key') {
13772
+ (0, logger_js_1.debug)("peekLoginState: detected CLOUDBASE_API_KEY env var");
13773
+ try {
13774
+ // 优先使用 IDE 注入的工作目录,其次 process.cwd()
13775
+ const projectCwd = process.env.WORKSPACE_FOLDER_PATHS || process.cwd();
13776
+ const credential = await auth.loginByApiKey(envVarLoginState.apiKey, envVarLoginState.envId, { cwd: projectCwd });
13777
+ return credential;
13778
+ }
13779
+ catch (e) {
13780
+ (0, logger_js_1.debug)("peekLoginState: API Key login failed", { error: e instanceof Error ? e.message : String(e) });
13781
+ return null;
13782
+ }
13783
+ }
13784
+ // 腾讯云密钥模式:直接返回
13614
13785
  (0, logger_js_1.debug)("loginByApiSecret");
13615
13786
  return envVarLoginState;
13616
13787
  }
13788
+ // 尝试从 toolbox 本地存储读取(包括项目级 API Key 凭证)
13617
13789
  return auth.getLoginState();
13618
13790
  }
13619
13791
  async function ensureLogin(options) {
13620
13792
  (0, logger_js_1.debug)("TENCENTCLOUD_SECRETID", { hasSecretId: !!process.env.TENCENTCLOUD_SECRETID });
13793
+ (0, logger_js_1.debug)("CLOUDBASE_API_KEY", { hasApiKey: !!process.env.CLOUDBASE_API_KEY });
13621
13794
  const loginState = await peekLoginState({
13622
13795
  ignoreEnvVars: options?.ignoreEnvVars,
13623
13796
  });
@@ -13693,7 +13866,8 @@ async function getLoginState(options) {
13693
13866
  return ensureLogin(options);
13694
13867
  }
13695
13868
  async function logout() {
13696
- const result = await auth.logout();
13869
+ const cwd = process.env.WORKSPACE_FOLDER_PATHS || process.cwd();
13870
+ const result = await auth.logout({ cwd });
13697
13871
  resetAuthProgressState();
13698
13872
  return result;
13699
13873
  }
@@ -14376,10 +14550,11 @@ function normalizeAgentPayload(params) {
14376
14550
  if (!params) {
14377
14551
  return {};
14378
14552
  }
14379
- const { name, agentId, runtime, timeout, memorySize, installDependency, ...rest } = params;
14553
+ const { name, description, agentId, runtime, timeout, memorySize, installDependency, zipFile, cosBucketRegion, tempCosObjectName, sessionConfig, ...rest } = params;
14380
14554
  return {
14381
14555
  ...rest,
14382
14556
  ...(normalizeString(name) ? { Name: normalizeString(name) } : {}),
14557
+ ...(normalizeString(description) ? { Description: normalizeString(description) } : {}),
14383
14558
  ...(normalizeString(agentId) ? { AgentId: normalizeString(agentId) } : {}),
14384
14559
  ...(normalizeString(runtime) ? { Runtime: normalizeString(runtime) } : {}),
14385
14560
  ...(normalizeNumber(timeout) !== undefined ? { Timeout: normalizeNumber(timeout) } : {}),
@@ -14389,6 +14564,14 @@ function normalizeAgentPayload(params) {
14389
14564
  ...(normalizeBoolean(installDependency) !== undefined
14390
14565
  ? { InstallDependency: normalizeBoolean(installDependency) }
14391
14566
  : {}),
14567
+ ...(normalizeString(zipFile) ? { ZipFile: normalizeString(zipFile) } : {}),
14568
+ ...(normalizeString(cosBucketRegion)
14569
+ ? { CosBucketRegion: normalizeString(cosBucketRegion) }
14570
+ : {}),
14571
+ ...(normalizeString(tempCosObjectName)
14572
+ ? { TempCosObjectName: normalizeString(tempCosObjectName) }
14573
+ : {}),
14574
+ ...(sessionConfig && typeof sessionConfig === "object" ? { SessionConfig: sessionConfig } : {}),
14392
14575
  };
14393
14576
  }
14394
14577
  function registerAgentTools(server) {
@@ -14463,6 +14646,17 @@ function registerAgentTools(server) {
14463
14646
  inputSchema: {
14464
14647
  action: zod_1.z.enum(MANAGE_AGENT_ACTIONS),
14465
14648
  agentId: zod_1.z.string().optional(),
14649
+ name: zod_1.z.string().optional(),
14650
+ description: zod_1.z.string().optional(),
14651
+ runtime: zod_1.z.string().optional(),
14652
+ timeout: zod_1.z.number().optional(),
14653
+ memorySize: zod_1.z.number().optional(),
14654
+ installDependency: zod_1.z.boolean().optional(),
14655
+ zipFile: zod_1.z.string().optional(),
14656
+ cosBucketRegion: zod_1.z.string().optional(),
14657
+ tempCosObjectName: zod_1.z.string().optional(),
14658
+ sessionConfig: zod_1.z.record(zod_1.z.any()).optional(),
14659
+ cwd: zod_1.z.string().optional(),
14466
14660
  params: zod_1.z.record(zod_1.z.any()).optional(),
14467
14661
  },
14468
14662
  annotations: {
@@ -14472,14 +14666,31 @@ function registerAgentTools(server) {
14472
14666
  openWorldHint: true,
14473
14667
  category: "agents",
14474
14668
  },
14475
- }, async ({ action, agentId, params, }) => {
14669
+ }, async ({ action, agentId, name, description, runtime, timeout, memorySize, installDependency, zipFile, cosBucketRegion, tempCosObjectName, sessionConfig, cwd, params, }) => {
14476
14670
  try {
14477
14671
  const cloudbase = await getManager();
14478
- const payload = normalizeAgentPayload(params);
14672
+ const payload = normalizeAgentPayload({
14673
+ name,
14674
+ description,
14675
+ runtime,
14676
+ timeout,
14677
+ memorySize,
14678
+ installDependency,
14679
+ zipFile,
14680
+ cosBucketRegion,
14681
+ tempCosObjectName,
14682
+ sessionConfig,
14683
+ cwd,
14684
+ ...(params ?? {}),
14685
+ });
14479
14686
  if (action === "createAgent") {
14687
+ const normalizedName = normalizeString(payload.Name);
14688
+ if (!normalizedName) {
14689
+ throw new Error("action=createAgent 时必须提供 name(可通过顶层 name 或 params.name 传入)");
14690
+ }
14480
14691
  const createPayload = {
14481
14692
  ...payload,
14482
- Name: normalizeString(payload.Name) ?? "",
14693
+ Name: normalizedName,
14483
14694
  Runtime: normalizeString(payload.Runtime) ?? "Nodejs20.19",
14484
14695
  };
14485
14696
  const result = await cloudbase.agent.createAgent(createPayload);
@@ -14607,6 +14818,8 @@ function shouldRegisterTool(toolName) {
14607
14818
  'manageCloudRun',
14608
14819
  // Download tools - local file downloads
14609
14820
  'manageStorage',
14821
+ // Apps tools - deployApp reads local filePath for code upload
14822
+ 'manageApps',
14610
14823
  ];
14611
14824
  const shouldRegister = !cloudIncompatibleTools.includes(toolName);
14612
14825
  if (!shouldRegister) {
@@ -14637,6 +14850,7 @@ const QUERY_ACTIONS = [
14637
14850
  "describeCreateResult",
14638
14851
  "describeTaskStatus",
14639
14852
  "getInstanceInfo",
14853
+ "describeInstance",
14640
14854
  ];
14641
14855
  const MANAGE_ACTIONS = [
14642
14856
  "provisionMySQL",
@@ -14900,7 +15114,9 @@ async function getSqlInstanceInfo({ getManager, cloudBaseOptions, server, }) {
14900
15114
  }
14901
15115
  catch (error) {
14902
15116
  const errorCode = extractErrorCode(error);
14903
- const isNotFound = errorCode === "FailedOperation.DataSourceNotExist";
15117
+ const errorMessage = error instanceof Error ? error.message : String(error);
15118
+ const isNotFound = errorCode === "FailedOperation.DataSourceNotExist"
15119
+ || /envLink not exist|not exist|not found/i.test(errorMessage);
14904
15120
  if (!isNotFound) {
14905
15121
  throw error;
14906
15122
  }
@@ -14910,7 +15126,7 @@ async function getSqlInstanceInfo({ getManager, cloudBaseOptions, server, }) {
14910
15126
  instanceId: "default",
14911
15127
  schema: envId,
14912
15128
  rawStatus: typeof createRawStatus === "string" ? createRawStatus : null,
14913
- status: createStatus,
15129
+ status: createStatus === "PENDING" || createStatus === "RUNNING" ? createStatus : "NOT_CREATED",
14914
15130
  createResult: createData ?? createResult,
14915
15131
  };
14916
15132
  }
@@ -15457,7 +15673,7 @@ function registerSQLDatabaseTools(server) {
15457
15673
  inputSchema: {
15458
15674
  action: zod_1.z
15459
15675
  .enum(QUERY_ACTIONS)
15460
- .describe("runQuery=execute read-only SQL; describeCreateResult=query CreateMySQL result; describeTaskStatus=query MySQL task status; getInstanceInfo=get current SQL instance context"),
15676
+ .describe("runQuery=execute read-only SQL; describeCreateResult=query CreateMySQL result; describeTaskStatus=query MySQL task status; getInstanceInfo=get current SQL instance context; describeInstance=alias of getInstanceInfo"),
15461
15677
  sql: zod_1.z
15462
15678
  .string()
15463
15679
  .optional()
@@ -15488,6 +15704,7 @@ function registerSQLDatabaseTools(server) {
15488
15704
  case "describeTaskStatus":
15489
15705
  return handleDescribeTaskStatus(args, context);
15490
15706
  case "getInstanceInfo":
15707
+ case "describeInstance":
15491
15708
  return handleGetInstanceInfo(context);
15492
15709
  default:
15493
15710
  throw new Error(`Unsupported SQL query action: ${args.action}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudbase/cloudbase-mcp",
3
- "version": "2.19.5",
3
+ "version": "2.20.0",
4
4
  "description": "腾讯云开发 MCP Server,通过AI提示词和MCP协议+云开发,让开发更智能、更高效,当你在Cursor/ VSCode GitHub Copilot/WinSurf/CodeBuddy/Augment Code/Claude Code等AI编程工具里写代码时,它能自动帮你生成可直接部署的前后端应用+小程序,并一键发布到腾讯云开发 CloudBase。",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",