@cloudbase/cloudbase-mcp 2.10.0 → 2.11.1

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
@@ -562,7 +562,7 @@ ${envIdSection}
562
562
  ## 环境信息
563
563
  - 操作系统: ${os_1.default.type()} ${os_1.default.release()}
564
564
  - Node.js版本: ${process.version}
565
- - MCP 版本:${process.env.npm_package_version || "2.10.0" || 0}
565
+ - MCP 版本:${process.env.npm_package_version || "2.11.1" || 0}
566
566
  - 系统架构: ${os_1.default.arch()}
567
567
  - 时间: ${new Date().toISOString()}
568
568
  - 请求ID: ${requestId}
@@ -971,11 +971,43 @@ exports.resetInteractiveServer = resetInteractiveServer;
971
971
  exports.getInteractiveServerSafe = getInteractiveServerSafe;
972
972
  const express_1 = __importDefault(__webpack_require__(2674));
973
973
  const http_1 = __importDefault(__webpack_require__(3782));
974
+ const child_process_1 = __webpack_require__(5814);
974
975
  const open_1 = __importDefault(__webpack_require__(1622));
975
976
  const ws_1 = __webpack_require__(6220);
976
977
  const index_js_1 = __webpack_require__(9529);
977
978
  const logger_js_1 = __webpack_require__(3039);
