@cloudbase/cloudbase-mcp 2.20.2 → 2.21.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.js CHANGED
@@ -1703,7 +1703,7 @@ ${envIdSection}
1703
1703
  ## 环境信息
1704
1704
  - 操作系统: ${os_1.default.type()} ${os_1.default.release()}
1705
1705
  - Node.js版本: ${process.version}
1706
- - MCP 版本:${process.env.npm_package_version || "2.20.2" || 0}
1706
+ - MCP 版本:${process.env.npm_package_version || "2.21.0" || 0}
1707
1707
  - 系统架构: ${os_1.default.arch()}
1708
1708
  - 时间: ${new Date().toISOString()}
1709
1709
  - 请求ID: ${requestId}
@@ -1784,7 +1784,7 @@ function createWrappedHandler(name, handler, server) {
1784
1784
  requestId: (typeof error === 'object' && error && 'requestId' in error) ? error.requestId : '',
1785
1785
  ide: server.ide || process.env.INTEGRATION_IDE || ''
1786
1786
  });
1787
- const mcpVersion = true ? "2.20.2" : 0;
1787
+ const mcpVersion = true ? "2.21.0" : 0;
1788
1788
  const requestIdSuffix = requestId ? `\n🆔 RequestId: ${requestId}` : '';
1789
1789
  const enhancedErrorMessage = `${errorMessage}${requestIdSuffix}\n\n📦 CloudBase MCP v${mcpVersion}\n🔗 遇到问题?请复制以下链接到浏览器打开\n即可自动携带错误详情快速创建 GitHub Issue:\n${issueLink}`;
1790
1790
  // 创建新的错误对象,保持原有的错误类型但更新消息
