Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,15 @@ When using custom certificates, you can modify your MCP configuration to mount t
- `codeSnippet` - Code snippet or full file content - _Required String_
- `language` - Optional language of the code snippet - _String_

### Dependency Risks

**Note: Dependency risks are only available when connecting to SonarQube Server 2025.4 Enterprise or higher.**

- **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.
- `projectKey` - Project key - _String_
- `branchKey` - Optional branch key - _String_
- `pullRequestKey` - Optional pull request key - _String_

### Languages

- **list_languages** - List all programming languages supported in this instance
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import org.sonarsource.sonarqube.mcp.tools.qualitygates.ProjectStatusTool;
import org.sonarsource.sonarqube.mcp.tools.rules.ListRuleRepositoriesTool;
import org.sonarsource.sonarqube.mcp.tools.rules.ShowRuleTool;
import org.sonarsource.sonarqube.mcp.tools.dependencyrisks.SearchDependencyRisksTool;
import org.sonarsource.sonarqube.mcp.tools.sources.GetRawSourceTool;
import org.sonarsource.sonarqube.mcp.tools.sources.GetScmInfoTool;
import org.sonarsource.sonarqube.mcp.tools.system.SystemHealthTool;
Expand Down Expand Up @@ -91,6 +92,16 @@ public SonarQubeMcpServer(StdioServerTransportProvider transportProvider, Map<St
new SystemLogsTool(serverApi),
new SystemPingTool(serverApi),
new SystemStatusTool(serverApi)));

if (sonarQubeVersionChecker.isSonarQubeServerVersionHigherOrEqualsThan("2025.4")) {
if (sonarQubeVersionChecker.isScaEnabled()) {
this.supportedTools.add(new SearchDependencyRisksTool(serverApi));
} else {
LOG.info("Search Dependency Risks tool is not available because Advanced Security is not enabled.");
}
} else {
LOG.info("Search Dependency Risks tool is not available because it requires SonarQube Server 2025.4 Enterprise or higher.");
}
}

