关于 Sonar MCP Server
Sonar MCP Server 为 AI 编码 Agent 提供对自托管 SonarQube Community Build 26.4+ 实例的只读访问。其精简工具可读取问题、规则说明、代码片段和 Security Hotspots,支持诊断并在本地修改代码。
主要功能
- 01浏览项目、分支和拉取请求
- 02筛选并汇总项目问题
- 03获取问题详情和代码片段
- 04查看规则说明及修复建议
- 05检查 Security Hotspots 和代码流
适用场景
- 01定位项目或文件子目录中的问题
- 02查看分支或拉取请求的检查结果
- 03修改代码前理解相关规则
- 04检查需要人工审查的安全热点
MCP 相关
它通过 stdio 提供 13 种只读 MCP 工具,用于查询 SonarQube 的 web API。服务不会修改 SonarQube 数据,也不会开放入站 HTTP 端口。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
docker run -i --rm -e SONAR_URL -e SONAR_TOKEN -e SONAR_DEFAULT_PROJECT_KEY ghcr.io/igorolv/sonar-mcp-server:0.1.0SONAR_URLBase URL of the SonarQube instance, e.g. https://sonar.example.comSONAR_TOKEN密钥SonarQube user token (My Account -> Security -> Generate Tokens)SONAR_DEFAULT_PROJECT_KEY密钥Project key used when a tool call does not name one本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
ghcr.io/igorolv/sonar-mcp-server:0.1.0stdio工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
Sonar MCP Server
一个本地 MCP 服务,通过 web-api 提供对 SonarQube Community Build(26.4+)实例的只读访问。 它可以让 AI Agent(Claude Code、Cursor、VS Code Copilot 等)获取项目问题列表、问题所在的文件和位置、规则说明、问题周边的源代码片段以及安全热点。
典型场景:“查找并修复某某项目中的 Sonar 问题”——大模型调用 listIssues,并可选择调用 getRule 和 getIssueSnippets,然后在本地编辑文件。
为什么选择此服务
SonarSource 提供了官方 MCP 服务,但它面向 SonarQube 10+(以及 SonarCloud),并假设采用云端部署方式。此服务专为可自托管的 SonarQube Community Build 26.4+ 安装环境构建,支持使用经典的 /api/ web-api。它同时支持 Standard Experience 和 MQR 模式,并能在实例运行于 MQR 模式时处理各软件质量类别的影响。
此外,此服务有意缩小了范围:
- 默认只读。 此服务绝不会在 SonarQube 中创建、更新或删除任何内容——不会将问题标记为误报、不会编辑评论,也不会调用管理端点。SonarQube 中令牌的写入权限无关紧要,因为此服务从不调用这些端点。
- 精选工具集。 此服务不会照搬 SonarQube API 的全部功能,而是提供一组小巧、专注的工具(共 13 个),服务于单一工作流:让 AI Agent 读取 Sonar 的发现结果,并据此修复代码。列出组件、问题和安全热点,深入查看单条发现,获取规则说明,以及提取问题位置周边的源代码片段——仅此而已。为保持工具列表精简且 Agent 的选择明确,“诊断 -> 在本地修复代码”这一闭环之外的所有功能都刻意不予提供。
简而言之:这是一个专注的只读桥梁,将可自托管的 SonarQube Community Build 连接到 AI 编码 Agent。
快速开始
- 安装 JDK 25+。
- 从最新版本下载
sonar-mcp-server.jar, 或自行构建:./gradlew bootJar(参见构建)。此外也发布了一个 Docker 镜像。 - 获取 SonarQube URL 和用户令牌(参见配置)。
- 将 JAR 添加到客户端的 MCP 配置中(参见连接到 AI 客户端)。
对于 Claude Code,只需一条命令:
claude mcp add --scope user -e SONAR_URL=https://sonar.example.com -e SONAR_TOKEN=your_token -- sonar java -jar /path/to/sonar-mcp-server.jar
架构
此服务仅支持 stdio 传输方式。
┌─────────────┐ stdio ┌──────────────────┐ web-api ┌──────────┐
│ AI agent │ <------------> │ sonar-mcp- │ -------------> │ SonarQube│
│ (Claude Code│ stdin/stdout │ server (Java) │ HTTP + Bearer │ CB 26.4+│
│ Cursor...) │ │ │ auth (token) │ │
└─────────────┘ └──────────────────┘ └──────────┘
AI 客户端会将此服务作为子进程启动;通信通过 stdin/stdout 使用 MCP 协议。 此服务不会打开任何 HTTP 端口,也不接受传入连接。
工具
此服务提供 13 个只读 MCP 工具。
项目
| 工具 | 描述 |
|---|---|
listProjects | SonarQube 项目列表。参数:query(名称子字符串)、limit、offset。返回 key、name、qualifier。 |
listComponents | 使用 Sonar 的组件树搜索/浏览项目中的组件。参数:projectKey、query、qualifiers、branch / pullRequest、limit、offset。返回不透明的组件 key 值,以及 path、qualifier、名称、语言和项目。将返回的 key 值原样用作 listIssues.componentKeys;不要直接将 Java 包名作为组件键传入。 |
getProject | 项目概览:头部信息(名称、qualifier、可见性、描述、版本、上次分析日期)、质量门状态及未通过的条件,以及精选指标(ncloc、错误、漏洞、安全热点、代码异味、覆盖率、重复行密度、以分钟计的技术债务、警报状态)。参数:projectKey、branch(可选)、pullRequest(可选)。 |
listProjectBranches | 项目已分析的分支列表。每个条目包含:name、isMain、type(LONG/SHORT/BRANCH)、excludedFromPurge、analysisDate、qualityGateStatus,以及错误/漏洞/代码异味计数。不分页——Sonar 会一次返回所有分支。 |
listProjectPullRequests | 项目的 PR 分析列表。每个条目包含:PR key(可在其他地方用作 pullRequest=)、title、branch(头分支)、base、url、analysisDate、qualityGateStatus,以及错误/漏洞/代码异味计数。如果 Sonar 安装未集成 DevOps,则返回空列表。 |
问题
| 工具 | 描述 |
|---|---|
listIssues | 项目的扁平问题列表。参数:projectKey(除非已设置默认值,否则必需)、componentPathPrefix(可选)——相对于 Sonar 项目根目录的单个子树或文件筛选条件(例如 bc-doc/src/main/java/ru/foo 或 bc-doc/src/main/java/ru/foo/Bar.java);将 Java/Kotlin 包名中的点转换为斜杠;按目录边界匹配(bc-doc/src 不会匹配 bc-doc/srcExtra)。此外还有 severities、types、statuses、rules、branch / pullRequest(互斥)、resolved、limit、offset。默认仅返回未关闭的问题(resolved=false,状态为 OPEN/CONFIRMED/REOPENED)。每项包含规则、严重性、类型、状态、文件路径、行号、主要 textRange,以及跨文件规则的次要流程。使用 componentPathPrefix 时,服务会扫描项目并在客户端进行筛选;扫描有上限(默认为 10000 个问题)——达到上限时,响应中的 pathPrefixTruncated=true。 |
getIssue | 按键获取单个问题的详情及其变更历史记录(changelog)。可接受可选的 branch / pullRequest。 |
getIssueSnippets | 获取所有问题位置(主要位置及跨文件规则的流程)周围的源代码片段。每个位置包含:componentPath、语言,以及带 SCM 信息的源代码行数组。当本地没有该代码库,或需要准确查看 Sonar 分析的文件版本时,此工具很有用。可接受可选的 branch / pullRequest——当问题位于文件与主分支不同的非主引用上时,这一点很重要。 |
getProjectIssuesSummary | 项目未关闭问题的汇总:总数,以及按严重性、类型、状态、规则、标签和 SCM 作者分类的统计。参数与 listIssues 类似(包括 componentPathPrefix),但不包含分页参数。 |
getProjectIssuesBreakdown | 按逻辑模块和规则聚合多模块问题。模块根据第一个 componentPath 片段推导。参数与 getProjectIssuesSummary 相同。 |
规则
| 工具 | 描述 |
|---|---|
getRule | 按键获取 Sonar 规则详情(例如 java:S1234):标题、严重性、类型、语言、标签、描述部分(简介、根本原因、修复方法、资源)。基于内存缓存,重复调用无需额外开销。 |
安全热点
| 工具 | 描述 |
|---|---|
listHotspots | 列出项目的安全热点。热点是与问题不同的独立类别,用于标记需要手动进行安全审查的位置。Sonar 默认返回状态为 TO_REVIEW 的热点。参数:projectKey、componentPathPrefix(可选)——前缀语义与 listIssues 相同、status(可选)、branch / pullRequest(可选,二者互斥)、limit、offset。受相同的客户端扫描上限约束(响应中的 pathPrefixTruncated 标志)。 |
getHotspot | 安全热点详情:完整的规则描述(风险、漏洞、修复建议)、主要 textRange、次要流程、变更日志。热点键全局唯一,因此无需 branch / pullRequest 参数。 |
所有工具均为只读——不会修改 SonarQube 中的任何数据。
使用分支和拉取请求
Sonar 将分支和拉取请求视为两个不同且互斥的范围进行分析。Sonar web-api 的单个请求只能接受 branch= 或 pullRequest=,不能同时接受两者。
branch——长期存在的分支(main、develop、feature/...)。解析顺序:显式的branch参数 →SONAR_DEFAULT_BRANCH→ 无(Sonar 使用项目的主分支)。pullRequest——Sonar PR 键,通常是 PR/MR 编号。它独立于分支分析;PR 分析通常包含进行中工作的最相关发现。拉取请求键不会回退到服务器级默认值——请显式传入。
在同一次工具调用中同时传入 branch 和 pullRequest 会导致错误。使用 listProjectBranches / listProjectPullRequests 查找可用的引用。
分支范围至关重要。 每个 Sonar 分支都是一次独立分析:
main和功能分支上的未解决问题数量可能相差很大,因为功能分支上可能有尚未合并的新修复(或新代码)。如果 Agent 悄悄省略branch=,就会读取main(或SONAR_DEFAULT_BRANCH)上的数据,可能漏掉功能分支上已经完成的工作——更糟的是,还可能重复修复同一个问题。当用户的本地 git 位于feature/...分支时,Agent 应先调用listProjectBranches,找到匹配的 Sonar 分支,并在每次问题、摘要或分类调用中显式传入该分支。作为服务器端的安全措施,当满足以下条件时,
listIssues、getProjectIssuesSummary和getProjectIssuesBreakdown会在响应中附加branchAdvisory字段:(1)未传入branch或pullRequest;且(2)项目在 Sonar 中还有其他已分析的分支。该提示包含实际使用的(main)分支名称,以及按最近的 analysisDate 排序的非 main 分支列表。其他情况下不会出现此字段。
技术栈
- Java 25、Spring Boot 4.0.0、Spring AI MCP 2.0.0(stdio 传输)
- 使用 Jackson 3(
tools.jackson)处理 JSON - 使用版本目录(
gradle/libs.versions.toml)的 Gradle 9.3.1
构建
# Point to a JDK 25+ if it's not the default:
export JAVA_HOME="$HOME/.jdks/jdk-25.0.2"
./gradlew build
Windows 上:.\gradlew.bat build。
输出:build/libs/sonar-mcp-server.jar
配置
服务需要 SonarQube URL 和令牌;其余配置均为可选项。
| 变量 | 描述 |
|---|---|
SONAR_URL | SonarQube 基础 URL(例如 http://sonar.example.com) |
SONAR_TOKEN | SonarQube 用户令牌 |
SONAR_DEFAULT_PROJECT_KEY | 默认 SonarQube 项目键。设置后,调用 listIssues、getProjectIssuesSummary、listHotspots 时可以省略 projectKey。 |
SONAR_DEFAULT_BRANCH | 默认 Sonar 分支。设置后,所有支持分支的工具(listIssues、getIssue、getIssueSnippets、getProjectIssuesSummary、listHotspots)在省略 branch 时都会回退到此分支。未设置时,Sonar 会使用项目的主分支。 |
SONAR_MCP_DATA_DIR | 服务的本地数据目录;默认为 ~/.sonar-mcp-server |
SONAR_MCP_PAGINATION_DEFAULT_LIMIT | 列表工具的默认页面限制;默认为 50 |
SONAR_MCP_PAGINATION_DEFAULT_OFFSET | 列表工具的默认偏移量;默认为 0 |
SONAR_MCP_PAGINATION_MAX_LIMIT | 最大页面限制(Sonar API 本身的上限为 500);默认为 500 |
SONAR_MCP_SNIPPET_MAX_LINES | 为未来的每个代码片段行数上限预留;目前未使用(Sonar 会自行选择窗口)。默认为 50 |
SONAR_MCP_PATH_FILTER_MAX_SCANNED_ISSUES | 设置 componentPathPrefix 时,在客户端扫描的问题/热点数量硬上限;默认为 10000。达到上限时,响应中会包含 pathPrefixTruncated=true。 |
工具组
MCP tools/list 清单分为四个组,每组由一个环境变量控制。默认情况下,所有组均为开启状态,因此开箱即用时清单不会改变。关闭某个组可缩小清单范围——这对小上下文(本地)模型很有用,否则这类模型在会话开始时可能会被工具和输出架构淹没。
| 变量 | 组内工具 |
|---|---|
SONAR_MCP_TOOLS_ISSUE | listIssues、getIssue、getIssueSnippets、getProjectIssuesSummary、getProjectIssuesBreakdown |
SONAR_MCP_TOOLS_PROJECT | listProjects、listComponents、getProject、listProjectBranches、listProjectPullRequests |
SONAR_MCP_TOOLS_HOTSPOT | listHotspots、getHotspot |
SONAR_MCP_TOOLS_RULE | getRule |
将某个标志设为 false(例如 SONAR_MCP_TOOLS_HOTSPOT=false),即可从清单中移除该组的工具。
获取 SonarQube URL
在浏览器中打开 SonarQube,复制地址栏中的地址,不要包含路径——只保留协议和主机。
| 浏览器地址栏中的地址 | URL 值 |
|---|---|
https://sonar.example.com/dashboard?id=... | https://sonar.example.com |
http://192.168.1.50:9000/projects | http://192.168.1.50:9000 |
http://10.0.0.5/sonar/projects | http://10.0.0.5/sonar |
如果只能通过 IP 访问 SonarQube,请直接使用该 IP。如果 SonarQube 部署在子路径下(例如
/sonar),也请将其包含在 URL 中。
获取 SonarQube 令牌
- 使用你的账号登录 SonarQube。
- 打开 My Account -> Security。
- 在 Generate Tokens 中输入令牌名称,并选择类型 User Token。
- 点击 Generate——令牌只会显示一次。请立即复制。
- 将令牌值用作
SONAR_TOKEN。
如果令牌丢失,必须重新生成——SonarQube 不会再次显示已有令牌。
服务使用 HTTP Bearer 身份验证,通过 Authorization 标头传递令牌——这是 SonarQube Community Build 对用户令牌采用的标准方案。
运行
SONAR_URL=http://sonar.example.com SONAR_TOKEN=your_token \
java -jar build/libs/sonar-mcp-server.jar
服务通过 stdio 运行。成功启动后,它不会打开任何 HTTP 端口,并会等待通过 stdin/stdout 传入的 MCP 请求。
日志写入 ${SONAR_MCP_DATA_DIR:-~/.sonar-mcp-server}/logs/sonar-mcp-server.log。
日志文件按日期和大小轮换:10MB,保留 30 天,总容量上限为 512MB。
Docker
每次发布时,都会将镜像发布到 GHCR:
docker run -i --rm -e SONAR_URL=https://sonar.example.com -e SONAR_TOKEN=your_token ghcr.io/igorolv/sonar-mcp-server:latest
MCP 客户端也应使用同一条命令启动服务(-i 可让标准输入在 stdio 传输期间保持打开)。将主机目录挂载到 /data,即可在多次运行之间保留日志。如果 SonarQube 运行在同一台机器上,请使用其主机名而非 localhost,或者在 Linux 上添加 --network host。
在本地构建镜像:docker build -t sonar-mcp-server .
连接到 AI 客户端
{
"command": "java",
"args": ["-jar", "<absolute-path>/sonar-mcp-server.jar"],
"env": {
"SONAR_URL": "http://sonar.example.com",
"SONAR_TOKEN": "your_token"
}
}
具体位置:
| 客户端 | 连接方式 |
|---|---|
| Claude Code | claude mcp add --scope user -e SONAR_URL=... -e SONAR_TOKEN=... -- sonar java -jar /path/to/sonar-mcp-server.jar |
| Qwen Code | ~/.qwen/settings.json -> "mcpServers" -> "sonar",或 qwen mcp add --scope user -e SONAR_URL=... -e SONAR_TOKEN=... sonar java -jar /path/to/sonar-mcp-server.jar |
| VS Code | .vscode/mcp.json -> "servers" -> "sonar" |
| Cursor | .cursor/mcp.json -> "mcpServers" -> "sonar" |
| Claude Desktop | claude_desktop_config.json -> "mcpServers" -> "sonar" |
对于命令行客户端,也可以使用命令查看和移除注册项:claude mcp list / claude mcp remove --scope user sonar(qwen 也有类似命令)。
添加后,请重启客户端。
AI Agent 提示词示例
Find and fix Sonar issues in project my-project
Show the top 10 rules by number of open issues in project my-project
Show Sonar issues under src/main/java/com/example/foo
运维与安全
此 MCP 服务旨在与 AI 客户端一起在本地运行。它不会打开 HTTP 端口,也不接受传入的网络连接:客户端会将 JAR 作为子进程启动,并通过 stdin/stdout 与其通信。
访问模型
- 服务使用
SONAR_TOKEN中令牌所对应的 SonarQube 用户权限运行。 - 所有 MCP 工具均为只读:服务不会创建、修改或删除问题、安全热点、规则或项目。
- 可用项目和问题由用户在 SonarQube 中的权限决定。如果用户在 SonarQube 中看不到某个项目,服务也不应能访问该项目。
- 请将令牌视为机密。不要将其提交到代码仓库、shell 脚本、
.vscode/mcp.json、.cursor/mcp.json或任何其他共享项目文件中。
发送给 AI 客户端的数据
AI 客户端只会接收通过 MCP 工具请求的数据:
- 包含规则、严重级别、类型、文件路径、行号、消息、标签和 SCM 作者的问题列表;
- 问题变更历史;
- Sonar 规则说明(包括 HTML/markdown);
- 问题位置周围的源代码片段(通过
getIssueSnippets获取); - 安全热点及其详细信息。
连接到外部或基于云的 AI 客户端之前,请先查看公司的内部政策:源代码可能包含商业机密。
诊断
环境检查:
java -version
echo "$SONAR_URL"
test -n "$SONAR_TOKEN" && echo "SONAR_TOKEN is set"
SonarQube web-api 访问检查(使用令牌进行 HTTP Bearer 身份验证):
curl -H "Authorization: Bearer $SONAR_TOKEN" "$SONAR_URL/api/components/search?qualifiers=TRK&p=1&ps=1"
预期响应为项目 JSON 列表。401 Unauthorized 表示令牌无效或已过期;403 表示用户缺少 API 权限。
构建检查:
./gradlew test
./gradlew build
针对运行中的 SonarQube 执行集成测试:
SONAR_URL=<url> SONAR_TOKEN=<token> ./gradlew integrationTest
集成测试需要能够访问 SonarQube,且 SonarQube 中存在真实数据。默认情况下,单元测试会排除 integration 标签。
已知运行限制
- HTTP 超时和重试策略目前无法单独配置。
- Sonar API 使用基于页码的分页(
p/ps);工具接受offset/limit,且不是limit倍数的偏移量会向下取整到最近的页边界。Sonar 还会将ps限制为最多 500。 componentPathPrefix是客户端筛选条件:服务端会遍历项目的问题 / 安全热点,并保留componentPath以此前缀开头的条目(安全遵循目录边界)。扫描设有上限(SONAR_MCP_PATH_FILTER_MAX_SCANNED_ISSUES,默认为 10000);达到上限时,响应会设置pathPrefixTruncated=true,调用方应缩小前缀范围。Issue上的author字段是问题所在行的 SCM 作者(配置 SCM 提供程序后由 Sonar 填充)。Sonar 不会在issues/search中返回单独的scmAuthor/scmDate字段;如需行级 SCM 信息,请使用getIssueSnippets。
项目结构
├── src/main/java/ru/it_spectrum/ai/sonar/mcp/
│ ├── SonarMcpServerApplication.java — Spring Boot entry point
│ ├── api/ — stable MCP wire format: records returned by tools/services
│ │ ├── Issue.java, IssuePage.java, IssueDetails.java, IssueLocation.java, IssueFlow.java, IssueImpact.java, IssueSnippets.java
│ │ ├── Project.java, ProjectPage.java, ProjectOverview.java, ProjectMetrics.java, ProjectComponent.java, ProjectComponentPage.java
│ │ ├── RuleDetails.java, RuleSection.java
│ │ ├── Hotspot.java, HotspotDetails.java, HotspotPage.java, HotspotRule.java
│ │ ├── SourceSnippet.java, SnippetLine.java
│ │ ├── ChangelogEntry.java, ChangelogDiff.java
│ │ ├── TextRange.java, FacetCount.java, BranchAdvisory.java
│ │ ├── ProjectIssuesSummary.java, ProjectIssuesBreakdown.java, ModuleIssuesSummary.java
│ │ ├── ProjectBranch.java, ProjectBranches.java, ProjectPullRequest.java, ProjectPullRequests.java
│ │ ├── QualityGateStatus.java, QualityGateCondition.java
│ │ └── Opaque.java
│ ├── client/
│ │ ├── SonarClient.java — SonarQube web-api wrapper
│ │ └── model/ — raw DTOs of the SonarQube web-api, not exposed directly via MCP
│ │ └── Sonar*.java
│ ├── config/
│ │ ├── SonarClientProperties.java — url + token from env
│ │ ├── SonarMcpProperties.java — all sonar-mcp.* runtime settings
│ │ ├── SonarConfig.java — RestClient with Bearer auth
│ │ ├── McpServerConfig.java — stdio MCP customizer with immediateExecution(true)
│ │ └── JsonConfig.java — ObjectMapper for MCP JSON
│ ├── service/
│ │ ├── ProjectService.java
│ │ ├── IssueService.java
│ │ ├── RuleService.java — with in-memory rule cache
│ │ ├── SnippetService.java
│ │ ├── HotspotService.java
│ │ ├── PaginationHelper.java — offset/limit -> p/ps
│ │ └── SonarMappers.java — client.model -> api mapping
│ └── tools/
│ ├── ProjectTools.java — 5 MCP tools
│ ├── IssueTools.java — 5 MCP tools
│ ├── RuleTools.java — 1 MCP tool
│ ├── HotspotTools.java — 2 MCP tools
│ ├── ToolDescriptions.java — shared @McpTool / @McpToolParam description constants
│ ├── SonarPrompts.java — MCP prompts (analyzePath, fixPath, fixFile, investigateIssue, reviewPullRequest)
│ ├── RefResolver.java — branch/pullRequest resolution with default-branch fallback
│ └── ToolLogger.java
└── src/main/resources/
├── application.yml — MCP server configuration (stdio)
└── logback-spring.xml — logging configuration
故障排除
- “Gradle requires JVM 17 or later” — 将
JAVA_HOME设置为 JDK 25+。 - 连接被拒绝 / 401 — 检查 URL 和令牌。测试:
curl -H "Authorization: Bearer $SONAR_TOKEN" "$SONAR_URL/api/components/search?qualifiers=TRK&p=1&ps=1"。 - 403 Forbidden — 令牌用户没有项目或 web-api 的权限。请检查 SonarQube 中的角色。
- 包 / 模块范围返回 0 个问题 — 将路径形式的前缀作为
componentPathPrefix传入(例如bc-doc/src/main/java/ru/foo),而不是 Sonar 组件键。将 Java/Kotlin 包名中的点转换为斜杠。筛选会安全遵循目录边界,因此bc-doc/src不会匹配bc-doc/srcExtra。 - 响应中出现
pathPrefixTruncated=true— 客户端扫描在到达末尾前触及了SONAR_MCP_PATH_FILTER_MAX_SCANNED_ISSUES上限。缩小前缀范围以减少扫描量;如果项目确实需要超过 10k,则提高上限。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。