tca-mcp-server 1.2.0 → 1.3.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.
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env sh
2
+ . "$(dirname -- "$0")/_/husky.sh"
3
+
4
+ npx lint-staged
package/.prettierrc ADDED
@@ -0,0 +1,4 @@
1
+ printWidth: 100
2
+ tabWidth: 4
3
+ singleQuote: true
4
+ trailingComma: 'es5'
@@ -0,0 +1,58 @@
1
+ import TcaClient from './client.js';
2
+ const opPluginType = 2;
3
+ // 操作类型枚举,目前只有scan_remote
4
+ export var LogType;
5
+ (function (LogType) {
6
+ LogType["ScanRemote"] = "scan_remote";
7
+ LogType["GetReport"] = "get_report";
8
+ LogType["GetIssue"] = "get_issues";
9
+ LogType["GetAiSug"] = "get_ai_sug";
10
+ LogType["GetJobStatus"] = "get_job_status";
11
+ })(LogType || (LogType = {}));
12
+ // 状态枚举, 1 成功,2 失败
13
+ export var LogStatus;
14
+ (function (LogStatus) {
15
+ LogStatus[LogStatus["Success"] = 1] = "Success";
16
+ LogStatus[LogStatus["Failed"] = 2] = "Failed";
17
+ })(LogStatus || (LogStatus = {}));
18
+ /**
19
+ * 记录MCP操作日志(带超时控制,不影响调用方)
20
+ * @param opType 操作类型
21
+ * @param startTime 开始时间
22
+ * @param endTime 结束时间(可选)
23
+ * @param status 操作状态(可选)
24
+ * @param resultMsg 结果信息(可选)
25
+ * @param opPluginData 插件操作数据(可选)
26
+ * @param timeout 超时时间,默认5000ms
27
+ */
28
+ export function recordOpLog(opType, startTime, endTime, status, resultMsg, opPluginData, timeout = 5000) {
29
+ // IIFE,异步代码,不阻塞调用方
30
+ // 增加try catch,捕获异常但不抛出,仅输出错误,不影响主流程
31
+ if (TcaClient.getInstance().runInOA()) {
32
+ return;
33
+ }
34
+ (async () => {
35
+ try {
36
+ const clientInst = TcaClient.getInstance();
37
+ if (!clientInst.runInOA()) {
38
+ const { orgSid } = TcaClient.getcommonParams();
39
+ const path = `/server/main/api/orgs/${orgSid}/oplogs/pluginoprecords/`;
40
+ const logData = {
41
+ op_action: opType,
42
+ start_time: startTime,
43
+ op_plugin_type: opPluginType,
44
+ ...(endTime && { end_time: endTime }),
45
+ ...(status && { status }),
46
+ ...(resultMsg && { result_msg: resultMsg }),
47
+ ...(opPluginData && { op_plugin_data: opPluginData }),
48
+ };
49
+ const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('日志记录超时')), timeout));
50
+ await Promise.race([clientInst.request(path, 'POST', logData), timeoutPromise]);
51
+ }
52
+ }
53
+ catch (error) {
54
+ // 仅在输出错误,但不影响调用方
55
+ console.error('记录操作日志失败:', error);
56
+ }
57
+ })();
58
+ }
package/dist/stdio.js CHANGED
@@ -1,16 +1,16 @@
1
1
  #!/usr/bin/env node
2
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
- import { registerTools } from "./tools/index.js";
2
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { registerTools } from './tools/index.js';
5
5
  // Create an MCP server
6
6
  const server = new McpServer({
7
- name: "腾讯云代码分析(TCA)",
8
- version: "1.0.0"
7
+ name: '腾讯云代码分析(TCA)',
8
+ version: '1.3.2',
9
9
  });
10
10
  // 从命令行获取参数,如果 --oa参数存在,则表示运行在OA环境,否则运行在SaaS环境
11
- const env = process.argv.includes("--oa") ? "oa" : "saas";
11
+ const env = process.argv.includes('--oa') ? 'oa' : 'saas';
12
12
  process.env.TCA_ENV = env;
13
13
  registerTools(server);
14
14
  const transport = new StdioServerTransport();
15
15
  await server.connect(transport);
