sonarqube-issue-mcp 0.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/README.md +246 -0
- package/bin/sonarqube-issue-mcp.js +10 -0
- package/dist/config.d.ts +28 -0
- package/dist/config.js +90 -0
- package/dist/config.js.map +1 -0
- package/dist/errors.d.ts +37 -0
- package/dist/errors.js +67 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/project-ref.d.ts +44 -0
- package/dist/project-ref.js +120 -0
- package/dist/project-ref.js.map +1 -0
- package/dist/server.d.ts +22 -0
- package/dist/server.js +240 -0
- package/dist/server.js.map +1 -0
- package/dist/sonarqube/api-types.d.ts +339 -0
- package/dist/sonarqube/api-types.js +2 -0
- package/dist/sonarqube/api-types.js.map +1 -0
- package/dist/sonarqube/client.d.ts +183 -0
- package/dist/sonarqube/client.js +411 -0
- package/dist/sonarqube/client.js.map +1 -0
- package/dist/sonarqube/service.d.ts +106 -0
- package/dist/sonarqube/service.js +472 -0
- package/dist/sonarqube/service.js.map +1 -0
- package/dist/types.d.ts +207 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +37 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { SonarQubeMcpError } from "./errors.js";
|
|
2
|
+
/**
|
|
3
|
+
* 当前实现仅支持从这些 SonarQube 页面入口解析项目上下文。
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* v1 明确拒绝其他页面入口,避免 URL 语义不稳定带来兼容成本。
|
|
7
|
+
*/
|
|
8
|
+
const SUPPORTED_PATHS = new Set(["/dashboard", "/project/overview"]);
|
|
9
|
+
/**
|
|
10
|
+
* 从 SonarQube dashboard URL 提取项目定位信息。
|
|
11
|
+
*
|
|
12
|
+
* @param projectUrl - 用户传入的 SonarQube 项目链接。
|
|
13
|
+
* @returns 规范化后的项目上下文。
|
|
14
|
+
*
|
|
15
|
+
* 这是整个 MCP 的唯一输入入口,因此这里会做严格校验,确保后续 API 请求有稳定上下文。
|
|
16
|
+
*
|
|
17
|
+
* @throws {SonarQubeMcpError}
|
|
18
|
+
* 当 URL 非法、协议不支持、路径不支持或缺少 `id` 参数时抛出。
|
|
19
|
+
*/
|
|
20
|
+
export function parseProjectUrl(projectUrl) {
|
|
21
|
+
let url;
|
|
22
|
+
try {
|
|
23
|
+
url = new URL(projectUrl);
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
throw new SonarQubeMcpError("VALIDATION", `项目链接不是合法 URL: ${projectUrl}`, {
|
|
27
|
+
cause: error
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
31
|
+
throw new SonarQubeMcpError("VALIDATION", `项目链接必须使用 http 或 https 协议: ${projectUrl}`);
|
|
32
|
+
}
|
|
33
|
+
if (!SUPPORTED_PATHS.has(url.pathname)) {
|
|
34
|
+
throw new SonarQubeMcpError("VALIDATION", `暂只接受 SonarQube dashboard/project 链接,当前路径 "${url.pathname}" 不支持。`);
|
|
35
|
+
}
|
|
36
|
+
const projectKey = url.searchParams.get("id")?.trim();
|
|
37
|
+
if (!projectKey) {
|
|
38
|
+
throw new SonarQubeMcpError("VALIDATION", "项目链接缺少 id 参数,无法解析 SonarQube project key。");
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
origin: url.origin,
|
|
42
|
+
projectKey,
|
|
43
|
+
branch: url.searchParams.get("branch")?.trim() || null,
|
|
44
|
+
pullRequest: url.searchParams.get("pullRequest")?.trim() || null,
|
|
45
|
+
dashboardUrl: url.toString()
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 将 branch / pullRequest 上下文透传到任意 SonarQube API 查询参数中。
|
|
50
|
+
*
|
|
51
|
+
* @param searchParams - 已有查询参数。
|
|
52
|
+
* @param projectRef - 解析后的项目上下文。
|
|
53
|
+
* @returns 追加了分支/PR 上下文的新查询参数对象。
|
|
54
|
+
*/
|
|
55
|
+
export function withProjectContext(searchParams, projectRef) {
|
|
56
|
+
const next = new URLSearchParams(searchParams);
|
|
57
|
+
if (projectRef.branch) {
|
|
58
|
+
next.set("branch", projectRef.branch);
|
|
59
|
+
}
|
|
60
|
+
if (projectRef.pullRequest) {
|
|
61
|
+
next.set("pullRequest", projectRef.pullRequest);
|
|
62
|
+
}
|
|
63
|
+
return next;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 构造项目级 SonarQube 浏览深链。
|
|
67
|
+
*
|
|
68
|
+
* @param projectRef - 解析后的项目上下文。
|
|
69
|
+
* @returns 指向项目 dashboard 的可点击链接。
|
|
70
|
+
*/
|
|
71
|
+
export function buildProjectBrowseUrl(projectRef) {
|
|
72
|
+
const url = new URL("/dashboard", projectRef.origin);
|
|
73
|
+
url.searchParams.set("id", projectRef.projectKey);
|
|
74
|
+
if (projectRef.branch) {
|
|
75
|
+
url.searchParams.set("branch", projectRef.branch);
|
|
76
|
+
}
|
|
77
|
+
if (projectRef.pullRequest) {
|
|
78
|
+
url.searchParams.set("pullRequest", projectRef.pullRequest);
|
|
79
|
+
}
|
|
80
|
+
return url.toString();
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* 构造单条 issue 的 SonarQube 页面深链。
|
|
84
|
+
*
|
|
85
|
+
* @param projectRef - 解析后的项目上下文。
|
|
86
|
+
* @param issueKey - SonarQube issue key。
|
|
87
|
+
* @returns 指向 issue 详情面板的可点击链接。
|
|
88
|
+
*/
|
|
89
|
+
export function buildIssueBrowseUrl(projectRef, issueKey) {
|
|
90
|
+
const url = new URL("/project/issues", projectRef.origin);
|
|
91
|
+
url.searchParams.set("id", projectRef.projectKey);
|
|
92
|
+
url.searchParams.set("open", issueKey);
|
|
93
|
+
if (projectRef.branch) {
|
|
94
|
+
url.searchParams.set("branch", projectRef.branch);
|
|
95
|
+
}
|
|
96
|
+
if (projectRef.pullRequest) {
|
|
97
|
+
url.searchParams.set("pullRequest", projectRef.pullRequest);
|
|
98
|
+
}
|
|
99
|
+
return url.toString();
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* 构造单条 security hotspot 的 SonarQube 页面深链。
|
|
103
|
+
*
|
|
104
|
+
* @param projectRef - 解析后的项目上下文。
|
|
105
|
+
* @param hotspotKey - SonarQube hotspot key。
|
|
106
|
+
* @returns 指向 hotspot 详情面板的可点击链接。
|
|
107
|
+
*/
|
|
108
|
+
export function buildHotspotBrowseUrl(projectRef, hotspotKey) {
|
|
109
|
+
const url = new URL("/security_hotspots", projectRef.origin);
|
|
110
|
+
url.searchParams.set("id", projectRef.projectKey);
|
|
111
|
+
url.searchParams.set("hotspots", hotspotKey);
|
|
112
|
+
if (projectRef.branch) {
|
|
113
|
+
url.searchParams.set("branch", projectRef.branch);
|
|
114
|
+
}
|
|
115
|
+
if (projectRef.pullRequest) {
|
|
116
|
+
url.searchParams.set("pullRequest", projectRef.pullRequest);
|
|
117
|
+
}
|
|
118
|
+
return url.toString();
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=project-ref.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-ref.js","sourceRoot":"","sources":["../src/project-ref.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { AppConfig } from "./config.js";
|
|
3
|
+
/**
|
|
4
|
+
* MCP 服务启动后需要保留的运行期对象。
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* 当前只需要返回 MCP 服务实例本身。
|
|
8
|
+
*/
|
|
9
|
+
export interface ServerContext {
|
|
10
|
+
/** 已注册完 tools 的 MCP 服务实例。 */
|
|
11
|
+
server: McpServer;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 创建并注册整个 MCP 服务器。
|
|
15
|
+
*
|
|
16
|
+
* @param config - 运行期配置。
|
|
17
|
+
* @returns 已完成 tool 注册的 MCP 服务及其配套运行时对象。
|
|
18
|
+
*
|
|
19
|
+
* @remarks
|
|
20
|
+
* 这里集中完成 schema 定义、tool 注册和运行时依赖装配。
|
|
21
|
+
*/
|
|
22
|
+
export declare function createServer(config: AppConfig): ServerContext;
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import * as z from "zod/v4";
|
|
3
|
+
import { formatErrorForText, normalizeError } from "./errors.js";
|
|
4
|
+
import { SonarQubeFindingService } from "./sonarqube/service.js";
|
|
5
|
+
/**
|
|
6
|
+
* 与 `TextRange` 对应的输出 schema。
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* 结构化输出 schema 负责约束 MCP tool 的 `structuredContent` 形状。
|
|
10
|
+
*/
|
|
11
|
+
const textRangeSchema = z.object({
|
|
12
|
+
startLine: z.number().int(),
|
|
13
|
+
endLine: z.number().int(),
|
|
14
|
+
startOffset: z.number().int(),
|
|
15
|
+
endOffset: z.number().int()
|
|
16
|
+
});
|
|
17
|
+
/**
|
|
18
|
+
* 与 `FindingImpact` 对应的输出 schema。
|
|
19
|
+
*
|
|
20
|
+
* @remarks
|
|
21
|
+
* 保持与 `types.ts` 中的结构一致,避免文本输出和结构化输出脱节。
|
|
22
|
+
*/
|
|
23
|
+
const findingImpactSchema = z.object({
|
|
24
|
+
softwareQuality: z.string(),
|
|
25
|
+
severity: z.string()
|
|
26
|
+
});
|
|
27
|
+
/**
|
|
28
|
+
* 与 `ProjectInfo` 对应的输出 schema。
|
|
29
|
+
*
|
|
30
|
+
* @remarks
|
|
31
|
+
* 所有工具的项目级返回都通过它统一约束。
|
|
32
|
+
*/
|
|
33
|
+
const projectInfoSchema = z.object({
|
|
34
|
+
key: z.string(),
|
|
35
|
+
name: z.string(),
|
|
36
|
+
qualifier: z.string().nullable(),
|
|
37
|
+
branch: z.string().nullable(),
|
|
38
|
+
pullRequest: z.string().nullable(),
|
|
39
|
+
dashboardUrl: z.string().url(),
|
|
40
|
+
browseUrl: z.string().url(),
|
|
41
|
+
serverVersion: z.string()
|
|
42
|
+
});
|
|
43
|
+
/**
|
|
44
|
+
* 与 `FindingSummary` 对应的输出 schema。
|
|
45
|
+
*
|
|
46
|
+
* @remarks
|
|
47
|
+
* `issue` 与 `hotspot` 在结构化输出侧共用这一套 schema。
|
|
48
|
+
*/
|
|
49
|
+
const findingSummarySchema = z.object({
|
|
50
|
+
key: z.string(),
|
|
51
|
+
kind: z.enum(["issue", "hotspot"]),
|
|
52
|
+
category: z.enum(["security", "reliability", "security-hotspot"]),
|
|
53
|
+
ruleKey: z.string(),
|
|
54
|
+
ruleName: z.string().nullable(),
|
|
55
|
+
message: z.string(),
|
|
56
|
+
status: z.string(),
|
|
57
|
+
resolution: z.string().nullable(),
|
|
58
|
+
severity: z.string().nullable(),
|
|
59
|
+
vulnerabilityProbability: z.string().nullable(),
|
|
60
|
+
impacts: z.array(findingImpactSchema),
|
|
61
|
+
cleanCodeAttribute: z.string().nullable(),
|
|
62
|
+
file: z.string().nullable(),
|
|
63
|
+
line: z.number().int().nullable(),
|
|
64
|
+
textRange: textRangeSchema.nullable(),
|
|
65
|
+
author: z.string().nullable(),
|
|
66
|
+
assignee: z.string().nullable(),
|
|
67
|
+
tags: z.array(z.string()),
|
|
68
|
+
createdAt: z.string(),
|
|
69
|
+
updatedAt: z.string(),
|
|
70
|
+
sonarUrl: z.string().url(),
|
|
71
|
+
ruleDescription: z.string().nullable(),
|
|
72
|
+
riskDescription: z.string().nullable(),
|
|
73
|
+
fixRecommendations: z.string().nullable(),
|
|
74
|
+
vulnerabilityDescription: z.string().nullable()
|
|
75
|
+
});
|
|
76
|
+
/**
|
|
77
|
+
* `sonarqube_get_project_findings` 的结构化输出 schema。
|
|
78
|
+
*
|
|
79
|
+
* @remarks
|
|
80
|
+
* 与 `ProjectFindingsResult` 一一对应。
|
|
81
|
+
*/
|
|
82
|
+
const projectFindingsResultSchema = z.object({
|
|
83
|
+
project: projectInfoSchema,
|
|
84
|
+
summary: z.object({
|
|
85
|
+
securityIssueCount: z.number().int(),
|
|
86
|
+
reliabilityIssueCount: z.number().int(),
|
|
87
|
+
securityHotspotCount: z.number().int(),
|
|
88
|
+
totalFindings: z.number().int(),
|
|
89
|
+
detailLevel: z.enum(["standard", "full"])
|
|
90
|
+
}),
|
|
91
|
+
securityIssues: z.array(findingSummarySchema),
|
|
92
|
+
reliabilityIssues: z.array(findingSummarySchema),
|
|
93
|
+
securityHotspots: z.array(findingSummarySchema)
|
|
94
|
+
});
|
|
95
|
+
/**
|
|
96
|
+
* `sonarqube_get_finding_detail` 的结构化输出 schema。
|
|
97
|
+
*
|
|
98
|
+
* @remarks
|
|
99
|
+
* 与 `FindingDetail` 一一对应。
|
|
100
|
+
*/
|
|
101
|
+
const findingDetailResultSchema = z.object({
|
|
102
|
+
project: projectInfoSchema,
|
|
103
|
+
summary: findingSummarySchema,
|
|
104
|
+
comments: z.array(z.unknown()),
|
|
105
|
+
changelog: z.array(z.unknown()),
|
|
106
|
+
flows: z.array(z.unknown()),
|
|
107
|
+
raw: z.unknown()
|
|
108
|
+
});
|
|
109
|
+
/**
|
|
110
|
+
* 创建并注册整个 MCP 服务器。
|
|
111
|
+
*
|
|
112
|
+
* @param config - 运行期配置。
|
|
113
|
+
* @returns 已完成 tool 注册的 MCP 服务及其配套运行时对象。
|
|
114
|
+
*
|
|
115
|
+
* @remarks
|
|
116
|
+
* 这里集中完成 schema 定义、tool 注册和运行时依赖装配。
|
|
117
|
+
*/
|
|
118
|
+
export function createServer(config) {
|
|
119
|
+
const server = new McpServer({
|
|
120
|
+
name: "sonarqube-issue-mcp",
|
|
121
|
+
version: "0.0.1"
|
|
122
|
+
});
|
|
123
|
+
const service = new SonarQubeFindingService(config);
|
|
124
|
+
server.registerTool("sonarqube_get_project_findings", {
|
|
125
|
+
title: "Get SonarQube Project Findings",
|
|
126
|
+
description: "根据 SonarQube 项目 dashboard URL,返回当前待处理的 Security、Reliability 和 Security Hotspots 问题。",
|
|
127
|
+
inputSchema: {
|
|
128
|
+
projectUrl: z.string().url().describe("SonarQube 项目 dashboard URL"),
|
|
129
|
+
detailLevel: z
|
|
130
|
+
.enum(["standard", "full"])
|
|
131
|
+
.optional()
|
|
132
|
+
.describe("standard 返回标准字段,full 额外带规则说明")
|
|
133
|
+
},
|
|
134
|
+
outputSchema: projectFindingsResultSchema
|
|
135
|
+
}, async ({ projectUrl, detailLevel }) => {
|
|
136
|
+
try {
|
|
137
|
+
const result = await service.getProjectFindings(projectUrl, detailLevel ?? "standard");
|
|
138
|
+
return {
|
|
139
|
+
content: [
|
|
140
|
+
{
|
|
141
|
+
type: "text",
|
|
142
|
+
text: formatProjectFindingsText(result)
|
|
143
|
+
}
|
|
144
|
+
],
|
|
145
|
+
structuredContent: result
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
const normalized = normalizeError(error);
|
|
150
|
+
return {
|
|
151
|
+
isError: true,
|
|
152
|
+
content: [
|
|
153
|
+
{
|
|
154
|
+
type: "text",
|
|
155
|
+
text: formatErrorForText(normalized)
|
|
156
|
+
}
|
|
157
|
+
]
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
/**
|
|
162
|
+
* 单条详情工具:适合上层先取列表,再按 key 追查单项。
|
|
163
|
+
*
|
|
164
|
+
* @remarks
|
|
165
|
+
* 这里复用同一个 service,保持列表与详情口径一致。
|
|
166
|
+
*/
|
|
167
|
+
server.registerTool("sonarqube_get_finding_detail", {
|
|
168
|
+
title: "Get SonarQube Finding Detail",
|
|
169
|
+
description: "根据 SonarQube 项目 dashboard URL 和问题 key,返回单条 issue 或 security hotspot 的完整详情。",
|
|
170
|
+
inputSchema: {
|
|
171
|
+
projectUrl: z.string().url().describe("SonarQube 项目 dashboard URL"),
|
|
172
|
+
kind: z.enum(["issue", "hotspot"]),
|
|
173
|
+
key: z.string().min(1).describe("issue key 或 hotspot key")
|
|
174
|
+
},
|
|
175
|
+
outputSchema: findingDetailResultSchema
|
|
176
|
+
}, async ({ projectUrl, kind, key }) => {
|
|
177
|
+
try {
|
|
178
|
+
const result = await service.getFindingDetail(projectUrl, kind, key);
|
|
179
|
+
return {
|
|
180
|
+
content: [
|
|
181
|
+
{
|
|
182
|
+
type: "text",
|
|
183
|
+
text: formatFindingDetailText(result)
|
|
184
|
+
}
|
|
185
|
+
],
|
|
186
|
+
structuredContent: result
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
const normalized = normalizeError(error);
|
|
191
|
+
return {
|
|
192
|
+
isError: true,
|
|
193
|
+
content: [
|
|
194
|
+
{
|
|
195
|
+
type: "text",
|
|
196
|
+
text: formatErrorForText(normalized)
|
|
197
|
+
}
|
|
198
|
+
]
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
return {
|
|
203
|
+
server
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* 把结构化项目结果压缩成一段适合 MCP 聊天窗口展示的摘要文本。
|
|
208
|
+
*
|
|
209
|
+
* @param result - 项目级结构化结果。
|
|
210
|
+
* @returns 适合直接展示的多行摘要文本。
|
|
211
|
+
*/
|
|
212
|
+
function formatProjectFindingsText(result) {
|
|
213
|
+
return [
|
|
214
|
+
`项目: ${result.project.name} (${result.project.key})`,
|
|
215
|
+
`SonarQube 版本: ${result.project.serverVersion}`,
|
|
216
|
+
`Security: ${result.summary.securityIssueCount}`,
|
|
217
|
+
`Reliability: ${result.summary.reliabilityIssueCount}`,
|
|
218
|
+
`Security Hotspots: ${result.summary.securityHotspotCount}`,
|
|
219
|
+
`总计: ${result.summary.totalFindings}`,
|
|
220
|
+
`详情级别: ${result.summary.detailLevel}`
|
|
221
|
+
].join("\n");
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* 把单条详情压缩成一段适合 MCP 聊天窗口展示的摘要文本。
|
|
225
|
+
*
|
|
226
|
+
* @param result - 单条问题的结构化详情。
|
|
227
|
+
* @returns 适合直接展示的多行摘要文本。
|
|
228
|
+
*/
|
|
229
|
+
function formatFindingDetailText(result) {
|
|
230
|
+
return [
|
|
231
|
+
`项目: ${result.project.name} (${result.project.key})`,
|
|
232
|
+
`问题类型: ${result.summary.category}`,
|
|
233
|
+
`规则: ${result.summary.ruleKey}${result.summary.ruleName ? ` - ${result.summary.ruleName}` : ""}`,
|
|
234
|
+
`状态: ${result.summary.status}`,
|
|
235
|
+
`文件: ${result.summary.file ?? "unknown"}${result.summary.line ? `:${result.summary.line}` : ""}`,
|
|
236
|
+
`更新时间: ${result.summary.updatedAt}`,
|
|
237
|
+
`深链: ${result.summary.sonarUrl}`
|
|
238
|
+
].join("\n");
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|