@junjun-org/bd-ke-mcp 1.0.0 → 1.0.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/index.js +396 -237
- 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,42 @@ 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.namespace - 命名空间(必填)
|
|
844
|
+
* @param {string} options.service - 服务名称(必填)
|
|
845
|
+
* @param {string} options.env - 环境名称(必填)
|
|
846
|
+
* @param {number} options.startTime - 开始时间(纳秒时间戳,必填)
|
|
847
|
+
* @param {number} options.endTime - 结束时间(纳秒时间戳,必填)
|
|
848
|
+
* @param {number} options.size - 返回记录数量,默认 100
|
|
849
|
+
* @param {number} options.offset - 偏移量,默认 0
|
|
850
|
+
* @param {string} options.sort - 排序方式,默认 desc
|
|
637
851
|
*/
|
|
638
|
-
async function kePodLogList(
|
|
852
|
+
async function kePodLogList(options = {}) {
|
|
639
853
|
reloadConfig();
|
|
640
854
|
|
|
855
|
+
const domain = options.domain || getConfig("domain");
|
|
856
|
+
const accessToken = options.access_token || getConfig("access_token");
|
|
857
|
+
const tenant = options.tenant || getConfig("tenant");
|
|
858
|
+
const namespace = options.namespace;
|
|
859
|
+
const service = options.service;
|
|
860
|
+
const env = options.env;
|
|
861
|
+
const startTime = options.startTime;
|
|
862
|
+
const endTime = options.endTime;
|
|
863
|
+
const size = options.size || 100;
|
|
864
|
+
const offset = options.offset || 0;
|
|
865
|
+
const sort = options.sort || "desc";
|
|
866
|
+
|
|
867
|
+
if (!tenant) {
|
|
868
|
+
throw new Error("参数 tenant 未指定且配置文件中未设置");
|
|
869
|
+
}
|
|
641
870
|
if (!namespace) throw new Error("参数 namespace 不能为空");
|
|
642
871
|
if (!service) throw new Error("参数 service 不能为空");
|
|
643
872
|
if (!env) throw new Error("参数 env 不能为空");
|
|
644
|
-
if (!startTime) throw new Error("参数
|
|
645
|
-
if (!endTime) throw new Error("参数
|
|
646
|
-
|
|
647
|
-
const tenant = getConfig("tenant");
|
|
648
|
-
if (!tenant) {
|
|
649
|
-
throw new Error("配置项 tenant 未设置");
|
|
650
|
-
}
|
|
873
|
+
if (!startTime) throw new Error("参数 startTime 不能为空");
|
|
874
|
+
if (!endTime) throw new Error("参数 endTime 不能为空");
|
|
651
875
|
|
|
652
876
|
const url = `armilla/v1/group/${tenant}/env/${env}/log/list`;
|
|
653
877
|
|
|
@@ -679,7 +903,7 @@ async function kePodLogList(namespace, service, env, startTime, endTime, size =
|
|
|
679
903
|
log_version: "v3"
|
|
680
904
|
};
|
|
681
905
|
|
|
682
|
-
const data = await keRequest("POST", url, { body: requestBody });
|
|
906
|
+
const data = await keRequest("POST", url, { domain, access_token: accessToken, body: requestBody });
|
|
683
907
|
const records = data.rtnData?.records || [];
|
|
684
908
|
|
|
685
909
|
if (records.length === 0) {
|
|
@@ -718,106 +942,6 @@ async function kePodLogList(namespace, service, env, startTime, endTime, size =
|
|
|
718
942
|
return result;
|
|
719
943
|
}
|
|
720
944
|
|
|
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
945
|
// ==================== MCP 服务器 ====================
|
|
822
946
|
|
|
823
947
|
const server = new McpServer({
|
|
@@ -825,16 +949,48 @@ const server = new McpServer({
|
|
|
825
949
|
version: VERSION
|
|
826
950
|
});
|
|
827
951
|
|
|
952
|
+
// 注册工具:ke_config_reset
|
|
953
|
+
server.registerTool(
|
|
954
|
+
"ke_config_reset",
|
|
955
|
+
{
|
|
956
|
+
description: "重置/更新 KE MCP 配置参数,配置后其他工具可自动读取这些默认值。",
|
|
957
|
+
inputSchema: {
|
|
958
|
+
domain: z.string().optional().describe("KE 平台域名(如:https://ke.example.com)。【必须】由用户手动输入,这是访问 KE 平台的基础地址,不可自动猜测"),
|
|
959
|
+
access_token: z.string().optional().describe("访问令牌(leo-user-token)。【必须】由用户手动输入,可从 KE 平台的用户设置中获取,不可自动猜测"),
|
|
960
|
+
tenant: z.string().optional().describe("租户名称。可通过 ke_tenants_list 工具获取可用租户列表,如果存在多个租户【必须】展示列表并让用户明确选择,不可自动选择"),
|
|
961
|
+
project: z.string().optional().describe("项目名称。可通过 ke_projects_list 工具获取指定租户下的项目列表,如果存在多个项目【必须】展示列表并让用户明确选择,不可自动选择"),
|
|
962
|
+
application: z.string().optional().describe("应用名称。可通过 ke_app_list 工具获取指定租户和项目下的应用列表,如果存在多个应用【必须】展示列表并让用户明确选择,不可自动选择"),
|
|
963
|
+
env: z.string().optional().describe("环境名称(如:test、prod)。可通过 ke_env_list 工具获取指定租户下的环境列表,如果存在多个环境【必须】展示列表并让用户明确选择,不可自动选择")
|
|
964
|
+
}
|
|
965
|
+
},
|
|
966
|
+
async ({ domain, access_token, tenant, project, application, env }) => {
|
|
967
|
+
try {
|
|
968
|
+
const result = await keConfigReset({ domain, access_token, tenant, project, application, env });
|
|
969
|
+
return { content: [{ type: "text", text: result }] };
|
|
970
|
+
} catch (error) {
|
|
971
|
+
logger.error("ke_config_reset 失败", { error: error.message });
|
|
972
|
+
return { content: [{ type: "text", text: `错误: ${error.message}` }], isError: true };
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
);
|
|
976
|
+
|
|
828
977
|
// 注册工具:ke_app_restart
|
|
829
978
|
server.registerTool(
|
|
830
979
|
"ke_app_restart",
|
|
831
980
|
{
|
|
832
|
-
description: "重启 KE 应用(通过删除 Pod
|
|
833
|
-
inputSchema: {
|
|
981
|
+
description: "重启 KE 应用(通过删除 Pod 触发自动重建)。所有配置项如果未指定,则默认从项目目录下的 .mcp/bd-ke-mcp.json 配置文件中获取。",
|
|
982
|
+
inputSchema: {
|
|
983
|
+
domain: z.string().optional().describe("KE 平台域名(如:https://ke.example.com),用于 API 请求。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
984
|
+
access_token: z.string().optional().describe("访问令牌(leo-user-token),用于 API 认证。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
985
|
+
tenant: z.string().optional().describe("租户名称,用于构建命名空间。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
986
|
+
project: z.string().optional().describe("项目名称,用于构建命名空间。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
987
|
+
application: z.string().optional().describe("要重启的应用名称。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
988
|
+
env: z.string().optional().describe("环境名称(如:production、development),指定要重启的环境。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取")
|
|
989
|
+
}
|
|
834
990
|
},
|
|
835
|
-
async () => {
|
|
991
|
+
async ({ domain, access_token, tenant, project, application, env }) => {
|
|
836
992
|
try {
|
|
837
|
-
const result = await keAppRestart();
|
|
993
|
+
const result = await keAppRestart({ domain, access_token, tenant, project, application, env });
|
|
838
994
|
return { content: [{ type: "text", text: result }] };
|
|
839
995
|
} catch (error) {
|
|
840
996
|
logger.error("ke_app_restart 失败", { error: error.message });
|
|
@@ -847,15 +1003,20 @@ server.registerTool(
|
|
|
847
1003
|
server.registerTool(
|
|
848
1004
|
"ke_pipeline_list",
|
|
849
1005
|
{
|
|
850
|
-
description: "获取 KE
|
|
1006
|
+
description: "获取 KE 流水线列表,根据 tenant、project、application 筛选。所有配置项如果未指定,则默认从项目目录下的 .mcp/bd-ke-mcp.json 配置文件中获取。",
|
|
851
1007
|
inputSchema: {
|
|
852
|
-
|
|
853
|
-
|
|
1008
|
+
domain: z.string().optional().describe("KE 平台域名(如:https://ke.example.com),用于 API 请求和构建流水线访问地址。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1009
|
+
access_token: z.string().optional().describe("访问令牌(leo-user-token),用于 API 认证。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1010
|
+
tenant: z.string().optional().describe("租户名称,用于筛选流水线。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1011
|
+
project: z.string().optional().describe("项目名称,用于筛选流水线。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1012
|
+
application: z.string().optional().describe("应用名称,用于进一步筛选特定应用的流水线。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取,可为空表示不按应用筛选"),
|
|
1013
|
+
page: z.number().optional().default(1).describe("分页页码,从 1 开始,默认为 1"),
|
|
1014
|
+
pageSize: z.number().optional().default(20).describe("每页返回的流水线数量,默认为 20")
|
|
854
1015
|
}
|
|
855
1016
|
},
|
|
856
|
-
async ({ page, pageSize }) => {
|
|
1017
|
+
async ({ domain, access_token, tenant, project, application, page, pageSize }) => {
|
|
857
1018
|
try {
|
|
858
|
-
const result = await kePipelineList(page, pageSize);
|
|
1019
|
+
const result = await kePipelineList({ domain, access_token, tenant, project, application, page, pageSize });
|
|
859
1020
|
return { content: [{ type: "text", text: result }] };
|
|
860
1021
|
} catch (error) {
|
|
861
1022
|
logger.error("ke_pipeline_list 失败", { error: error.message });
|
|
@@ -868,14 +1029,15 @@ server.registerTool(
|
|
|
868
1029
|
server.registerTool(
|
|
869
1030
|
"ke_pipeline_info",
|
|
870
1031
|
{
|
|
871
|
-
description: "获取 KE
|
|
1032
|
+
description: "获取 KE 流水线信息,包括流水线名称和执行参数。access_token 如果未指定,则默认从项目目录下的 .mcp/bd-ke-mcp.json 配置文件中获取。",
|
|
872
1033
|
inputSchema: {
|
|
873
|
-
|
|
1034
|
+
access_token: z.string().optional().describe("访问令牌(leo-user-token),用于 API 认证。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1035
|
+
pipelineUrl: z.string().describe("流水线地址,完整的 URL 路径(如:https://ke.example.com/pipeline/v1/group/tenant-project/pipelines/app-xxx/view),可从 ke_pipeline_list 获取")
|
|
874
1036
|
}
|
|
875
1037
|
},
|
|
876
|
-
async ({ pipelineUrl }) => {
|
|
1038
|
+
async ({ access_token, pipelineUrl }) => {
|
|
877
1039
|
try {
|
|
878
|
-
const result = await kePipelineInfo(pipelineUrl);
|
|
1040
|
+
const result = await kePipelineInfo({ access_token, pipelineUrl });
|
|
879
1041
|
return { content: [{ type: "text", text: result }] };
|
|
880
1042
|
} catch (error) {
|
|
881
1043
|
logger.error("ke_pipeline_info 失败", { error: error.message });
|
|
@@ -888,17 +1050,18 @@ server.registerTool(
|
|
|
888
1050
|
server.registerTool(
|
|
889
1051
|
"ke_pipeline_create",
|
|
890
1052
|
{
|
|
891
|
-
description: "执行 KE
|
|
1053
|
+
description: "执行 KE 流水线(创建流水线运行时)。access_token 如果未指定,则默认从项目目录下的 .mcp/bd-ke-mcp.json 配置文件中获取。",
|
|
892
1054
|
inputSchema: {
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
1055
|
+
access_token: z.string().optional().describe("访问令牌(leo-user-token),用于 API 认证。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1056
|
+
pipelineUrl: z.string().describe("流水线地址,完整的 URL 路径(如:https://ke.example.com/pipeline/v1/group/tenant-project/pipelines/app-xxx/view),可从 ke_pipeline_list 获取"),
|
|
1057
|
+
globalArguments: z.record(z.string()).describe("全局参数字典,键为参数名,值为参数值。参数列表可通过 ke_pipeline_info 获取"),
|
|
1058
|
+
deadline: z.number().optional().default(86400).describe("流水线执行超时时间(秒),超过此时间流水线将被终止,默认 86400(24小时)"),
|
|
1059
|
+
review: z.string().optional().default("").describe("审核信息,可选的审核备注或说明")
|
|
897
1060
|
}
|
|
898
1061
|
},
|
|
899
|
-
async ({ pipelineUrl, globalArguments, deadline, review }) => {
|
|
1062
|
+
async ({ access_token, pipelineUrl, globalArguments, deadline, review }) => {
|
|
900
1063
|
try {
|
|
901
|
-
const result = await kePipelineCreate(pipelineUrl, globalArguments, deadline, review);
|
|
1064
|
+
const result = await kePipelineCreate({ access_token, pipelineUrl, globalArguments, deadline, review });
|
|
902
1065
|
return { content: [{ type: "text", text: result }] };
|
|
903
1066
|
} catch (error) {
|
|
904
1067
|
logger.error("ke_pipeline_create 失败", { error: error.message });
|
|
@@ -911,12 +1074,15 @@ server.registerTool(
|
|
|
911
1074
|
server.registerTool(
|
|
912
1075
|
"ke_tenants_list",
|
|
913
1076
|
{
|
|
914
|
-
description: "
|
|
915
|
-
inputSchema: {
|
|
1077
|
+
description: "列出当前用户有权限访问的所有租户。所有配置项如果未指定,则默认从项目目录下的 .mcp/bd-ke-mcp.json 配置文件中获取。",
|
|
1078
|
+
inputSchema: {
|
|
1079
|
+
domain: z.string().optional().describe("KE 平台域名(如:https://ke.example.com),用于 API 请求。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1080
|
+
access_token: z.string().optional().describe("访问令牌(leo-user-token),用于 API 认证。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取")
|
|
1081
|
+
}
|
|
916
1082
|
},
|
|
917
|
-
async () => {
|
|
1083
|
+
async ({ domain, access_token }) => {
|
|
918
1084
|
try {
|
|
919
|
-
const result = await keTenantsList();
|
|
1085
|
+
const result = await keTenantsList({ domain, access_token });
|
|
920
1086
|
return { content: [{ type: "text", text: result }] };
|
|
921
1087
|
} catch (error) {
|
|
922
1088
|
logger.error("ke_tenants_list 失败", { error: error.message });
|
|
@@ -929,12 +1095,16 @@ server.registerTool(
|
|
|
929
1095
|
server.registerTool(
|
|
930
1096
|
"ke_projects_list",
|
|
931
1097
|
{
|
|
932
|
-
description: "
|
|
933
|
-
inputSchema: {
|
|
1098
|
+
description: "列出指定租户下的所有项目。所有配置项如果未指定,则默认从项目目录下的 .mcp/bd-ke-mcp.json 配置文件中获取。",
|
|
1099
|
+
inputSchema: {
|
|
1100
|
+
domain: z.string().optional().describe("KE 平台域名(如:https://ke.example.com),用于 API 请求。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1101
|
+
access_token: z.string().optional().describe("访问令牌(leo-user-token),用于 API 认证。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1102
|
+
tenant: z.string().optional().describe("租户名称,指定要查询项目的租户。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取")
|
|
1103
|
+
}
|
|
934
1104
|
},
|
|
935
|
-
async () => {
|
|
1105
|
+
async ({ domain, access_token, tenant }) => {
|
|
936
1106
|
try {
|
|
937
|
-
const result = await keProjectsList();
|
|
1107
|
+
const result = await keProjectsList({ domain, access_token, tenant });
|
|
938
1108
|
return { content: [{ type: "text", text: result }] };
|
|
939
1109
|
} catch (error) {
|
|
940
1110
|
logger.error("ke_projects_list 失败", { error: error.message });
|
|
@@ -947,12 +1117,17 @@ server.registerTool(
|
|
|
947
1117
|
server.registerTool(
|
|
948
1118
|
"ke_app_list",
|
|
949
1119
|
{
|
|
950
|
-
description: "
|
|
951
|
-
inputSchema: {
|
|
1120
|
+
description: "列出指定租户和项目下的所有应用。所有配置项如果未指定,则默认从项目目录下的 .mcp/bd-ke-mcp.json 配置文件中获取。",
|
|
1121
|
+
inputSchema: {
|
|
1122
|
+
domain: z.string().optional().describe("KE 平台域名(如:https://ke.example.com),用于 API 请求。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1123
|
+
access_token: z.string().optional().describe("访问令牌(leo-user-token),用于 API 认证。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1124
|
+
tenant: z.string().optional().describe("租户名称,用于构建命名空间。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1125
|
+
project: z.string().optional().describe("项目名称,用于构建命名空间。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取")
|
|
1126
|
+
}
|
|
952
1127
|
},
|
|
953
|
-
async () => {
|
|
1128
|
+
async ({ domain, access_token, tenant, project }) => {
|
|
954
1129
|
try {
|
|
955
|
-
const result = await keAppList();
|
|
1130
|
+
const result = await keAppList({ domain, access_token, tenant, project });
|
|
956
1131
|
return { content: [{ type: "text", text: result }] };
|
|
957
1132
|
} catch (error) {
|
|
958
1133
|
logger.error("ke_app_list 失败", { error: error.message });
|
|
@@ -965,14 +1140,16 @@ server.registerTool(
|
|
|
965
1140
|
server.registerTool(
|
|
966
1141
|
"ke_env_list",
|
|
967
1142
|
{
|
|
968
|
-
description: "
|
|
1143
|
+
description: "获取指定租户下的所有可用环境(集群)列表。所有配置项如果未指定,则默认从项目目录下的 .mcp/bd-ke-mcp.json 配置文件中获取。",
|
|
969
1144
|
inputSchema: {
|
|
970
|
-
|
|
1145
|
+
domain: z.string().optional().describe("KE 平台域名(如:https://ke.example.com),用于 API 请求。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1146
|
+
access_token: z.string().optional().describe("访问令牌(leo-user-token),用于 API 认证。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1147
|
+
tenant: z.string().optional().describe("租户名称,指定要查询环境的租户。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取")
|
|
971
1148
|
}
|
|
972
1149
|
},
|
|
973
|
-
async () => {
|
|
1150
|
+
async ({ domain, access_token, tenant }) => {
|
|
974
1151
|
try {
|
|
975
|
-
const result = await keEnvList();
|
|
1152
|
+
const result = await keEnvList({ domain, access_token, tenant });
|
|
976
1153
|
return { content: [{ type: "text", text: result }] };
|
|
977
1154
|
} catch (error) {
|
|
978
1155
|
logger.error("ke_env_list 失败", { error: error.message });
|
|
@@ -985,14 +1162,19 @@ server.registerTool(
|
|
|
985
1162
|
server.registerTool(
|
|
986
1163
|
"ke_pods_list",
|
|
987
1164
|
{
|
|
988
|
-
description: "获取指定应用的 Pod 实例列表,包含实例名称、状态、创建时间、容器IP
|
|
1165
|
+
description: "获取指定应用的 Pod 实例列表,包含实例名称、状态、创建时间、容器IP、运行节点、重启次数等信息。所有配置项如果未指定,则默认从项目目录下的 .mcp/bd-ke-mcp.json 配置文件中获取。",
|
|
989
1166
|
inputSchema: {
|
|
990
|
-
|
|
1167
|
+
domain: z.string().optional().describe("KE 平台域名(如:https://ke.example.com),用于 API 请求。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1168
|
+
access_token: z.string().optional().describe("访问令牌(leo-user-token),用于 API 认证。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1169
|
+
tenant: z.string().optional().describe("租户名称,用于构建命名空间。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1170
|
+
project: z.string().optional().describe("项目名称,用于构建命名空间。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1171
|
+
appName: z.string().describe("应用名称,指定要查询 Pod 的应用,可通过 ke_app_list 获取"),
|
|
1172
|
+
env: z.string().describe("环境名称,指定要查询的环境(集群),可通过 ke_env_list 获取")
|
|
991
1173
|
}
|
|
992
1174
|
},
|
|
993
|
-
async ({ appName }) => {
|
|
1175
|
+
async ({ domain, access_token, tenant, project, appName, env }) => {
|
|
994
1176
|
try {
|
|
995
|
-
const result = await kePodsList(appName);
|
|
1177
|
+
const result = await kePodsList({ domain, access_token, tenant, project, appName, env });
|
|
996
1178
|
return { content: [{ type: "text", text: result }] };
|
|
997
1179
|
} catch (error) {
|
|
998
1180
|
logger.error("ke_pods_list 失败", { error: error.message });
|
|
@@ -1005,21 +1187,24 @@ server.registerTool(
|
|
|
1005
1187
|
server.registerTool(
|
|
1006
1188
|
"ke_pod_log_list",
|
|
1007
1189
|
{
|
|
1008
|
-
description: "获取指定应用的 Pod
|
|
1190
|
+
description: "获取指定应用的 Pod 日志列表,支持按时间范围查询和分页。所有配置项如果未指定,则默认从项目目录下的 .mcp/bd-ke-mcp.json 配置文件中获取。",
|
|
1009
1191
|
inputSchema: {
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1192
|
+
domain: z.string().optional().describe("KE 平台域名(如:https://ke.example.com),用于 API 请求。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1193
|
+
access_token: z.string().optional().describe("访问令牌(leo-user-token),用于 API 认证。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1194
|
+
tenant: z.string().optional().describe("租户名称,用于日志查询 API 路径。未指定时从 .mcp/bd-ke-mcp.json 配置文件获取"),
|
|
1195
|
+
namespace: z.string().describe("命名空间,格式为 tenant-project(如:myapp-production),用于筛选日志来源"),
|
|
1196
|
+
service: z.string().describe("服务名称(即应用名称),指定要查询日志的应用,可通过 ke_app_list 获取"),
|
|
1197
|
+
env: z.string().describe("环境名称,指定要查询日志的环境(集群),可通过 ke_env_list 获取"),
|
|
1198
|
+
startTime: z.number().describe("查询开始时间,纳秒级时间戳(如:1704067200000000000 表示 2024-01-01 00:00:00)"),
|
|
1199
|
+
endTime: z.number().describe("查询结束时间,纳秒级时间戳(如:1704153600000000000 表示 2024-01-02 00:00:00)"),
|
|
1200
|
+
size: z.number().optional().default(100).describe("返回的日志记录数量,默认 100 条,建议不超过 1000"),
|
|
1201
|
+
offset: z.number().optional().default(0).describe("分页偏移量,用于翻页查询,默认从 0 开始"),
|
|
1202
|
+
sort: z.string().optional().default("desc").describe("日志排序方式,desc 表示按时间降序(最新在前,默认),asc 表示按时间升序(最早在前)")
|
|
1018
1203
|
}
|
|
1019
1204
|
},
|
|
1020
|
-
async ({ namespace, service, env,
|
|
1205
|
+
async ({ domain, access_token, tenant, namespace, service, env, startTime, endTime, size, offset, sort }) => {
|
|
1021
1206
|
try {
|
|
1022
|
-
const result = await kePodLogList(namespace, service, env,
|
|
1207
|
+
const result = await kePodLogList({ domain, access_token, tenant, namespace, service, env, startTime, endTime, size, offset, sort });
|
|
1023
1208
|
return { content: [{ type: "text", text: result }] };
|
|
1024
1209
|
} catch (error) {
|
|
1025
1210
|
logger.error("ke_pod_log_list 失败", { error: error.message });
|
|
@@ -1028,32 +1213,6 @@ server.registerTool(
|
|
|
1028
1213
|
}
|
|
1029
1214
|
);
|
|
1030
1215
|
|
|
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
1216
|
// ==================== 启动服务 ====================
|
|
1058
1217
|
|
|
1059
1218
|
async function main() {
|
|
@@ -1067,6 +1226,7 @@ async function main() {
|
|
|
1067
1226
|
pid: process.pid,
|
|
1068
1227
|
projectRoot: getProjectRoot(),
|
|
1069
1228
|
tools: [
|
|
1229
|
+
"ke_config_reset",
|
|
1070
1230
|
"ke_app_restart",
|
|
1071
1231
|
"ke_pipeline_list",
|
|
1072
1232
|
"ke_pipeline_info",
|
|
@@ -1076,8 +1236,7 @@ async function main() {
|
|
|
1076
1236
|
"ke_app_list",
|
|
1077
1237
|
"ke_env_list",
|
|
1078
1238
|
"ke_pods_list",
|
|
1079
|
-
"ke_pod_log_list"
|
|
1080
|
-
"ke_pod_exec"
|
|
1239
|
+
"ke_pod_log_list"
|
|
1081
1240
|
]
|
|
1082
1241
|
});
|
|
1083
1242
|
}
|