Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 with SonarQube Advanced Security enabled.**

- **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