mcp-log-query-server 3.1.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config.js +18 -0
- package/index.js +51 -3
- package/loki-client.js +24 -5
- package/package.json +1 -1
package/config.js
CHANGED
|
@@ -314,6 +314,24 @@ export const SERVICES = {
|
|
|
314
314
|
logPath: '/www/logs/clife-senior-work-order-center-app/normal_logs',
|
|
315
315
|
logFile: 'normal.log',
|
|
316
316
|
aliases: ['work-order-center', 'work-order', '工单中心', '工单']
|
|
317
|
+
},
|
|
318
|
+
'clife-senior-station-assistant-bff': {
|
|
319
|
+
name: 'clife-senior-station-assistant-bff',
|
|
320
|
+
description: '养老驿站助手BFF服务',
|
|
321
|
+
namespace: 'saas-itest',
|
|
322
|
+
podPattern: 'clife-senior-station-assistant-bff-app',
|
|
323
|
+
logPath: '/www/logs/clife-senior-station-assistant-bff-app/normal_logs',
|
|
324
|
+
logFile: 'normal.log',
|
|
325
|
+
aliases: ['station-assistant-bff', 'station-assistant', '驿站助手', '驿站BFF']
|
|
326
|
+
},
|
|
327
|
+
'clife-senior-third-bff': {
|
|
328
|
+
name: 'clife-senior-third-bff',
|
|
329
|
+
description: '养老第三方BFF服务',
|
|
330
|
+
namespace: 'saas-itest',
|
|
331
|
+
podPattern: 'clife-senior-third-bff-app',
|
|
332
|
+
logPath: '/www/logs/clife-senior-third-bff-app/normal_logs',
|
|
333
|
+
logFile: 'normal.log',
|
|
334
|
+
aliases: ['third-bff', '第三方BFF']
|
|
317
335
|
}
|
|
318
336
|
};
|
|
319
337
|
|
package/index.js
CHANGED
|
@@ -34,6 +34,23 @@ import {
|
|
|
34
34
|
listLokiEnvironments as getLokiEnvList, listLokiServices as getLokiSvcList
|
|
35
35
|
} from './loki-client.js';
|
|
36
36
|
|
|
37
|
+
// 超时配置
|
|
38
|
+
const REQUEST_TIMEOUT = 60000; // MCP 请求兜底超时 60s
|
|
39
|
+
const WATCHDOG_TIMEOUT = 120000; // 进程看门狗 120s,卡死则强制退出
|
|
40
|
+
|
|
41
|
+
function withTimeout(promise, ms, label) {
|
|
42
|
+
return Promise.race([
|
|
43
|
+
promise,
|
|
44
|
+
new Promise((_, reject) =>
|
|
45
|
+
setTimeout(() => reject(new Error(`${label} 超时(${ms}ms)`)), ms)
|
|
46
|
+
),
|
|
47
|
+
]);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 进程级安全网
|
|
51
|
+
process.on('unhandledRejection', (err) => console.error('[unhandledRejection]', err));
|
|
52
|
+
process.on('uncaughtException', (err) => { console.error('[uncaughtException]', err); process.exit(1); });
|
|
53
|
+
|
|
37
54
|
// 创建 MCP Server
|
|
38
55
|
const server = new Server(
|
|
39
56
|
{
|
|
@@ -320,7 +337,38 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
320
337
|
// 处理工具调用
|
|
321
338
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
322
339
|
const { name, arguments: args } = request.params;
|
|
340
|
+
const startTime = Date.now();
|
|
341
|
+
|
|
342
|
+
// 进程级看门狗:如果整个请求卡死超时,强制重启进程
|
|
343
|
+
const watchdog = setTimeout(() => {
|
|
344
|
+
console.error(`[Watchdog] ${name} 卡死超过 ${WATCHDOG_TIMEOUT}ms,强制退出进程`);
|
|
345
|
+
process.exit(1);
|
|
346
|
+
}, WATCHDOG_TIMEOUT);
|
|
347
|
+
watchdog.unref();
|
|
348
|
+
|
|
349
|
+
try {
|
|
350
|
+
const result = await withTimeout(
|
|
351
|
+
handleToolCall(name, args),
|
|
352
|
+
REQUEST_TIMEOUT,
|
|
353
|
+
name
|
|
354
|
+
);
|
|
355
|
+
clearTimeout(watchdog);
|
|
356
|
+
console.error(`[MCP] ${name} ${Date.now() - startTime}ms`);
|
|
357
|
+
return result;
|
|
358
|
+
} catch (error) {
|
|
359
|
+
clearTimeout(watchdog);
|
|
360
|
+
console.error(`[Error] ${name} ${Date.now() - startTime}ms: ${error.message}`);
|
|
361
|
+
return {
|
|
362
|
+
content: [{ type: 'text', text: `## 执行错误\n\n❌ ${error.message}` }],
|
|
363
|
+
isError: true
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
});
|
|
323
367
|
|
|
368
|
+
/**
|
|
369
|
+
* 实际的工具调用处理逻辑
|
|
370
|
+
*/
|
|
371
|
+
async function handleToolCall(name, args) {
|
|
324
372
|
try {
|
|
325
373
|
switch (name) {
|
|
326
374
|
case 'query_log': {
|
|
@@ -741,7 +789,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
741
789
|
};
|
|
742
790
|
}
|
|
743
791
|
} catch (error) {
|
|
744
|
-
console.error(`[MCP]
|
|
792
|
+
console.error(`[MCP] 工具内部错误: ${error.message}`);
|
|
745
793
|
return {
|
|
746
794
|
content: [{
|
|
747
795
|
type: 'text',
|
|
@@ -750,13 +798,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
750
798
|
isError: true
|
|
751
799
|
};
|
|
752
800
|
}
|
|
753
|
-
}
|
|
801
|
+
}
|
|
754
802
|
|
|
755
803
|
// 启动服务器
|
|
756
804
|
async function main() {
|
|
757
805
|
const transport = new StdioServerTransport();
|
|
758
806
|
await server.connect(transport);
|
|
759
|
-
console.error('[MCP] Log Query Server v3.
|
|
807
|
+
console.error('[MCP] Log Query Server v3.2.0 已启动 (支持超时保护 + 进程看门狗)');
|
|
760
808
|
}
|
|
761
809
|
|
|
762
810
|
main().catch((error) => {
|
package/loki-client.js
CHANGED
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
|
|
12
12
|
import { LOKI_ENVIRONMENTS, LOKI_DEFAULTS } from './config.js';
|
|
13
13
|
|
|
14
|
+
// Loki 查询超时(毫秒)
|
|
15
|
+
const LOKI_FETCH_TIMEOUT = 30000;
|
|
16
|
+
|
|
14
17
|
// 时间范围自动递进策略(毫秒)
|
|
15
18
|
const AUTO_RANGE_STEPS = [
|
|
16
19
|
{ range: 5 * 60 * 1000, label: '5 分钟' },
|
|
@@ -63,11 +66,27 @@ export async function queryLoki(envName, expr, options = {}) {
|
|
|
63
66
|
|
|
64
67
|
console.error(`[Loki] 查询: env=${envName}, expr=${expr}`);
|
|
65
68
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
// 带超时的 fetch
|
|
70
|
+
const controller = new AbortController();
|
|
71
|
+
const timer = setTimeout(() => controller.abort(), LOKI_FETCH_TIMEOUT);
|
|
72
|
+
|
|
73
|
+
let resp;
|
|
74
|
+
try {
|
|
75
|
+
resp = await fetch(url, {
|
|
76
|
+
method: 'POST',
|
|
77
|
+
headers: buildHeaders(env),
|
|
78
|
+
body: JSON.stringify(body),
|
|
79
|
+
signal: controller.signal
|
|
80
|
+
});
|
|
81
|
+
} catch (e) {
|
|
82
|
+
clearTimeout(timer);
|
|
83
|
+
if (e.name === 'AbortError') {
|
|
84
|
+
throw new Error(`Loki 查询超时(${LOKI_FETCH_TIMEOUT}ms)`);
|
|
85
|
+
}
|
|
86
|
+
throw e;
|
|
87
|
+
} finally {
|
|
88
|
+
clearTimeout(timer);
|
|
89
|
+
}
|
|
71
90
|
|
|
72
91
|
if (!resp.ok) {
|
|
73
92
|
const text = await resp.text();
|