SonarQube MCP Server
A read-only Model Context Protocol (MCP) server that gives AI assistants structured access to SonarQube — issues, metrics, rules, and projects.
Features
- 6 read-only tools covering the full SonarQube quality workflow
- Safe by design — no mutations, every tool returns a consistent
{"ok": ...}envelope - Input validation before any API call (severities, types, statuses)
- Structured errors with machine-readable
error_codefields - Works with SonarQube Community, Developer, and Enterprise editions
Requirements
- Python 3.10+
- A running SonarQube instance
- A SonarQube user token (
squ_...)
Installation
pip install sonarqube-mcp-server
Or install from source:
git clone <repo>
cd sonarqube-mcp
pip install -e .
Quick Start
SONARQUBE_URL=http://localhost:9000 \
SONARQUBE_TOKEN=squ_xxxxxxxxxxxx \
sonarqube-mcp-server
Or with python -m:
python -m sonarqube_mcp
Configuration
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
SONARQUBE_URL | No | http://localhost:9000 | Base URL of your SonarQube instance |
SONARQUBE_TOKEN | Yes | — | User token for authentication |
SONARQUBE_REQUEST_TIMEOUT_SEC | No | 30 | HTTP request timeout in seconds |
Copy .env.example to .env and fill in your values:
cp .env.example .env
Generating a Token
In SonarQube: My Account → Security → Generate Tokens. A user token (squ_...) with Browse permission on the target projects is sufficient for all read-only operations.
MCP Client Integration
Claude Code
Add to your Claude Code MCP settings (~/.claude/claude_code_config.json):
{
"mcpServers": {
"sonarqube": {
"command": "sonarqube-mcp-server",
"env": {
"SONARQUBE_URL": "http://localhost:9000",
"SONARQUBE_TOKEN": "squ_xxxxxxxxxxxx"
}
}
}
}
Claude Desktop
Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"sonarqube": {
"command": "sonarqube-mcp-server",
"env": {
"SONARQUBE_URL": "http://localhost:9000",
"SONARQUBE_TOKEN": "squ_xxxxxxxxxxxx"
}
}
}
}
Tools
All tools are read-only (readOnlyHint: true, destructiveHint: false).
check_status
Verify connectivity and retrieve server version.
{}
Response:
{
"ok": true,
"server_url": "http://localhost:9000",
"status": "UP",
"version": "10.4.1"
}
list_projects
List or search SonarQube projects with pagination.
| Parameter | Type | Default | Description |
|---|---|---|---|
query | string | — | Filter by project name or key |
page | integer | 1 | Page number (1-indexed) |
page_size | integer | 20 | Results per page (1–500) |
Response:
{
"ok": true,
"total": 42,
"page": 1,
"page_size": 20,
"projects": [
{"key": "my-app", "name": "My Application", "qualifier": "TRK"}
]
}
search_issues
Search issues across all projects or scoped to one project, with rich filtering.
| Parameter | Type | Default | Description |
|---|---|---|---|
project_key | string | — | Scope to a specific project |
severities | string | — | CSV: INFO, MINOR, MAJOR, CRITICAL, BLOCKER |
types | string | — | CSV: CODE_SMELL, BUG, VULNERABILITY, SECURITY_HOTSPOT |
statuses | string | — | CSV: OPEN, CONFIRMED, REOPENED, RESOLVED, CLOSED |
tags | string | — | CSV of tag names |
assigned | boolean | — | true = assigned only, false = unassigned only |
page | integer | 1 | Page number |
page_size | integer | 20 | Results per page (1–500) |
Example — find all open blockers in a project:
{
"project_key": "my-app",
"severities": "BLOCKER,CRITICAL",
"statuses": "OPEN"
}
get_issue
Get full detail for a single issue by key, including text range, effort, assignee, comments, and data-flow information.
| Parameter | Type | Description |
|---|---|---|
issue_key | string | Issue key (e.g. AXy1k2m3n4o5p6q7r8) |
get_project_metrics
Retrieve the quality dashboard for a project. Returns a standard set of metrics by default, or a custom selection.
| Parameter | Type | Default | Description |
|---|---|---|---|
project_key | string | — | Required. Project key |
metric_keys | string | See below | CSV of metric keys |
Default metrics: bugs, vulnerabilities, code_smells, security_hotspots, coverage, duplicated_lines_density, ncloc, sqale_index, reliability_rating, security_rating, sqale_rating, alert_status, quality_gate_details
Response:
{
"ok": true,
"project_key": "my-app",
"project_name": "My Application",
"metrics": {
"bugs": "3",
"coverage": "78.4",
"alert_status": "OK"
}
}
get_rule
Retrieve the description and metadata for a SonarQube rule.
| Parameter | Type | Description |
|---|---|---|
rule_key | string | Rule key (e.g. python:S1192, java:S106) |
Error Handling
All tools return a consistent envelope. On failure:
{
"ok": false,
"error_code": "auth_error",
"message": "Authentication failed. Check SONARQUBE_TOKEN.",
"details": {}
}
error_code | Cause |
|---|---|
auth_error | Invalid or missing token (HTTP 401) |
forbidden | Token lacks permissions (HTTP 403) |
not_found | Project, issue, or rule does not exist (HTTP 404) |
connection_error | Cannot reach the SonarQube instance |
timeout | Request exceeded SONARQUBE_REQUEST_TIMEOUT_SEC |
invalid_input | Bad parameter value (e.g. unknown severity) |
api_error | Other non-2xx SonarQube response |
internal_error | Unexpected server-side error |
Development
Setup
pip install -e ".[dev]"
Running Tests
pytest
Project Layout
src/sonarqube_mcp/
├── server.py # FastMCP server, tool definitions, main()
├── sonarqube_client.py # httpx.Client wrapper for SonarQube REST API
├── settings.py # Frozen dataclass + env var loading
├── errors.py # SonarQubeError + error_response()
├── __main__.py # python -m sonarqube_mcp entrypoint
└── __init__.py
tests/
├── conftest.py
├── test_server.py
├── test_client.py
├── test_settings.py
└── test_errors.py
Architecture Notes
create_server()is a factory that capturessettingsandclientin closure scope — makes unit testing straightforward by injecting a pre-builtSonarQubeSettings.@_safe_toolwraps every tool so it never raises — exceptions are caught and returned as structured error envelopes._clamp()keepspage_sizewithin SonarQube's supported API limits (1–500).- Settings use a
frozen=Truedataclass — immutable after load, safe to share across tool closures.
License
MIT