@cloudbase/cloudbase-mcp 2.23.6 → 2.23.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1990,13 +1990,14 @@ async function fetchAvailableEnvCandidates(cloudBaseOptions, server) {
1990
1990
  return [];
1991
1991
  }
1992
1992
  }
1993
- const CODEBUDDY_AUTH_ACTIONS = ["status", "set_env"];
1993
+ const CODEBUDDY_AUTH_ACTIONS = ["status", "set_env", "login_by_api_key"];
1994
1994
  const DEFAULT_AUTH_ACTIONS = [
1995
1995
  "status",
1996
1996
  "start_auth",
1997
1997
  "set_env",
1998
1998
  "logout",
1999
1999
  "get_temp_credentials",
2000
+ "login_by_api_key",
2000
2001
  ];
2001
2002
  function maskSensitiveValue(value) {
2002
2003
  if (value.length <= 4) {
@@ -2553,12 +2554,12 @@ function registerEnvTools(server) {
2553
2554
  if (server.ide !== 'wxide') {
2554
2555
  server.registerTool?.("auth", {
2555
2556
  title: "CloudBase 开发阶段登录与环境",
2556
- description: "CloudBase(腾讯云开发)开发阶段登录与环境绑定。登录后即可访问云资源;环境(env)是云函数、数据库、静态托管等资源的隔离单元,绑定环境后其他 MCP 工具才能操作该环境。支持:查询状态、发起登录、绑定环境(set_env)、退出登录。",
2557
+ description: "CloudBase(腾讯云开发)开发阶段登录与环境绑定。登录后即可访问云资源;环境(env)是云函数、数据库、静态托管等资源的隔离单元,绑定环境后其他 MCP 工具才能操作该环境。支持:查询状态、发起登录、API Key登录、绑定环境(set_env)、退出登录。",
2557
2558
  inputSchema: {
2558
2559
  action: zod_1.z
2559
2560
  .enum(authActionEnum)
2560
2561
  .optional()
2561
- .describe("动作:status=查询状态,start_auth=发起登录,set_env=绑定环境(传envId),logout=退出登录"),
2562
+ .describe("动作:status=查询状态,start_auth=发起登录,login_by_api_key=API Key登录,set_env=绑定环境(传envId),logout=退出登录"),
2562
2563
  ...(supportedAuthActions.includes("start_auth")
2563
2564
  ? {
2564
2565
  authMode: zod_1.z
@@ -2583,6 +2584,18 @@ function registerEnvTools(server) {
2583
2584
  .string()
2584
2585
  .optional()
2585
2586
  .describe("环境ID(CloudBase 环境唯一标识),绑定后工具将操作该环境。action=set_env 时必填"),
2587
+ ...(supportedAuthActions.includes("login_by_api_key")
2588
+ ? {
2589
+ apiKey: zod_1.z
2590
+ .string()
2591
+ .optional()
2592
+ .describe("CloudBase API Key,action=login_by_api_key 时必填"),
2593
+ apiKeyEnvId: zod_1.z
2594
+ .string()
2595
+ .optional()
2596
+ .describe("CloudBase 环境ID(EnvId),action=login_by_api_key 时必填,用于指定 API Key 所属环境"),
2597
+ }
2598
+ : {}),
2586
2599
  ...(supportedAuthActions.includes("logout")
2587
2600
  ? {
2588
2601
  confirm: zod_1.z
@@ -2898,6 +2911,78 @@ function registerEnvTools(server) {
2898
2911
  next_step: envPreparation.nextStep,
2899
2912
  });
2900
2913
  }
2914
+ if (action === "login_by_api_key") {
2915
+ const toolApiKey = normalizeOptionalToolString(rawArgs.apiKey);
2916
+ const toolApiKeyEnvId = normalizeOptionalToolString(rawArgs.apiKeyEnvId);
2917
+ if (!toolApiKey || !toolApiKeyEnvId) {
2918
+ return (0, tool_result_js_1.buildJsonToolResult)({
2919
+ ok: false,
2920
+ code: "INVALID_ARGS",
2921
+ message: "action=login_by_api_key 时必须同时提供 apiKey 和 envId。",
2922
+ next_step: (0, tool_result_js_1.buildAuthNextStep)("login_by_api_key", {
2923
+ suggestedArgs: { action: "login_by_api_key", apiKey: "<your-api-key>", envId: "<your-env-id>" },
2924
+ }),
2925
+ });
2926
+ }
2927
+ // 写入 process.env(与 cli.ts 处理 --api-key/--env-id 的逻辑一致)
2928
+ process.env.CLOUDBASE_API_KEY = toolApiKey;
2929
+ process.env.CLOUDBASE_ENV_ID = toolApiKeyEnvId;
2930
+ try {
2931
+ const loginState = await (0, auth_js_1.peekLoginState)();
2932
+ if (loginState) {
2933
+ const envPreparation = await prepareAuthEnvironment({
2934
+ server,
2935
+ cloudBaseOptions,
2936
+ loginState,
2937
+ });
2938
+ return (0, tool_result_js_1.buildJsonToolResult)({
2939
+ ok: true,
2940
+ code: "AUTH_READY",
2941
+ message: "API Key 认证成功,已获取临时密钥。",
2942
+ auth_mode: "api_key",
2943
+ ...buildAuthEnvSetupPayload(envPreparation),
2944
+ next_step: envPreparation.nextStep,
2945
+ });
2946
+ }
2947
+ // API Key 换取失败:清理 env vars 并返回诊断信息
2948
+ delete process.env.CLOUDBASE_API_KEY;
2949
+ delete process.env.CLOUDBASE_ENV_ID;
2950
+ let diagMessage = "API Key 换取临时密钥失败。";
2951
+ diagMessage += `\n\n诊断信息:`;
2952
+ diagMessage += `\n- CLOUDBASE_ENV_ID: ${toolApiKeyEnvId}`;
2953
+ diagMessage += `\n- CLOUDBASE_API_KEY: ${toolApiKey.slice(0, 20)}...(已截断)`;
2954
+ diagMessage += `\n\n可能原因:`;
2955
+ diagMessage += `\n1. API Key 已过期或被删除`;
2956
+ diagMessage += `\n2. CLOUDBASE_ENV_ID 与 API Key 所属环境不匹配`;
2957
+ diagMessage += `\n3. 网络连接问题`;
2958
+ diagMessage += `\n\n建议:请检查 API Key 和环境 ID 是否正确。`;
2959
+ return (0, tool_result_js_1.buildJsonToolResult)({
2960
+ ok: false,
2961
+ code: "API_KEY_AUTH_FAILED",
2962
+ message: diagMessage,
2963
+ auth_mode: "api_key",
2964
+ envId: toolApiKeyEnvId,
2965
+ next_step: (0, tool_result_js_1.buildAuthNextStep)("login_by_api_key", {
2966
+ suggestedArgs: { action: "login_by_api_key", apiKey: "<your-api-key>", envId: "<your-env-id>" },
2967
+ }),
2968
+ });
2969
+ }
2970
+ catch (error) {
2971
+ // 异常时清理 env vars
2972
+ delete process.env.CLOUDBASE_API_KEY;
2973
+ delete process.env.CLOUDBASE_ENV_ID;
2974
+ const message = error instanceof Error ? error.message : String(error);
2975
+ return (0, tool_result_js_1.buildJsonToolResult)({
2976
+ ok: false,
2977
+ code: "API_KEY_AUTH_FAILED",
2978
+ message: `API Key 认证异常: ${message}`,
2979
+ auth_mode: "api_key",
2980
+ next_step: (0, tool_result_js_1.buildAuthNextStep)("login_by_api_key", {
2981
+ suggestedArgs: { action: "login_by_api_key", apiKey: "<your-api-key>", envId: "<your-env-id>" },
2982
+ }),
2983
+ });
2984
+ }
2985
+ }
2901
2986
  if (action === "set_env") {
2902
2987
  const loginState = await (0, auth_js_1.peekLoginState)();
2903
2988
  if (!loginState) {
@@ -39137,6 +39222,18 @@ function buildErrorEnvelope(error) {
39137
39222
  function normalizeString(value) {
39138
39223
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
39139
39224
  }
39225
+ /**
39226
+ * Sanitize agent name for CloudBase backend.
39227
+ * The backend creates a cloud function alias from the agent name (e.g. "agent-{name}"),
39228
+ * and SCF aliases only allow alphanumeric characters and hyphens.
39229
+ * This function replaces any character that is not [a-zA-Z0-9] with a hyphen,
39230
+ * then collapses consecutive hyphens into one.
39231
+ */
39232
+ function sanitizeAgentName(name) {
39233
+ return name
39234
+ .replace(/[^a-zA-Z0-9]+/g, "-")
39235
+ .replace(/^-+|-+$/g, "");
39236
+ }
39140
39237
  function normalizeNumber(value) {
39141
39238
  return typeof value === "number" && Number.isFinite(value) ? value : undefined;
39142
39239
  }
@@ -39291,13 +39388,19 @@ function registerAgentTools(server) {
39291
39388
  }
39292
39389
  // CloudBase 后端在创建 Agent 时会同步创建云函数,函数名/别名长度受 SCF 限制(最大 64 字符)。
39293
39390
  // envId 可能被用作前缀或别名的一部分,因此这里预留足够余量,避免后端 CreateFunction 失败。
39294
- if (normalizedName.length > 30) {
39295
- throw new Error(`agent name 过长(当前 ${normalizedName.length} 字符)。CloudBase 创建 Agent 时会同步创建云函数,` +
39296
- `函数名/别名受 SCF 64 字符限制,且 envId 可能作为前缀拼接。请将 name 控制在 30 字符以内。`);
39391
+ // 同时,后端会用 name 生成云函数 alias(如 "agent-{name}"),而 SCF alias 只允许字母、数字和连字符,
39392
+ // 因此需要对 name 做 sanitize,将非法字符(如下划线)替换为连字符。
39393
+ const sanitizedName = sanitizeAgentName(normalizedName);
39394
+ if (!sanitizedName) {
39395
+ throw new Error(`name "${normalizedName}" 经 sanitize 后为空,请使用包含字母或数字的 name`);
39396
+ }
39397
+ if (sanitizedName.length > 30) {
39398
+ throw new Error(`agent name 过长(当前 ${sanitizedName.length} 字符)。CloudBase 创建 Agent 时会同步创建云函数,` +
39399
+ `函数名/别名受 SCF 64 字符长度限制,且 envId 可能作为前缀拼接。请将 name 控制在 30 字符以内。`);
39297
39400
  }
39298
39401
  const createPayload = {
39299
39402
  ...payload,
39300
- Name: normalizedName,
39403
+ Name: sanitizedName,
39301
39404
  Runtime: normalizeString(payload.Runtime) ?? "Nodejs20.19",
39302
39405
  };
39303
39406
  const result = await cloudbase.agent.createAgent(createPayload);
@@ -113931,18 +114034,31 @@ function checkIfAgentProject(projectPath) {
113931
114034
  }
113932
114035
  }
113933
114036
  /**
113934
- * Validate and normalize file path
114037
+ * Validate and normalize file path.
114038
+ * Accepts absolute paths as-is; for relative paths, resolves against CWD and
114039
+ * ensures the result does not escape the CWD (path-traversal protection).
113935
114040
  * @param inputPath User provided path
113936
114041
  * @returns Absolute path
113937
114042
  */
113938
114043
  function validateAndNormalizePath(inputPath) {
113939
- let normalizedPath = path_1.default.resolve(inputPath);
113940
- // Security check - ensure path is within current working directory
114044
+ const normalizedPath = path_1.default.resolve(inputPath);
114045
+ // On Windows, path.resolve may return a path on a different drive/UNC share.
114046
+ // That is safe — the user explicitly provided an absolute path there.
114047
+ // Only apply the traversal check when the resolved path shares the same root
114048
+ // as CWD (i.e. the path was relative or on the same drive).
113941
114049
  const cwd = process.cwd();
113942
- const prefix = cwd.endsWith(path_1.default.sep) ? cwd : cwd + path_1.default.sep;
113943
- if (!normalizedPath.startsWith(prefix) && normalizedPath !== cwd) {
113944
- throw new Error(`Path must be within current working directory: ${cwd}`);
113945
- }
114050
+ const cwdRoot = path_1.default.parse(cwd).root;
114051
+ const pathRoot = path_1.default.parse(normalizedPath).root;
114052
+ if (cwdRoot === pathRoot) {
114053
+ // Same filesystem root — ensure the resolved path is still inside CWD
114054
+ // (or is exactly CWD) to block "../" traversal.
114055
+ const prefix = cwd.endsWith(path_1.default.sep) ? cwd : cwd + path_1.default.sep;
114056
+ if (!normalizedPath.startsWith(prefix) && normalizedPath !== cwd) {
114057
+ throw new Error(`Path must be within current working directory: ${cwd}`);
114058
+ }
114059
+ }
114060
+ // Cross-root absolute paths (e.g. D:\ on Windows when CWD is C:\) are
114061
+ // allowed — the user own the machine and the path is explicitly absolute.
113946
114062
  return normalizedPath;
113947
114063
  }
113948
114064
  function buildManageCloudRunErrorMessage(action, serverName, error) {
@@ -136691,7 +136807,7 @@ class TelemetryReporter {
136691
136807
  const nodeVersion = process.version; // Node.js版本
136692
136808
  const arch = os_1.default.arch(); // 系统架构
136693
136809
  // 从构建时注入的版本号获取MCP版本信息
136694
- const mcpVersion = process.env.npm_package_version || "2.23.6" || 0;
136810
+ const mcpVersion = process.env.npm_package_version || "2.23.7" || 0;
136695
136811
  return {
136696
136812
  userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
136697
136813
  deviceId: this.deviceId,
@@ -196476,7 +196592,7 @@ ${envIdSection}
196476
196592
  ## 环境信息
196477
196593
  - 操作系统: ${os_1.default.type()} ${os_1.default.release()}
196478
196594
  - Node.js版本: ${process.version}
196479
- - MCP 版本:${process.env.npm_package_version || "2.23.6" || 0}
196595
+ - MCP 版本:${process.env.npm_package_version || "2.23.7" || 0}
196480
196596
  - 系统架构: ${os_1.default.arch()}
196481
196597
  - 时间: ${new Date().toISOString()}
196482
196598
  - 请求ID: ${requestId}
@@ -196557,7 +196673,7 @@ function createWrappedHandler(name, handler, server) {
196557
196673
  requestId: (typeof error === 'object' && error && 'requestId' in error) ? error.requestId : '',
196558
196674
  ide: server.ide || process.env.INTEGRATION_IDE || ''
196559
196675
  });
196560
- const mcpVersion = true ? "2.23.6" : 0;
196676
+ const mcpVersion = true ? "2.23.7" : 0;
196561
196677
  const requestIdSuffix = requestId ? `\n🆔 RequestId: ${requestId}` : '';
196562
196678
  const enhancedErrorMessage = `${errorMessage}${requestIdSuffix}\n\n📦 CloudBase MCP v${mcpVersion}\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
196563
196679
  // 创建新的错误对象,保持原有的错误类型但更新消息
@@ -212571,7 +212687,7 @@ function registerSetupTools(server) {
212571
212687
  server.registerTool("downloadTemplate", {
212572
212688
  title: "下载项目模板",
212573
212689
  description: `自动下载并部署CloudBase项目模板。
212574
- **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.6" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
212690
+ **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.7" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
212575
212691
  inputSchema: {
212576
212692
  template: zod_1.z
212577
212693
  .enum(["react", "vue", "miniprogram", "uniapp", "rules"])
@@ -229992,6 +230108,7 @@ function registerGatewayTools(server) {
229992
230108
  const cloudbase = await getManager();
229993
230109
  const accessPath = input.path ? normalizeAccessPath(input.path) : undefined;
229994
230110
  let resolvedAccessId = input.accessId;
230111
+ let resolvedName;
229995
230112
  if (!resolvedAccessId) {
229996
230113
  if (!input.targetName && !accessPath) {
229997
230114
  throw new Error("action=deleteAccess 时至少需要提供 accessId,或提供 targetName/path 用于定位入口");
@@ -230013,18 +230130,32 @@ function registerGatewayTools(server) {
230013
230130
  throw new Error("匹配到多个访问入口,请补充 path 或直接传 accessId");
230014
230131
  }
230015
230132
  resolvedAccessId = candidates[0]?.APIId;
230133
+ resolvedName = candidates[0]?.Name;
230134
+ }
230135
+ else {
230136
+ // 仅传入 accessId 时,先查询获取 name 等信息,确保删除参数完整
230137
+ try {
230138
+ const accessList = await cloudbase.access.getAccessList({});
230139
+ const match = (accessList.APISet ?? []).find((item) => item.APIId === resolvedAccessId);
230140
+ if (match) {
230141
+ resolvedName = match.Name;
230142
+ }
230143
+ }
230144
+ catch {
230145
+ // 查询失败不影响主流程,继续用 accessId 删除
230146
+ }
230016
230147
  }
230017
230148
  if (!resolvedAccessId) {
230018
230149
  throw new Error("无法解析访问入口 accessId,请改为显式传入 accessId");
230019
230150
  }
230020
230151
  const result = await cloudbase.access.deleteAccess({
230021
- ...(input.targetName ? { name: input.targetName } : {}),
230152
+ ...(resolvedName ? { name: resolvedName } : {}),
230022
230153
  apiId: resolvedAccessId,
230023
230154
  });
230024
230155
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
230025
230156
  return buildEnvelope({
230026
230157
  action: input.action,
230027
- targetName: input.targetName ?? null,
230158
+ targetName: resolvedName ?? input.targetName ?? null,
230028
230159
  path: accessPath ?? null,
230029
230160
  accessId: resolvedAccessId,
230030
230161
  raw: result,
package/dist/index.js CHANGED
@@ -261,13 +261,14 @@ async function fetchAvailableEnvCandidates(cloudBaseOptions, server) {
261
261
  return [];
262
262
  }
263
263
  }
264
- const CODEBUDDY_AUTH_ACTIONS = ["status", "set_env"];
264
+ const CODEBUDDY_AUTH_ACTIONS = ["status", "set_env", "login_by_api_key"];
265
265
  const DEFAULT_AUTH_ACTIONS = [
266
266
  "status",
267
267
  "start_auth",
268
268
  "set_env",
269
269
  "logout",
270
270
  "get_temp_credentials",
271
+ "login_by_api_key",
271
272
  ];
272
273
  function maskSensitiveValue(value) {
273
274
  if (value.length <= 4) {
@@ -824,12 +825,12 @@ function registerEnvTools(server) {
824
825
  if (server.ide !== 'wxide') {
825
826
  server.registerTool?.("auth", {
826
827
  title: "CloudBase 开发阶段登录与环境",
827
- description: "CloudBase(腾讯云开发)开发阶段登录与环境绑定。登录后即可访问云资源;环境(env)是云函数、数据库、静态托管等资源的隔离单元,绑定环境后其他 MCP 工具才能操作该环境。支持:查询状态、发起登录、绑定环境(set_env)、退出登录。",
828
+ description: "CloudBase(腾讯云开发)开发阶段登录与环境绑定。登录后即可访问云资源;环境(env)是云函数、数据库、静态托管等资源的隔离单元,绑定环境后其他 MCP 工具才能操作该环境。支持:查询状态、发起登录、API Key登录、绑定环境(set_env)、退出登录。",
828
829
  inputSchema: {
829
830
  action: zod_1.z
830
831
  .enum(authActionEnum)
831
832
  .optional()
832
- .describe("动作:status=查询状态,start_auth=发起登录,set_env=绑定环境(传envId),logout=退出登录"),
833
+ .describe("动作:status=查询状态,start_auth=发起登录,login_by_api_key=API Key登录,set_env=绑定环境(传envId),logout=退出登录"),
833
834
  ...(supportedAuthActions.includes("start_auth")
834
835
  ? {
835
836
  authMode: zod_1.z
@@ -854,6 +855,18 @@ function registerEnvTools(server) {
854
855
  .string()
855
856
  .optional()
856
857
  .describe("环境ID(CloudBase 环境唯一标识),绑定后工具将操作该环境。action=set_env 时必填"),
858
+ ...(supportedAuthActions.includes("login_by_api_key")
859
+ ? {
860
+ apiKey: zod_1.z
861
+ .string()
862
+ .optional()
863
+ .describe("CloudBase API Key,action=login_by_api_key 时必填"),
864
+ apiKeyEnvId: zod_1.z
865
+ .string()
866
+ .optional()
867
+ .describe("CloudBase 环境ID(EnvId),action=login_by_api_key 时必填,用于指定 API Key 所属环境"),
868
+ }
869
+ : {}),
857
870
  ...(supportedAuthActions.includes("logout")
858
871
  ? {
859
872
  confirm: zod_1.z
@@ -1169,6 +1182,78 @@ function registerEnvTools(server) {
1169
1182
  next_step: envPreparation.nextStep,
1170
1183
  });
1171
1184
  }
1185
+ if (action === "login_by_api_key") {
1186
+ const toolApiKey = normalizeOptionalToolString(rawArgs.apiKey);
1187
+ const toolApiKeyEnvId = normalizeOptionalToolString(rawArgs.apiKeyEnvId);
1188
+ if (!toolApiKey || !toolApiKeyEnvId) {
1189
+ return (0, tool_result_js_1.buildJsonToolResult)({
1190
+ ok: false,
1191
+ code: "INVALID_ARGS",
1192
+ message: "action=login_by_api_key 时必须同时提供 apiKey 和 envId。",
1193
+ next_step: (0, tool_result_js_1.buildAuthNextStep)("login_by_api_key", {
1194
+ suggestedArgs: { action: "login_by_api_key", apiKey: "<your-api-key>", envId: "<your-env-id>" },
1195
+ }),
1196
+ });
1197
+ }
1198
+ // 写入 process.env(与 cli.ts 处理 --api-key/--env-id 的逻辑一致)
1199
+ process.env.CLOUDBASE_API_KEY = toolApiKey;
1200
+ process.env.CLOUDBASE_ENV_ID = toolApiKeyEnvId;
1201
+ try {
1202
+ const loginState = await (0, auth_js_1.peekLoginState)();
1203
+ if (loginState) {
1204
+ const envPreparation = await prepareAuthEnvironment({
1205
+ server,
1206
+ cloudBaseOptions,
1207
+ loginState,
1208
+ });
1209
+ return (0, tool_result_js_1.buildJsonToolResult)({
1210
+ ok: true,
1211
+ code: "AUTH_READY",
1212
+ message: "API Key 认证成功,已获取临时密钥。",
1213
+ auth_mode: "api_key",
1214
+ ...buildAuthEnvSetupPayload(envPreparation),
1215
+ next_step: envPreparation.nextStep,
1216
+ });
1217
+ }
1218
+ // API Key 换取失败:清理 env vars 并返回诊断信息
1219
+ delete process.env.CLOUDBASE_API_KEY;
1220
+ delete process.env.CLOUDBASE_ENV_ID;
1221
+ let diagMessage = "API Key 换取临时密钥失败。";
1222
+ diagMessage += `\n\n诊断信息:`;
1223
+ diagMessage += `\n- CLOUDBASE_ENV_ID: ${toolApiKeyEnvId}`;
1224
+ diagMessage += `\n- CLOUDBASE_API_KEY: ${toolApiKey.slice(0, 20)}...(已截断)`;
1225
+ diagMessage += `\n\n可能原因:`;
1226
+ diagMessage += `\n1. API Key 已过期或被删除`;
1227
+ diagMessage += `\n2. CLOUDBASE_ENV_ID 与 API Key 所属环境不匹配`;
1228
+ diagMessage += `\n3. 网络连接问题`;
1229
+ diagMessage += `\n\n建议:请检查 API Key 和环境 ID 是否正确。`;
1230
+ return (0, tool_result_js_1.buildJsonToolResult)({
1231
+ ok: false,
1232
+ code: "API_KEY_AUTH_FAILED",
1233
+ message: diagMessage,
1234
+ auth_mode: "api_key",
1235
+ envId: toolApiKeyEnvId,
1236
+ next_step: (0, tool_result_js_1.buildAuthNextStep)("login_by_api_key", {
1237
+ suggestedArgs: { action: "login_by_api_key", apiKey: "<your-api-key>", envId: "<your-env-id>" },
1238
+ }),
1239
+ });
1240
+ }
1241
+ catch (error) {
1242
+ // 异常时清理 env vars
1243
+ delete process.env.CLOUDBASE_API_KEY;
1244
+ delete process.env.CLOUDBASE_ENV_ID;
1245
+ const message = error instanceof Error ? error.message : String(error);
1246
+ return (0, tool_result_js_1.buildJsonToolResult)({
1247
+ ok: false,
1248
+ code: "API_KEY_AUTH_FAILED",
1249
+ message: `API Key 认证异常: ${message}`,
1250
+ auth_mode: "api_key",
1251
+ next_step: (0, tool_result_js_1.buildAuthNextStep)("login_by_api_key", {
1252
+ suggestedArgs: { action: "login_by_api_key", apiKey: "<your-api-key>", envId: "<your-env-id>" },
1253
+ }),
1254
+ });
1255
+ }
1256
+ }
1172
1257
  if (action === "set_env") {
1173
1258
  const loginState = await (0, auth_js_1.peekLoginState)();
1174
1259
  if (!loginState) {
@@ -2117,7 +2202,7 @@ ${envIdSection}
2117
2202
  ## 环境信息
2118
2203
  - 操作系统: ${os_1.default.type()} ${os_1.default.release()}
2119
2204
  - Node.js版本: ${process.version}
2120
- - MCP 版本:${process.env.npm_package_version || "2.23.6" || 0}
2205
+ - MCP 版本:${process.env.npm_package_version || "2.23.7" || 0}
2121
2206
  - 系统架构: ${os_1.default.arch()}
2122
2207
  - 时间: ${new Date().toISOString()}
2123
2208
  - 请求ID: ${requestId}
@@ -2198,7 +2283,7 @@ function createWrappedHandler(name, handler, server) {
2198
2283
  requestId: (typeof error === 'object' && error && 'requestId' in error) ? error.requestId : '',
2199
2284
  ide: server.ide || process.env.INTEGRATION_IDE || ''
2200
2285
  });
2201
- const mcpVersion = true ? "2.23.6" : 0;
2286
+ const mcpVersion = true ? "2.23.7" : 0;
2202
2287
  const requestIdSuffix = requestId ? `\n🆔 RequestId: ${requestId}` : '';
2203
2288
  const enhancedErrorMessage = `${errorMessage}${requestIdSuffix}\n\n📦 CloudBase MCP v${mcpVersion}\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
2204
2289
  // 创建新的错误对象,保持原有的错误类型但更新消息
@@ -7367,6 +7452,7 @@ function registerGatewayTools(server) {
7367
7452
  const cloudbase = await getManager();
7368
7453
  const accessPath = input.path ? normalizeAccessPath(input.path) : undefined;
7369
7454
  let resolvedAccessId = input.accessId;
7455
+ let resolvedName;
7370
7456
  if (!resolvedAccessId) {
7371
7457
  if (!input.targetName && !accessPath) {
7372
7458
  throw new Error("action=deleteAccess 时至少需要提供 accessId,或提供 targetName/path 用于定位入口");
@@ -7388,18 +7474,32 @@ function registerGatewayTools(server) {
7388
7474
  throw new Error("匹配到多个访问入口,请补充 path 或直接传 accessId");
7389
7475
  }
7390
7476
  resolvedAccessId = candidates[0]?.APIId;
7477
+ resolvedName = candidates[0]?.Name;
7478
+ }
7479
+ else {
7480
+ // 仅传入 accessId 时,先查询获取 name 等信息,确保删除参数完整
7481
+ try {
7482
+ const accessList = await cloudbase.access.getAccessList({});
7483
+ const match = (accessList.APISet ?? []).find((item) => item.APIId === resolvedAccessId);
7484
+ if (match) {
7485
+ resolvedName = match.Name;
7486
+ }
7487
+ }
7488
+ catch {
7489
+ // 查询失败不影响主流程,继续用 accessId 删除
7490
+ }
7391
7491
  }
7392
7492
  if (!resolvedAccessId) {
7393
7493
  throw new Error("无法解析访问入口 accessId,请改为显式传入 accessId");
7394
7494
  }
7395
7495
  const result = await cloudbase.access.deleteAccess({
7396
- ...(input.targetName ? { name: input.targetName } : {}),
7496
+ ...(resolvedName ? { name: resolvedName } : {}),
7397
7497
  apiId: resolvedAccessId,
7398
7498
  });
7399
7499
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
7400
7500
  return buildEnvelope({
7401
7501
  action: input.action,
7402
- targetName: input.targetName ?? null,
7502
+ targetName: resolvedName ?? input.targetName ?? null,
7403
7503
  path: accessPath ?? null,
7404
7504
  accessId: resolvedAccessId,
7405
7505
  raw: result,
@@ -8231,18 +8331,31 @@ function checkIfAgentProject(projectPath) {
8231
8331
  }
8232
8332
  }
8233
8333
  /**
8234
- * Validate and normalize file path
8334
+ * Validate and normalize file path.
8335
+ * Accepts absolute paths as-is; for relative paths, resolves against CWD and
8336
+ * ensures the result does not escape the CWD (path-traversal protection).
8235
8337
  * @param inputPath User provided path
8236
8338
  * @returns Absolute path
8237
8339
  */
8238
8340
  function validateAndNormalizePath(inputPath) {
8239
- let normalizedPath = path_1.default.resolve(inputPath);
8240
- // Security check - ensure path is within current working directory
8341
+ const normalizedPath = path_1.default.resolve(inputPath);
8342
+ // On Windows, path.resolve may return a path on a different drive/UNC share.
8343
+ // That is safe — the user explicitly provided an absolute path there.
8344
+ // Only apply the traversal check when the resolved path shares the same root
8345
+ // as CWD (i.e. the path was relative or on the same drive).
8241
8346
  const cwd = process.cwd();
8242
- const prefix = cwd.endsWith(path_1.default.sep) ? cwd : cwd + path_1.default.sep;
8243
- if (!normalizedPath.startsWith(prefix) && normalizedPath !== cwd) {
8244
- throw new Error(`Path must be within current working directory: ${cwd}`);
8245
- }
8347
+ const cwdRoot = path_1.default.parse(cwd).root;
8348
+ const pathRoot = path_1.default.parse(normalizedPath).root;
8349
+ if (cwdRoot === pathRoot) {
8350
+ // Same filesystem root — ensure the resolved path is still inside CWD
8351
+ // (or is exactly CWD) to block "../" traversal.
8352
+ const prefix = cwd.endsWith(path_1.default.sep) ? cwd : cwd + path_1.default.sep;
8353
+ if (!normalizedPath.startsWith(prefix) && normalizedPath !== cwd) {
8354
+ throw new Error(`Path must be within current working directory: ${cwd}`);
8355
+ }
8356
+ }
8357
+ // Cross-root absolute paths (e.g. D:\ on Windows when CWD is C:\) are
8358
+ // allowed — the user own the machine and the path is explicitly absolute.
8246
8359
  return normalizedPath;
8247
8360
  }
8248
8361
  function buildManageCloudRunErrorMessage(action, serverName, error) {
@@ -9775,7 +9888,7 @@ class TelemetryReporter {
9775
9888
  const nodeVersion = process.version; // Node.js版本
9776
9889
  const arch = os_1.default.arch(); // 系统架构
9777
9890
  // 从构建时注入的版本号获取MCP版本信息
9778
- const mcpVersion = process.env.npm_package_version || "2.23.6" || 0;
9891
+ const mcpVersion = process.env.npm_package_version || "2.23.7" || 0;
9779
9892
  return {
9780
9893
  userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
9781
9894
  deviceId: this.deviceId,
@@ -13786,7 +13899,7 @@ function registerSetupTools(server) {
13786
13899
  server.registerTool("downloadTemplate", {
13787
13900
  title: "下载项目模板",
13788
13901
  description: `自动下载并部署CloudBase项目模板。
13789
- **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.6" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
13902
+ **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.7" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
13790
13903
  inputSchema: {
13791
13904
  template: zod_1.z
13792
13905
  .enum(["react", "vue", "miniprogram", "uniapp", "rules"])
@@ -16939,6 +17052,18 @@ function buildErrorEnvelope(error) {
16939
17052
  function normalizeString(value) {
16940
17053
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
16941
17054
  }
17055
+ /**
17056
+ * Sanitize agent name for CloudBase backend.
17057
+ * The backend creates a cloud function alias from the agent name (e.g. "agent-{name}"),
17058
+ * and SCF aliases only allow alphanumeric characters and hyphens.
17059
+ * This function replaces any character that is not [a-zA-Z0-9] with a hyphen,
17060
+ * then collapses consecutive hyphens into one.
17061
+ */
17062
+ function sanitizeAgentName(name) {
17063
+ return name
17064
+ .replace(/[^a-zA-Z0-9]+/g, "-")
17065
+ .replace(/^-+|-+$/g, "");
17066
+ }
16942
17067
  function normalizeNumber(value) {
16943
17068
  return typeof value === "number" && Number.isFinite(value) ? value : undefined;
16944
17069
  }
@@ -17093,13 +17218,19 @@ function registerAgentTools(server) {
17093
17218
  }
17094
17219
  // CloudBase 后端在创建 Agent 时会同步创建云函数,函数名/别名长度受 SCF 限制(最大 64 字符)。
17095
17220
  // envId 可能被用作前缀或别名的一部分,因此这里预留足够余量,避免后端 CreateFunction 失败。
17096
- if (normalizedName.length > 30) {
17097
- throw new Error(`agent name 过长(当前 ${normalizedName.length} 字符)。CloudBase 创建 Agent 时会同步创建云函数,` +
17098
- `函数名/别名受 SCF 64 字符限制,且 envId 可能作为前缀拼接。请将 name 控制在 30 字符以内。`);
17221
+ // 同时,后端会用 name 生成云函数 alias(如 "agent-{name}"),而 SCF alias 只允许字母、数字和连字符,
17222
+ // 因此需要对 name 做 sanitize,将非法字符(如下划线)替换为连字符。
17223
+ const sanitizedName = sanitizeAgentName(normalizedName);
17224
+ if (!sanitizedName) {
17225
+ throw new Error(`name "${normalizedName}" 经 sanitize 后为空,请使用包含字母或数字的 name`);
17226
+ }
17227
+ if (sanitizedName.length > 30) {
17228
+ throw new Error(`agent name 过长(当前 ${sanitizedName.length} 字符)。CloudBase 创建 Agent 时会同步创建云函数,` +
17229
+ `函数名/别名受 SCF 64 字符长度限制,且 envId 可能作为前缀拼接。请将 name 控制在 30 字符以内。`);
17099
17230
  }
17100
17231
  const createPayload = {
17101
17232
  ...payload,
17102
- Name: normalizedName,
17233
+ Name: sanitizedName,
17103
17234
  Runtime: normalizeString(payload.Runtime) ?? "Nodejs20.19",
17104
17235
  };
17105
17236
  const result = await cloudbase.agent.createAgent(createPayload);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudbase/cloudbase-mcp",
3
- "version": "2.23.6",
3
+ "version": "2.23.7",
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",