this.supportedTools.addAll(List.of(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@
*/
package org.sonarsource.sonarqube.mcp;

import org.sonarsource.sonarqube.mcp.log.McpLogger;
import org.sonarsource.sonarqube.mcp.serverapi.ServerApi;
import org.sonarsource.sonarqube.mcp.serverapi.system.Version;

public class SonarQubeVersionChecker {
// this version does not exist but it enables us to check for both SQS and SQCB

private static final McpLogger LOG = McpLogger.getInstance();
// this version does not exist, but it enables us to check for both SQS and SQCB
private static final Version MINIMAL_SUPPORTED_SONARQUBE_SERVER_VERSION = Version.create("10.9");

private final ServerApi serverApi;
Expand All @@ -37,4 +40,23 @@ public void failIfSonarQubeServerVersionIsNotSupported() {
}
}
}

public boolean isSonarQubeServerVersionHigherOrEqualsThan(String minVersion) {
if (!serverApi.isSonarQubeCloud()) {
var version = Version.create(serverApi.systemApi().getStatus().version());
return version.satisfiesMinRequirement(Version.create(minVersion));
}
return false;
}

public boolean isScaEnabled() {
try {
var settingsResponse = serverApi.settingsApi().getSettings();
return settingsResponse.isBooleanSettingEnabled("sonar.sca.enabled");
} catch (Exception e) {
LOG.error("Failed to check sonar.sca.enabled setting. Assuming SCA is disabled.", e);
return false;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import org.sonarsource.sonarqube.mcp.serverapi.qualitygates.QualityGatesApi;
import org.sonarsource.sonarqube.mcp.serverapi.qualityprofiles.QualityProfilesApi;
import org.sonarsource.sonarqube.mcp.serverapi.rules.RulesApi;
import org.sonarsource.sonarqube.mcp.serverapi.sca.ScaApi;
import org.sonarsource.sonarqube.mcp.serverapi.settings.SettingsApi;
import org.sonarsource.sonarqube.mcp.serverapi.sources.SourcesApi;
import org.sonarsource.sonarqube.mcp.serverapi.system.SystemApi;

Expand Down Expand Up @@ -80,6 +82,14 @@ public PluginsApi pluginsApi() {
return new PluginsApi(helper);
}

public ScaApi scaApi() {
return new ScaApi(helper);
}

public SettingsApi settingsApi() {
return new SettingsApi(helper);
}

public boolean isSonarQubeCloud() {
return helper.getOrganization() != null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* SonarQube MCP Server
* Copyright (C) 2025 SonarSource
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the Sonar Source-Available License for more details.
*
* You should have received a copy of the Sonar Source-Available License
* along with this program; if not, see https://sonarsource.com/license/ssal/
*/
package org.sonarsource.sonarqube.mcp.serverapi.sca;

import com.google.gson.Gson;
import javax.annotation.Nullable;
import org.sonarsource.sonarqube.mcp.serverapi.ServerApiHelper;
import org.sonarsource.sonarqube.mcp.serverapi.UrlBuilder;
import org.sonarsource.sonarqube.mcp.serverapi.sca.response.DependencyRisksResponse;

public class ScaApi {

public static final String DEPENDENCY_RISKS_PATH = "/api/v2/sca/issues-releases";

private final ServerApiHelper helper;

public ScaApi(ServerApiHelper helper) {
this.helper = helper;
}

public DependencyRisksResponse getDependencyRisks(String projectKey, @Nullable String branchKey, @Nullable String pullRequestKey) {
try (var response = helper.get(buildPath(projectKey, branchKey, pullRequestKey))) {
var responseStr = response.bodyAsString();
return new Gson().fromJson(responseStr, DependencyRisksResponse.class);
}
}

private static String buildPath(String projectKey, @Nullable String branchKey, @Nullable String pullRequestKey) {
var builder = new UrlBuilder(DEPENDENCY_RISKS_PATH);
builder.addParam("projectKey", projectKey);
builder.addParam("branchKey", branchKey);
builder.addParam("pullRequestKey", pullRequestKey);
return builder.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* You should have received a copy of the Sonar Source-Available License
* along with this program; if not, see https://sonarsource.com/license/ssal/
*/
/**
* This package contains the tests for the system tools.
*/
package org.sonarsource.sonarqube.mcp.tools.system;
@ParametersAreNonnullByDefault
package org.sonarsource.sonarqube.mcp.serverapi.sca;

import javax.annotation.ParametersAreNonnullByDefault;
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* SonarQube MCP Server
* Copyright (C) 2025 SonarSource
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the Sonar Source-Available License for more details.
*
* You should have received a copy of the Sonar Source-Available License
* along with this program; if not, see https://sonarsource.com/license/ssal/
*/
package org.sonarsource.sonarqube.mcp.serverapi.sca.response;

import java.util.List;

public record DependencyRisksResponse(List<IssueRelease> issuesReleases, List<Branch> branches, Integer countWithoutFilters, Page page) {

public record IssueRelease(String key, String severity, String originalSeverity, String manualSeverity, Boolean showIncreasedSeverityWarning,
Release release, String type, String quality, String status, String createdAt, Assignee assignee,
Integer commentCount, String vulnerabilityId, List<String> cweIds, String cvssScore, Boolean withdrawn,
String spdxLicenseId, List<String> transitions, List<String> actions) {
}

public record Release(String key, String branchUuid, String packageUrl, String packageManager, String packageName, String version,
String licenseExpression, Boolean known, Boolean knownPackage, Boolean newlyIntroduced, Boolean directSummary,
String scopeSummary, Boolean productionScopeSummary, List<String> dependencyFilePaths) {
}

public record Assignee(String login, String name, String avatar, Boolean active) {
}

public record Branch(String uuid, String key, Boolean isPullRequest, String projectKey, String projectName) {
}

public record Page(Integer pageIndex, Integer pageSize, Integer total) {
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* SonarQube MCP Server
* Copyright (C) 2025 SonarSource
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the Sonar Source-Available License for more details.
*
* You should have received a copy of the Sonar Source-Available License
* along with this program; if not, see https://sonarsource.com/license/ssal/
*/
@ParametersAreNonnullByDefault
package org.sonarsource.sonarqube.mcp.serverapi.sca.response;

import javax.annotation.ParametersAreNonnullByDefault;
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* SonarQube MCP Server
* Copyright (C) 2025 SonarSource
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the Sonar Source-Available License for more details.
*
* You should have received a copy of the Sonar Source-Available License
* along with this program; if not, see https://sonarsource.com/license/ssal/
*/
package org.sonarsource.sonarqube.mcp.serverapi.settings;

import com.google.gson.Gson;
import org.sonarsource.sonarqube.mcp.serverapi.ServerApiHelper;
import org.sonarsource.sonarqube.mcp.serverapi.settings.response.ValuesResponse;

public class SettingsApi {

public static final String SETTINGS_PATH = "/api/settings/values";

private final ServerApiHelper helper;

public SettingsApi(ServerApiHelper helper) {
this.helper = helper;
}

public ValuesResponse getSettings() {
try (var response = helper.get(SETTINGS_PATH)) {
var responseStr = response.bodyAsString();
return new Gson().fromJson(responseStr, ValuesResponse.class);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* SonarQube MCP Server
* Copyright (C) 2025 SonarSource
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the Sonar Source-Available License for more details.
*
* You should have received a copy of the Sonar Source-Available License
* along with this program; if not, see https://sonarsource.com/license/ssal/
*/
@ParametersAreNonnullByDefault
package org.sonarsource.sonarqube.mcp.serverapi.settings;

import javax.annotation.ParametersAreNonnullByDefault;
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* SonarQube MCP Server
* Copyright (C) 2025 SonarSource
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the Sonar Source-Available License for more details.
*
* You should have received a copy of the Sonar Source-Available License
* along with this program; if not, see https://sonarsource.com/license/ssal/
*/
package org.sonarsource.sonarqube.mcp.serverapi.settings.response;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.annotation.Nullable;

public record ValuesResponse(
List<Setting> settings,
@Nullable List<String> setSecuredSettings
) {

public record Setting(
String key,
@Nullable String value,
@Nullable List<String> values,
@Nullable List<Map<String, Object>> fieldValues,
boolean inherited
) {}

/**
* Helper method to get a specific setting value by key
* @param key the setting key to look for
* @return the setting value if found, null otherwise
*/
public String getSettingValue(String key) {
if (settings == null) {
return null;
}

return settings.stream()
.filter(setting -> key.equals(setting.key()))
.map(Setting::value)
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
}

/**
* Helper method to check if a boolean setting is enabled
* @param key the setting key to check
* @return true if the setting exists and is set to "true", false otherwise
*/
public boolean isBooleanSettingEnabled(String key) {
return "true".equalsIgnoreCase(getSettingValue(key));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* SonarQube MCP Server
* Copyright (C) 2025 SonarSource
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the Sonar Source-Available License for more details.
*
* You should have received a copy of the Sonar Source-Available License
* along with this program; if not, see https://sonarsource.com/license/ssal/
*/
@ParametersAreNonnullByDefault
package org.sonarsource.sonarqube.mcp.serverapi.settings.response;

import javax.annotation.ParametersAreNonnullByDefault;
Loading