Skip to content

Commit 212f074

Browse files
authored
MCP-84 Add a new tool to fetch dependency risks from SonarQube Server (#73)
1 parent 9ac6ad9 commit 212f074

File tree

18 files changed

+933
-8
lines changed

18 files changed

+933
-8
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,15 @@ When using custom certificates, you can modify your MCP configuration to mount t
237237
- `codeSnippet` - Code snippet or full file content - _Required String_
238238
- `language` - Optional language of the code snippet - _String_
239239

240+
### Dependency Risks
241+
242+
**Note: Dependency risks are only available when connecting to SonarQube Server 2025.4 Enterprise or higher with SonarQube Advanced Security enabled.**
243+
244+
- **search_dependency_risks** - Search for software composition analysis issues (dependency risks) of a project, paired with releases that appear in the analyzed project, application, or portfolio.
245+
- `projectKey` - Project key - _String_
246+
- `branchKey` - Optional branch key - _String_
247+
- `pullRequestKey` - Optional pull request key - _String_
248+
240249
### Languages
241250

242251
- **list_languages** - List all programming languages supported in this instance

src/main/java/org/sonarsource/sonarqube/mcp/SonarQubeMcpServer.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import org.sonarsource.sonarqube.mcp.tools.qualitygates.ProjectStatusTool;
4646
import org.sonarsource.sonarqube.mcp.tools.rules.ListRuleRepositoriesTool;
4747
import org.sonarsource.sonarqube.mcp.tools.rules.ShowRuleTool;
48+
import org.sonarsource.sonarqube.mcp.tools.dependencyrisks.SearchDependencyRisksTool;
4849
import org.sonarsource.sonarqube.mcp.tools.sources.GetRawSourceTool;
4950
import org.sonarsource.sonarqube.mcp.tools.sources.GetScmInfoTool;
5051
import org.sonarsource.sonarqube.mcp.tools.system.SystemHealthTool;
@@ -91,6 +92,16 @@ public SonarQubeMcpServer(StdioServerTransportProvider transportProvider, Map<St
9192
new SystemLogsTool(serverApi),
9293
new SystemPingTool(serverApi),
9394
new SystemStatusTool(serverApi)));
95+
96+
if (sonarQubeVersionChecker.isSonarQubeServerVersionHigherOrEqualsThan("2025.4")) {
97+
if (sonarQubeVersionChecker.isScaEnabled()) {
98+
this.supportedTools.add(new SearchDependencyRisksTool(serverApi));
99+
} else {
100+
LOG.info("Search Dependency Risks tool is not available because Advanced Security is not enabled.");
101+
}
102+
} else {
103+
LOG.info("Search Dependency Risks tool is not available because it requires SonarQube Server 2025.4 Enterprise or higher.");
104+
}
94105
}
95106

96107
this.supportedTools.addAll(List.of(

src/main/java/org/sonarsource/sonarqube/mcp/SonarQubeVersionChecker.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,14 @@
1616
*/
1717
package org.sonarsource.sonarqube.mcp;
1818

19+
import org.sonarsource.sonarqube.mcp.log.McpLogger;
1920
import org.sonarsource.sonarqube.mcp.serverapi.ServerApi;
2021
import org.sonarsource.sonarqube.mcp.serverapi.system.Version;
2122

2223
public class SonarQubeVersionChecker {
23-
// this version does not exist but it enables us to check for both SQS and SQCB
24+
25+
private static final McpLogger LOG = McpLogger.getInstance();
26+
// this version does not exist, but it enables us to check for both SQS and SQCB
2427
private static final Version MINIMAL_SUPPORTED_SONARQUBE_SERVER_VERSION = Version.create("10.9");
2528

2629
private final ServerApi serverApi;
@@ -37,4 +40,23 @@ public void failIfSonarQubeServerVersionIsNotSupported() {
3740
}
3841
}
3942
}
43+
44+
public boolean isSonarQubeServerVersionHigherOrEqualsThan(String minVersion) {
45+
if (!serverApi.isSonarQubeCloud()) {
46+
var version = Version.create(serverApi.systemApi().getStatus().version());
47+
return version.satisfiesMinRequirement(Version.create(minVersion));
48+
}
49+
return false;
50+
}
51+
52+
public boolean isScaEnabled() {
53+
try {
54+
var settingsResponse = serverApi.settingsApi().getSettings();
55+
return settingsResponse.isBooleanSettingEnabled("sonar.sca.enabled");
56+
} catch (Exception e) {
57+
LOG.error("Failed to check sonar.sca.enabled setting. Assuming SCA is disabled.", e);
58+
return false;
59+
}
60+
}
61+
4062
}

