cls-mcp-server 0.2.8 → 0.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.
Files changed (2) hide show
  1. package/dist/index.js +185 -29
  2. package/package.json +10 -8
package/dist/index.js CHANGED
@@ -4,11 +4,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
5
  };
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.SEARCH_TIME_TEXT_FORMAT = exports.TIMEZONE_SHANGHAI = void 0;
7
8
  const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
8
9
  const sse_js_1 = require("@modelcontextprotocol/sdk/server/sse.js");
9
10
  const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
10
11
  require("dotenv/config");
11
12
  const express_1 = __importDefault(require("express"));
13
+ const moment_timezone_1 = __importDefault(require("moment-timezone"));
12
14
  const tencentcloud_sdk_nodejs_cls_1 = require("tencentcloud-sdk-nodejs-cls");
13
15
  const tencentcloud_sdk_nodejs_region_1 = require("tencentcloud-sdk-nodejs-region");
14
16
  const zod_1 = require("zod");
@@ -26,10 +28,10 @@ const MultiTopicSearchInformationSchema = zod_1.z.object({
26
28
  const SearchLogRequestSchema = {
27
29
  From: zod_1.z
28
30
  .number()
29
- .describe('要检索分析的日志的起始时间,Unix时间戳(需要传毫秒粒度)。To减去From的时间范围建议不要过大,建议默认近15分钟,否则会导致返回的日志过多,影响性能。计算相对时间(如近15分钟)时,如果AI模型不知道当前时间,AI模型应该先调用GetCurrentTimestamp工具获取当前时间,再基于这个当前时间去计算相对时间。传入的参数是数字整形,如涉及加减乘除等数学运算,应传入运算结果的数字整形,不要传入一个数学运算公式。'),
31
+ .describe('要检索分析的日志的起始时间,Unix时间戳(需要传毫秒粒度)。To减去From的时间范围建议不要过大,建议默认近15分钟,否则会导致返回的日志过多,影响性能。计算相对时间(如近15分钟)时,如果AI模型不知道当前时间,AI模型应该先调用 GetCurrentTimestamp 工具获取当前时间,再基于这个当前时间去计算相对时间。传入的参数是数字整形,如涉及加减乘除等数学运算,应传入运算结果的数字整形,不要传入一个数学运算公式。也可以调用 ConvertTimeStringToTimestamp 直接获取时间戳参数。'),
30
32
  To: zod_1.z
31
33
  .number()
32
- .describe('要检索分析的日志的结束时间,Unix时间戳(需要传毫秒粒度)。To减去From的时间范围建议不要过大,建议默认近15分钟,否则会导致返回的日志过多,影响性能。计算相对时间(如近15分钟)时,如果AI模型不知道当前时间,AI模型应该先调用GetCurrentTimestamp工具获取当前时间,再基于这个当前时间去计算相对时间。传入的参数是数字整形,如涉及加减乘除等数学运算,应传入运算结果的数字整形,不要传入一个数学运算公式。'),
34
+ .describe('要检索分析的日志的结束时间,Unix时间戳(需要传毫秒粒度)。To减去From的时间范围建议不要过大,建议默认近15分钟,否则会导致返回的日志过多,影响性能。计算相对时间(如近15分钟)时,如果AI模型不知道当前时间,AI模型应该先调用 GetCurrentTimestamp 工具获取当前时间,再基于这个当前时间去计算相对时间。传入的参数是数字整形,如涉及加减乘除等数学运算,应传入运算结果的数字整形,不要传入一个数学运算公式。也可以调用 ConvertTimeStringToTimestamp 直接获取时间戳参数。'),
33
35
  Query: zod_1.z.string().describe('检索分析语句,最大长度为12KB。如果不限定检索条件,可传 * 或 空字符串,可查询所有日志'),
34
36
  TopicId: zod_1.z.string().optional().describe('要检索分析的日志主题ID,仅能指定一个日志主题'),
35
37
  Topics: zod_1.z
@@ -50,7 +52,35 @@ const SearchLogRequestSchema = {
50
52
  .describe('执行统计分析时是否对原始日志先进行采样,0:自动采样;0~1:按指定采样率采样;1:不采样'),
51
53
  Region: zod_1.z.string().describe('地域信息,必传,如:ap-guangzhou'),
52
54
  };
53
- mcpServer.tool('SearchLog', 'Search logs based on query parameters', SearchLogRequestSchema, async ({ From, To, Query, TopicId, Topics, Sort, Limit, Offset, SamplingRate, Region: regionFromAI, }) => {
55
+ mcpServer.registerTool('SearchLog', {
56
+ description: 'Search logs based on query parameters',
57
+ inputSchema: SearchLogRequestSchema,
58
+ /* outputSchema: z.object({
59
+ Analysis: z.boolean().describe('返回的是否为统计分析(即SQL)结果'),
60
+ AnalysisRecords: z.array(z.string()).describe('统计分析(即SQL)结果。Analysis为true时,返回该字段'),
61
+ Results: z
62
+ .array(
63
+ z.object({
64
+ Time: z.number().describe('日志时间,单位ms'),
65
+ Source: z.string().describe('日志来源IP'),
66
+ FileName: z.string().describe('日志文件名称'),
67
+ PkgId: z
68
+ .string()
69
+ .describe(
70
+ '日志上报请求包的ID。结合 PkgLogId 一起,可作为参数通过 DescribeLogContext 工具获取日志上下文信息。',
71
+ ),
72
+ PkgLogId: z
73
+ .string()
74
+ .describe(
75
+ '请求包内日志的ID。结合 PkgId 一起,可作为参数通过 DescribeLogContext 工具获取日志上下文信息。',
76
+ ),
77
+ LogJson: z.string().describe('日志内容的Json序列化字符串'),
78
+ HostName: z.string().describe('日志来源主机名称'),
79
+ }),
80
+ )
81
+ .describe('匹配检索条件的原始日志。Analysis为false时,返回该字段'),
82
+ }), */
83
+ }, async ({ From, To, Query, TopicId, Topics, Sort, Limit, Offset, SamplingRate, Region: regionFromAI, }) => {
54
84
  try {
55
85
  const region = regionFromAI || process.env.TENCENTCLOUD_REGION;
56
86
  if (!region) {
@@ -101,23 +131,117 @@ mcpServer.tool('SearchLog', 'Search logs based on query parameters', SearchLogRe
101
131
  if (response.Analysis) {
102
132
  return formatResponse(response.AnalysisRecords);
103
133
  }
104
- return formatResponse(response.Results?.map((result) => result?.LogJson));
134
+ return formatResponse(response.Results?.map((result) => ({
135
+ Time: result.Time,
136
+ Source: result.Source,
137
+ FileName: result.FileName,
138
+ PkgId: result.PkgId,
139
+ PkgLogId: result.PkgLogId,
140
+ LogJson: result.LogJson,
141
+ HostName: result.HostName,
142
+ })));
105
143
  }
106
144
  catch (e) {
107
145
  return formatResponse({ message: e?.toString ? e.toString() : e?.message, stack: e?.stack, ...e }, true);
108
146
  }
109
147
  });
110
- mcpServer.tool('GetTopicInfoByName', 'search topic info by topic name', {
111
- searchText: zod_1.z
112
- .string()
113
- .optional()
114
- .describe('Text to search, possible value: topic name. If not provided, will search all topics'),
115
- preciseSearch: zod_1.z
116
- .boolean()
117
- .default(false)
118
- .describe('If precise search(true) or fuzzy search(false), default false. Recommend to use fuzzy search.'),
119
- region: zod_1.z.string().describe('地域信息,必传,如:ap-guangzhou'),
120
- }, async ({ region: regionFromAI, searchText, preciseSearch }) => {
148
+ exports.TIMEZONE_SHANGHAI = 'Asia/Shanghai';
149
+ exports.SEARCH_TIME_TEXT_FORMAT = 'YYYY-MM-DD HH:mm:ss.SSS';
150
+ mcpServer.registerTool('DescribeLogContext', {
151
+ description: '搜索日志上下文附近的内容',
152
+ inputSchema: {
153
+ Region: zod_1.z.string().describe('地域信息,必传,如:ap-guangzhou'),
154
+ TopicId: zod_1.z.string().describe('要检索分析的日志主题ID,仅能指定一个日志主题'),
155
+ Time: zod_1.z
156
+ .number()
157
+ .describe('日志时间,单位ms。通过 SearchLog 工具检索原始日志时,Results 结构体中会返回 Time 字段。'),
158
+ PkgId: zod_1.z
159
+ .string()
160
+ .describe('日志上报请求包的ID。通过 SearchLog 工具检索原始日志时,Results 结构体中会返回 PkgId 字段。'),
161
+ PkgLogId: zod_1.z
162
+ .string()
163
+ .describe('请求包内日志的ID。通过 SearchLog 工具检索原始日志时,Results 结构体中会返回 PkgLogId 字段。'),
164
+ PrevLogs: zod_1.z.number().optional().default(10).describe('前${PrevLogs}条日志,默认值10。'),
165
+ NextLogs: zod_1.z.number().optional().default(10).describe('后${NextLogs}条日志,默认值10。'),
166
+ Query: zod_1.z.string().optional().describe('检索语句,对日志上下文进行过滤,不支持SQL语句。'),
167
+ From: zod_1.z.number().optional().describe('上下文检索的开始时间,单位ms。'),
168
+ To: zod_1.z.number().optional().describe('上下文检索的结束时间,单位ms。'),
169
+ },
170
+ }, async ({ Region: regionFromAI, TopicId, Time, PkgId, PkgLogId, PrevLogs = 10, NextLogs = 10, Query, From, To, }) => {
171
+ try {
172
+ const region = regionFromAI || process.env.TENCENTCLOUD_REGION;
173
+ if (!region) {
174
+ throw new Error('no Region is provided.');
175
+ }
176
+ if (!TopicId) {
177
+ throw new Error('no TopicId is provided.');
178
+ }
179
+ if (!Time) {
180
+ throw new Error('no Time is provided.');
181
+ }
182
+ if (!PkgId) {
183
+ throw new Error('no PkgId is provided.');
184
+ }
185
+ if (!PkgLogId) {
186
+ throw new Error('no PkgLogId is provided.');
187
+ }
188
+ const cloudApiBaseHost = process.env.TENCENTCLOUD_API_BASE_HOST || 'tencentcloudapi.com';
189
+ const clsClient = new ClsClient({
190
+ credential: {
191
+ secretId: process.env.TENCENTCLOUD_SECRET_ID,
192
+ secretKey: process.env.TENCENTCLOUD_SECRET_KEY,
193
+ },
194
+ region,
195
+ profile: {
196
+ language: 'zh-CN',
197
+ httpProfile: {
198
+ endpoint: `cls.${cloudApiBaseHost}`,
199
+ },
200
+ },
201
+ });
202
+ clsClient.sdkVersion = 'cls-mcp-server';
203
+ const capiParams = {
204
+ TopicId,
205
+ BTime: (0, moment_timezone_1.default)(Time).tz(exports.TIMEZONE_SHANGHAI).format(exports.SEARCH_TIME_TEXT_FORMAT),
206
+ PkgId,
207
+ PkgLogId: Number(PkgLogId),
208
+ PrevLogs,
209
+ NextLogs,
210
+ From,
211
+ To,
212
+ ...(Query && {
213
+ Query,
214
+ }),
215
+ ...(From && {
216
+ From,
217
+ }),
218
+ ...(To && {
219
+ To,
220
+ }),
221
+ };
222
+ const response = await clsClient.DescribeLogContext(capiParams);
223
+ return formatResponse(response);
224
+ }
225
+ catch (e) {
226
+ return formatResponse({ message: e?.toString ? e.toString() : e?.message, stack: e?.stack, ...e }, true);
227
+ }
228
+ });
229
+ mcpServer.registerTool('GetTopicInfoByName', {
230
+ description: 'search topic info by topic name',
231
+ inputSchema: {
232
+ searchText: zod_1.z
233
+ .string()
234
+ .optional()
235
+ .describe('Text to search, possible value: topic name. If not provided, will search all topics'),
236
+ preciseSearch: zod_1.z
237
+ .boolean()
238
+ .default(false)
239
+ .describe('If precise search(true) or fuzzy search(false), default false. Recommend to use fuzzy search.'),
240
+ region: zod_1.z.string().describe('地域信息,必传,如:ap-guangzhou'),
241
+ offset: zod_1.z.number().optional().default(0).describe('Offset of the topic list, default 0'),
242
+ limit: zod_1.z.number().optional().default(20).describe('Limit of the topic list, default 20'),
243
+ },
244
+ }, async ({ region: regionFromAI, searchText, preciseSearch, offset = 0, limit = 20 }) => {
121
245
  try {
122
246
  const region = regionFromAI || process.env.TENCENTCLOUD_REGION;
123
247
  if (!region) {
@@ -148,6 +272,8 @@ mcpServer.tool('GetTopicInfoByName', 'search topic info by topic name', {
148
272
  ]
149
273
  : [],
150
274
  PreciseSearch: preciseSearch ? 1 : 0,
275
+ Offset: offset,
276
+ Limit: limit,
151
277
  });
152
278
  const topics = response?.Topics?.map((topic) => ({
153
279
  TopicName: topic.TopicName,
@@ -160,13 +286,16 @@ mcpServer.tool('GetTopicInfoByName', 'search topic info by topic name', {
160
286
  return formatResponse({ message: e?.toString ? e.toString() : e?.message, stack: e?.stack, ...e }, true);
161
287
  }
162
288
  });
163
- mcpServer.tool('GetRegionCodeByName', 'search region parameter by region name', {
164
- searchText: zod_1.z.string().describe('region name to search, e.g. Hong Kong or 广州'),
165
- language: zod_1.z
166
- .string()
167
- .optional()
168
- .default('zh-CN')
169
- .describe('search text language, "zh-CN" or "en-US", default zh-CN'),
289
+ mcpServer.registerTool('GetRegionCodeByName', {
290
+ description: 'search region parameter by region name',
291
+ inputSchema: {
292
+ searchText: zod_1.z.string().describe('region name to search, e.g. Hong Kong or 广州'),
293
+ language: zod_1.z
294
+ .string()
295
+ .optional()
296
+ .default('zh-CN')
297
+ .describe('search text language, "zh-CN" or "en-US", default zh-CN'),
298
+ },
170
299
  }, async ({ searchText, language }) => {
171
300
  try {
172
301
  const cloudApiBaseHost = process.env.TENCENTCLOUD_API_BASE_HOST || 'tencentcloudapi.com';
@@ -203,13 +332,40 @@ mcpServer.tool('GetRegionCodeByName', 'search region parameter by region name',
203
332
  return formatResponse({ message: e?.toString ? e.toString() : e?.message, stack: e?.stack, ...e }, true);
204
333
  }
205
334
  });
206
- mcpServer.tool('GetCurrentTimestamp', 'get current timestamp in milliseconds', {}, () => formatResponse(Date.now()));
207
- mcpServer.tool('TextToSearchLogQuery', 'get cls SearchLog Query with natual language generated by user', {
208
- Text: zod_1.z
209
- .string()
210
- .describe('natual language input generated by user, e.g. 查询日志条数 or Get error logs distribution over time.'),
211
- Region: zod_1.z.string().describe('地域信息,必传,如:ap-guangzhou'),
212
- TopicId: zod_1.z.string().optional().describe('要检索分析的日志主题ID,仅能指定一个日志主题'),
335
+ mcpServer.registerTool('GetCurrentTimestamp', { description: 'get current timestamp in milliseconds' }, () => formatResponse(Date.now()));
336
+ mcpServer.registerTool('ConvertTimeStringToTimestamp', {
337
+ description: 'convert time string to timestamp in milliseconds',
338
+ inputSchema: {
339
+ timeString: zod_1.z.string().describe('time string to convert, e.g. 2025-06-01 12:00:00'),
340
+ timeFormat: zod_1.z
341
+ .string()
342
+ .optional()
343
+ .default('%Y-%m-%d %H:%M:%S')
344
+ .describe('time format to use, e.g. YYYY-MM-DD HH:mm:ss'),
345
+ timeZone: zod_1.z.string().optional().default(exports.TIMEZONE_SHANGHAI).describe('time zone to use, e.g. Asia/Shanghai'),
346
+ },
347
+ }, ({ timeString, timeFormat, timeZone }) => formatResponse(moment_timezone_1.default.tz(timeString, timeFormat, timeZone).valueOf()));
348
+ mcpServer.registerTool('ConvertTimestampToTimeString', {
349
+ description: 'convert timestamp to time string',
350
+ inputSchema: {
351
+ timestamp: zod_1.z.number().describe('timestamp in milliseconds to convert, e.g. 1717286400000'),
352
+ timeFormat: zod_1.z
353
+ .string()
354
+ .optional()
355
+ .default('%Y-%m-%d %H:%M:%S')
356
+ .describe('time format to use, e.g. YYYY-MM-DD HH:mm:ss'),
357
+ timeZone: zod_1.z.string().optional().default(exports.TIMEZONE_SHANGHAI).describe('time zone to use, e.g. Asia/Shanghai'),
358
+ },
359
+ }, ({ timestamp, timeFormat, timeZone }) => formatResponse((0, moment_timezone_1.default)(timestamp).tz(timeZone).format(timeFormat)));
360
+ mcpServer.registerTool('TextToSearchLogQuery', {
361
+ description: 'get cls SearchLog Query with natual language generated by user',
362
+ inputSchema: {
363
+ Text: zod_1.z
364
+ .string()
365
+ .describe('natual language input generated by user, e.g. 查询日志条数 or Get error logs distribution over time.'),
366
+ Region: zod_1.z.string().describe('地域信息,必传,如:ap-guangzhou'),
367
+ TopicId: zod_1.z.string().optional().describe('要检索分析的日志主题ID,仅能指定一个日志主题'),
368
+ },
213
369
  }, async ({ Text, TopicId, Region: regionFromAI }) => {
214
370
  const region = regionFromAI || process.env.TENCENTCLOUD_REGION;
215
371
  if (!region) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cls-mcp-server",
3
- "version": "0.2.8",
3
+ "version": "0.3.0",
4
4
  "main": "index.js",
5
5
  "files": [
6
6
  "dist"
@@ -20,17 +20,19 @@
20
20
  "license": "ISC",
21
21
  "description": "",
22
22
  "dependencies": {
23
- "@modelcontextprotocol/sdk": "^1.9.0",
23
+ "@modelcontextprotocol/sdk": "^1.25.1",
24
24
  "dotenv": "^16.5.0",
25
25
  "express": "^5.1.0",
26
- "tencentcloud-sdk-nodejs-cls": "^4.1.18",
27
- "tencentcloud-sdk-nodejs-common": "^4.1.1",
28
- "tencentcloud-sdk-nodejs-region": "^4.0.997",
29
- "zod": "^3.24.3"
26
+ "moment": "^2.30.1",
27
+ "moment-timezone": "^0.6.0",
28
+ "tencentcloud-sdk-nodejs-cls": "^4.1.158",
29
+ "tencentcloud-sdk-nodejs-common": "^4.1.122",
30
+ "tencentcloud-sdk-nodejs-region": "^4.1.137",
31
+ "zod": "^3.25.76"
30
32
  },
31
33
  "devDependencies": {
32
- "@commitlint/cli": "^18.6.1",
33
- "@commitlint/config-conventional": "^18.6.3",
34
+ "@commitlint/cli": "^20.2.0",
35
+ "@commitlint/config-conventional": "^20.2.0",
34
36
  "@types/express": "^5.0.1",
35
37
  "@types/ip": "^1.1.3",
36
38
  "@types/jest": "^29.5.0",