@cloudbase/cloudbase-mcp 1.7.0 → 1.7.2

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/tools/env.js CHANGED
@@ -2,6 +2,7 @@ import { z } from "zod";
2
2
  import { getCloudBaseManager } from '../cloudbase-manager.js';
3
3
  import { logout } from '../auth.js';
4
4
  import { clearUserEnvId, _promptAndSetEnvironmentId } from './interactive.js';
5
+ import { debug } from '../utils/logger.js';
5
6
  export function registerEnvTools(server) {
6
7
  // login - 登录云开发环境
7
8
  server.tool("login", "登录云开发环境并选择要使用的环境", {
@@ -9,6 +10,7 @@ export function registerEnvTools(server) {
9
10
  }, async ({ forceUpdate = false }) => {
10
11
  try {
11
12
  const { selectedEnvId, cancelled, error, noEnvs } = await _promptAndSetEnvironmentId(false);
13
+ debug("login", { selectedEnvId, cancelled, error, noEnvs });
12
14
  if (error) {
13
15
  return { content: [{ type: "text", text: error }] };
14
16
  }
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { getCloudBaseManager } from '../cloudbase-manager.js';
3
+ import path from 'path';
3
4
  // 支持的 Node.js 运行时列表
4
5
  export const SUPPORTED_NODEJS_RUNTIMES = [
5
6
  'Nodejs 18.15',
@@ -10,6 +11,25 @@ export const SUPPORTED_NODEJS_RUNTIMES = [
10
11
  'Nodejs 8.9(即将下线)',
11
12
  ];
12
13
  export const DEFAULT_NODEJS_RUNTIME = 'Nodejs 18.15';
14
+ /**
15
+ * 处理函数根目录路径,确保不包含函数名
16
+ * @param functionRootPath 用户输入的路径
17
+ * @param functionName 函数名称
18
+ * @returns 处理后的根目录路径
19
+ */
20
+ function processFunctionRootPath(functionRootPath, functionName) {
21
+ if (!functionRootPath)
22
+ return functionRootPath;
23
+ const normalizedPath = path.normalize(functionRootPath);
24
+ const lastDir = path.basename(normalizedPath);
25
+ // 如果路径的最后一级目录名与函数名相同,说明用户可能传入了包含函数名的路径
26
+ if (lastDir === functionName) {
27
+ const parentPath = path.dirname(normalizedPath);
28
+ console.warn(`检测到 functionRootPath 包含函数名 "${functionName}",已自动调整为父目录: ${parentPath}`);
29
+ return parentPath;
30
+ }
31
+ return functionRootPath;
32
+ }
13
33
  export function registerFunctionTools(server) {
14
34
  // getFunctionList - 获取云函数列表(推荐)
15
35
  server.tool("getFunctionList", "获取云函数列表", {
@@ -52,17 +72,19 @@ export function registerFunctionTools(server) {
52
72
  version: z.number()
53
73
  })).optional().describe("Layer配置")
54
74
  }).describe("函数配置"),
55
- functionRootPath: z.string().optional().describe("函数根目录(云函数目录的父目录),这里需要传操作系统上文件的绝对路径,指定之后可以自动上传这部分的文件作为代码"),
75
+ functionRootPath: z.string().optional().describe("函数根目录(云函数目录的父目录),这里需要传操作系统上文件的绝对路径,注意:不要包含函数名本身,例如函数名为 'hello',应传入 '/path/to/cloudfunctions',而不是 '/path/to/cloudfunctions/hello'"),
56
76
  force: z.boolean().describe("是否覆盖")
57
77
  }, async ({ func, functionRootPath, force }) => {
58
78
  // 自动填充默认 runtime
59
79
  if (!func.runtime) {
60
80
  func.runtime = DEFAULT_NODEJS_RUNTIME;
61
81
  }
82
+ // 处理函数根目录路径,确保不包含函数名
83
+ const processedRootPath = processFunctionRootPath(functionRootPath, func.name);
62
84
  const cloudbase = await getCloudBaseManager();
63
85
  const result = await cloudbase.functions.createFunction({
64
86
  func,
65
- functionRootPath,
87
+ functionRootPath: processedRootPath,
66
88
  force
67
89
  });
68
90
  return {
@@ -79,15 +101,17 @@ export function registerFunctionTools(server) {
79
101
  func: z.object({
80
102
  name: z.string().describe("函数名称")
81
103
  }).describe("函数配置"),
82
- functionRootPath: z.string().optional().describe("函数根目录(云函数目录的父目录),这里需要传操作系统上文件的绝对路径,指定之后可以自动上传这部分的文件作为代码")
104
+ functionRootPath: z.string().optional().describe("函数根目录(云函数目录的父目录),这里需要传操作系统上文件的绝对路径,注意:不要包含函数名本身,例如函数名为 'hello',应传入 '/path/to/cloudfunctions',而不是 '/path/to/cloudfunctions/hello'")
83
105
  }, async ({ func, functionRootPath }) => {
106
+ // 处理函数根目录路径,确保不包含函数名
107
+ const processedRootPath = processFunctionRootPath(functionRootPath, func.name);
84
108
  const cloudbase = await getCloudBaseManager();
85
109
  const result = await cloudbase.functions.updateFunctionCode({
86
110
  func: {
87
111
  ...func,
88
112
  installDependency: true // 默认安装依赖
89
113
  },
90
- functionRootPath,
114
+ functionRootPath: processedRootPath,
91
115
  });
92
116
  return {
93
117
  content: [
@@ -96,109 +96,16 @@ export function registerHostingTools(server) {
96
96
  ]
97
97
  };
98
98
  });
99
- // // setWebsiteDocument - 配置静态网站文档
100
- // server.tool(
101
- // "setWebsiteDocument",
102
- // "配置静态网站的错误文档、索引文档和重定向规则",
103
- // {
104
- // indexDocument: z.string().describe("索引文档路径"),
105
- // errorDocument: z.string().optional().describe("错误文档路径"),
106
- // routingRules: z.array(z.object({
107
- // keyPrefixEquals: z.string().optional(),
108
- // httpErrorCodeReturnedEquals: z.string().optional(),
109
- // replaceKeyWith: z.string().optional(),
110
- // replaceKeyPrefixWith: z.string().optional()
111
- // })).optional().describe("重定向规则")
112
- // },
113
- // async ({ indexDocument, errorDocument, routingRules }) => {
114
- // const cloudbase = await getCloudBaseManager()
115
- // const result = await cloudbase.hosting.setWebsiteDocument({
116
- // indexDocument,
117
- // errorDocument,
118
- // routingRules
119
- // });
120
- // return {
121
- // content: [
122
- // {
123
- // type: "text",
124
- // text: JSON.stringify(result, null, 2)
125
- // }
126
- // ]
127
- // };
128
- // }
129
- // );
130
- // createHostingDomain - 绑定自定义域名
131
- server.tool("createHostingDomain", "绑定自定义域名", {
132
- domain: z.string().describe("自定义域名"),
133
- certId: z.string().describe("证书ID")
134
- }, async ({ domain, certId }) => {
135
- const cloudbase = await getCloudBaseManager();
136
- const result = await cloudbase.hosting.CreateHostingDomain({
137
- domain,
138
- certId
139
- });
140
- return {
141
- content: [
142
- {
143
- type: "text",
144
- text: JSON.stringify(result, null, 2)
145
- }
146
- ]
147
- };
148
- });
149
- // deleteHostingDomain - 解绑自定义域名
150
- server.tool("deleteHostingDomain", "解绑自定义域名", {
151
- domain: z.string().describe("自定义域名")
152
- }, async ({ domain }) => {
153
- const cloudbase = await getCloudBaseManager();
154
- const result = await cloudbase.hosting.deleteHostingDomain({
155
- domain
156
- });
157
- return {
158
- content: [
159
- {
160
- type: "text",
161
- text: JSON.stringify(result, null, 2)
162
- }
163
- ]
164
- };
165
- });
166
- // getWebsiteConfig - 获取静态网站配置
167
- server.tool("getWebsiteConfig", "获取静态网站配置", {
168
- confirm: z.literal("yes").describe("确认操作,默认传 yes")
169
- }, async () => {
170
- const cloudbase = await getCloudBaseManager();
171
- const result = await cloudbase.hosting.getWebsiteConfig();
172
- return {
173
- content: [
174
- {
175
- type: "text",
176
- text: JSON.stringify(result, null, 2)
177
- }
178
- ]
179
- };
180
- });
181
- // tcbCheckResource - 获取域名配置
182
- server.tool("tcbCheckResource", "获取域名配置", {
183
- domains: z.array(z.string()).describe("域名列表")
184
- }, async ({ domains }) => {
185
- const cloudbase = await getCloudBaseManager();
186
- const result = await cloudbase.hosting.tcbCheckResource({
187
- domains
188
- });
189
- return {
190
- content: [
191
- {
192
- type: "text",
193
- text: JSON.stringify(result, null, 2)
194
- }
195
- ]
196
- };
197
- });
198
- // tcbModifyAttribute - 修改域名配置
199
- server.tool("tcbModifyAttribute", "修改域名配置", {
200
- domain: z.string().describe("域名"),
201
- domainId: z.number().describe("域名ID"),
99
+ // domainManagement - 统一的域名管理工具
100
+ server.tool("domainManagement", "统一的域名管理工具,支持绑定、解绑、查询和修改域名配置", {
101
+ action: z.enum(["create", "delete", "check", "modify"]).describe("操作类型: create=绑定域名, delete=解绑域名, check=查询域名配置, modify=修改域名配置"),
102
+ // 绑定域名参数
103
+ domain: z.string().optional().describe("域名"),
104
+ certId: z.string().optional().describe("证书ID(绑定域名时必需)"),
105
+ // 查询域名参数
106
+ domains: z.array(z.string()).optional().describe("域名列表(查询配置时使用)"),
107
+ // 修改域名参数
108
+ domainId: z.number().optional().describe("域名ID(修改配置时必需)"),
202
109
  domainConfig: z.object({
203
110
  Refer: z.object({
204
111
  Switch: z.string(),
@@ -222,14 +129,67 @@ export function registerHostingTools(server) {
222
129
  Switch: z.string(),
223
130
  Qps: z.number().optional()
224
131
  }).optional()
225
- }).describe("域名配置")
226
- }, async ({ domain, domainId, domainConfig }) => {
132
+ }).optional().describe("域名配置(修改配置时使用)")
133
+ }, async ({ action, domain, certId, domains, domainId, domainConfig }) => {
227
134
  const cloudbase = await getCloudBaseManager();
228
- const result = await cloudbase.hosting.tcbModifyAttribute({
229
- domain,
230
- domainId,
231
- domainConfig
232
- });
135
+ let result;
136
+ switch (action) {
137
+ case "create":
138
+ if (!domain || !certId) {
139
+ throw new Error("绑定域名需要提供域名和证书ID");
140
+ }
141
+ result = await cloudbase.hosting.CreateHostingDomain({
142
+ domain,
143
+ certId
144
+ });
145
+ break;
146
+ case "delete":
147
+ if (!domain) {
148
+ throw new Error("解绑域名需要提供域名");
149
+ }
150
+ result = await cloudbase.hosting.deleteHostingDomain({
151
+ domain
152
+ });
153
+ break;
154
+ case "check":
155
+ if (!domains || domains.length === 0) {
156
+ throw new Error("查询域名配置需要提供域名列表");
157
+ }
158
+ result = await cloudbase.hosting.tcbCheckResource({
159
+ domains
160
+ });
161
+ break;
162
+ case "modify":
163
+ if (!domain || domainId === undefined || !domainConfig) {
164
+ throw new Error("修改域名配置需要提供域名、域名ID和配置信息");
165
+ }
166
+ result = await cloudbase.hosting.tcbModifyAttribute({
167
+ domain,
168
+ domainId,
169
+ domainConfig
170
+ });
171
+ break;
172
+ default:
173
+ throw new Error(`不支持的操作类型: ${action}`);
174
+ }
175
+ return {
176
+ content: [
177
+ {
178
+ type: "text",
179
+ text: JSON.stringify({
180
+ action,
181
+ result
182
+ }, null, 2)
183
+ }
184
+ ]
185
+ };
186
+ });
187
+ // getWebsiteConfig - 获取静态网站配置
188
+ server.tool("getWebsiteConfig", "获取静态网站配置", {
189
+ confirm: z.literal("yes").describe("确认操作,默认传 yes")
190
+ }, async () => {
191
+ const cloudbase = await getCloudBaseManager();
192
+ const result = await cloudbase.hosting.getWebsiteConfig();
233
193
  return {
234
194
  content: [
235
195
  {
@@ -29,7 +29,7 @@ export function registerInteractiveTools(server) {
29
29
  return {
30
30
  content: [{
31
31
  type: "text",
32
- text: `📝 用户澄清反馈:\n${result.data.response}`
32
+ text: `📝 用户澄清反馈:\n${result.data}`
33
33
  }]
34
34
  };
35
35
  }
@@ -45,13 +45,13 @@ export function registerInteractiveTools(server) {
45
45
  const dialogOptions = options || ["确认执行", "取消操作", "需要修改任务"];
46
46
  const interactiveServer = getInteractiveServer();
47
47
  const result = await interactiveServer.clarifyRequest(dialogMessage, dialogOptions);
48
- if (result.cancelled || result.data.response.includes('取消')) {
48
+ if (result.cancelled || (result.data && result.data.includes && result.data.includes('取消'))) {
49
49
  return { content: [{ type: "text", text: "❌ 用户取消了任务执行" }] };
50
50
  }
51
51
  return {
52
52
  content: [{
53
53
  type: "text",
54
- text: `✅ 用户确认: ${result.data.response}`
54
+ text: `✅ 用户确认: ${result.data}`
55
55
  }]
56
56
  };
57
57
  }
@@ -95,7 +95,7 @@ export async function _promptAndSetEnvironmentId(autoSelectSingle) {
95
95
  if (result.cancelled) {
96
96
  return { selectedEnvId: null, cancelled: true };
97
97
  }
98
- selectedEnvId = result.data.envId;
98
+ selectedEnvId = result.data;
99
99
  }
100
100
  // 4. 保存并设置环境ID
101
101
  if (selectedEnvId) {
@@ -97,6 +97,27 @@ async function copyFileIfNotExists(src, dest) {
97
97
  return { copied: false, reason: `复制失败: ${error instanceof Error ? error.message : '未知错误'}` };
98
98
  }
99
99
  }
100
+ // 复制文件,支持覆盖模式
101
+ async function copyFile(src, dest, overwrite = false) {
102
+ try {
103
+ const destExists = fs.existsSync(dest);
104
+ // 如果目标文件存在且不允许覆盖
105
+ if (destExists && !overwrite) {
106
+ return { copied: false, reason: '文件已存在', action: 'skipped' };
107
+ }
108
+ // 创建目标目录
109
+ await fsPromises.mkdir(path.dirname(dest), { recursive: true });
110
+ // 复制文件
111
+ await fsPromises.copyFile(src, dest);
112
+ return {
113
+ copied: true,
114
+ action: destExists ? 'overwritten' : 'created'
115
+ };
116
+ }
117
+ catch (error) {
118
+ return { copied: false, reason: `复制失败: ${error instanceof Error ? error.message : '未知错误'}` };
119
+ }
120
+ }
100
121
  export function registerSetupTools(server) {
101
122
  server.tool("downloadTemplate", `自动下载并部署CloudBase项目模板。
102
123
 
@@ -105,9 +126,10 @@ export function registerSetupTools(server) {
105
126
  - miniprogram: 微信小程序 + 云开发模板
106
127
  - rules: 只包含AI编辑器配置文件(包含Cursor、WindSurf、CodeBuddy等所有主流编辑器配置),适合在已有项目中补充AI编辑器配置
107
128
 
108
- 工具会自动下载模板到临时目录,解压后如果检测到WORKSPACE_FOLDER_PATHS环境变量,则复制到项目目录(不覆盖已存在文件)。`, {
109
- template: z.enum(["react", "miniprogram", "rules"]).describe("要下载的模板类型")
110
- }, async ({ template }) => {
129
+ 工具会自动下载模板到临时目录,解压后如果检测到WORKSPACE_FOLDER_PATHS环境变量,则复制到项目目录。`, {
130
+ template: z.enum(["react", "miniprogram", "rules"]).describe("要下载的模板类型"),
131
+ overwrite: z.boolean().optional().describe("是否覆盖已存在的文件,默认为false(不覆盖)")
132
+ }, async ({ template, overwrite = false }) => {
111
133
  try {
112
134
  const templateConfig = TEMPLATES[template];
113
135
  if (!templateConfig) {
@@ -131,16 +153,22 @@ export function registerSetupTools(server) {
131
153
  // 检查是否需要复制到项目目录
132
154
  const workspaceFolder = process.env.WORKSPACE_FOLDER_PATHS;
133
155
  let finalFiles = [];
134
- let copiedCount = 0;
156
+ let createdCount = 0;
157
+ let overwrittenCount = 0;
135
158
  let skippedCount = 0;
136
159
  const results = [];
137
160
  if (workspaceFolder) {
138
161
  for (const relativePath of extractedFiles) {
139
162
  const srcPath = path.join(extractDir, relativePath);
140
163
  const destPath = path.join(workspaceFolder, relativePath);
141
- const copyResult = await copyFileIfNotExists(srcPath, destPath);
164
+ const copyResult = await copyFile(srcPath, destPath, overwrite);
142
165
  if (copyResult.copied) {
143
- copiedCount++;
166
+ if (copyResult.action === 'overwritten') {
167
+ overwrittenCount++;
168
+ }
169
+ else {
170
+ createdCount++;
171
+ }
144
172
  finalFiles.push(destPath);
145
173
  }
146
174
  else {
@@ -149,8 +177,20 @@ export function registerSetupTools(server) {
149
177
  }
150
178
  }
151
179
  results.push(`✅ ${templateConfig.description} 同步完成`);
152
- results.push(`📁 保存在临时目录: ${extractDir}`);
153
- results.push(`📊 复制了 ${copiedCount} 个文件${skippedCount > 0 ? `,跳过 ${skippedCount} 个已存在文件` : ''}`);
180
+ results.push(`📁 临时目录: ${extractDir}`);
181
+ const stats = [];
182
+ if (createdCount > 0)
183
+ stats.push(`新建 ${createdCount} 个文件`);
184
+ if (overwrittenCount > 0)
185
+ stats.push(`覆盖 ${overwrittenCount} 个文件`);
186
+ if (skippedCount > 0)
187
+ stats.push(`跳过 ${skippedCount} 个已存在文件`);
188
+ if (stats.length > 0) {
189
+ results.push(`📊 ${stats.join(',')}`);
190
+ }
191
+ if (overwrite || overwrittenCount > 0 || skippedCount > 0) {
192
+ results.push(`🔄 覆盖模式: ${overwrite ? '启用' : '禁用'}`);
193
+ }
154
194
  }
155
195
  else {
156
196
  finalFiles = extractedFiles.map(relativePath => path.join(extractDir, relativePath));
@@ -14,8 +14,8 @@ class Logger {
14
14
  logFile;
15
15
  useConsole;
16
16
  constructor(options = {}) {
17
- // 通过环境变量控制是否启用日志
18
- this.enabled = options.enabled ?? (process.env.MCP_DEBUG === 'true' || process.env.NODE_ENV === 'development');
17
+ // 默认开启
18
+ this.enabled = options.enabled ?? true;
19
19
  this.level = options.level ?? LogLevel.INFO;
20
20
  this.useConsole = options.console ?? false;
21
21
  // 默认日志文件路径
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudbase/cloudbase-mcp",
3
- "version": "1.7.0",
3
+ "version": "1.7.2",
4
4
  "description": "腾讯云开发 MCP Server,支持静态托管/环境查询/",
5
5
  "main": "index.js",
6
6
  "type": "module",