src/main/java/org/sonarsource/sonarqube/mcp/serverapi/ServerApi.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import org.sonarsource.sonarqube.mcp.serverapi.qualitygates.QualityGatesApi;
2626
import org.sonarsource.sonarqube.mcp.serverapi.qualityprofiles.QualityProfilesApi;
2727
import org.sonarsource.sonarqube.mcp.serverapi.rules.RulesApi;
28+
import org.sonarsource.sonarqube.mcp.serverapi.sca.ScaApi;
29+
import org.sonarsource.sonarqube.mcp.serverapi.settings.SettingsApi;
2830
import org.sonarsource.sonarqube.mcp.serverapi.sources.SourcesApi;
2931
import org.sonarsource.sonarqube.mcp.serverapi.system.SystemApi;
3032

@@ -80,6 +82,14 @@ public PluginsApi pluginsApi() {
8082
return new PluginsApi(helper);
8183
}
8284

85+
public ScaApi scaApi() {
86+
return new ScaApi(helper);
87+
}
88+
89+
public SettingsApi settingsApi() {
90+
return new SettingsApi(helper);
91+
}
92+
8393
public boolean isSonarQubeCloud() {
8494
return helper.getOrganization() != null;
8595
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* SonarQube MCP Server
3+
* Copyright (C) 2025 SonarSource
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
* See the Sonar Source-Available License for more details.
13+
*
14+
* You should have received a copy of the Sonar Source-Available License
15+
* along with this program; if not, see https://sonarsource.com/license/ssal/
16+
*/
17+
package org.sonarsource.sonarqube.mcp.serverapi.sca;
18+
19+
import com.google.gson.Gson;
20+
import javax.annotation.Nullable;
21+
import org.sonarsource.sonarqube.mcp.serverapi.ServerApiHelper;
22+
import org.sonarsource.sonarqube.mcp.serverapi.UrlBuilder;
23+
import org.sonarsource.sonarqube.mcp.serverapi.sca.response.DependencyRisksResponse;
24+
25+
public class ScaApi {
26+
27+
public static final String DEPENDENCY_RISKS_PATH = "/api/v2/sca/issues-releases";
28+
29+
private final ServerApiHelper helper;
30+
31+
public ScaApi(ServerApiHelper helper) {
32+
this.helper = helper;
33+
}
34+
35+
public DependencyRisksResponse getDependencyRisks(String projectKey, @Nullable String branchKey, @Nullable String pullRequestKey) {
36+
try (var response = helper.get(buildPath(projectKey, branchKey, pullRequestKey))) {
37+
var responseStr = response.bodyAsString();
38+
return new Gson().fromJson(responseStr, DependencyRisksResponse.class);
39+
}
40+
}
41+
42+
private static String buildPath(String projectKey, @Nullable String branchKey, @Nullable String pullRequestKey) {
43+
var builder = new UrlBuilder(DEPENDENCY_RISKS_PATH);
44+
builder.addParam("projectKey", projectKey);
45+
builder.addParam("branchKey", branchKey);
46+
builder.addParam("pullRequestKey", pullRequestKey);
47+
return builder.build();
48+
}
49+
}

src/test/java/org/sonarsource/sonarqube/mcp/tools/system/package-info.java renamed to src/main/java/org/sonarsource/sonarqube/mcp/serverapi/sca/package-info.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* You should have received a copy of the Sonar Source-Available License
1515
* along with this program; if not, see https://sonarsource.com/license/ssal/
1616
*/
17-
/**
18-
* This package contains the tests for the system tools.
19-
*/
20-
package org.sonarsource.sonarqube.mcp.tools.system;
17+
@ParametersAreNonnullByDefault
18+
package org.sonarsource.sonarqube.mcp.serverapi.sca;
19+
20+
import javax.annotation.ParametersAreNonnullByDefault;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* SonarQube MCP Server
3+
* Copyright (C) 2025 SonarSource
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
* See the Sonar Source-Available License for more details.
13+
*
14+
* You should have received a copy of the Sonar Source-Available License
15+
* along with this program; if not, see https://sonarsource.com/license/ssal/
16+
*/
17+
package org.sonarsource.sonarqube.mcp.serverapi.sca.response;
18+
19+
import java.util.List;
20+
21+
public record DependencyRisksResponse(List<IssueRelease> issuesReleases, List<Branch> branches, Integer countWithoutFilters, Page page) {
22+
23+
public record IssueRelease(String key, String severity, String originalSeverity, String manualSeverity, Boolean showIncreasedSeverityWarning,
24+
Release release, String type, String quality, String status, String createdAt, Assignee assignee,
25+
Integer commentCount, String vulnerabilityId, List<String> cweIds, String cvssScore, Boolean withdrawn,
26+
String spdxLicenseId, List<String> transitions, List<String> actions) {
27+
}
28+
29+
public record Release(String key, String branchUuid, String packageUrl, String packageManager, String packageName, String version,
30+
String licenseExpression, Boolean known, Boolean knownPackage, Boolean newlyIntroduced, Boolean directSummary,
31+
String scopeSummary, Boolean productionScopeSummary, List<String> dependencyFilePaths) {
32+
}
33+
34+
public record Assignee(String login, String name, String avatar, Boolean active) {
35+
}
36+
37+
public record Branch(String uuid, String key, Boolean isPullRequest, String projectKey, String projectName) {
38+
}
39+
40+
public record Page(Integer pageIndex, Integer pageSize, Integer total) {
41+
}
42+
43+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* SonarQube MCP Server
3+
* Copyright (C) 2025 SonarSource
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
* See the Sonar Source-Available License for more details.
13+
*
14+
* You should have received a copy of the Sonar Source-Available License
15+
* along with this program; if not, see https://sonarsource.com/license/ssal/
16+
*/
17+
@ParametersAreNonnullByDefault
18+
package org.sonarsource.sonarqube.mcp.serverapi.sca.response;
19+
20+
import javax.annotation.ParametersAreNonnullByDefault;
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* SonarQube MCP Server
3+
* Copyright (C) 2025 SonarSource
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
* See the Sonar Source-Available License for more details.
13+
*
14+
* You should have received a copy of the Sonar Source-Available License
15+
* along with this program; if not, see https://sonarsource.com/license/ssal/
16+
*/
17+
package org.sonarsource.sonarqube.mcp.serverapi.settings;
18+
19+
import com.google.gson.Gson;
20+
import org.sonarsource.sonarqube.mcp.serverapi.ServerApiHelper;
21+
import org.sonarsource.sonarqube.mcp.serverapi.settings.response.ValuesResponse;
22+
23+
public class SettingsApi {
24+
25+
public static final String SETTINGS_PATH = "/api/settings/values";
26+
27+
private final ServerApiHelper helper;
28+
29+
public SettingsApi(ServerApiHelper helper) {
30+
this.helper = helper;
31+
}
32+
33+
public ValuesResponse getSettings() {
34+
try (var response = helper.get(SETTINGS_PATH)) {
35+
var responseStr = response.bodyAsString();
36+
return new Gson().fromJson(responseStr, ValuesResponse.class);
37+
}
38+
}
39+
40+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* SonarQube MCP Server
3+
* Copyright (C) 2025 SonarSource
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
* See the Sonar Source-Available License for more details.
13+
*
14+
* You should have received a copy of the Sonar Source-Available License
15+
* along with this program; if not, see https://sonarsource.com/license/ssal/
16+
*/
17+
@ParametersAreNonnullByDefault
18+
package org.sonarsource.sonarqube.mcp.serverapi.settings;
19+
20+
import javax.annotation.ParametersAreNonnullByDefault;

0 commit comments

Comments
 (0)