16
- console.log("Server started");
16
+ console.log('Server started');
@@ -1,7 +1,9 @@
1
- import { z } from "zod";
2
- import TcaClient, { webUrlOA } from "../api/client.js";
3
- import { formatTextToolResult, formatToolError } from "../utils/resp.js";
4
- import { getIssueList } from "../api/issue.js";
1
+ import { z } from 'zod';
2
+ import TcaClient, { webUrlOA } from '../api/client.js';
3
+ import { formatTextToolResult, formatToolError } from '../utils/resp.js';
4
+ import { getIssueList } from '../api/issue.js';
5
+ import { recordOpLog, LogType, LogStatus } from '../api/oplog.js';
6
+ import { getNowCstDateTime } from '../utils/time.js';
5
7
  export default function registerIssueReport(server) {
6
8
  server.tool('tca_issue_list', `获取当前项目未解决的问题列表, 参数如下:
7
9
  -mcpConfigFile: mcp配置文件路径,当前项目根目录查找 tca-mcp.ini(绝对路径)
@@ -11,17 +13,27 @@ export default function registerIssueReport(server) {
11
13
  -limit: 限制数量
12
14
  `, {
13
15
  mcpConfigFile: z.string(),
14
- state: z.string().default("0,1,2,3,4,5"),
15
- severity: z.string().default("0,1,2"),
16
+ state: z.string().default('0,1,2,3,4,5'),
17
+ severity: z.string().default('0,1,2'),
16
18
  offset: z.number().default(0),
17
19
  limit: z.number().default(10),
18
20
  }, async ({ mcpConfigFile, state, severity, offset, limit }) => {
19
21
  await TcaClient.initConfig(mcpConfigFile);
22
+ const startTime = getNowCstDateTime();
20
23
  try {
21
24
  const res = await getIssueList(state, severity, offset, limit);
25
+ const endTime = getNowCstDateTime();
26
+ recordOpLog(LogType.GetIssue, startTime, endTime, LogStatus.Success);
22
27
  return formatTextToolResult(JSON.stringify(res, null, 2), 'get issue list');
23
28
  }
24
29
  catch (error) {
30
+ const endTime = getNowCstDateTime();
31
+ recordOpLog(LogType.GetIssue, startTime, endTime, LogStatus.Failed, String(error), {
32
+ state: state,
33
+ severity: severity,
34
+ offset: offset,
35
+ limit: limit,
36
+ });
25
37
  return formatToolError(error, 'get issue list error');
26
38
  }
27
39
  });
@@ -29,10 +41,11 @@ export default function registerIssueReport(server) {
29
41
  -mcpConfigFile: mcp配置文件路径,当前项目根目录查找 tca-mcp.ini(绝对路径)
30
42
  -jobId: 任务id, 启动分析任务后,从返回结果中获取`, {
31
43
  mcpConfigFile: z.string(),
32
- jobId: z.string().describe("jobId"),
44
+ jobId: z.string().describe('jobId'),
33
45
  }, async ({ mcpConfigFile, jobId }) => {
34
46
  await TcaClient.initConfig(mcpConfigFile);
35
47
  const clientInst = TcaClient.getInstance();
48
+ const startTime = getNowCstDateTime();
36
49
  if (clientInst.runInOA()) {
37
50
  const paramsOA = TcaClient.getcommonOAParams();
38
51
  const path = `code-analysis/repos/${paramsOA.repoId}/projects/${paramsOA.projectId}/scan-history/${jobId}/result`;
@@ -42,7 +55,10 @@ export default function registerIssueReport(server) {
42
55
  const baseUrl = TcaClient.getInstance().baseUrl;
43
56
  const params = TcaClient.getcommonParams();
44
57
  const path = `t/${params.orgSid}/p/${params.teamName}/repos/${params.repoId}/projects/${params.projectId}/jobs/${jobId}/result`;
45
- return formatTextToolResult(baseUrl + path, 'tca issue report link');
58
+ const endTime = getNowCstDateTime();
59
+ var reportUrl = baseUrl + path;
60
+ recordOpLog(LogType.GetReport, startTime, endTime, LogStatus.Success, reportUrl);
61
+ return formatTextToolResult(reportUrl, 'tca issue report link');
46
62
  }
47
63
  });
48
64
  }
package/dist/tools/job.js CHANGED
@@ -1,20 +1,27 @@
1
- import { z } from "zod";
2
- import TcaClient from "../api/client.js";
3
- import { formatTextToolResult, formatToolError } from "../utils/resp.js";
4
- import { getJobDetail, getJobList } from "../api/job.js";
1
+ import { z } from 'zod';
2
+ import TcaClient from '../api/client.js';
3
+ import { formatTextToolResult, formatToolError } from '../utils/resp.js';
4
+ import { getJobDetail, getJobList } from '../api/job.js';
5
+ import { recordOpLog, LogType, LogStatus } from '../api/oplog.js';
6
+ import { getNowCstDateTime } from '../utils/time.js';
5
7
  export default function registerJob(server) {
6
8
  server.tool('job_detail', `获取任务详情, 参数如下:
7
9
  -mcpConfigFile: mcp配置文件路径,当前项目根目录查找 tca-mcp.ini(绝对路径)
8
10
  -jobId: 任务id`, {
9
11
  mcpConfigFile: z.string(),
10
- jobId: z.string().describe("任务id")
12
+ jobId: z.string().describe('任务id'),
11
13
  }, async ({ mcpConfigFile, jobId }) => {
12
14
  await TcaClient.initConfig(mcpConfigFile);
15
+ const startTime = getNowCstDateTime();
13
16
  try {
14
17
  const res = await getJobDetail(jobId);
18
+ const endTime = getNowCstDateTime();
19
+ recordOpLog(LogType.GetJobStatus, startTime, endTime, LogStatus.Success);
15
20
  return formatTextToolResult(JSON.stringify(res, null, 2), 'get job detail');
16
21
  }
17
22
  catch (error) {
23
+ const endTime = getNowCstDateTime();
24
+ recordOpLog(LogType.GetJobStatus, startTime, endTime, LogStatus.Failed);
18
25
  return formatToolError(error, 'get job detail error!');
19
26
  }
20
27
  });
@@ -24,16 +31,24 @@ export default function registerJob(server) {
24
31
  -limit: 限制数量
25
32
  -state: 查询任务状态, (0为等待中,1为执行中,2为关闭,3为入库中,4正在初始化, 5初始化完成 可多选,格式为1,2,3)`, {
26
33
  mcpConfigFile: z.string(),
27
- offset: z.number().default(0).describe("偏移量"),
28
- limit: z.number().default(10).describe("限制数量"),
29
- state: z.string().default("0,1,2,3,4,5").describe("查询任务状态, (0为等待中,1为执行中,2为关闭,3为入库中,4正在初始化, 5初始化完成 可多选,格式为1,2,3)")
34
+ offset: z.number().default(0).describe('偏移量'),
35
+ limit: z.number().default(10).describe('限制数量'),
36
+ state: z
37
+ .string()
38
+ .default('0,1,2,3,4,5')
39
+ .describe('查询任务状态, (0为等待中,1为执行中,2为关闭,3为入库中,4正在初始化, 5初始化完成 可多选,格式为1,2,3)'),
30
40
  }, async ({ mcpConfigFile, offset, limit, state }) => {
31
41
  await TcaClient.initConfig(mcpConfigFile);
42
+ const startTime = getNowCstDateTime();
32
43
  try {
33
44
  const res = await getJobList(offset, limit, state);
45
+ const endTime = getNowCstDateTime();
46
+ recordOpLog(LogType.GetJobStatus, startTime, endTime, LogStatus.Success);
34
47
  return formatTextToolResult(JSON.stringify(res, null, 2), 'get job list');
35
48
  }
36
49
  catch (error) {
50
+ const endTime = getNowCstDateTime();
51
+ recordOpLog(LogType.GetJobStatus, startTime, endTime, LogStatus.Failed, String(error));
37
52
  return formatToolError(error, 'get job list error!');
38
53
  }
39
54
  });
@@ -1,22 +1,32 @@
1
- import { z } from "zod";
2
- import { startScan } from "../api/scan.js";
3
- import { formatTextToolResult, formatToolError } from "../utils/resp.js";
4
- import TcaClient from "../api/client.js";
1
+ import { z } from 'zod';
2
+ import { startScan } from '../api/scan.js';
3
+ import { formatTextToolResult, formatToolError } from '../utils/resp.js';
4
+ import TcaClient from '../api/client.js';
5
+ import { recordOpLog, LogType, LogStatus } from '../api/oplog.js';
6
+ import { getNowCstDateTime } from '../utils/time.js';
5
7
  export default function registerStartScan(server) {
6
8
  server.tool('start_scan', `启动tca代码分析 参数如下:
7
9
  -mcpConfigFile: mcp配置文件路径,当前项目根目录查找 tca-mcp.ini(绝对路径)
8
10
  -incrScan: 是否增量扫描
9
11
  -forceCreate: 如果已经存在扫描结果,是否强制启动新扫描`, {
10
12
  mcpConfigFile: z.string(),
11
- incrScan: z.boolean().default(false).describe("是否增量扫描"),
12
- forceCreate: z.boolean().default(false).describe("如果已经存在扫描结果,是否强制启动新扫描"),
13
+ incrScan: z.boolean().default(false).describe('是否增量扫描'),
14
+ forceCreate: z
15
+ .boolean()
16
+ .default(false)
17
+ .describe('如果已经存在扫描结果,是否强制启动新扫描'),
13
18
  }, async ({ mcpConfigFile, incrScan, forceCreate }) => {
14
19
  await TcaClient.initConfig(mcpConfigFile);
20
+ const startTime = getNowCstDateTime();
15
21
  try {
16
22
  const res = await startScan(incrScan, forceCreate);
23
+ const endTime = getNowCstDateTime();
24
+ recordOpLog(LogType.ScanRemote, startTime, endTime, LogStatus.Success, 'mcp无法获取到返回结果, endTime仅调用接口的结束时间');
17
25
  return formatTextToolResult(JSON.stringify(res, null, 2), 'start tca scan');
18
26
  }
19
27
  catch (error) {
28
+ const endTime = getNowCstDateTime();
29
+ recordOpLog(LogType.ScanRemote, startTime, endTime, LogStatus.Failed, String(error), { incrScan: incrScan, forceCreate: forceCreate });
20
30
  return formatToolError(error, 'start tca scan error!');
21
31
  }
22
32
  });
@@ -0,0 +1,6 @@
1
+ import { format } from 'date-fns';
2
+ export function getNowCstDateTime() {
3
+ var now = new Date();
4
+ now.setHours(now.getHours() + 8);
5
+ return format(now, 'yyyy-MM-dd HH:mm:ss');
6
+ }
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "tca-mcp-server",
3
- "version": "1.2.0",
3
+ "version": "1.3.2",
4
4
  "description": "",
5
5
  "main": "./dist/stdio.js",
6
6
  "scripts": {
7
7
  "build": "tsc",
8
8
  "test": "echo \"Error: no test specified\" && exit 1",
9
- "start": "node dist/stdio.js"
9
+ "start": "node dist/stdio.js",
10
+ "prepare": "husky"
10
11
  },
11
12
  "bin": {
12
13
  "tca-mcp-stdio": "dist/stdio.js"
@@ -17,11 +18,25 @@
17
18
  "devDependencies": {
18
19
  "@types/ini": "^4.1.1",
19
20
  "@types/node": "^24.0.1",
21
+ "husky": "^9.1.7",
22
+ "lint-staged": "^16.1.2",
23
+ "prettier": "^3.6.2",
20
24
  "ts-node": "^10.9.2",
21
25
  "typescript": "^5.8.3"
22
26
  },
23
27
  "dependencies": {
24
28
  "@modelcontextprotocol/sdk": "^1.12.1",
29
+ "date-fns": "^4.1.0",
25
30
  "ini": "^5.0.0"
31
+ },
32
+ "lint-staged": {
33
+ "*.{js,jsx,ts,tsx}": [
34
+ "prettier --write",
35
+ "prettier --check"
36
+ ],
37
+ "*.{html,css,md}": [
38
+ "prettier --write",
39
+ "prettier --check"
40
+ ]
26
41
  }
27
42
  }
@@ -0,0 +1,87 @@
1
+ import TcaClient from './client.js';
2
+
3
+ const opPluginType = 2;
4
+
5
+ // 操作类型枚举,目前只有scan_remote
6
+ export enum LogType {
7
+ ScanRemote = 'scan_remote',
8
+ GetReport = 'get_report',
9
+ GetIssue = 'get_issues',
10
+ GetAiSug = 'get_ai_sug',
11
+ GetJobStatus = 'get_job_status',
12
+ }
13
+
14
+ // 状态枚举, 1 成功,2 失败
15
+ export enum LogStatus {
16
+ Success = 1,
17
+ Failed = 2,
18
+ }
19
+
20
+ /**
21
+ * 操作日志数据接口
22
+ */
23
+ export interface OpLogData {
24
+ op_action: LogType;
25
+ start_time: string;
26
+ status?: LogStatus;
27
+ end_time?: string;
28
+ result_msg?: string;
29
+ op_plugin_type: number;
30
+ op_plugin_data?: Record<string, any>;
31
+ }
32
+
33
+ /**
34
+ * 记录MCP操作日志(带超时控制,不影响调用方)
35
+ * @param opType 操作类型
36
+ * @param startTime 开始时间
37
+ * @param endTime 结束时间(可选)
38
+ * @param status 操作状态(可选)
39
+ * @param resultMsg 结果信息(可选)
40
+ * @param opPluginData 插件操作数据(可选)
41
+ * @param timeout 超时时间,默认5000ms
42
+ */
43
+ export function recordOpLog(
44
+ opType: LogType,
45
+ startTime: string,
46
+ endTime?: string,
47
+ status?: LogStatus,
48
+ resultMsg?: string,
49
+ opPluginData?: Record<string, any>,
50
+ timeout: number = 5000
51
+ ): void {
52
+ // IIFE,异步代码,不阻塞调用方
53
+ // 增加try catch,捕获异常但不抛出,仅输出错误,不影响主流程
54
+ if (TcaClient.getInstance().runInOA()) {
55
+ return;
56
+ }
57
+
58
+ (async () => {
59
+ try {
60
+ const clientInst = TcaClient.getInstance();
61
+
62
+ if (!clientInst.runInOA()) {
63
+ const { orgSid } = TcaClient.getcommonParams();
64
+ const path = `/server/main/api/orgs/${orgSid}/oplogs/pluginoprecords/`;
65
+
66
+ const logData: OpLogData = {
67
+ op_action: opType,
68
+ start_time: startTime,
69
+ op_plugin_type: opPluginType,
70
+ ...(endTime && { end_time: endTime }),
71
+ ...(status && { status }),
72
+ ...(resultMsg && { result_msg: resultMsg }),
73
+ ...(opPluginData && { op_plugin_data: opPluginData }),
74
+ };
75
+
76
+ const timeoutPromise = new Promise((_, reject) =>
77
+ setTimeout(() => reject(new Error('日志记录超时')), timeout)
78
+ );
79
+
80
+ await Promise.race([clientInst.request(path, 'POST', logData), timeoutPromise]);
81
+ }
82
+ } catch (error) {
83
+ // 仅在输出错误,但不影响调用方
84
+ console.error('记录操作日志失败:', error);
85
+ }
86
+ })();
87
+ }
package/src/stdio.ts CHANGED
@@ -1,20 +1,20 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
- import { registerTools } from "./tools/index.js";
3
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
4
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
5
+ import { registerTools } from './tools/index.js';
6
6
 
7
7
  // Create an MCP server
8
8
  const server = new McpServer({
9
- name: "腾讯云代码分析(TCA)",
10
- version: "1.0.0"
9
+ name: '腾讯云代码分析(TCA)',
10
+ version: '1.3.2',
11
11
  });
12
12
 
13
13
  // 从命令行获取参数,如果 --oa参数存在,则表示运行在OA环境,否则运行在SaaS环境
14
- const env = process.argv.includes("--oa") ? "oa" : "saas";
14
+ const env = process.argv.includes('--oa') ? 'oa' : 'saas';
15
15
  process.env.TCA_ENV = env;
16
16
 
17
17
  registerTools(server);
18
18
  const transport = new StdioServerTransport();
19
19
  await server.connect(transport);
20
- console.log("Server started");
20
+ console.log('Server started');
@@ -1,9 +1,10 @@
1
- import { z } from "zod";
2
- import TcaClient, { webUrlOA } from "../api/client.js";
3
- import { format } from "path";
4
- import { formatTextToolResult, formatToolError } from "../utils/resp.js";
5
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
- import { getIssueList } from "../api/issue.js";
1
+ import { z } from 'zod';
2
+ import TcaClient, { webUrlOA } from '../api/client.js';
3
+ import { formatTextToolResult, formatToolError } from '../utils/resp.js';
4
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
5
+ import { getIssueList } from '../api/issue.js';
6
+ import { recordOpLog, LogType, LogStatus } from '../api/oplog.js';
7
+ import { getNowCstDateTime } from '../utils/time.js';
7
8
 
8
9
  export default function registerIssueReport(server: McpServer) {
9
10
  server.tool(
@@ -17,21 +18,31 @@ export default function registerIssueReport(server: McpServer) {
17
18
  `,
18
19
  {
19
20
  mcpConfigFile: z.string(),
20
- state: z.string().default("0,1,2,3,4,5"),
21
- severity: z.string().default("0,1,2"),
21
+ state: z.string().default('0,1,2,3,4,5'),
22
+ severity: z.string().default('0,1,2'),
22
23
  offset: z.number().default(0),
23
24
  limit: z.number().default(10),
24
25
  },
25
26
  async ({ mcpConfigFile, state, severity, offset, limit }) => {
26
- await TcaClient.initConfig(mcpConfigFile)
27
+ await TcaClient.initConfig(mcpConfigFile);
28
+ const startTime = getNowCstDateTime();
27
29
  try {
28
- const res = await getIssueList(state, severity, offset, limit)
30
+ const res = await getIssueList(state, severity, offset, limit);
31
+ const endTime = getNowCstDateTime();
32
+ recordOpLog(LogType.GetIssue, startTime, endTime, LogStatus.Success);
29
33
  return formatTextToolResult(JSON.stringify(res, null, 2), 'get issue list');
30
34
  } catch (error) {
31
- return formatToolError(error, 'get issue list error')
35
+ const endTime = getNowCstDateTime();
36
+ recordOpLog(LogType.GetIssue, startTime, endTime, LogStatus.Failed, String(error), {
37
+ state: state,
38
+ severity: severity,
39
+ offset: offset,
40
+ limit: limit,
41
+ });
42
+ return formatToolError(error, 'get issue list error');
32
43
  }
33
44
  }
34
- )
45
+ );
35
46
 
36
47
  server.tool(
37
48
  'tca_issue_report',
@@ -40,20 +51,24 @@ export default function registerIssueReport(server: McpServer) {
40
51
  -jobId: 任务id, 启动分析任务后,从返回结果中获取`,
41
52
  {
42
53
  mcpConfigFile: z.string(),
43
- jobId: z.string().describe("jobId"),
54
+ jobId: z.string().describe('jobId'),
44
55
  },
45
56
  async ({ mcpConfigFile, jobId }) => {
46
- await TcaClient.initConfig(mcpConfigFile)
47
- const clientInst = TcaClient.getInstance()
57
+ await TcaClient.initConfig(mcpConfigFile);
58
+ const clientInst = TcaClient.getInstance();
59
+ const startTime = getNowCstDateTime();
48
60
  if (clientInst.runInOA()) {
49
- const paramsOA = TcaClient.getcommonOAParams()
50
- const path = `code-analysis/repos/${paramsOA.repoId}/projects/${paramsOA.projectId}/scan-history/${jobId}/result`
51
- return formatTextToolResult(webUrlOA + path, 'tca issue report link')
61
+ const paramsOA = TcaClient.getcommonOAParams();
62
+ const path = `code-analysis/repos/${paramsOA.repoId}/projects/${paramsOA.projectId}/scan-history/${jobId}/result`;
63
+ return formatTextToolResult(webUrlOA + path, 'tca issue report link');
52
64
  } else {
53
- const baseUrl = TcaClient.getInstance().baseUrl
54
- const params = TcaClient.getcommonParams()
55
- const path = `t/${params.orgSid}/p/${params.teamName}/repos/${params.repoId}/projects/${params.projectId}/jobs/${jobId}/result`
56
- return formatTextToolResult(baseUrl + path, 'tca issue report link');
65
+ const baseUrl = TcaClient.getInstance().baseUrl;
66
+ const params = TcaClient.getcommonParams();
67
+ const path = `t/${params.orgSid}/p/${params.teamName}/repos/${params.repoId}/projects/${params.projectId}/jobs/${jobId}/result`;
68
+ const endTime = getNowCstDateTime();
69
+ var reportUrl = baseUrl + path;
70
+ recordOpLog(LogType.GetReport, startTime, endTime, LogStatus.Success, reportUrl);
71
+ return formatTextToolResult(reportUrl, 'tca issue report link');
57
72
  }
58
73
  }
59
74
  );
package/src/tools/job.ts CHANGED
@@ -1,8 +1,10 @@
1
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { z } from "zod";
3
- import TcaClient from "../api/client.js";
4
- import { formatTextToolResult, formatToolError } from "../utils/resp.js";
5
- import { getJobDetail, getJobList } from "../api/job.js";
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { z } from 'zod';
3
+ import TcaClient from '../api/client.js';
4
+ import { formatTextToolResult, formatToolError } from '../utils/resp.js';
5
+ import { getJobDetail, getJobList } from '../api/job.js';
6
+ import { recordOpLog, LogType, LogStatus } from '../api/oplog.js';
7
+ import { getNowCstDateTime } from '../utils/time.js';
6
8
 
7
9
  export default function registerJob(server: McpServer) {
8
10
  server.tool(
@@ -12,40 +14,61 @@ export default function registerJob(server: McpServer) {
12
14
  -jobId: 任务id`,
13
15
  {
14
16
  mcpConfigFile: z.string(),
15
- jobId: z.string().describe("任务id")
17
+ jobId: z.string().describe('任务id'),
16
18
  },
17
19
  async ({ mcpConfigFile, jobId }) => {
18
- await TcaClient.initConfig(mcpConfigFile)
20
+ await TcaClient.initConfig(mcpConfigFile);
21
+ const startTime = getNowCstDateTime();
19
22
  try {
20
- const res = await getJobDetail(jobId)
23
+ const res = await getJobDetail(jobId);
24
+ const endTime = getNowCstDateTime();
25
+ recordOpLog(LogType.GetJobStatus, startTime, endTime, LogStatus.Success);
21
26
  return formatTextToolResult(JSON.stringify(res, null, 2), 'get job detail');
22
27
  } catch (error) {
23
- return formatToolError(error, 'get job detail error!')
28
+ const endTime = getNowCstDateTime();
29
+ recordOpLog(LogType.GetJobStatus, startTime, endTime, LogStatus.Failed);
30
+ return formatToolError(error, 'get job detail error!');
24
31
  }
25
32
  }
26
- )
27
-
33
+ );
34
+
28
35
  server.tool(
29
- 'job_list',
36
+ 'job_list',
30
37
  `获取当前任务列表, 参数如下:
31
38
  -mcpConfigFile: mcp配置文件路径,当前项目根目录查找 tca-mcp.ini(绝对路径)
32
39
  -offset: 偏移量
33
40
  -limit: 限制数量
34
- -state: 查询任务状态, (0为等待中,1为执行中,2为关闭,3为入库中,4正在初始化, 5初始化完成 可多选,格式为1,2,3)`,
41
+ -state: 查询任务状态, (0为等待中,1为执行中,2为关闭,3为入库中,4正在初始化, 5初始化完成 可多选,格式为1,2,3)`,
35
42
  {
36
43
  mcpConfigFile: z.string(),
37
- offset: z.number().default(0).describe("偏移量"),
38
- limit: z.number().default(10).describe("限制数量"),
39
- state: z.string().default("0,1,2,3,4,5").describe("查询任务状态, (0为等待中,1为执行中,2为关闭,3为入库中,4正在初始化, 5初始化完成 可多选,格式为1,2,3)")
44
+ offset: z.number().default(0).describe('偏移量'),
45
+ limit: z.number().default(10).describe('限制数量'),
46
+ state: z
47
+ .string()
48
+ .default('0,1,2,3,4,5')
49
+ .describe(
50
+ '查询任务状态, (0为等待中,1为执行中,2为关闭,3为入库中,4正在初始化, 5初始化完成 可多选,格式为1,2,3)'
51
+ ),
40
52
  },
41
53
  async ({ mcpConfigFile, offset, limit, state }) => {
42
- await TcaClient.initConfig(mcpConfigFile)
54
+ await TcaClient.initConfig(mcpConfigFile);
55
+ const startTime = getNowCstDateTime();
43
56
  try {
44
- const res = await getJobList(offset, limit, state)
57
+ const res = await getJobList(offset, limit, state);
58
+ const endTime = getNowCstDateTime();
59
+ recordOpLog(LogType.GetJobStatus, startTime, endTime, LogStatus.Success);
45
60
  return formatTextToolResult(JSON.stringify(res, null, 2), 'get job list');
46
61
  } catch (error) {
47
- return formatToolError(error, 'get job list error!')
62
+ const endTime = getNowCstDateTime();
63
+ recordOpLog(
64
+ LogType.GetJobStatus,
65
+ startTime,
66
+ endTime,
67
+ LogStatus.Failed,
68
+ String(error)
69
+ );
70
+ return formatToolError(error, 'get job list error!');
48
71
  }
49
72
  }
50
- )
73
+ );
51
74
  }
package/src/tools/scan.ts CHANGED
@@ -1,9 +1,10 @@
1
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { z } from "zod";
3
- import { startScan } from "../api/scan.js";
4
- import { formatTextToolResult, formatToolError } from "../utils/resp.js";
5
- import TcaClient from "../api/client.js";
6
-
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { z } from 'zod';
3
+ import { startScan } from '../api/scan.js';
4
+ import { formatTextToolResult, formatToolError } from '../utils/resp.js';
5
+ import TcaClient from '../api/client.js';
6
+ import { recordOpLog, LogType, LogStatus } from '../api/oplog.js';
7
+ import { getNowCstDateTime } from '../utils/time.js';
7
8
 
8
9
  export default function registerStartScan(server: McpServer) {
9
10
  server.tool(
@@ -14,16 +15,37 @@ export default function registerStartScan(server: McpServer) {
14
15
  -forceCreate: 如果已经存在扫描结果,是否强制启动新扫描`,
15
16
  {
16
17
  mcpConfigFile: z.string(),
17
- incrScan: z.boolean().default(false).describe("是否增量扫描"),
18
- forceCreate: z.boolean().default(false).describe("如果已经存在扫描结果,是否强制启动新扫描"),
18
+ incrScan: z.boolean().default(false).describe('是否增量扫描'),
19
+ forceCreate: z
20
+ .boolean()
21
+ .default(false)
22
+ .describe('如果已经存在扫描结果,是否强制启动新扫描'),
19
23
  },
20
24
  async ({ mcpConfigFile, incrScan, forceCreate }) => {
21
- await TcaClient.initConfig(mcpConfigFile)
25
+ await TcaClient.initConfig(mcpConfigFile);
26
+ const startTime = getNowCstDateTime();
22
27
  try {
23
- const res = await startScan(incrScan, forceCreate)
28
+ const res = await startScan(incrScan, forceCreate);
29
+ const endTime = getNowCstDateTime();
30
+ recordOpLog(
31
+ LogType.ScanRemote,
32
+ startTime,
33
+ endTime,
34
+ LogStatus.Success,
35
+ 'mcp无法获取到返回结果, endTime仅调用接口的结束时间'
36
+ );
24
37
  return formatTextToolResult(JSON.stringify(res, null, 2), 'start tca scan');
25
38
  } catch (error) {
26
- return formatToolError(error, 'start tca scan error!')
39
+ const endTime = getNowCstDateTime();
40
+ recordOpLog(
41
+ LogType.ScanRemote,
42
+ startTime,
43
+ endTime,
44
+ LogStatus.Failed,
45
+ String(error),
46
+ { incrScan: incrScan, forceCreate: forceCreate }
47
+ );
48
+ return formatToolError(error, 'start tca scan error!');
27
49
  }
28
50
  }
29
51
  );
@@ -0,0 +1,7 @@
1
+ import { format } from 'date-fns';
2
+
3
+ export function getNowCstDateTime(): string {
4
+ var now = new Date();
5
+ now.setHours(now.getHours() + 8);
6
+ return format(now, 'yyyy-MM-dd HH:mm:ss');
7
+ }