978
- // 动态导入 open 模块,兼容 ESM/CJS 环境
979
+ function isVSCodeEnvironment() {
980
+ return Boolean(process.env.VSCODE_IPC_HOOK_CLI ||
981
+ process.env.VSCODE_PID ||
982
+ process.env.TERM_PROGRAM === "vscode");
983
+ }
984
+ function parseBrowserCommand(browser) {
985
+ const parts = browser.match(/(?:[^\s"]+|"[^"]*")+/g) || [];
986
+ return parts.map((item) => item.replace(/^"(.*)"$/, "$1"));
987
+ }
988
+ async function openUrlByBrowserEnv(url) {
989
+ const browser = process.env.BROWSER?.trim();
990
+ if (!browser) {
991
+ return false;
992
+ }
993
+ const [command, ...args] = parseBrowserCommand(browser);
994
+ if (!command) {
995
+ return false;
996
+ }
997
+ const finalArgs = args.map((arg) => (arg === "%s" ? url : arg));
998
+ if (!args.includes("%s")) {
999
+ finalArgs.push(url);
1000
+ }
1001
+ return new Promise((resolve) => {
1002
+ (0, child_process_1.execFile)(command, finalArgs, (execError) => {
1003
+ resolve(!execError);
1004
+ });
1005
+ });
1006
+ }
1007
+ function shouldUseBrowserEnvFallback() {
1008
+ return process.platform === "linux" && isVSCodeEnvironment();
1009
+ }
1010
+ // Dynamic open behavior with IDE awareness and VSCode/Linux fallback
979
1011
  async function openUrl(url, options, mcpServer) {
980
1012
  // mcpServer 是 ExtendedMcpServer 实例,它有 server 和 ide 属性
981
1013
  // server 属性是 MCP server 的内部 server 实例,有 sendLoggingMessage 方法
@@ -1007,13 +1039,28 @@ async function openUrl(url, options, mcpServer) {
1007
1039
  return;
1008
1040
  }
1009
1041
  }
1010
- // 默认行为:直接打开网页
1042
+ // 默认行为:直接打开网页(带 VSCode/Linux 降级逻辑)
1011
1043
  (0, logger_js_1.debug)(`[openUrl] Opening URL in browser: ${url}`);
1012
1044
  try {
1013
- return await (0, open_1.default)(url, options);
1045
+ const openOptions = options ? { url: true, ...options } : { url: true };
1046
+ const child = await (0, open_1.default)(url, openOptions);
1047
+ if (child?.once) {
1048
+ child.once("error", async () => {
1049
+ if (shouldUseBrowserEnvFallback()) {
1050
+ (0, logger_js_1.debug)("[openUrl] open() failed, trying BROWSER fallback");
1051
+ await openUrlByBrowserEnv(url);
1052
+ }
1053
+ });
1054
+ }
1055
+ return;
1014
1056
  }
1015
1057
  catch (err) {
1016
1058
  (0, logger_js_1.error)(`Failed to open ${url} ${options} ${err instanceof Error ? err.message : err} `, err instanceof Error ? err : new Error(String(err)));
1059
+ if (shouldUseBrowserEnvFallback()) {
1060
+ (0, logger_js_1.debug)("[openUrl] open() threw, trying BROWSER fallback");
1061
+ await openUrlByBrowserEnv(url);
1062
+ return;
1063
+ }
1017
1064
  (0, logger_js_1.warn)(`Please manually open: ${url}`);
1018
1065
  }
1019
1066
  }
@@ -1038,10 +1085,15 @@ class InteractiveServer {
1038
1085
  3722, 3723, 3724, 3725, 3726, 3727, 3728, 3729, 3730, 3731, 3732, 3733,
1039
1086
  3734, 3735,
1040
1087
  ];
1088
+ /** Idle timeout for HTTP/WS connections (e.g. long login). Avoids "connection disconnected" after ~1 min. */
1089
+ static SERVER_IDLE_MS = 30 * 60 * 1000; // 30 minutes
1090
+ /** WebSocket ping interval to keep connection alive past proxies/firewalls. */
1091
+ static WS_PING_INTERVAL_MS = 25 * 1000; // 25 seconds
1041
1092
  constructor(mcpServer) {
1042
1093
  this._mcpServer = mcpServer;
1043
1094
  this.app = (0, express_1.default)();
1044
1095
  this.server = http_1.default.createServer(this.app);
1096
+ this.applyServerTimeouts();
1045
1097
  this.wss = new ws_1.WebSocketServer({ server: this.server });
1046
1098
  this.setupExpress();
1047
1099
  this.setupWebSocket();
@@ -1057,6 +1109,16 @@ class InteractiveServer {
1057
1109
  this.isRunning = false;
1058
1110
  }
1059
1111
  }
1112
+ /** Apply timeouts so long-lived login does not cause "connection disconnected". */
1113
+ applyServerTimeouts() {
1114
+ this.server.timeout = 0;
1115
+ if (typeof this.server.keepAliveTimeout === "number") {
1116
+ this.server.keepAliveTimeout = InteractiveServer.SERVER_IDLE_MS;
1117
+ }
1118
+ if (typeof this.server.headersTimeout === "number") {
1119
+ this.server.headersTimeout = Math.max(this.server.headersTimeout || 0, InteractiveServer.SERVER_IDLE_MS);
1120
+ }
1121
+ }
1060
1122
  setupExpress() {
1061
1123
  this.app.use(express_1.default.json());
1062
1124
  this.app.get("/env-setup/:sessionId", (req, res) => {
@@ -1160,6 +1222,16 @@ class InteractiveServer {
1160
1222
  setupWebSocket() {
1161
1223
  this.wss.on("connection", (ws) => {
1162
1224
  (0, logger_js_1.debug)("WebSocket client connected");
1225
+ // Keep connection alive during long login so proxies/firewalls do not close it
1226
+ const pingInterval = setInterval(() => {
1227
+ if (ws.readyState === ws_1.WebSocket.OPEN) {
1228
+ ws.ping();
1229
+ }
1230
+ }, InteractiveServer.WS_PING_INTERVAL_MS);
1231
+ ws.on("close", () => {
1232
+ clearInterval(pingInterval);
1233
+ (0, logger_js_1.debug)("WebSocket client disconnected");
1234
+ });
1163
1235
  ws.on("message", async (message) => {
1164
1236
  try {
1165
1237
  const data = JSON.parse(message.toString());
@@ -1269,9 +1341,6 @@ class InteractiveServer {
1269
1341
  (0, logger_js_1.error)("WebSocket message parsing error", err instanceof Error ? err : new Error(String(err)));
1270
1342
  }
1271
1343
  });
1272
- ws.on("close", () => {
1273
- (0, logger_js_1.debug)("WebSocket client disconnected");
1274
- });
1275
1344
  });
1276
1345
  }
1277
1346
  async start() {
@@ -1313,13 +1382,15 @@ class InteractiveServer {
1313
1382
  reject(err);
1314
1383
  }
1315
1384
  };
1385
+ // Host: default 0.0.0.0 so Cloud IDE / VSCode Remote port-forward can connect; set INTERACTIVE_SERVER_HOST=127.0.0.1 for local-only
1386
+ const host = process.env.INTERACTIVE_SERVER_HOST ?? "0.0.0.0";
1316
1387
  // 设置成功监听处理
1317
1388
  const listeningHandler = () => {
1318
1389
  const address = this.server.address();
1319
1390
  if (address && typeof address === "object") {
1320
1391
  this.port = address.port;
1321
1392
  this.isRunning = true;
1322
- (0, logger_js_1.info)(`Interactive server started successfully on http://localhost:${this.port}`);
1393
+ (0, logger_js_1.info)(`Interactive server started successfully on http://${host}:${this.port}`);
1323
1394
  // 移除临时监听器
1324
1395
  this.server.removeListener("error", errorHandler);
1325
1396
  this.server.removeListener("listening", listeningHandler);
@@ -1334,7 +1405,7 @@ class InteractiveServer {
1334
1405
  this.server.once("error", errorHandler);
1335
1406
  this.server.once("listening", listeningHandler);
1336
1407
  try {
1337
- this.server.listen(portToTry, "127.0.0.1");
1408
+ this.server.listen(portToTry, host);
1338
1409
  }
1339
1410
  catch (err) {
1340
1411
  (0, logger_js_1.error)(`Failed to bind to port ${portToTry}:`, err instanceof Error ? err : new Error(String(err)));
@@ -1375,6 +1446,7 @@ class InteractiveServer {
1375
1446
  this.port = 0;
1376
1447
  // 重新创建整个服务器实例以便下次使用
1377
1448
  this.server = http_1.default.createServer(this.app);
1449
+ this.applyServerTimeouts();
1378
1450
  this.wss = new ws_1.WebSocketServer({ server: this.server });
1379
1451
  this.setupWebSocket();
1380
1452
  (0, logger_js_1.debug)("HTTP and WebSocket servers recreated for next use");
@@ -1465,11 +1537,9 @@ class InteractiveServer {
1465
1537
  (0, logger_js_1.warn)(`Please manually open: ${url}`);
1466
1538
  }
1467
1539
  (0, logger_js_1.info)("Waiting for user selection...");
1468
- // Use shorter timeout for CodeBuddy when notification is sent (2 minutes)
1469
- // This prevents hanging while still giving users enough time to respond
1470
- // Otherwise use the default 10 minutes timeout
1471
- const timeoutDuration = (isCodeBuddy && notificationSent) ? 2 * 60 * 1000 : 10 * 60 * 1000;
1472
- (0, logger_js_1.debug)(`[collectEnvId] Using timeout duration: ${timeoutDuration / 1000} seconds (CodeBuddy: ${isCodeBuddy}, notification sent: ${notificationSent})`);
1540
+ // Use same 10 minutes for all IDEs so long login (re-auth, switch account) does not close the server
1541
+ const timeoutDuration = 10 * 60 * 1000;
1542
+ (0, logger_js_1.debug)(`[collectEnvId] Using timeout duration: ${timeoutDuration / 1000} seconds`);
1473
1543
  return new Promise((resolve) => {
1474
1544
  this.currentResolver = (result) => {
1475
1545
  // 用户选择完成后,关闭服务器
@@ -5267,8 +5337,11 @@ checkIndex: 检查索引是否存在`),
5267
5337
  title: "修改 NoSQL 数据库结构",
5268
5338
  description: "修改 NoSQL 数据库结构",
5269
5339
  inputSchema: {
5270
- action: zod_1.z.enum(["createCollection", "updateCollection", "deleteCollection"])
5271
- .describe(`createCollection: 创建集合
5340
+ action: zod_1.z.enum([
5341
+ "createCollection",
5342
+ "updateCollection",
5343
+ "deleteCollection",
5344
+ ]).describe(`createCollection: 创建集合
5272
5345
  updateCollection: 更新集合
5273
5346
  deleteCollection: 删除集合`),
5274
5347
  collectionName: zod_1.z.string().describe("集合名称"),
@@ -5348,9 +5421,7 @@ deleteCollection: 删除集合`),
5348
5421
  success: true,
5349
5422
  requestId: result.RequestId,
5350
5423
  action,
5351
- message: result.Exists === false
5352
- ? "集合不存在"
5353
- : "云开发数据库集合删除成功",
5424
+ message: result.Exists === false ? "集合不存在" : "云开发数据库集合删除成功",
5354
5425
  };
5355
5426
  if (result.Exists === false) {
5356
5427
  body.exists = false;
@@ -5381,9 +5452,17 @@ deleteCollection: 删除集合`),
5381
5452
  .optional()
5382
5453
  .describe("返回字段投影(对象或字符串,推荐对象)"),
5383
5454
  sort: zod_1.z
5384
- .union([zod_1.z.object({}).passthrough(), zod_1.z.string()])
5455
+ .union([
5456
+ zod_1.z.array(zod_1.z
5457
+ .object({
5458
+ key: zod_1.z.string().describe("sort 字段名"),
5459
+ direction: zod_1.z.number().describe("排序方向,1:升序,-1:降序"),
5460
+ })
5461
+ .passthrough()),
5462
+ zod_1.z.string(),
5463
+ ])
5385
5464
  .optional()
5386
- .describe("排序条件(对象或字符串,推荐对象)"),
5465
+ .describe("排序条件,使用对象或字符串。"),
5387
5466
  limit: zod_1.z.number().optional().describe("返回数量限制"),
5388
5467
  offset: zod_1.z.number().optional().describe("跳过的记录数"),
5389
5468
  },
@@ -5429,7 +5508,8 @@ deleteCollection: 删除集合`),
5429
5508
  title: "修改 NoSQL 数据库数据记录",
5430
5509
  description: "修改 NoSQL 数据库数据记录",
5431
5510
  inputSchema: {
5432
- action: zod_1.z.enum(["insert", "update", "delete"])
5511
+ action: zod_1.z
5512
+ .enum(["insert", "update", "delete"])
5433
5513
  .describe(`insert: 插入数据(新增文档)\nupdate: 更新数据\ndelete: 删除数据`),
5434
5514
  collectionName: zod_1.z.string().describe("集合名称"),
5435
5515
  documents: zod_1.z
@@ -8606,7 +8686,7 @@ class TelemetryReporter {
8606
8686
  const nodeVersion = process.version; // Node.js版本
8607
8687
  const arch = os_1.default.arch(); // 系统架构
8608
8688
  // 从构建时注入的版本号获取MCP版本信息
8609
- const mcpVersion = process.env.npm_package_version || "2.10.0" || 0;
8689
+ const mcpVersion = process.env.npm_package_version || "2.11.1" || 0;
8610
8690
  return {
8611
8691
  userAgent: `${osType} ${osRelease} ${arch} ${nodeVersion} CloudBase-MCP/${mcpVersion}`,
8612
8692
  deviceId: this.deviceId,
@@ -10039,7 +10119,7 @@ function registerSetupTools(server) {
10039
10119
  title: "下载项目模板",
10040
10120
  description: `自动下载并部署CloudBase项目模板。⚠️ **MANDATORY FOR NEW PROJECTS** ⚠️
10041
10121
 
10042
- **CRITICAL**: This tool MUST be called FIRST when starting a new project.\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.10.0" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
10122
+ **CRITICAL**: This tool MUST be called FIRST when starting a new project.\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.11.1" : 0}),便于后续维护和版本追踪\n- 下载 rules 模板时,如果项目中已存在 README.md 文件,系统会自动保护该文件不被覆盖(除非设置 overwrite=true)`,
10043
10123
  inputSchema: {
10044
10124
  template: zod_1.z
10045
10125
  .enum(["react", "vue", "miniprogram", "uniapp", "rules"])
@@ -11448,8 +11528,10 @@ function getJavaScripts(wsPort) {
11448
11528
  }
11449
11529
  }
11450
11530
 
11451
- // WebSocket connection
11452
- const ws = new WebSocket('ws://localhost:${wsPort}');
11531
+ // WebSocket: same host as page so it works in remote VS Code / Cloud IDE (no localhost)
11532
+ const wsScheme = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
11533
+ const wsUrl = wsScheme + '//' + window.location.host;
11534
+ const ws = new WebSocket(wsUrl);
11453
11535
 
11454
11536
  ws.onopen = () => {
11455
11537
  console.log('[env-setup] WebSocket connected');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudbase/cloudbase-mcp",
3
- "version": "2.10.0",
3
+ "version": "2.11.1",
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",