@junjun-org/bd-ke-mcp 1.0.0 → 1.0.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/index.js +404 -238
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -78,7 +78,7 @@ function loadConfig() {
|
|
|
78
78
|
const fileContent = fs.readFileSync(configFile, "utf8");
|
|
79
79
|
const fileConfig = JSON.parse(fileContent);
|
|
80
80
|
Object.assign(config, fileConfig);
|
|
81
|
-
logger.
|
|
81
|
+
logger.debug("已加载配置文件", { path: configFile });
|
|
82
82
|
} catch (error) {
|
|
83
83
|
logger.error("配置文件加载失败", { error: error.message });
|
|
84
84
|
}
|
|
@@ -143,16 +143,23 @@ async function fetchWithTimeout(url, options = {}, timeoutMs = FETCH_TIMEOUT) {
|
|
|
143
143
|
|
|
144
144
|
/**
|
|
145
145
|
* KE API 请求
|
|
146
|
+
* @param {string} method - HTTP 方法
|
|
147
|
+
* @param {string} urlPath - API 路径
|
|
148
|
+
* @param {Object} options - 请求选项
|
|
149
|
+
* @param {string} options.domain - KE 平台域名,未指定时从配置文件获取
|
|
150
|
+
* @param {string} options.access_token - 访问令牌,未指定时从配置文件获取
|
|
151
|
+
* @param {Object} options.body - 请求体
|
|
152
|
+
* @param {Object} options.headers - 额外的请求头
|
|
146
153
|
*/
|
|
147
154
|
async function keRequest(method, urlPath, options = {}) {
|
|
148
|
-
const domain = getConfig("domain");
|
|
149
|
-
const accessToken = getConfig("access_token");
|
|
155
|
+
const domain = options.domain || getConfig("domain");
|
|
156
|
+
const accessToken = options.access_token || getConfig("access_token");
|
|
150
157
|
|
|
151
158
|
if (!domain) {
|
|
152
|
-
throw new Error("
|
|
159
|
+
throw new Error("参数 domain 未指定且配置文件中未设置");
|
|
153
160
|
}
|
|
154
161
|
if (!accessToken) {
|
|
155
|
-
throw new Error("
|
|
162
|
+
throw new Error("参数 access_token 未指定且配置文件中未设置");
|
|
156
163
|
}
|
|
157
164
|
|
|
158
165
|
const url = `${domain.replace(/\/+$/, "")}/${urlPath.replace(/^\/+/, "")}`;
|
|
@@ -187,26 +194,135 @@ async function keRequest(method, urlPath, options = {}) {
|
|
|
187
194
|
|
|
188
195
|
// ==================== 工具实现 ====================
|
|
189
196
|
|
|
197
|
+
/**
|
|
198
|
+
· * ke_config_reset - 重置/更新 KE MCP 配置参数
|
|
199
|
+
* 将配置写入当前项目空间下的 .mcp/bd-ke-mcp.json 文件
|
|
200
|
+
* @param {Object} config - 配置选项
|
|
201
|
+
* @param {string} config.domain - KE 平台域名(用户输入)
|
|
202
|
+
* @param {string} config.access_token - 访问令牌(用户输入)
|
|
203
|
+
* @param {string} config.tenant - 租户名称(从 ke_tenants_list 获取后选择)
|
|
204
|
+
* @param {string} config.project - 项目名称(从 ke_projects_list 获取后选择)
|
|
205
|
+
* @param {string} config.application - 应用名称(从 ke_app_list 获取后选择)
|
|
206
|
+
* @param {string} config.env - 环境名称(从 ke_env_list 获取后选择)
|
|
207
|
+
*/
|
|
208
|
+
async function keConfigReset(config) {
|
|
209
|
+
const projectRoot = getProjectRoot();
|
|
210
|
+
|
|
211
|
+
if (!projectRoot) {
|
|
212
|
+
throw new Error("PROJECT_ROOT 环境变量未设置,无法确定项目路径");
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const mcpDir = path.join(projectRoot, ".mcp");
|
|
216
|
+
const configFile = path.join(mcpDir, CONFIG_FILE_NAME);
|
|
217
|
+
|
|
218
|
+
// 确保 .mcp 目录存在
|
|
219
|
+
if (!fs.existsSync(mcpDir)) {
|
|
220
|
+
fs.mkdirSync(mcpDir, { recursive: true });
|
|
221
|
+
logger.info("已创建 .mcp 目录", { path: mcpDir });
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// 读取现有配置(如果存在)
|
|
225
|
+
let existingConfig = {};
|
|
226
|
+
if (fs.existsSync(configFile)) {
|
|
227
|
+
try {
|
|
228
|
+
const fileContent = fs.readFileSync(configFile, "utf8");
|
|
229
|
+
existingConfig = JSON.parse(fileContent);
|
|
230
|
+
logger.info("已读取现有配置文件", { path: configFile });
|
|
231
|
+
} catch (error) {
|
|
232
|
+
logger.warn("读取现有配置文件失败,将创建新配置", { error: error.message });
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// 合并配置(新配置覆盖旧配置)
|
|
237
|
+
const newConfig = { ...existingConfig };
|
|
238
|
+
|
|
239
|
+
// 只更新提供的非空配置项
|
|
240
|
+
if (config.domain !== undefined && config.domain !== "") {
|
|
241
|
+
newConfig.domain = config.domain;
|
|
242
|
+
}
|
|
243
|
+
if (config.access_token !== undefined && config.access_token !== "") {
|
|
244
|
+
newConfig.access_token = config.access_token;
|
|
245
|
+
}
|
|
246
|
+
if (config.tenant !== undefined && config.tenant !== "") {
|
|
247
|
+
newConfig.tenant = config.tenant;
|
|
248
|
+
}
|
|
249
|
+
if (config.project !== undefined && config.project !== "") {
|
|
250
|
+
newConfig.project = config.project;
|
|
251
|
+
}
|
|
252
|
+
if (config.application !== undefined && config.application !== "") {
|
|
253
|
+
newConfig.application = config.application;
|
|
254
|
+
}
|
|
255
|
+
if (config.env !== undefined && config.env !== "") {
|
|
256
|
+
newConfig.env = config.env;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// 写入配置文件
|
|
260
|
+
try {
|
|
261
|
+
fs.writeFileSync(configFile, JSON.stringify(newConfig, null, 2) + "\n", "utf8");
|
|
262
|
+
logger.info("配置已写入", { path: configFile });
|
|
263
|
+
} catch (error) {
|
|
264
|
+
throw new Error(`写入配置文件失败: ${error.message}`);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// 重新加载全局配置
|
|
268
|
+
reloadConfig();
|
|
269
|
+
|
|
270
|
+
// 构建返回消息
|
|
271
|
+
let result = `KE 配置已成功保存\n`;
|
|
272
|
+
result += `配置文件路径: ${configFile}\n\n`;
|
|
273
|
+
result += `当前配置:\n`;
|
|
274
|
+
result += ` domain: ${newConfig.domain || "(未设置)"}\n`;
|
|
275
|
+
result += ` access_token: ${newConfig.access_token ? "******(已设置)" : "(未设置)"}\n`;
|
|
276
|
+
result += ` tenant: ${newConfig.tenant || "(未设置)"}\n`;
|
|
277
|
+
result += ` project: ${newConfig.project || "(未设置)"}\n`;
|
|
278
|
+
result += ` application: ${newConfig.application || "(未设置)"}\n`;
|
|
279
|
+
result += ` env: ${newConfig.env || "(未设置)"}\n`;
|
|
280
|
+
|
|
281
|
+
return result;
|
|
282
|
+
}
|
|
283
|
+
|
|
190
284
|
/**
|
|
191
285
|
* ke_app_restart - 重启 KE 应用(通过删除 Pod)
|
|
286
|
+
* @param {Object} options - 配置选项
|
|
287
|
+
* @param {string} options.domain - KE 平台域名,未指定时从配置文件获取
|
|
288
|
+
* @param {string} options.access_token - 访问令牌,未指定时从配置文件获取
|
|
289
|
+
* @param {string} options.tenant - 租户名称,未指定时从配置文件获取
|
|
290
|
+
* @param {string} options.project - 项目名称,未指定时从配置文件获取
|
|
291
|
+
* @param {string} options.application - 应用名称,未指定时从配置文件获取
|
|
292
|
+
* @param {string} options.env - 环境名称,未指定时从配置文件获取
|
|
192
293
|
*/
|
|
193
|
-
async function keAppRestart() {
|
|
294
|
+
async function keAppRestart(options = {}) {
|
|
194
295
|
reloadConfig();
|
|
195
296
|
|
|
196
|
-
|
|
197
|
-
const
|
|
297
|
+
// 配置项优先从参数获取,未指定则从配置文件获取
|
|
298
|
+
const domain = options.domain || getConfig("domain");
|
|
299
|
+
const accessToken = options.access_token || getConfig("access_token");
|
|
300
|
+
const tenant = options.tenant || getConfig("tenant");
|
|
301
|
+
const project = options.project || getConfig("project");
|
|
302
|
+
const application = options.application || getConfig("application");
|
|
303
|
+
const env = options.env || getConfig("env");
|
|
198
304
|
|
|
199
|
-
if (!
|
|
200
|
-
throw new Error("
|
|
305
|
+
if (!tenant) {
|
|
306
|
+
throw new Error("参数 tenant 未指定且配置文件中未设置");
|
|
307
|
+
}
|
|
308
|
+
if (!project) {
|
|
309
|
+
throw new Error("参数 project 未指定且配置文件中未设置");
|
|
310
|
+
}
|
|
311
|
+
if (!application) {
|
|
312
|
+
throw new Error("参数 application 未指定且配置文件中未设置");
|
|
313
|
+
}
|
|
314
|
+
if (!env) {
|
|
315
|
+
throw new Error("参数 env 未指定且配置文件中未设置");
|
|
201
316
|
}
|
|
202
317
|
|
|
203
|
-
const namespace =
|
|
318
|
+
const namespace = `${tenant}-${project}`;
|
|
319
|
+
const requestOptions = { domain, access_token: accessToken };
|
|
204
320
|
|
|
205
321
|
logger.info("重启应用", { application, env, namespace });
|
|
206
322
|
|
|
207
323
|
// 1. 获取应用状态
|
|
208
324
|
const statusUrl = `amp/v4/namespace/${namespace}/env/${env}/applications/${application}/appstatus/view?canary=false`;
|
|
209
|
-
const statusData = await keRequest("GET", statusUrl);
|
|
325
|
+
const statusData = await keRequest("GET", statusUrl, requestOptions);
|
|
210
326
|
|
|
211
327
|
// 2. 提取 Pod 名称
|
|
212
328
|
const podNames = [];
|
|
@@ -233,7 +349,7 @@ async function keAppRestart() {
|
|
|
233
349
|
for (const podName of podNames) {
|
|
234
350
|
try {
|
|
235
351
|
const deleteUrl = `amp/v4/namespace/${namespace}/env/${env}/applications/${application}/pods/${podName}/delete?cluster=${env}`;
|
|
236
|
-
await keRequest("DELETE", deleteUrl);
|
|
352
|
+
await keRequest("DELETE", deleteUrl, requestOptions);
|
|
237
353
|
deletedPods.push(podName);
|
|
238
354
|
logger.info(`已删除 Pod: ${podName}`);
|
|
239
355
|
} catch (error) {
|
|
@@ -268,17 +384,35 @@ async function keAppRestart() {
|
|
|
268
384
|
|
|
269
385
|
/**
|
|
270
386
|
* ke_pipeline_list - 获取流水线列表
|
|
387
|
+
* @param {Object} options - 配置选项
|
|
388
|
+
* @param {string} options.domain - KE 平台域名,未指定时从配置文件获取
|
|
389
|
+
* @param {string} options.access_token - 访问令牌,未指定时从配置文件获取
|
|
390
|
+
* @param {string} options.tenant - 租户名称,未指定时从配置文件获取
|
|
391
|
+
* @param {string} options.project - 项目名称,未指定时从配置文件获取
|
|
392
|
+
* @param {string} options.application - 应用名称(可选),未指定时从配置文件获取
|
|
393
|
+
* @param {number} options.page - 页码,默认 1
|
|
394
|
+
* @param {number} options.pageSize - 每页数量,默认 20
|
|
271
395
|
*/
|
|
272
|
-
async function kePipelineList(
|
|
396
|
+
async function kePipelineList(options = {}) {
|
|
273
397
|
reloadConfig();
|
|
274
398
|
|
|
275
|
-
|
|
276
|
-
const
|
|
277
|
-
const
|
|
278
|
-
const
|
|
399
|
+
// 配置项优先从参数获取,未指定则从配置文件获取
|
|
400
|
+
const domain = options.domain || getConfig("domain");
|
|
401
|
+
const accessToken = options.access_token || getConfig("access_token");
|
|
402
|
+
const tenant = options.tenant || getConfig("tenant");
|
|
403
|
+
const project = options.project || getConfig("project");
|
|
404
|
+
const application = options.application || getConfig("application");
|
|
405
|
+
const page = options.page || 1;
|
|
406
|
+
const pageSize = options.pageSize || 20;
|
|
279
407
|
|
|
280
|
-
if (!
|
|
281
|
-
throw new Error("
|
|
408
|
+
if (!domain) {
|
|
409
|
+
throw new Error("参数 domain 未指定且配置文件中未设置");
|
|
410
|
+
}
|
|
411
|
+
if (!tenant) {
|
|
412
|
+
throw new Error("参数 tenant 未指定且配置文件中未设置");
|
|
413
|
+
}
|
|
414
|
+
if (!project) {
|
|
415
|
+
throw new Error("参数 project 未指定且配置文件中未设置");
|
|
282
416
|
}
|
|
283
417
|
|
|
284
418
|
// 构建 label 参数
|
|
@@ -298,7 +432,7 @@ async function kePipelineList(page = 1, pageSize = 20) {
|
|
|
298
432
|
|
|
299
433
|
logger.info("获取流水线列表", { group, application });
|
|
300
434
|
|
|
301
|
-
const data = await keRequest("GET", url);
|
|
435
|
+
const data = await keRequest("GET", url, { domain, access_token: accessToken });
|
|
302
436
|
const pipelinesData = data.rtnData?.data || [];
|
|
303
437
|
const envMapping = data.rtnData?.env || {};
|
|
304
438
|
const totalCount = data.rtnData?.count || 0;
|
|
@@ -334,17 +468,21 @@ async function kePipelineList(page = 1, pageSize = 20) {
|
|
|
334
468
|
|
|
335
469
|
/**
|
|
336
470
|
* ke_pipeline_info - 获取流水线信息
|
|
471
|
+
* @param {Object} options - 配置选项
|
|
472
|
+
* @param {string} options.access_token - 访问令牌,未指定时从配置文件获取
|
|
473
|
+
* @param {string} options.pipelineUrl - 流水线地址(必填)
|
|
337
474
|
*/
|
|
338
|
-
async function kePipelineInfo(
|
|
475
|
+
async function kePipelineInfo(options = {}) {
|
|
339
476
|
reloadConfig();
|
|
340
477
|
|
|
478
|
+
const accessToken = options.access_token || getConfig("access_token");
|
|
479
|
+
const pipelineUrl = options.pipelineUrl;
|
|
480
|
+
|
|
341
481
|
if (!pipelineUrl) {
|
|
342
482
|
throw new Error("参数 pipelineUrl 不能为空");
|
|
343
483
|
}
|
|
344
|
-
|
|
345
|
-
const accessToken = getConfig("access_token");
|
|
346
484
|
if (!accessToken) {
|
|
347
|
-
throw new Error("
|
|
485
|
+
throw new Error("参数 access_token 未指定且配置文件中未设置");
|
|
348
486
|
}
|
|
349
487
|
|
|
350
488
|
logger.info("获取流水线信息", { url: pipelineUrl });
|
|
@@ -393,17 +531,27 @@ async function kePipelineInfo(pipelineUrl) {
|
|
|
393
531
|
|
|
394
532
|
/**
|
|
395
533
|
* ke_pipeline_create - 执行流水线
|
|
534
|
+
* @param {Object} options - 配置选项
|
|
535
|
+
* @param {string} options.access_token - 访问令牌,未指定时从配置文件获取
|
|
536
|
+
* @param {string} options.pipelineUrl - 流水线地址(必填)
|
|
537
|
+
* @param {Object} options.globalArguments - 全局参数字典(必填)
|
|
538
|
+
* @param {number} options.deadline - 超时时间(秒),默认 86400
|
|
539
|
+
* @param {string} options.review - 审核信息,默认空
|
|
396
540
|
*/
|
|
397
|
-
async function kePipelineCreate(
|
|
541
|
+
async function kePipelineCreate(options = {}) {
|
|
398
542
|
reloadConfig();
|
|
399
543
|
|
|
544
|
+
const accessToken = options.access_token || getConfig("access_token");
|
|
545
|
+
const pipelineUrl = options.pipelineUrl;
|
|
546
|
+
const globalArguments = options.globalArguments;
|
|
547
|
+
const deadline = options.deadline || 86400;
|
|
548
|
+
const review = options.review || "";
|
|
549
|
+
|
|
400
550
|
if (!pipelineUrl) {
|
|
401
551
|
throw new Error("参数 pipelineUrl 不能为空");
|
|
402
552
|
}
|
|
403
|
-
|
|
404
|
-
const accessToken = getConfig("access_token");
|
|
405
553
|
if (!accessToken) {
|
|
406
|
-
throw new Error("
|
|
554
|
+
throw new Error("参数 access_token 未指定且配置文件中未设置");
|
|
407
555
|
}
|
|
408
556
|
if (!globalArguments || Object.keys(globalArguments).length === 0) {
|
|
409
557
|
throw new Error("参数 globalArguments 不能为空");
|
|
@@ -454,12 +602,18 @@ async function kePipelineCreate(pipelineUrl, globalArguments, deadline = 86400,
|
|
|
454
602
|
|
|
455
603
|
/**
|
|
456
604
|
* ke_tenants_list - 列出所有租户
|
|
605
|
+
* @param {Object} options - 配置选项
|
|
606
|
+
* @param {string} options.domain - KE 平台域名,未指定时从配置文件获取
|
|
607
|
+
* @param {string} options.access_token - 访问令牌,未指定时从配置文件获取
|
|
457
608
|
*/
|
|
458
|
-
async function keTenantsList() {
|
|
609
|
+
async function keTenantsList(options = {}) {
|
|
459
610
|
reloadConfig();
|
|
460
611
|
|
|
612
|
+
const domain = options.domain || getConfig("domain");
|
|
613
|
+
const accessToken = options.access_token || getConfig("access_token");
|
|
614
|
+
|
|
461
615
|
const url = "user-center/v4/tenants/list?page=1&pageSize=100&type=all&single=true";
|
|
462
|
-
const data = await keRequest("GET", url);
|
|
616
|
+
const data = await keRequest("GET", url, { domain, access_token: accessToken });
|
|
463
617
|
|
|
464
618
|
const tenantsData = data.rtnData?.data || [];
|
|
465
619
|
|
|
@@ -480,17 +634,24 @@ async function keTenantsList() {
|
|
|
480
634
|
|
|
481
635
|
/**
|
|
482
636
|
* ke_projects_list - 列出所有项目
|
|
637
|
+
* @param {Object} options - 配置选项
|
|
638
|
+
* @param {string} options.domain - KE 平台域名,未指定时从配置文件获取
|
|
639
|
+
* @param {string} options.access_token - 访问令牌,未指定时从配置文件获取
|
|
640
|
+
* @param {string} options.tenant - 租户名称,未指定时从配置文件获取
|
|
483
641
|
*/
|
|
484
|
-
async function keProjectsList() {
|
|
642
|
+
async function keProjectsList(options = {}) {
|
|
485
643
|
reloadConfig();
|
|
486
644
|
|
|
487
|
-
const
|
|
645
|
+
const domain = options.domain || getConfig("domain");
|
|
646
|
+
const accessToken = options.access_token || getConfig("access_token");
|
|
647
|
+
const tenant = options.tenant || getConfig("tenant");
|
|
648
|
+
|
|
488
649
|
if (!tenant) {
|
|
489
|
-
throw new Error("
|
|
650
|
+
throw new Error("参数 tenant 未指定且配置文件中未设置");
|
|
490
651
|
}
|
|
491
652
|
|
|
492
653
|
const url = `user-center/v4/tenants/${tenant}/projects/list`;
|
|
493
|
-
const data = await keRequest("GET", url);
|
|
654
|
+
const data = await keRequest("GET", url, { domain, access_token: accessToken });
|
|
494
655
|
|
|
495
656
|
const projectsData = data.rtnData?.data || [];
|
|
496
657
|
|
|
@@ -513,13 +674,30 @@ async function keProjectsList() {
|
|
|
513
674
|
|
|
514
675
|
/**
|
|
515
676
|
* ke_app_list - 列出所有应用
|
|
677
|
+
* @param {Object} options - 配置选项
|
|
678
|
+
* @param {string} options.domain - KE 平台域名,未指定时从配置文件获取
|
|
679
|
+
* @param {string} options.access_token - 访问令牌,未指定时从配置文件获取
|
|
680
|
+
* @param {string} options.tenant - 租户名称,未指定时从配置文件获取
|
|
681
|
+
* @param {string} options.project - 项目名称,未指定时从配置文件获取
|
|
516
682
|
*/
|
|
517
|
-
async function keAppList() {
|
|
683
|
+
async function keAppList(options = {}) {
|
|
518
684
|
reloadConfig();
|
|
519
685
|
|
|
520
|
-
const
|
|
686
|
+
const domain = options.domain || getConfig("domain");
|
|
687
|
+
const accessToken = options.access_token || getConfig("access_token");
|
|
688
|
+
const tenant = options.tenant || getConfig("tenant");
|
|
689
|
+
const project = options.project || getConfig("project");
|
|
690
|
+
|
|
691
|
+
if (!tenant) {
|
|
692
|
+
throw new Error("参数 tenant 未指定且配置文件中未设置");
|
|
693
|
+
}
|
|
694
|
+
if (!project) {
|
|
695
|
+
throw new Error("参数 project 未指定且配置文件中未设置");
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
const namespace = `${tenant}-${project}`;
|
|
521
699
|
const url = `amp/v4/namespace/${namespace}/applications/list?page=1&pageSize=999`;
|
|
522
|
-
const data = await keRequest("GET", url);
|
|
700
|
+
const data = await keRequest("GET", url, { domain, access_token: accessToken });
|
|
523
701
|
|
|
524
702
|
const appsData = data.rtnData?.data || [];
|
|
525
703
|
|
|
@@ -540,17 +718,24 @@ async function keAppList() {
|
|
|
540
718
|
|
|
541
719
|
/**
|
|
542
720
|
* ke_env_list - 获取环境列表
|
|
721
|
+
* @param {Object} options - 配置选项
|
|
722
|
+
* @param {string} options.domain - KE 平台域名,未指定时从配置文件获取
|
|
723
|
+
* @param {string} options.access_token - 访问令牌,未指定时从配置文件获取
|
|
724
|
+
* @param {string} options.tenant - 租户名称,未指定时从配置文件获取
|
|
543
725
|
*/
|
|
544
|
-
async function keEnvList() {
|
|
726
|
+
async function keEnvList(options = {}) {
|
|
545
727
|
reloadConfig();
|
|
546
728
|
|
|
547
|
-
const
|
|
729
|
+
const domain = options.domain || getConfig("domain");
|
|
730
|
+
const accessToken = options.access_token || getConfig("access_token");
|
|
731
|
+
const tenant = options.tenant || getConfig("tenant");
|
|
732
|
+
|
|
548
733
|
if (!tenant) {
|
|
549
|
-
throw new Error("
|
|
734
|
+
throw new Error("参数 tenant 未指定且配置文件中未设置");
|
|
550
735
|
}
|
|
551
736
|
|
|
552
737
|
const url = `user-center/v4/tenants/${tenant}/clusters/all/list`;
|
|
553
|
-
const data = await keRequest("GET", url);
|
|
738
|
+
const data = await keRequest("GET", url, { domain, access_token: accessToken });
|
|
554
739
|
|
|
555
740
|
const clustersData = data.rtnData?.data || [];
|
|
556
741
|
const envList = clustersData.map(c => c.name).filter(Boolean);
|
|
@@ -570,23 +755,40 @@ async function keEnvList() {
|
|
|
570
755
|
|
|
571
756
|
/**
|
|
572
757
|
* ke_pods_list - 获取 Pod 列表
|
|
758
|
+
* @param {Object} options - 配置选项
|
|
759
|
+
* @param {string} options.domain - KE 平台域名,未指定时从配置文件获取
|
|
760
|
+
* @param {string} options.access_token - 访问令牌,未指定时从配置文件获取
|
|
761
|
+
* @param {string} options.tenant - 租户名称,未指定时从配置文件获取
|
|
762
|
+
* @param {string} options.project - 项目名称,未指定时从配置文件获取
|
|
763
|
+
* @param {string} options.appName - 应用名称(必填)
|
|
764
|
+
* @param {string} options.env - 环境名称(必填)
|
|
573
765
|
*/
|
|
574
|
-
async function kePodsList(
|
|
766
|
+
async function kePodsList(options = {}) {
|
|
575
767
|
reloadConfig();
|
|
576
768
|
|
|
769
|
+
const domain = options.domain || getConfig("domain");
|
|
770
|
+
const accessToken = options.access_token || getConfig("access_token");
|
|
771
|
+
const tenant = options.tenant || getConfig("tenant");
|
|
772
|
+
const project = options.project || getConfig("project");
|
|
773
|
+
const appName = options.appName;
|
|
774
|
+
const env = options.env;
|
|
775
|
+
|
|
776
|
+
if (!tenant) {
|
|
777
|
+
throw new Error("参数 tenant 未指定且配置文件中未设置");
|
|
778
|
+
}
|
|
779
|
+
if (!project) {
|
|
780
|
+
throw new Error("参数 project 未指定且配置文件中未设置");
|
|
781
|
+
}
|
|
577
782
|
if (!appName) {
|
|
578
783
|
throw new Error("参数 appName 不能为空");
|
|
579
784
|
}
|
|
580
|
-
|
|
581
|
-
const namespace = getTenantNamespace();
|
|
582
|
-
const env = getConfig("env");
|
|
583
|
-
|
|
584
785
|
if (!env) {
|
|
585
|
-
throw new Error("
|
|
786
|
+
throw new Error("参数 env 不能为空");
|
|
586
787
|
}
|
|
587
788
|
|
|
789
|
+
const namespace = `${tenant}-${project}`;
|
|
588
790
|
const url = `amp/v4/namespace/${namespace}/env/${env}/applications/${appName}/appstatus/view?canary=false`;
|
|
589
|
-
const data = await keRequest("GET", url);
|
|
791
|
+
const data = await keRequest("GET", url, { domain, access_token: accessToken });
|
|
590
792
|
|
|
591
793
|
// 提取 Pod 列表
|
|
592
794
|
const podsList = [];
|
|
@@ -634,20 +836,49 @@ async function kePodsList(appName) {
|
|
|
634
836
|
|
|
635
837
|
/**
|
|
636
838
|
* ke_pod_log_list - 获取 Pod 日志列表
|
|
839
|
+
* @param {Object} options - 配置选项
|
|
840
|
+
* @param {string} options.domain - KE 平台域名,未指定时从配置文件获取
|
|
841
|
+
* @param {string} options.access_token - 访问令牌,未指定时从配置文件获取
|
|
842
|
+
* @param {string} options.tenant - 租户名称,未指定时从配置文件获取
|
|
843
|
+
* @param {string} options.project - 项目名称,未指定时从配置文件获取(用于构建默认 namespace)
|
|
844
|
+
* @param {string} options.namespace - 命名空间,未指定时从配置文件的 tenant-project 构建
|
|
845
|
+
* @param {string} options.application - 应用名称,未指定时从配置文件获取
|
|
846
|
+
* @param {string} options.env - 环境名称,未指定时从配置文件获取
|
|
847
|
+
* @param {string} options.startTime - 开始时间(纳秒时间戳字符串,必填)
|
|
848
|
+
* @param {string} options.endTime - 结束时间(纳秒时间戳字符串,必填)
|
|
849
|
+
* @param {number} options.size - 返回记录数量(必填)
|
|
850
|
+
* @param {number} options.offset - 偏移量(必填)
|
|
851
|
+
* @param {string} options.sort - 排序方式(必填)
|
|
637
852
|
*/
|
|
638
|
-
async function kePodLogList(
|
|
853
|
+
async function kePodLogList(options = {}) {
|
|
639
854
|
reloadConfig();
|
|
640
855
|
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
const
|
|
856
|
+
const domain = options.domain || getConfig("domain");
|
|
857
|
+
const accessToken = options.access_token || getConfig("access_token");
|
|
858
|
+
const tenant = options.tenant || getConfig("tenant");
|
|
859
|
+
const project = options.project || getConfig("project");
|
|
860
|
+
const service = options.application || getConfig("application");
|
|
861
|
+
const env = options.env || getConfig("env");
|
|
862
|
+
const startTime = options.startTime;
|
|
863
|
+
const endTime = options.endTime;
|
|
864
|
+
const size = options.size;
|
|
865
|
+
const offset = options.offset;
|
|
866
|
+
const sort = options.sort;
|
|
867
|
+
|
|
868
|
+
// namespace 优先从参数获取,否则从 tenant-project 构建
|
|
869
|
+
const namespace = options.namespace || (tenant && project ? `${tenant}-${project}` : null);
|
|
870
|
+
|
|
648
871
|
if (!tenant) {
|
|
649
|
-
throw new Error("
|
|
872
|
+
throw new Error("参数 tenant 未指定且配置文件中未设置");
|
|
650
873
|
}
|
|
874
|
+
if (!namespace) throw new Error("参数 namespace 未指定且无法从配置文件的 tenant-project 构建");
|
|
875
|
+
if (!service) throw new Error("参数 application 未指定且配置文件中未设置");
|
|
876
|
+
if (!env) throw new Error("参数 env 未指定且配置文件中未设置");
|
|
877
|
+
if (!startTime) throw new Error("参数 startTime 不能为空");
|
|
878
|
+
if (!endTime) throw new Error("参数 endTime 不能为空");
|
|
879
|
+
if (size === undefined || size === null) throw new Error("参数 size 不能为空");
|
|
880
|
+
if (offset === undefined || offset === null) throw new Error("参数 offset 不能为空");
|
|
881
|
+
if (!sort) throw new Error("参数 sort 不能为空");
|
|
651
882
|
|
|
652
883
|
const url = `armilla/v1/group/${tenant}/env/${env}/log/list`;
|
|
653
884
|
|
|
@@ -679,7 +910,7 @@ async function kePodLogList(namespace, service, env, startTime, endTime, size =
|
|
|
679
910
|
log_version: "v3"
|
|
680
911
|
};
|
|
681
912
|
|
|
682
|
-
const data = await keRequest("POST", url, { body: requestBody });
|
|
913
|
+
const data = await keRequest("POST", url, { domain, access_token: accessToken, body: requestBody });
|
|
683
914
|
const records = data.rtnData?.records || [];
|
|
684
915
|
|
|
685
916
|
if (records.length === 0) {
|
|
@@ -718,106 +949,6 @@ async function kePodLogList(namespace, service, env, startTime, endTime, size =
|
|
|
718
949
|
return result;
|
|
719
950
|
}
|
|
720
951
|
|
|
721
|
-
/**
|
|
722
|
-
* ke_pod_exec - 在 Pod 容器中执行命令
|
|
723
|
-
*/
|
|
724
|
-
async function kePodExec(tenant, project, podName, cluster, command, container = "", nodeName = "") {
|
|
725
|
-
reloadConfig();
|
|
726
|
-
|
|
727
|
-
if (!tenant) throw new Error("参数 tenant 不能为空");
|
|
728
|
-
if (!project) throw new Error("参数 project 不能为空");
|
|
729
|
-
if (!podName) throw new Error("参数 podName 不能为空");
|
|
730
|
-
if (!cluster) throw new Error("参数 cluster 不能为空");
|
|
731
|
-
if (!command) throw new Error("参数 command 不能为空");
|
|
732
|
-
|
|
733
|
-
const namespace = `${tenant}-${project}`;
|
|
734
|
-
|
|
735
|
-
// 构建 exec API URL
|
|
736
|
-
// 根据 KE 平台的 API 结构,exec 接口可能在 amp/v4 下
|
|
737
|
-
const url = `amp/v4/namespace/${namespace}/env/${cluster}/pods/${podName}/exec`;
|
|
738
|
-
|
|
739
|
-
logger.info("执行 Pod 命令", { namespace, podName, cluster, command, container, nodeName });
|
|
740
|
-
|
|
741
|
-
const requestBody = {
|
|
742
|
-
command: command.split(/\s+/), // 将命令字符串拆分为数组
|
|
743
|
-
container: container || undefined,
|
|
744
|
-
nodeName: nodeName || undefined
|
|
745
|
-
};
|
|
746
|
-
|
|
747
|
-
// 移除 undefined 字段
|
|
748
|
-
Object.keys(requestBody).forEach(key => {
|
|
749
|
-
if (requestBody[key] === undefined) {
|
|
750
|
-
delete requestBody[key];
|
|
751
|
-
}
|
|
752
|
-
});
|
|
753
|
-
|
|
754
|
-
try {
|
|
755
|
-
const data = await keRequest("POST", url, { body: requestBody });
|
|
756
|
-
|
|
757
|
-
const execResult = data.rtnData || {};
|
|
758
|
-
const stdout = execResult.stdout || "";
|
|
759
|
-
const stderr = execResult.stderr || "";
|
|
760
|
-
const exitCode = execResult.exitCode !== undefined ? execResult.exitCode : (data.rtnCode === "000000" ? 0 : 1);
|
|
761
|
-
|
|
762
|
-
let result = `命令执行完成\n`;
|
|
763
|
-
result += `Pod: ${podName}\n`;
|
|
764
|
-
result += `命名空间: ${namespace}\n`;
|
|
765
|
-
result += `环境: ${cluster}\n`;
|
|
766
|
-
result += `命令: ${command}\n`;
|
|
767
|
-
result += `退出码: ${exitCode}\n\n`;
|
|
768
|
-
|
|
769
|
-
if (stdout) {
|
|
770
|
-
result += `标准输出:\n${stdout}\n`;
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
if (stderr) {
|
|
774
|
-
result += `错误输出:\n${stderr}\n`;
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
if (!stdout && !stderr) {
|
|
778
|
-
result += `(无输出)`;
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
return result;
|
|
782
|
-
} catch (error) {
|
|
783
|
-
// 如果 POST 失败,尝试 GET 方式(某些平台可能使用 GET)
|
|
784
|
-
logger.debug("POST 方式失败,尝试 GET 方式", { error: error.message });
|
|
785
|
-
|
|
786
|
-
const getUrl = `${url}?command=${encodeURIComponent(command)}${container ? `&container=${encodeURIComponent(container)}` : ''}${nodeName ? `&nodeName=${encodeURIComponent(nodeName)}` : ''}`;
|
|
787
|
-
|
|
788
|
-
try {
|
|
789
|
-
const data = await keRequest("GET", getUrl);
|
|
790
|
-
const execResult = data.rtnData || {};
|
|
791
|
-
const stdout = execResult.stdout || "";
|
|
792
|
-
const stderr = execResult.stderr || "";
|
|
793
|
-
const exitCode = execResult.exitCode !== undefined ? execResult.exitCode : 0;
|
|
794
|
-
|
|
795
|
-
let result = `命令执行完成\n`;
|
|
796
|
-
result += `Pod: ${podName}\n`;
|
|
797
|
-
result += `命名空间: ${namespace}\n`;
|
|
798
|
-
result += `环境: ${cluster}\n`;
|
|
799
|
-
result += `命令: ${command}\n`;
|
|
800
|
-
result += `退出码: ${exitCode}\n\n`;
|
|
801
|
-
|
|
802
|
-
if (stdout) {
|
|
803
|
-
result += `标准输出:\n${stdout}\n`;
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
if (stderr) {
|
|
807
|
-
result += `错误输出:\n${stderr}\n`;
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
if (!stdout && !stderr) {
|
|
811
|
-
result += `(无输出)`;
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
return result;
|
|
815
|
-
} catch (getError) {
|
|
816
|
-
throw new Error(`执行命令失败: ${error.message}。如果 KE 平台不支持 exec API,请使用 webshell 手动操作。`);
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
|
|
821
952
|
// ==================== MCP 服务器 ====================
|
|
822
953
|
|
|
823
954
|
const server = new McpServer({
|
|
@@ -825,16 +956,48 @@ const server = new McpServer({
|
|
|
825
956
|
version: VERSION
|
|
826
957
|
});
|
|
827
958
|
|
|
959
|
+
// 注册工具:ke_config_reset
|
|
960
|
+
server.registerTool(
|
|
961
|
+
"ke_config_reset",
|
|
962
|
+
{
|
|
963
|
+
description: "重置/更新 KE MCP 配置参数,配置后其他工具可自动读取这些默认值。",
|
|
964
|
+
inputSchema: {
|
|
965
|
+
domain: z.string().optional().describe("KE 平台域名(如:https://ke.example.com)。【必须】由用户手动输入,这是访问 KE 平台的基础地址,不可自动猜测"),
|
|
966
|
+
access_token: z.string().optional().describe("访问令牌(leo-user-token)。【必须】由用户手动输入,可从 KE 平台的用户设置中获取,不可自动猜测"),
|
|
967
|
+
tenant: z.string().optional().describe("租户名称。可通过 ke_tenants_list 工具获取可用租户列表,如果存在多个租户【必须】展示列表并让用户明确选择,不可自动选择"),
|
|
968
|
+
project: z.string().optional().describe("项目名称。可通过 ke_projects_list 工具获取指定租户下的项目列表,如果存在多个项目【必须】展示列表并让用户明确选择,不可自动选择"),
|
|
969
|
+
application: z.string().optional().describe("应用名称。可通过 ke_app_list 工具获取指定租户和项目下的应用列表,如果存在多个应用【必须】展示列表并让用户明确选择,不可自动选择"),
|
|
970
|
+
env: z.string().optional().describe("环境名称(如:test、prod)。可通过 ke_env_list 工具获取指定租户下的环境列表,如果存在多个环境【必须】展示列表并让用户明确选择,不可自动选择")
|
|
971
|
+
}
|
|
972
|
+
},
|
|
973
|
+
async ({ domain, access_token, tenant, project, application, env }) => {
|
|
974
|
+
try {
|
|
975
|
+
const result = await keConfigReset({ domain, access_token, tenant, project, application, env });
|
|
976
|
+
return { content: [{ type: "text", text: result }] };
|
|
977
|
+
} catch (error) {
|
|
978
|
+
logger.error("ke_config_reset 失败", { error: error.message });
|
|
979
|
+
return { content: [{ type: "text", text: `错误: ${error.message}` }], isError: true };
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
);
|
|
983
|
+
|
|
828
984
|
// 注册工具:ke_app_restart
|
|
829
985
|
server.registerTool(
|
|
830
986
|
"ke_app_restart",
|
|
831
987
|
{
|
|
832
|
-
description: "重启 KE 应用(通过删除 Pod
|
|
833
|
-
inputSchema: {
|
|
988
|
+
description: "重启 KE 应用(通过删除 Pod 触发自动重建)。所有配置项如果未指定,则默认从项目目录下的 .mcp/bd-ke-mcp.json 配置文件中获取。",
|
|
989
|
+
inputSchema: {
|
|
990
|
+
domain: z.string().optional().describe("KE 平台域名(如:https://ke.example.com),用于 API 请求。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
991
|
+
access_token: z.string().optional().describe("访问令牌(leo-user-token),用于 API 认证。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
992
|
+
tenant: z.string().optional().describe("租户名称,用于构建命名空间。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
993
|
+
project: z.string().optional().describe("项目名称,用于构建命名空间。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
994
|
+
application: z.string().optional().describe("要重启的应用名称。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
995
|
+
env: z.string().optional().describe("环境名称(如:production、development),指定要重启的环境。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取")
|
|
996
|
+
}
|
|
834
997
|
},
|
|
835
|
-
async () => {
|
|
998
|
+
async ({ domain, access_token, tenant, project, application, env }) => {
|
|
836
999
|
try {
|
|
837
|
-
const result = await keAppRestart();
|
|
1000
|
+
const result = await keAppRestart({ domain, access_token, tenant, project, application, env });
|
|
838
1001
|
return { content: [{ type: "text", text: result }] };
|
|
839
1002
|
} catch (error) {
|
|
840
1003
|
logger.error("ke_app_restart 失败", { error: error.message });
|
|
@@ -847,15 +1010,20 @@ server.registerTool(
|
|
|
847
1010
|
server.registerTool(
|
|
848
1011
|
"ke_pipeline_list",
|
|
849
1012
|
{
|
|
850
|
-
description: "获取 KE
|
|
1013
|
+
description: "获取 KE 流水线列表,根据 tenant、project、application 筛选。所有配置项如果未指定,则默认从项目目录下的 .mcp/bd-ke-mcp.json 配置文件中获取。",
|
|
851
1014
|
inputSchema: {
|
|
852
|
-
|
|
853
|
-
|
|
1015
|
+
domain: z.string().optional().describe("KE 平台域名(如:https://ke.example.com),用于 API 请求和构建流水线访问地址。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1016
|
+
access_token: z.string().optional().describe("访问令牌(leo-user-token),用于 API 认证。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1017
|
+
tenant: z.string().optional().describe("租户名称,用于筛选流水线。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1018
|
+
project: z.string().optional().describe("项目名称,用于筛选流水线。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1019
|
+
application: z.string().optional().describe("应用名称,用于进一步筛选特定应用的流水线。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取,可为空表示不按应用筛选"),
|
|
1020
|
+
page: z.number().optional().default(1).describe("分页页码,从 1 开始,默认为 1"),
|
|
1021
|
+
pageSize: z.number().optional().default(20).describe("每页返回的流水线数量,默认为 20")
|
|
854
1022
|
}
|
|
855
1023
|
},
|
|
856
|
-
async ({ page, pageSize }) => {
|
|
1024
|
+
async ({ domain, access_token, tenant, project, application, page, pageSize }) => {
|
|
857
1025
|
try {
|
|
858
|
-
const result = await kePipelineList(page, pageSize);
|
|
1026
|
+
const result = await kePipelineList({ domain, access_token, tenant, project, application, page, pageSize });
|
|
859
1027
|
return { content: [{ type: "text", text: result }] };
|
|
860
1028
|
} catch (error) {
|
|
861
1029
|
logger.error("ke_pipeline_list 失败", { error: error.message });
|
|
@@ -868,14 +1036,15 @@ server.registerTool(
|
|
|
868
1036
|
server.registerTool(
|
|
869
1037
|
"ke_pipeline_info",
|
|
870
1038
|
{
|
|
871
|
-
description: "获取 KE
|
|
1039
|
+
description: "获取 KE 流水线信息,包括流水线名称和执行参数。access_token 如果未指定,则默认从项目目录下的 .mcp/bd-ke-mcp.json 配置文件中获取。",
|
|
872
1040
|
inputSchema: {
|
|
873
|
-
|
|
1041
|
+
access_token: z.string().optional().describe("访问令牌(leo-user-token),用于 API 认证。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1042
|
+
pipelineUrl: z.string().describe("流水线地址,完整的 URL 路径(如:https://ke.example.com/pipeline/v1/group/tenant-project/pipelines/app-xxx/view),可从 ke_pipeline_list 获取")
|
|
874
1043
|
}
|
|
875
1044
|
},
|
|
876
|
-
async ({ pipelineUrl }) => {
|
|
1045
|
+
async ({ access_token, pipelineUrl }) => {
|
|
877
1046
|
try {
|
|
878
|
-
const result = await kePipelineInfo(pipelineUrl);
|
|
1047
|
+
const result = await kePipelineInfo({ access_token, pipelineUrl });
|
|
879
1048
|
return { content: [{ type: "text", text: result }] };
|
|
880
1049
|
} catch (error) {
|
|
881
1050
|
logger.error("ke_pipeline_info 失败", { error: error.message });
|
|
@@ -888,17 +1057,18 @@ server.registerTool(
|
|
|
888
1057
|
server.registerTool(
|
|
889
1058
|
"ke_pipeline_create",
|
|
890
1059
|
{
|
|
891
|
-
description: "执行 KE
|
|
1060
|
+
description: "执行 KE 流水线(创建流水线运行时)。access_token 如果未指定,则默认从项目目录下的 .mcp/bd-ke-mcp.json 配置文件中获取。",
|
|
892
1061
|
inputSchema: {
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
1062
|
+
access_token: z.string().optional().describe("访问令牌(leo-user-token),用于 API 认证。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1063
|
+
pipelineUrl: z.string().describe("流水线地址,完整的 URL 路径(如:https://ke.example.com/pipeline/v1/group/tenant-project/pipelines/app-xxx/view),可从 ke_pipeline_list 获取"),
|
|
1064
|
+
globalArguments: z.record(z.string()).describe("全局参数字典,键为参数名,值为参数值。参数列表可通过 ke_pipeline_info 获取"),
|
|
1065
|
+
deadline: z.number().optional().default(86400).describe("流水线执行超时时间(秒),超过此时间流水线将被终止,默认 86400(24小时)"),
|
|
1066
|
+
review: z.string().optional().default("").describe("审核信息,可选的审核备注或说明")
|
|
897
1067
|
}
|
|
898
1068
|
},
|
|
899
|
-
async ({ pipelineUrl, globalArguments, deadline, review }) => {
|
|
1069
|
+
async ({ access_token, pipelineUrl, globalArguments, deadline, review }) => {
|
|
900
1070
|
try {
|
|
901
|
-
const result = await kePipelineCreate(pipelineUrl, globalArguments, deadline, review);
|
|
1071
|
+
const result = await kePipelineCreate({ access_token, pipelineUrl, globalArguments, deadline, review });
|
|
902
1072
|
return { content: [{ type: "text", text: result }] };
|
|
903
1073
|
} catch (error) {
|
|
904
1074
|
logger.error("ke_pipeline_create 失败", { error: error.message });
|
|
@@ -911,12 +1081,15 @@ server.registerTool(
|
|
|
911
1081
|
server.registerTool(
|
|
912
1082
|
"ke_tenants_list",
|
|
913
1083
|
{
|
|
914
|
-
description: "
|
|
915
|
-
inputSchema: {
|
|
1084
|
+
description: "列出当前用户有权限访问的所有租户。所有配置项如果未指定,则默认从项目目录下的 .mcp/bd-ke-mcp.json 配置文件中获取。",
|
|
1085
|
+
inputSchema: {
|
|
1086
|
+
domain: z.string().optional().describe("KE 平台域名(如:https://ke.example.com),用于 API 请求。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1087
|
+
access_token: z.string().optional().describe("访问令牌(leo-user-token),用于 API 认证。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取")
|
|
1088
|
+
}
|
|
916
1089
|
},
|
|
917
|
-
async () => {
|
|
1090
|
+
async ({ domain, access_token }) => {
|
|
918
1091
|
try {
|
|
919
|
-
const result = await keTenantsList();
|
|
1092
|
+
const result = await keTenantsList({ domain, access_token });
|
|
920
1093
|
return { content: [{ type: "text", text: result }] };
|
|
921
1094
|
} catch (error) {
|
|
922
1095
|
logger.error("ke_tenants_list 失败", { error: error.message });
|
|
@@ -929,12 +1102,16 @@ server.registerTool(
|
|
|
929
1102
|
server.registerTool(
|
|
930
1103
|
"ke_projects_list",
|
|
931
1104
|
{
|
|
932
|
-
description: "
|
|
933
|
-
inputSchema: {
|
|
1105
|
+
description: "列出指定租户下的所有项目。所有配置项如果未指定,则默认从项目目录下的 .mcp/bd-ke-mcp.json 配置文件中获取。",
|
|
1106
|
+
inputSchema: {
|
|
1107
|
+
domain: z.string().optional().describe("KE 平台域名(如:https://ke.example.com),用于 API 请求。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1108
|
+
access_token: z.string().optional().describe("访问令牌(leo-user-token),用于 API 认证。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1109
|
+
tenant: z.string().optional().describe("租户名称,指定要查询项目的租户。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取")
|
|
1110
|
+
}
|
|
934
1111
|
},
|
|
935
|
-
async () => {
|
|
1112
|
+
async ({ domain, access_token, tenant }) => {
|
|
936
1113
|
try {
|
|
937
|
-
const result = await keProjectsList();
|
|
1114
|
+
const result = await keProjectsList({ domain, access_token, tenant });
|
|
938
1115
|
return { content: [{ type: "text", text: result }] };
|
|
939
1116
|
} catch (error) {
|
|
940
1117
|
logger.error("ke_projects_list 失败", { error: error.message });
|
|
@@ -947,12 +1124,17 @@ server.registerTool(
|
|
|
947
1124
|
server.registerTool(
|
|
948
1125
|
"ke_app_list",
|
|
949
1126
|
{
|
|
950
|
-
description: "
|
|
951
|
-
inputSchema: {
|
|
1127
|
+
description: "列出指定租户和项目下的所有应用。所有配置项如果未指定,则默认从项目目录下的 .mcp/bd-ke-mcp.json 配置文件中获取。",
|
|
1128
|
+
inputSchema: {
|
|
1129
|
+
domain: z.string().optional().describe("KE 平台域名(如:https://ke.example.com),用于 API 请求。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1130
|
+
access_token: z.string().optional().describe("访问令牌(leo-user-token),用于 API 认证。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1131
|
+
tenant: z.string().optional().describe("租户名称,用于构建命名空间。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1132
|
+
project: z.string().optional().describe("项目名称,用于构建命名空间。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取")
|
|
1133
|
+
}
|
|
952
1134
|
},
|
|
953
|
-
async () => {
|
|
1135
|
+
async ({ domain, access_token, tenant, project }) => {
|
|
954
1136
|
try {
|
|
955
|
-
const result = await keAppList();
|
|
1137
|
+
const result = await keAppList({ domain, access_token, tenant, project });
|
|
956
1138
|
return { content: [{ type: "text", text: result }] };
|
|
957
1139
|
} catch (error) {
|
|
958
1140
|
logger.error("ke_app_list 失败", { error: error.message });
|
|
@@ -965,14 +1147,16 @@ server.registerTool(
|
|
|
965
1147
|
server.registerTool(
|
|
966
1148
|
"ke_env_list",
|
|
967
1149
|
{
|
|
968
|
-
description: "
|
|
1150
|
+
description: "获取指定租户下的所有可用环境(集群)列表。所有配置项如果未指定,则默认从项目目录下的 .mcp/bd-ke-mcp.json 配置文件中获取。",
|
|
969
1151
|
inputSchema: {
|
|
970
|
-
|
|
1152
|
+
domain: z.string().optional().describe("KE 平台域名(如:https://ke.example.com),用于 API 请求。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1153
|
+
access_token: z.string().optional().describe("访问令牌(leo-user-token),用于 API 认证。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1154
|
+
tenant: z.string().optional().describe("租户名称,指定要查询环境的租户。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取")
|
|
971
1155
|
}
|
|
972
1156
|
},
|
|
973
|
-
async () => {
|
|
1157
|
+
async ({ domain, access_token, tenant }) => {
|
|
974
1158
|
try {
|
|
975
|
-
const result = await keEnvList();
|
|
1159
|
+
const result = await keEnvList({ domain, access_token, tenant });
|
|
976
1160
|
return { content: [{ type: "text", text: result }] };
|
|
977
1161
|
} catch (error) {
|
|
978
1162
|
logger.error("ke_env_list 失败", { error: error.message });
|
|
@@ -985,14 +1169,19 @@ server.registerTool(
|
|
|
985
1169
|
server.registerTool(
|
|
986
1170
|
"ke_pods_list",
|
|
987
1171
|
{
|
|
988
|
-
description: "获取指定应用的 Pod 实例列表,包含实例名称、状态、创建时间、容器IP
|
|
1172
|
+
description: "获取指定应用的 Pod 实例列表,包含实例名称、状态、创建时间、容器IP、运行节点、重启次数等信息。所有配置项如果未指定,则默认从项目目录下的 .mcp/bd-ke-mcp.json 配置文件中获取。",
|
|
989
1173
|
inputSchema: {
|
|
990
|
-
|
|
1174
|
+
domain: z.string().optional().describe("KE 平台域名(如:https://ke.example.com),用于 API 请求。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1175
|
+
access_token: z.string().optional().describe("访问令牌(leo-user-token),用于 API 认证。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1176
|
+
tenant: z.string().optional().describe("租户名称,用于构建命名空间。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1177
|
+
project: z.string().optional().describe("项目名称,用于构建命名空间。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1178
|
+
appName: z.string().describe("应用名称,指定要查询 Pod 的应用,可通过 ke_app_list 获取"),
|
|
1179
|
+
env: z.string().describe("环境名称,指定要查询的环境(集群),可通过 ke_env_list 获取")
|
|
991
1180
|
}
|
|
992
1181
|
},
|
|
993
|
-
async ({ appName }) => {
|
|
1182
|
+
async ({ domain, access_token, tenant, project, appName, env }) => {
|
|
994
1183
|
try {
|
|
995
|
-
const result = await kePodsList(appName);
|
|
1184
|
+
const result = await kePodsList({ domain, access_token, tenant, project, appName, env });
|
|
996
1185
|
return { content: [{ type: "text", text: result }] };
|
|
997
1186
|
} catch (error) {
|
|
998
1187
|
logger.error("ke_pods_list 失败", { error: error.message });
|
|
@@ -1005,21 +1194,24 @@ server.registerTool(
|
|
|
1005
1194
|
server.registerTool(
|
|
1006
1195
|
"ke_pod_log_list",
|
|
1007
1196
|
{
|
|
1008
|
-
description: "获取指定应用的 Pod
|
|
1197
|
+
description: "获取指定应用的 Pod 日志列表,支持按时间范围查询和分页。所有配置项如果未指定,则默认从项目目录下的 .mcp/bd-ke-mcp.json 配置文件中获取。",
|
|
1009
1198
|
inputSchema: {
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1199
|
+
domain: z.string().optional().describe("KE 平台域名(如:https://ke.example.com),用于 API 请求。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1200
|
+
access_token: z.string().optional().describe("访问令牌(leo-user-token),用于 API 认证。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1201
|
+
tenant: z.string().optional().describe("租户名称,用于日志查询 API 路径。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1202
|
+
namespace: z.string().optional().describe("命名空间,格式为 tenant-project(如:myapp-production),用于筛选日志来源。未指定时从配置文件的 tenant-project 自动构建"),
|
|
1203
|
+
application: z.string().optional().describe("应用名称,指定要查询日志的应用,可通过 ke_app_list 获取。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1204
|
+
env: z.string().optional().describe("环境名称,指定要查询日志的环境(集群),可通过 ke_env_list 获取。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1205
|
+
startTime: z.string().describe("查询开始时间,纳秒级时间戳字符串(如:\"1704067200000000000\" 表示 2024-01-01 00:00:00)"),
|
|
1206
|
+
endTime: z.string().describe("查询结束时间,纳秒级时间戳字符串(如:\"1704153600000000000\" 表示 2024-01-02 00:00:00)"),
|
|
1207
|
+
size: z.number().describe("返回的日志记录数量,建议不超过 1000"),
|
|
1208
|
+
offset: z.number().describe("分页偏移量,用于翻页查询,从 0 开始"),
|
|
1209
|
+
sort: z.string().describe("日志排序方式,desc 表示按时间降序(最新在前),asc 表示按时间升序(最早在前)")
|
|
1018
1210
|
}
|
|
1019
1211
|
},
|
|
1020
|
-
async ({ namespace,
|
|
1212
|
+
async ({ domain, access_token, tenant, namespace, application, env, startTime, endTime, size, offset, sort }) => {
|
|
1021
1213
|
try {
|
|
1022
|
-
const result = await kePodLogList(namespace,
|
|
1214
|
+
const result = await kePodLogList({ domain, access_token, tenant, namespace, application, env, startTime, endTime, size, offset, sort });
|
|
1023
1215
|
return { content: [{ type: "text", text: result }] };
|
|
1024
1216
|
} catch (error) {
|
|
1025
1217
|
logger.error("ke_pod_log_list 失败", { error: error.message });
|
|
@@ -1028,32 +1220,6 @@ server.registerTool(
|
|
|
1028
1220
|
}
|
|
1029
1221
|
);
|
|
1030
1222
|
|
|
1031
|
-
// 注册工具:ke_pod_exec
|
|
1032
|
-
server.registerTool(
|
|
1033
|
-
"ke_pod_exec",
|
|
1034
|
-
{
|
|
1035
|
-
description: "在 Pod 容器中执行命令(如:ls, pwd, cat 等)",
|
|
1036
|
-
inputSchema: {
|
|
1037
|
-
tenant: z.string().describe("租户名称"),
|
|
1038
|
-
project: z.string().describe("项目名称"),
|
|
1039
|
-
podName: z.string().describe("Pod 名称"),
|
|
1040
|
-
cluster: z.string().describe("集群/环境名称(如:funeng-test)"),
|
|
1041
|
-
command: z.string().describe("要执行的命令(如:ls, pwd, cat /etc/hosts)"),
|
|
1042
|
-
container: z.string().optional().describe("容器名称(可选,如果 Pod 有多个容器)"),
|
|
1043
|
-
nodeName: z.string().optional().describe("节点名称(可选)")
|
|
1044
|
-
}
|
|
1045
|
-
},
|
|
1046
|
-
async ({ tenant, project, podName, cluster, command, container, nodeName }) => {
|
|
1047
|
-
try {
|
|
1048
|
-
const result = await kePodExec(tenant, project, podName, cluster, command, container, nodeName);
|
|
1049
|
-
return { content: [{ type: "text", text: result }] };
|
|
1050
|
-
} catch (error) {
|
|
1051
|
-
logger.error("ke_pod_exec 失败", { error: error.message });
|
|
1052
|
-
return { content: [{ type: "text", text: `错误: ${error.message}` }], isError: true };
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
);
|
|
1056
|
-
|
|
1057
1223
|
// ==================== 启动服务 ====================
|
|
1058
1224
|
|
|
1059
1225
|
async function main() {
|
|
@@ -1067,6 +1233,7 @@ async function main() {
|
|
|
1067
1233
|
pid: process.pid,
|
|
1068
1234
|
projectRoot: getProjectRoot(),
|
|
1069
1235
|
tools: [
|
|
1236
|
+
"ke_config_reset",
|
|
1070
1237
|
"ke_app_restart",
|
|
1071
1238
|
"ke_pipeline_list",
|
|
1072
1239
|
"ke_pipeline_info",
|
|
@@ -1076,8 +1243,7 @@ async function main() {
|
|
|
1076
1243
|
"ke_app_list",
|
|
1077
1244
|
"ke_env_list",
|
|
1078
1245
|
"ke_pods_list",
|
|
1079
|
-
"ke_pod_log_list"
|
|
1080
|
-
"ke_pod_exec"
|
|
1246
|
+
"ke_pod_log_list"
|
|
1081
1247
|
]
|
|
1082
1248
|
});
|
|
1083
1249
|
}
|