@@ -7349,7 +7349,7 @@ function registerStorageTools(server) {
7349
7349
  // Tool 1: queryStorage - 查询存储信息(只读操作)
7350
7350
  server.registerTool("queryStorage", {
7351
7351
  title: "查询 CloudBase 存储信息",
7352
- description: "查询 CloudBase 云存储信息,支持列出目录文件、获取文件信息、获取临时下载链接等只读操作。返回的文件信息包括文件名、大小、修改时间、下载链接等。注意:action=url 返回的 temporaryUrl 是临时签名链接,有效期由 maxAge 参数决定(默认1小时),不要当作永久公网地址使用。工具还会基于 DescribeEnvs 返回的 Storages[0].CdnDomain 推导出 publicUrl,⚠️ 警告:publicUrl 仅在存储桶 ACL 为公有读(所有用户可读)时才能被匿名访问;默认私有读写存储桶返回的 publicUrl 会 403,此时请继续使用 temporaryUrl 或先通过控制台/SDK 将目标路径设置为公有读。",
7352
+ description: "查询 CloudBase 云存储信息,支持列出目录文件、获取文件信息、获取临时下载链接等只读操作。返回的文件信息包括文件名、大小、修改时间、下载链接等。注意:action=url 返回的 temporaryUrl 是临时签名链接,有效期由 maxAge 参数决定(默认1小时),不要当作永久公网地址使用。工具还会基于 DescribeEnvs 返回的 Storages[0].CdnDomain 推导出 publicUrl,⚠️ 警告:publicUrl 仅在存储桶 ACL 为公有读(所有用户可读)时才能被匿名访问;默认私有读写存储桶返回的 publicUrl 会 403,此时请继续使用 temporaryUrl 或先通过控制台/SDK 将目标路径设置为公有读。\n\n💡 存储桶 ACL 权限管理请使用 permissions 工具:queryPermissions(action=\"getResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\") 查询,managePermissions(action=\"updateResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\", permission=\"READONLY\") 设置。",
7353
7353
  inputSchema: queryStorageInputSchema,
7354
7354
  annotations: {
7355
7355
  readOnlyHint: true,
@@ -7478,7 +7478,7 @@ function registerStorageTools(server) {
7478
7478
  // Tool 2: manageStorage - 管理存储文件(写操作)
7479
7479
  server.registerTool("manageStorage", {
7480
7480
  title: "管理 CloudBase 存储文件",
7481
- description: "管理 CloudBase 云存储文件,仅用于 COS/Storage 对象,不用于静态网站托管。支持上传文件/目录、下载文件/目录、删除文件/目录等操作。删除操作需要设置force=true进行确认,防止误删除重要文件。注意:上传后返回的 temporaryUrl 是临时签名链接,1小时后过期,不要当作永久公网地址写入配置或持久化存储。工具还会基于 DescribeEnvs 返回的 Storages[0].CdnDomain 推导 publicUrl,⚠️ 警告:publicUrl 仅在存储桶 ACL 为公有读(所有用户可读)时才能被匿名访问;默认私有读写存储桶返回的 publicUrl 会 403,此时请继续使用 temporaryUrl 或先通过控制台/SDK 将目标路径设置为公有读。",
7481
+ description: "管理 CloudBase 云存储文件,仅用于 COS/Storage 对象,不用于静态网站托管。支持上传文件/目录、下载文件/目录、删除文件/目录等操作。删除操作需要设置force=true进行确认,防止误删除重要文件。注意:上传后返回的 temporaryUrl 是临时签名链接,1小时后过期,不要当作永久公网地址写入配置或持久化存储。工具还会基于 DescribeEnvs 返回的 Storages[0].CdnDomain 推导 publicUrl,⚠️ 警告:publicUrl 仅在存储桶 ACL 为公有读(所有用户可读)时才能被匿名访问;默认私有读写存储桶返回的 publicUrl 会 403,此时请继续使用 temporaryUrl 或先通过控制台/SDK 将目标路径设置为公有读。\n\n💡 存储桶 ACL 权限管理请使用 permissions 工具:queryPermissions(action=\"getResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\") 查询,managePermissions(action=\"updateResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\", permission=\"READONLY\") 设置。",
7482
7482
  inputSchema: manageStorageInputSchema,
7483
7483
  annotations: {
7484
7484
  readOnlyHint: false,
@@ -9275,7 +9275,7 @@ class TelemetryReporter {
9275
9275
  const nodeVersion = process.version; // Node.js版本
9276
9276
  const arch = os_1.default.arch(); // 系统架构
9277
9277
  // 从构建时注入的版本号获取MCP版本信息
9278
- const mcpVersion = process.env.npm_package_version || "2.20.2" || 0;
9278
+ const mcpVersion = process.env.npm_package_version || "2.21.0" || 0;
9279
9279
  return {
9280
9280
  userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
9281
9281
  deviceId: this.deviceId,
@@ -10993,8 +10993,8 @@ function registerPermissionTools(server) {
10993
10993
  }
10994
10994
  };
10995
10995
  server.registerTool?.("queryPermissions", {
10996
- title: "查询权限与用户配置",
10997
- description: "权限域统一只读入口。支持查询资源权限、角色列表/详情、应用用户列表/详情。",
10996
+ title: "查询 CloudBase 权限与用户配置",
10997
+ description: "查询 CloudBase 权限与用户配置,支持查询资源权限(数据库/云函数/存储桶等)、角色列表/详情、应用用户列表/详情。\n\n示例:\n- 查询存储桶权限:`action=\"getResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\"`",
10998
10998
  inputSchema: {
10999
10999
  action: zod_1.z.enum(QUERY_PERMISSION_ACTIONS),
11000
11000
  resourceType: zod_1.z
@@ -11154,8 +11154,8 @@ function registerPermissionTools(server) {
11154
11154
  }
11155
11155
  }));
11156
11156
  server.registerTool?.("managePermissions", {
11157
- title: "管理权限与用户配置",
11158
- description: "权限域统一写入口。支持修改资源权限、角色管理、成员与策略增删、应用用户 CRUD。`createUser` / `updateUser` 是环境侧应用用户管理能力,适合测试账号、管理员或预置用户,不应替代浏览器里的 Web SDK 注册表单;前端用户名密码注册应使用 `auth.signUp({ username, password })`,登录应使用 `auth.signInWithPassword({ username, password })`。注意:`securityRule` 的详细语义取决于 `resourceType`;`doc._openid`、`auth.openid`、查询条件子集校验,以及 `create` / `update` / `delete` JSON 模板仅适用于 `resourceType=\"noSqlDatabase\"` 的文档数据库安全规则。配置 `function` 或 `storage` 时,请参考各自官方安全规则文档,而不是复用 NoSQL 模板。",
11157
+ title: "管理 CloudBase 权限与用户配置",
11158
+ description: "管理 CloudBase 权限与用户配置,支持修改资源权限(数据库/云函数/存储桶等)、角色管理、成员与策略增删、应用用户 CRUD。\n\n示例:\n- 设置存储桶为私有:`action=\"updateResourcePermission\", resourceType=\"storage\", resourceId=\"bucket-name\", permission=\"PRIVATE\"`\n- 创建角色:`action=\"createRole\", roleName=\"admin\", roleIdentity=\"admin\"`\n\n注意:`createUser` / `updateUser` 是环境侧应用用户管理能力,适合测试账号、管理员或预置用户,不应替代浏览器里的 Web SDK 注册表单。前端用户名密码注册应使用 `auth.signUp({ username, password })`,登录应使用 `auth.signInWithPassword({ username, password })`。`securityRule` 的详细语义取决于 `resourceType`,请参考对应官方文档。",
11159
11159
  inputSchema: {
11160
11160
  action: zod_1.z.enum(MANAGE_PERMISSION_ACTIONS),
11161
11161
  resourceType: zod_1.z
@@ -11649,14 +11649,28 @@ async function downloadFile(url, filePath, redirectCount = 0) {
11649
11649
  .on("error", reject);
11650
11650
  });
11651
11651
  }
11652
- // 解压ZIP文件
11652
+ // 解压ZIP文件(含 zip slip 防护)
11653
11653
  async function extractZip(zipPath, extractPath) {
11654
11654
  try {
11655
11655
  // 创建解压目录
11656
11656
  await fsPromises.mkdir(extractPath, { recursive: true });
11657
- // 使用 adm-zip 库进行解压
11657
+ // 使用 adm-zip 库逐条目解压,防止 zip slip 路径穿越
11658
11658
  const zip = new adm_zip_1.default(zipPath);
11659
- zip.extractAllTo(extractPath, true);
11659
+ const entries = zip.getEntries();
11660
+ const resolvedExtractPath = path.resolve(extractPath);
11661
+ for (const entry of entries) {
11662
+ // 跳过目录条目,后面由 mkdir 自动创建
11663
+ if (entry.isDirectory)
11664
+ continue;
11665
+ const entryPath = path.resolve(resolvedExtractPath, entry.entryName);
11666
+ if (!entryPath.startsWith(resolvedExtractPath + path.sep)) {
11667
+ throw new Error(`解压失败: ZIP 文件中包含非法的路径 "${entry.entryName}",已安全拦截`);
11668
+ }
11669
+ // 确保目标目录存在
11670
+ await fsPromises.mkdir(path.dirname(entryPath), { recursive: true });
11671
+ // 写入文件
11672
+ await fsPromises.writeFile(entryPath, entry.getData());
11673
+ }
11660
11674
  }
11661
11675
  catch (error) {
11662
11676
  throw new Error(`解压失败: ${error instanceof Error ? error.message : "未知错误"}`);
@@ -11831,7 +11845,7 @@ function registerSetupTools(server) {
11831
11845
  server.registerTool("downloadTemplate", {
11832
11846
  title: "下载项目模板",
11833
11847
  description: `自动下载并部署CloudBase项目模板。
11834
- **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.2" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
11848
+ **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.21.0" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
11835
11849
  inputSchema: {
11836
11850
  template: zod_1.z
11837
11851
  .enum(["react", "vue", "miniprogram", "uniapp", "rules"])
@@ -12026,8 +12040,9 @@ exports.registerAppTools = registerAppTools;
12026
12040
  const zod_1 = __webpack_require__(2971);
12027
12041
  const cloudbase_manager_js_1 = __webpack_require__(3431);
12028
12042
  const json_content_js_1 = __webpack_require__(4365);
12029
- const QUERY_APP_ACTIONS = ["listApps", "getApp", "listAppVersions", "getAppVersion"];
12030
- const MANAGE_APP_ACTIONS = ["deployApp", "deleteApp", "deleteAppVersion"];
12043
+ const cloud_mode_js_1 = __webpack_require__(9684);
12044
+ const QUERY_APP_ACTIONS = ["listApps", "getApp", "listAppVersions", "getAppVersion", "getBuildLog"];
12045
+ const MANAGE_APP_ACTIONS = ["deployApp", "getUploadUrl", "deleteApp", "deleteAppVersion"];
12031
12046
  const APP_FRAMEWORKS = ["vue", "react", "next", "nuxt", "vite", "angular", "static"];
12032
12047
  function buildEnvelope(data, message) {
12033
12048
  return {
@@ -12046,18 +12061,37 @@ function buildErrorEnvelope(error) {
12046
12061
  function getCloudAppService(cloudbase) {
12047
12062
  return cloudbase.cloudAppService ?? cloudbase.getCloudAppService?.();
12048
12063
  }
12064
+ function normalizeAccessUrlFromDomain(domain) {
12065
+ if (typeof domain !== "string" || !domain.trim())
12066
+ return {};
12067
+ const trimmed = domain.trim();
12068
+ const withProtocol = /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
12069
+ try {
12070
+ const url = new URL(withProtocol);
12071
+ url.hash = "";
12072
+ url.search = "";
12073
+ url.pathname = url.pathname === "/" ? "" : url.pathname.replace(/\/+$/, "");
12074
+ return {
12075
+ domain: url.host,
12076
+ accessUrl: url.toString().replace(/\/$/, ""),
12077
+ };
12078
+ }
12079
+ catch {
12080
+ return {};
12081
+ }
12082
+ }
12049
12083
  function registerAppTools(server) {
12050
12084
  const cloudBaseOptions = server.cloudBaseOptions;
12051
12085
  const getManager = () => (0, cloudbase_manager_js_1.getCloudBaseManager)({ cloudBaseOptions });
12052
12086
  server.registerTool?.("queryApps", {
12053
- title: "查询 CloudApp",
12054
- description: "CloudApp 域统一只读入口。可先查应用/版本,再在重新部署后用 listAppVersions 或 getAppVersion 按 serviceName 验证是否生成了新版本与最新构建状态。",
12087
+ title: "查询 CloudBase 应用部署状态",
12088
+ description: "查询 CloudBase 应用部署的应用和版本。可查应用列表/详情、版本列表/详情;部署后用 getAppVersion 按 buildId 轮询构建状态;getBuildLog 可查询构建日志用于诊断失败原因。",
12055
12089
  inputSchema: {
12056
12090
  action: zod_1.z.enum(QUERY_APP_ACTIONS),
12057
12091
  serviceName: zod_1.z
12058
12092
  .string()
12059
12093
  .optional()
12060
- .describe("CloudApp 服务名。getApp / listAppVersions / getAppVersion 时必填;重新部署后复用同一个 serviceName 查询版本历史。"),
12094
+ .describe("CloudBase 应用服务名。getApp / listAppVersions / getAppVersion / getBuildLog 时必填;重新部署后复用同一个 serviceName 查询版本历史。"),
12061
12095
  searchKey: zod_1.z.string().optional().describe("按应用服务名模糊搜索关键词,仅 action=listApps 时使用。"),
12062
12096
  pageNo: zod_1.z.number().optional().describe("分页页码,从 1 开始。"),
12063
12097
  pageSize: zod_1.z.number().optional().describe("分页大小。"),
@@ -12068,14 +12102,18 @@ function registerAppTools(server) {
12068
12102
  buildId: zod_1.z
12069
12103
  .string()
12070
12104
  .optional()
12071
- .describe("构建 ID。getAppVersion 时可与 versionName 二选一;部署返回 BuildId 后可直接用它轮询状态。"),
12105
+ .describe("构建 ID。getAppVersion 时可与 versionName 二选一;部署返回 BuildId 后可直接用它轮询状态。getBuildLog 时必填。"),
12106
+ start: zod_1.z
12107
+ .number()
12108
+ .optional()
12109
+ .describe("构建日志偏移量,用于分页拉取后续日志。仅 action=getBuildLog 时使用,不传时从开头返回。"),
12072
12110
  },
12073
12111
  annotations: {
12074
12112
  readOnlyHint: true,
12075
12113
  openWorldHint: true,
12076
12114
  category: "apps",
12077
12115
  },
12078
- }, async ({ action, serviceName, searchKey, pageNo, pageSize, versionName, buildId, }) => {
12116
+ }, async ({ action, serviceName, searchKey, pageNo, pageSize, versionName, buildId, start, }) => {
12079
12117
  try {
12080
12118
  const cloudbase = await getManager();
12081
12119
  const appService = getCloudAppService(cloudbase);
@@ -12095,7 +12133,7 @@ function registerAppTools(server) {
12095
12133
  apps: result.ServiceList ?? [],
12096
12134
  total: result.Total ?? 0,
12097
12135
  raw: result,
12098
- }, "CloudApp 列表查询成功"));
12136
+ }, "CloudBase 应用列表查询成功"));
12099
12137
  }
12100
12138
  if (!serviceName) {
12101
12139
  throw new Error(`action=${action} 时必须提供 serviceName`);
@@ -12110,7 +12148,7 @@ function registerAppTools(server) {
12110
12148
  action,
12111
12149
  serviceName,
12112
12150
  app: result,
12113
- }, "CloudApp 详情查询成功"));
12151
+ }, "CloudBase 应用详情查询成功"));
12114
12152
  }
12115
12153
  if (action === "listAppVersions") {
12116
12154
  const result = await appService.describeAppVersionList({
@@ -12126,7 +12164,34 @@ function registerAppTools(server) {
12126
12164
  versions: result.VersionList ?? [],
12127
12165
  total: result.Total ?? 0,
12128
12166
  raw: result,
12129
- }, "CloudApp 版本列表查询成功"));
12167
+ }, "CloudBase 应用版本列表查询成功"));
12168
+ }
12169
+ if (action === "getBuildLog") {
12170
+ if (!buildId) {
12171
+ throw new Error("action=getBuildLog 时必须提供 buildId");
12172
+ }
12173
+ const result = await cloudbase.commonService("tcb", "2018-06-08").call({
12174
+ Action: "DescribeCloudBaseRunBuildLog",
12175
+ Param: {
12176
+ EnvId: cloudBaseOptions?.envId || process.env.CLOUDBASE_ENV_ID,
12177
+ ServiceName: serviceName,
12178
+ BuildId: buildId,
12179
+ Start: start ?? 0,
12180
+ },
12181
+ });
12182
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
12183
+ const logs = result.Response?.LogList || [];
12184
+ return (0, json_content_js_1.jsonContent)(buildEnvelope({
12185
+ action,
12186
+ serviceName,
12187
+ buildId,
12188
+ logs,
12189
+ total: result.Response?.Total || logs.length,
12190
+ nextStart: result.Response?.NextStart,
12191
+ raw: result,
12192
+ }, logs.length > 0
12193
+ ? `查询到 ${logs.length} 条构建日志`
12194
+ : "暂无构建日志"));
12130
12195
  }
12131
12196
  const result = await appService.describeAppVersion({
12132
12197
  deployType: "static-hosting",
@@ -12135,56 +12200,105 @@ function registerAppTools(server) {
12135
12200
  buildId,
12136
12201
  });
12137
12202
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
12138
- return (0, json_content_js_1.jsonContent)(buildEnvelope({
12203
+ const isFailed = result.Status === "FAILED";
12204
+ const payload = {
12139
12205
  action,
12140
12206
  serviceName,
12207
+ status: result.Status,
12208
+ buildId: result.BuildId,
12209
+ failReason: result.FailReason,
12210
+ buildDuration: result.BuildDuration,
12141
12211
  version: result,
12142
- }, "CloudApp 版本详情查询成功"));
12212
+ };
12213
+ if (isFailed) {
12214
+ payload.nextStep = {
12215
+ action: "查询构建日志",
12216
+ tool: "queryApps",
12217
+ args: {
12218
+ action: "getBuildLog",
12219
+ serviceName,
12220
+ buildId: result.BuildId,
12221
+ },
12222
+ hint: `构建失败。调用 queryApps(action="getBuildLog", serviceName="${serviceName}", buildId="${result.BuildId}") 查看构建日志,诊断失败原因。`,
12223
+ };
12224
+ }
12225
+ return (0, json_content_js_1.jsonContent)(buildEnvelope(payload, `CloudBase 应用版本详情查询成功(状态: ${result.Status}${result.FailReason ? `, 失败原因: ${result.FailReason}` : ""}${isFailed ? ",可查询构建日志" : ""})`));
12143
12226
  }
12144
12227
  catch (error) {
12145
12228
  return (0, json_content_js_1.jsonContent)(buildErrorEnvelope(error));
12146
12229
  }
12147
12230
  });
12148
12231
  server.registerTool?.("manageApps", {
12149
- title: "管理 CloudApp",
12150
- description: "CloudApp 域统一写入口。action=deployApp 会先 uploadCode 再 createApp;首次调用创建应用,后续复用同一个 serviceName 会直接触发重新部署并生成新版本,无需先删除旧应用。",
12232
+ title: "部署应用到 CloudBase(独立子域名)",
12233
+ description: "部署 Web 应用到 CloudBase(构建前后端,部署到独立子域名)。\n" +
12234
+ "action=getUploadUrl 获取预签名上传 URL(cloud mode 下使用),返回上传地址和 cosTimestamp。\n" +
12235
+ "action=deployApp 上传源码 ZIP 并触发远端构建部署管道:\n" +
12236
+ " 1. 远端 npm install(可通过 installCmd=\"\" 跳过)\n" +
12237
+ " 2. 远端 npm run build(可通过 buildCmd=\"\" 跳过)\n" +
12238
+ " 3. 远端 tcb hosting deploy\n" +
12239
+ "\n" +
12240
+ "域名格式:`<serviceName>-<envId>.webapps.tcloudbase.com`(每个 serviceName 一个独立子域名)\n" +
12241
+ "\n" +
12242
+ "✅ 推荐用法(新项目/需要独立域名的 Web 应用,首选此工具):\n" +
12243
+ " 新建项目首次部署时,传 framework=static, installCmd=\"\", buildCmd=\"\" 跳过远端构建,\n" +
12244
+ " 只执行 tcb hosting deploy。部署后获得独立子域名,支持版本管理。\n" +
12245
+ "\n" +
12246
+ "⚠️ 兼容性说明:\n" +
12247
+ "- 已有项目若之前用 manageHosting 部署过(域名格式:`<envId>-<appId>.tcloudbaseapp.com`),\n" +
12248
+ " 切换到 manageApps 会产生全新的 URL,老链接失效。请保持原部署方式不变。\n" +
12249
+ "- 如需判断:调用 queryHosting 检查是否已有托管文件。\n" +
12250
+ "\n" +
12251
+ "与 manageHosting 对比:\n" +
12252
+ "- manageApps(本工具,新项目首选):域名 `<serviceName>-<envId>.webapps.tcloudbase.com`,独立子域名,支持版本管理\n" +
12253
+ "- manageHosting(已有项目或 fallback):域名 `<envId>-<appId>.tcloudbaseapp.com/<path>`,共享环境域名\n" +
12254
+ "两者均可绑定自定义域名。\n" +
12255
+ "\n" +
12256
+ "⚠️ 如果 manageApps 构建失败,先用 queryApps(action=\"getBuildLog\") 查日志;仍不行再 fallback 到 manageHosting。",
12151
12257
  inputSchema: {
12152
12258
  action: zod_1.z.enum(MANAGE_APP_ACTIONS),
12153
12259
  serviceName: zod_1.z
12154
12260
  .string()
12155
- .describe("CloudApp 服务名。deployApp 时复用现有 serviceName 会新增一个部署版本并触发重新部署,而不是删除重建。"),
12261
+ .describe("CloudBase 应用服务名,会体现在域名中:`<serviceName>-<envId>.webapps.tcloudbase.com`。deployApp 时复用现有 serviceName 会新增一个部署版本并触发重新部署,而不是删除重建。首次部署请用新名称。"),
12156
12262
  filePath: zod_1.z
12157
12263
  .string()
12158
12264
  .optional()
12159
- .describe("要上传并部署的本地项目根目录或 zip 文件绝对路径。deployApp 时必填;通常传源码所在目录而不是 build 产物目录,构建产物目录请用 buildPath 指定。"),
12265
+ .describe("要上传并部署的本地项目根目录绝对路径。本地模式下 deployApp 时必填;通常传源码所在目录(含 package.json 和源码),不是 dist 目录。构建产物目录请用 buildPath 指定。cloud mode 下无需传此参数,改用 cosTimestamp。"),
12266
+ cosTimestamp: zod_1.z
12267
+ .string()
12268
+ .optional()
12269
+ .describe("可选 COS 时间戳。传入此值则直接使用已上传的代码创建应用,跳过本地文件上传。需先调用 getUploadUrl 获取预签名 URL,上传 ZIP 包后再传此时间戳。cloud mode 下为必填;本地模式也可传此值代替 filePath。两个路径二选一:filePath(本地打包上传)或 cosTimestamp(预签名 URL 上传)。"),
12160
12270
  appPath: zod_1.z
12161
12271
  .string()
12162
12272
  .optional()
12163
- .describe("应用线上访问路径(hosting mount path),例如 /my-web-app。不是本地目录路径;省略时默认为 /serviceName。"),
12273
+ .describe("应用线上访问路径(hosting mount path),例如 /my-web-app。不是本地目录路径;CloudApp 已有独立子域名,省略时默认为 /(根路径)。"),
12164
12274
  buildPath: zod_1.z
12165
12275
  .string()
12166
12276
  .optional()
12167
- .describe("构建产物目录,相对于 filePath,例如 dist 或 build。纯静态 HTML 如果入口文件直接在项目根目录,可省略。"),
12277
+ .describe("构建产物目录,相对于 filePath,例如 dist 或 build。\n" +
12278
+ "⚠️ 传此值后远端构建系统会 cd 到此目录再执行 tcb hosting deploy,因此 deployCmd 会自动使用 .(当前目录)而非目录名,避免路径重复(如 dist/dist 错误)。\n" +
12279
+ "纯静态 HTML 如果在项目根目录可省略,但注意 deployCmd 默认用 dist。"),
12168
12280
  framework: zod_1.z
12169
12281
  .enum(APP_FRAMEWORKS)
12170
12282
  .optional()
12171
- .describe("前端框架类型。可选值:vue、react、next、nuxt、vite、angular、static;纯 HTML/静态站点请传 static。"),
12283
+ .describe("前端框架类型。可选值:vue、react、next、nuxt、vite、angular、static。\n" +
12284
+ "即使传 static,仍会经过远端构建管道。如果本地已构建好,建议改用 manageHosting 直接上传,可完全跳过远端构建。"),
12172
12285
  nodeJsVersion: zod_1.z
12173
12286
  .string()
12174
12287
  .optional()
12175
- .describe("构建时使用的 Node.js 版本;不传时由 CloudApp 使用默认值。"),
12288
+ .describe("构建时使用的 Node.js 版本;不传时由 CloudBase 使用默认值。"),
12176
12289
  installCmd: zod_1.z
12177
12290
  .string()
12178
12291
  .optional()
12179
- .describe("依赖安装命令,例如 npm install;静态资源无需安装依赖时可省略。"),
12292
+ .describe("依赖安装命令,例如 npm install。不传时默认 npm install。本地已安装或无需安装可传空字符串 '' 跳过,但远端仍会执行 tcb hosting deploy。"),
12180
12293
  buildCmd: zod_1.z
12181
12294
  .string()
12182
12295
  .optional()
12183
- .describe("构建命令,例如 npm run build;纯静态 HTML 无构建步骤时可省略。"),
12296
+ .describe("构建命令,例如 npm run build。不传时默认 npm run build。本地已构建好可传空字符串 '' 跳过构建步骤。若希望完全跳过远端管道,请改用 manageHosting。"),
12184
12297
  deployCmd: zod_1.z
12185
12298
  .string()
12186
12299
  .optional()
12187
- .describe("自定义部署命令。通常无需填写,只有在默认部署步骤不满足要求时才传。"),
12300
+ .describe("自定义部署命令。通常无需填写,默认自动生成 tcb hosting deploy 命令。" +
12301
+ "有 buildPath 时远端已 cd 到该目录,默认用 . 作为源码路径;无 buildPath 时默认用 dist。"),
12188
12302
  ignore: zod_1.z.array(zod_1.z.string()).optional().describe("上传时忽略的文件/目录 glob 模式,例如 **/node_modules/**。"),
12189
12303
  versionName: zod_1.z
12190
12304
  .string()
@@ -12198,48 +12312,155 @@ function registerAppTools(server) {
12198
12312
  openWorldHint: true,
12199
12313
  category: "apps",
12200
12314
  },
12201
- }, async ({ action, serviceName, filePath, appPath, buildPath, framework, nodeJsVersion, installCmd, buildCmd, deployCmd, ignore, versionName, }) => {
12315
+ }, async ({ action, serviceName, filePath, cosTimestamp, appPath, buildPath, framework, nodeJsVersion, installCmd, buildCmd, deployCmd, ignore, versionName, }) => {
12202
12316
  try {
12203
12317
  const cloudbase = await getManager();
12204
12318
  const appService = getCloudAppService(cloudbase);
12205
12319
  if (!appService) {
12206
12320
  throw new Error("当前 manager 未提供 cloudAppService");
12207
12321
  }
12208
- if (action === "deployApp") {
12209
- if (!filePath) {
12210
- throw new Error("action=deployApp 时必须提供 filePath");
12322
+ // getUploadUrl — 获取预签名上传 URL(cloud mode 专用)
12323
+ if (action === "getUploadUrl") {
12324
+ if (!serviceName) {
12325
+ throw new Error("action=getUploadUrl 时必须提供 serviceName");
12211
12326
  }
12212
- const uploadResult = await appService.uploadCode({
12327
+ const cosInfoResult = await appService.describeCosInfo({
12213
12328
  deployType: "static-hosting",
12214
12329
  serviceName,
12215
- localPath: filePath,
12216
- ignore,
12217
12330
  });
12218
- (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, uploadResult);
12331
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, cosInfoResult);
12332
+ const defaultIgnore = ["node_modules/**", ".git/**", ".DS_Store", "**/.DS_Store"];
12333
+ // eslint-disable-next-line max-len
12334
+ const zipCmd = "zip -r upload.zip . -x 'node_modules/**' -x '.git/**' -x '.DS_Store' -x '**/.DS_Store'";
12335
+ const followupArgs = {
12336
+ action: "deployApp",
12337
+ serviceName,
12338
+ cosTimestamp: cosInfoResult.UnixTimestamp,
12339
+ };
12340
+ return (0, json_content_js_1.jsonContent)(buildEnvelope({
12341
+ action,
12342
+ serviceName,
12343
+ uploadUrl: cosInfoResult.UploadUrl,
12344
+ uploadHeaders: cosInfoResult.UploadHeaders,
12345
+ cosTimestamp: cosInfoResult.UnixTimestamp,
12346
+ method: "PUT",
12347
+ ignore: defaultIgnore,
12348
+ zipCommand: zipCmd,
12349
+ nextAction: {
12350
+ action: "上传代码到预签名 URL",
12351
+ hint: "请先在本地打包项目代码(排除 node_modules/.git),再将其上传到预签名 URL,然后调用 deployApp 触发构建",
12352
+ details: [
12353
+ `1. 打包: ${zipCmd}`,
12354
+ `2. 上传: curl -X PUT -T upload.zip '${cosInfoResult.UploadUrl}'`,
12355
+ `3. 触发构建: manageApps(action="deployApp", serviceName="${serviceName}", cosTimestamp="${cosInfoResult.UnixTimestamp}")`,
12356
+ ],
12357
+ followup: {
12358
+ tool: "manageApps",
12359
+ args: followupArgs,
12360
+ },
12361
+ },
12362
+ }, "预签名上传 URL 获取成功。请上传代码后调用 deployApp 触发构建。"));
12363
+ }
12364
+ if (action === "deployApp") {
12365
+ // cloud mode 下必须有 cosTimestamp;本地模式必须有 filePath
12366
+ if ((0, cloud_mode_js_1.isCloudMode)()) {
12367
+ if (!cosTimestamp) {
12368
+ throw new Error("cloud mode 下 deployApp 需要 cosTimestamp 参数。请先调用 getUploadUrl 获取预签名上传 URL。" +
12369
+ "上传代码后再用 cosTimestamp 调用 deployApp。");
12370
+ }
12371
+ }
12372
+ else if (!filePath && !cosTimestamp) {
12373
+ throw new Error("action=deployApp 时必须提供 filePath(本地模式)或 cosTimestamp(cloud mode)。");
12374
+ }
12375
+ // 上传代码到 COS(仅本地模式需要,cloud mode 用 cosTimestamp 跳过)
12376
+ let cosTs = cosTimestamp;
12377
+ if (filePath && !cosTs) {
12378
+ const uploadResult = await appService.uploadCode({
12379
+ deployType: "static-hosting",
12380
+ serviceName,
12381
+ localPath: filePath,
12382
+ ignore,
12383
+ });
12384
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, uploadResult);
12385
+ cosTs = uploadResult.cosTimestamp;
12386
+ }
12387
+ // 构建命令智能默认值
12388
+ const resolvedInstallCmd = installCmd ?? "npm install";
12389
+ const resolvedBuildCmd = buildCmd ?? "npm run build";
12390
+ const resolvedDeployPath = appPath || "/";
12391
+ const resolvedBuildPath = buildPath || "";
12392
+ // ⚠️ 远端构建系统在有 buildPath 时 cd 到此目录再执行 tcb hosting deploy
12393
+ // 部署命令用 "." 避免 dist/dist 重复。framework=static 无构建步骤,用根目录
12394
+ const resolvedDeployCmd = deployCmd || (resolvedBuildPath || framework === "static"
12395
+ ? `tcb hosting deploy . ${resolvedDeployPath}`
12396
+ : `tcb hosting deploy dist ${resolvedDeployPath}`);
12397
+ // 触发远端构建
12219
12398
  const result = await appService.createApp({
12220
12399
  deployType: "static-hosting",
12221
12400
  serviceName,
12222
12401
  buildType: "ZIP",
12223
12402
  staticConfig: {
12224
- appPath,
12225
- buildPath,
12403
+ appPath: resolvedDeployPath,
12404
+ buildPath: resolvedBuildPath,
12226
12405
  framework,
12227
12406
  nodeJsVersion,
12228
- cosTimestamp: uploadResult.cosTimestamp,
12407
+ cosTimestamp: cosTs,
12229
12408
  staticCmd: {
12230
- installCmd,
12231
- buildCmd,
12232
- deployCmd,
12409
+ installCmd: resolvedInstallCmd,
12410
+ buildCmd: resolvedBuildCmd,
12411
+ deployCmd: resolvedDeployCmd,
12233
12412
  },
12234
12413
  },
12235
12414
  });
12236
12415
  (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, result);
12416
+ const { BuildId, VersionName } = result;
12417
+ let appInfo;
12418
+ let domain;
12419
+ let accessUrl;
12420
+ let accessUrlLookupWarning;
12421
+ try {
12422
+ appInfo = await appService.describeAppInfo({
12423
+ deployType: "static-hosting",
12424
+ serviceName,
12425
+ });
12426
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(server.logger, appInfo);
12427
+ ({ domain, accessUrl } = normalizeAccessUrlFromDomain(appInfo?.Domain));
12428
+ }
12429
+ catch (error) {
12430
+ accessUrlLookupWarning = error instanceof Error ? error.message : String(error);
12431
+ }
12237
12432
  return (0, json_content_js_1.jsonContent)(buildEnvelope({
12238
12433
  action,
12239
12434
  serviceName,
12240
- upload: uploadResult,
12435
+ versionName: VersionName,
12436
+ buildId: BuildId,
12437
+ domain,
12438
+ accessUrl,
12439
+ accessUrlSource: accessUrl ? "describeAppInfo.Domain" : undefined,
12440
+ accessUrlLookupWarning,
12441
+ app: appInfo,
12442
+ upload: { cosTimestamp: cosTs },
12241
12443
  deployment: result,
12242
- }, "CloudApp 部署成功"));
12444
+ buildConfig: {
12445
+ installCmd: resolvedInstallCmd,
12446
+ buildCmd: resolvedBuildCmd,
12447
+ deployCmd: resolvedDeployCmd,
12448
+ },
12449
+ nextStep: {
12450
+ action: "轮询构建状态",
12451
+ tool: "queryApps",
12452
+ args: {
12453
+ action: "getAppVersion",
12454
+ serviceName,
12455
+ buildId: BuildId,
12456
+ },
12457
+ hint: accessUrl
12458
+ ? `调用 queryApps(action="getAppVersion", serviceName="${serviceName}", buildId="${BuildId}") 轮询构建状态,直到 status 变为 SUCCESS 或 FAILED。构建成功后,后续记录部署时必须使用本结果的 accessUrl=${accessUrl},不要自行拼接域名。若状态为 FAILED,可继续调用 queryApps(action="getBuildLog", serviceName="${serviceName}", buildId="${BuildId}") 查看构建日志诊断失败原因。`
12459
+ : `调用 queryApps(action="getAppVersion", serviceName="${serviceName}", buildId="${BuildId}") 轮询构建状态,直到 status 变为 SUCCESS 或 FAILED;再调用 queryApps(action="getApp", serviceName="${serviceName}") 读取 app.Domain 作为 accessUrl,不能自行拼接域名。若状态为 FAILED,可继续调用 queryApps(action="getBuildLog", serviceName="${serviceName}", buildId="${BuildId}") 查看构建日志诊断失败原因。`,
12460
+ },
12461
+ }, accessUrl
12462
+ ? "CloudBase 应用构建已触发,已返回真实 accessUrl;请通过 queryApps 轮询构建状态。"
12463
+ : "CloudBase 应用构建已触发,请通过 queryApps 轮询构建状态,并用 getApp 读取真实域名。"));
12243
12464
  }
12244
12465
  if (action === "deleteApp") {
12245
12466
  const result = await appService.deleteApp({
@@ -12251,7 +12472,7 @@ function registerAppTools(server) {
12251
12472
  action,
12252
12473
  serviceName,
12253
12474
  raw: result,
12254
- }, "CloudApp 删除成功"));
12475
+ }, "CloudBase 应用删除成功"));
12255
12476
  }
12256
12477
  if (!versionName) {
12257
12478
  throw new Error("action=deleteAppVersion 时必须提供 versionName");
@@ -12267,7 +12488,7 @@ function registerAppTools(server) {
12267
12488
  serviceName,
12268
12489
  versionName,
12269
12490
  raw: result,
12270
- }, "CloudApp 版本删除成功"));
12491
+ }, "CloudBase 应用版本删除成功"));
12271
12492
  }
12272
12493
  catch (error) {
12273
12494
  return (0, json_content_js_1.jsonContent)(buildErrorEnvelope(error));
@@ -13071,6 +13292,16 @@ async function resolveHostingStaticDomain(cloudbase, logger) {
13071
13292
  return undefined;
13072
13293
  }
13073
13294
  }
13295
+ async function assertHostingUploadEnvironmentReady(cloudbase, cloudBaseOptions, logger) {
13296
+ const envId = await (0, cloudbase_manager_js_1.getEnvId)(cloudBaseOptions);
13297
+ const envInfo = await cloudbase.env.getEnvInfo();
13298
+ (0, cloudbase_manager_js_1.logCloudBaseResult)(logger, envInfo);
13299
+ const staticStorage = envInfo.EnvInfo?.StaticStorages?.[0];
13300
+ if (staticStorage?.Bucket) {
13301
+ return;
13302
+ }
13303
+ throw new Error(`当前环境 ${envId} 未发现静态托管资源配置,无法上传文件。请先确认 MCP 已绑定到已开通静态托管的 CloudBase 环境;如需切换环境,请调用 auth(action="set_env", envId="目标环境ID") 后重试。`);
13304
+ }
13074
13305
  function buildFailureResult(action, error) {
13075
13306
  return (0, tool_result_js_1.buildJsonToolResult)({
13076
13307
  success: false,
@@ -13255,7 +13486,7 @@ function registerHostingTools(server) {
13255
13486
  });
13256
13487
  server.registerTool('manageHosting', {
13257
13488
  title: '管理 CloudBase 静态托管',
13258
- description: '管理 CloudBase 静态托管的变更操作。action=upload 上传本地构建产物;action=delete 删除托管文件或目录(必须 confirm=true);action=setWebsiteDocument 设置首页/错误页/路由规则;action=enableService 开通静态托管;action=bindDomain / unbindDomain / updateDomain 管理自定义域名;action=downloadFile / downloadDirectory 下载托管内容到本地。若任务只是查看配置、文件或域名状态,请改用 queryHosting。',
13489
+ description: '管理 CloudBase 静态托管的变更操作。action=upload 上传本地构建产物到共享域名(域名格式:<envId>-<appId>.tcloudbaseapp.com/<cloudPath>);action=delete 删除托管文件或目录(必须 confirm=true);action=setWebsiteDocument 设置首页/错误页/路由规则;action=enableService 开通静态托管;action=bindDomain / unbindDomain / updateDomain 管理自定义域名;action=downloadFile / downloadDirectory 下载托管内容到本地。⚠️ 新项目部署优先使用 manageApps(部署到独立子域名),本工具适合已有老项目继续使用或作为 manageApps 的 fallback。manageApps 与 manageHosting 域名不同,切换会导致老链接失效。若任务只是查看配置、文件或域名状态,请改用 queryHosting。',
13259
13490
  inputSchema: manageHostingInputSchema,
13260
13491
  annotations: {
13261
13492
  readOnlyHint: false,
@@ -13276,6 +13507,7 @@ function registerHostingTools(server) {
13276
13507
  }
13277
13508
  let result;
13278
13509
  try {
13510
+ await assertHostingUploadEnvironmentReady(cloudbase, cloudBaseOptions, server.logger);
13279
13511
  result = await cloudbase.hosting.uploadFiles({
13280
13512
  localPath: input.localPath,
13281
13513
  cloudPath: input.cloudPath,
@@ -14841,8 +15073,6 @@ function shouldRegisterTool(toolName) {
14841
15073
  'manageCloudRun',
14842
15074
  // Download tools - local file downloads
14843
15075
  'manageStorage',
14844
- // Apps tools - deployApp reads local filePath for code upload
14845
- 'manageApps',
14846
15076
  ];
14847
15077
  const shouldRegister = !cloudIncompatibleTools.includes(toolName);
14848
15078
  if (!shouldRegister) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudbase/cloudbase-mcp",
3
- "version": "2.20.2",
3
+ "version": "2.21.0",
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",