# SonarQube MCP Server
A read-only [Model Context Protocol (MCP)](https://modelcontextprotocol.io) 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_code` fields
- Works with SonarQube Community, Developer, and Enterprise editions
---
## Requirements
- Python 3.10+
- A running SonarQube instance
- A SonarQube user token (`squ_...`)
---
## Installation
```bash
pip install sonarqube-mcp-server
```
Or install from source:
```bash
git clone <repo>
cd sonarqube-mcp
pip install -e .
```
---
## Quick Start
```bash
SONARQUBE_URL=http://localhost:9000 \
SONARQUBE_TOKEN=squ_xxxxxxxxxxxx \
sonarqube-mcp-server
```
Or with `python -m`:
```bash
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:
```bash
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`):
```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):
```json
{
"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.
```json
{}
```
**Response:**
```json
{
"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:**
```json
{
"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:**
```json
{
"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:**
```json
{
"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:
```json
{
"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
```bash
pip install -e ".[dev]"
```
### Running Tests
```bash
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 captures `settings` and `client` in closure scope — makes unit testing straightforward by injecting a pre-built `SonarQubeSettings`.
- `@_safe_tool` wraps every tool so it never raises — exceptions are caught and returned as structured error envelopes.
- `_clamp()` keeps `page_size` within SonarQube's supported API limits (1–500).
- Settings use a `frozen=True` dataclass — immutable after load, safe to share across tool closures.
---
## License
MIT