Skip to content
Closed
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
44 changes: 44 additions & 0 deletions .cursor/rules/sonarqube_mcp_instructions.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
description: SonarQube MCP Server usage guidelines
globs:
alwaysApply: true
---

These are some guidelines when using the SonarQube MCP server.

# Important Tool Guidelines

## Basic usage
- **IMPORTANT**: After you finish generating or modifying any code files at the very end of the task, you MUST call the `analyze_file_list` tool (if it exists) to analyze the files you created or modified.
- **IMPORTANT**: When starting a new task, you MUST disable automatic analysis with the `toggle_automatic_analysis` tool if it exists.
- **IMPORTANT**: When you are done generating code at the very end of the task, you MUST re-enable automatic analysis with the `toggle_automatic_analysis` tool if it exists.

## Project Keys
- When a user mentions a project key, use `search_my_sonarqube_projects` first to find the exact project key
- Don't guess project keys - always look them up

## Code Language Detection
- When analyzing code snippets, try to detect the programming language from the code syntax
- If unclear, ask the user or make an educated guess based on syntax

## Branch and Pull Request Context
- Many operations support branch-specific analysis
- If user mentions working on a feature branch, include the branch parameter

## Code Issues and Violations
- After fixing issues, do not attempt to verify them using `search_sonar_issues_in_projects`, as the server will not yet reflect the updates

# Common Troubleshooting

## Authentication Issues
- SonarQube requires USER tokens (not project tokens)
- When the error `SonarQube answered with Not authorized` occurs, verify the token type

## Project Not Found
- Use `search_my_sonarqube_projects` to find available projects
- Verify project key spelling and format

## Code Analysis Issues
- Ensure programming language is correctly specified
- Remind users that snippet analysis doesn't replace full project scans
- Provide full file content for better analysis results
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ dist/
.env
.env.local
.claude/
.codex/
.idea/
plan.md
/coverage/
Expand Down
10 changes: 9 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
{
"eslint.workingDirectories": [{ "mode": "auto" }]
"eslint.workingDirectories": [
{
"mode": "auto"
}
],
"sonarlint.connectedMode.project": {
"connectionId": "sonarsource",
"projectKey": "SonarSource_sonarqube-cli"
}
}
6 changes: 5 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ bun run test:coverage # Full merged lcov report (unit + integration, slow)

Each command lives in `src/cli/commands/`. The command tree is defined in `src/cli/command-tree.ts` and the entry point is `src/index.ts`.

- **`sonar integrate claude` (Claude Code):** Installs SonarQube MCP and Sonar hooks: **sonar-secrets**, plus project-scoped **sonar-sqaa** when eligible. Writes to `<project>/.claude/` unless `-g` / `--global` (`~/.claude/`). Docker is required for MCP.

- **`sonar integrate codex` (OpenAI Codex):** Installs SonarQube MCP and Sonar hooks: **sonar-secrets**, plus project-scoped **sonar-sqaa** when eligible. Writes to `<project>/.codex/` unless `-g` / `--global` (`~/.codex/`). Docker is required for MCP. Codex hooks are not supported on Windows. Project installs may set `[features] codex_hooks = true` in `~/.codex/config.toml` when that file exists.

To add a new command: add it to `src/cli/command-tree.ts` and implement the logic in a new folder under `src/cli/commands/`.
Please declare commands using the type defined in `src/cli/commands/_common/sonar-command.ts`.
By default, new commands should register a `authenticatedAction()`, only technical commands will use `anonymousAction()`.
Expand All @@ -52,7 +56,7 @@ Please use the exception types defined in `src/cli/commands/_common/error.ts` fo
Please try to create integration tests in priority. If the test is too complicated to set up, write unit tests.
Try to get inspiration from other tests to follow the same structure.

- Unit tests: `tests/unit/` — run with `bun test:unit`
- Unit tests: `tests/unit/` — run with `bun test:unit`. Prefer one file per CLI command where it fits: e.g. `integrate-claude.test.ts` (`sonar integrate claude`), `integrate-codex.test.ts` (`sonar integrate codex`), `integrate-git.test.ts` (`sonar integrate git`).
- Integration tests: `tests/integration/` — require env vars. They are using a harness to help set up tests and make assertions. Run with `bun test:integration`.
- E2E tests: `tests/e2e/` — end-to-end tests to verify full integration with external systems. Run with `bun test ./tests/e2e/`.
- The UI module has a built-in mock system (`src/ui/mock.ts`) — use it instead of mocking stdout directly.
Expand Down
22 changes: 10 additions & 12 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,7 @@ Output goes to `dist/`.
bun run build:binary
```

Produces `dist/sonarqube-cli` using Bun's single-file compiler. To install it locally:

```bash
bun run setup
```
Produces `dist/sonarqube-cli` using Bun's single-file compiler. Run it directly (for example `./dist/sonarqube-cli --help`). To hack on the CLI without rebuilding the binary, use `bun run dev`, which runs `src/index.ts`.

## Checks

Expand All @@ -45,27 +41,29 @@ bun run lint:fix

# TypeScript type checking
bun run typecheck

# Formatting (Prettier)
bun run format:check
```

## Testing

```bash
# Unit tests
bun test
bun run test:unit

# Unit tests with coverage
# Unit tests with coverage (also runs integration with coverage — slower)
bun run test:coverage

# Script tests
bun run test:scripts

# Integration tests (require env vars — see below)
bun run test:integration
bun run test:integration # runs pretest:integration first (binary + resources)

# All tests
# All tests (unit, then integration)
bun run test:all
```

To run a single integration test file with `bun test <path>`, run `bun run pretest:integration` once first so the binary and resources exist. Unit tests can use `bun test <path>` without that step.

### Integration tests

Integration tests hit real external services and require environment variables:
Expand Down
25 changes: 18 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,11 @@ curl -o- https://raw.githubusercontent.com/SonarSource/sonarqube-cli/refs/heads/
irm https://raw.githubusercontent.com/SonarSource/sonarqube-cli/refs/heads/master/user-scripts/install.ps1 | iex
```

## Setup steps for Claude Code integration
Below is an example of a setup which will work for SonarQube Cloud.
The authentication step is optional. With authentication, more types of secrets can be detected.
## AI coding assistants

```
sonar auth login
sonar integrate claude -g
```
Install SonarQube MCP and hooks with `sonar integrate claude` (Claude Code) or `sonar integrate codex` (OpenAI Codex). By default, files are written under the current project (for example `.claude/` or `.codex/`); use `-g` / `--global` for a user-wide install. Before integrating run `sonar auth login` first so more kinds of secrets can be detected.

**Contributors and AI coding agents working in this repository** should follow **[AGENTS.md](./AGENTS.md)** for setup, checks, formatting, and how integrate commands behave. This README is for users of the published CLI; it does not replace the agent guide.

## Commands

Expand Down Expand Up @@ -136,6 +133,20 @@ Setup SonarQube integration for Claude Code. This will install secrets scanning

---

#### `sonar integrate codex`

Setup SonarQube integration for OpenAI Codex. This will install secrets scanning hooks, and configure SonarQube MCP Server.

**Options:**

| Option | Type | Required | Description | Default |
| ------------------- | ------- | -------- | -------------------------------------------------------------- | ------- |
| `--project`, `-p` | string | No | Project key | - |
| `--non-interactive` | boolean | No | Non-interactive mode (no prompts) | - |
| `--global`, `-g` | boolean | No | Install hooks and MCP to ~/.codex instead of project directory | - |

---

#### `sonar integrate git`

Install a git hook that scans staged files for secrets before each commit (pre-commit) or scans committed files for secrets before each push (pre-push).
Expand Down
11 changes: 4 additions & 7 deletions build-scripts/README.template.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,11 @@ curl -o- https://raw.githubusercontent.com/SonarSource/sonarqube-cli/refs/heads/
irm https://raw.githubusercontent.com/SonarSource/sonarqube-cli/refs/heads/master/user-scripts/install.ps1 | iex
```

## Setup steps for Claude Code integration
Below is an example of a setup which will work for SonarQube Cloud.
The authentication step is optional. With authentication, more types of secrets can be detected.
## AI coding assistants

```
sonar auth login
sonar integrate claude -g
```
Install SonarQube MCP and hooks with `sonar integrate claude` (Claude Code) or `sonar integrate codex` (OpenAI Codex). By default, files are written under the current project (for example `.claude/` or `.codex/`); use `-g` / `--global` for a user-wide install. Before integrating run `sonar auth login` first so more kinds of secrets can be detected.

**Contributors and AI coding agents working in this repository** should follow **[AGENTS.md](./AGENTS.md)** for setup, checks, formatting, and how integrate commands behave. This README is for users of the published CLI; it does not replace the agent guide.

## Commands

Expand Down
11 changes: 11 additions & 0 deletions src/cli/command-tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { authLogout } from './commands/auth/logout';
import { authPurge } from './commands/auth/purge';
import { authStatus } from './commands/auth/status';
import { integrateClaude, type IntegrateClaudeOptions } from './commands/integrate/claude';
import { integrateCodex, type IntegrateCodexOptions } from './commands/integrate/codex';
import { integrateGit, type IntegrateGitOptions } from './commands/integrate/git/index';
import { analyzeSecrets, type AnalyzeSecretsOptions } from './commands/analyze/secrets';
import { analyzeSqaa, type AnalyzeSqaaOptions } from './commands/analyze/sqaa';
Expand Down Expand Up @@ -83,6 +84,16 @@ integrateCommand
)
.authenticatedAction((auth, options: IntegrateClaudeOptions) => integrateClaude(options, auth));

integrateCommand
.command('codex')
.description(
'Setup SonarQube integration for OpenAI Codex. This will install secrets scanning hooks, and configure SonarQube MCP Server.',
)
.option('-p, --project <project>', 'Project key')
.option('--non-interactive', 'Non-interactive mode (no prompts)')
.option('-g, --global', 'Install hooks and MCP to ~/.codex instead of project directory')
.authenticatedAction((auth, options: IntegrateCodexOptions) => integrateCodex(options, auth));

integrateCommand
.command('git')
.description(
Expand Down
101 changes: 101 additions & 0 deletions src/cli/commands/integrate/_common/integrate-configuration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* SonarQube CLI
* Copyright (C) 2026 SonarSource Sàrl
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* 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 GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { isSonarQubeCloud } from '../../../../lib/auth-resolver';
import type { ResolvedAuth } from '../../../../lib/auth-resolver';
import { blank, text, warn } from '../../../../ui';
import type { DiscoveredProject } from '../../_common/discovery';
import { CommandFailedError } from '../../_common/error';

export interface IntegrateProjectOptions {
project?: string;
}

export interface ConfigurationData {
serverURL: string;
projectKey: string | undefined;
organization: string | undefined;
token: string;
}

const DEFAULT_NO_PROJECT_KEY_MESSAGE =
'No project key provided - project related actions will be skipped.';

/**
* Load configuration from auth and discovered project (shared by Claude and Codex integrate).
*/
export function loadIntegrateConfiguration(
project: DiscoveredProject,
options: IntegrateProjectOptions,
auth: ResolvedAuth,
): ConfigurationData {
if (!!auth.serverUrl && !!project.serverUrl && auth.serverUrl != project.serverUrl) {
warn(
'Detected a Server URL mismatch between the current project configuration and the auth logged in configuration. If this is not intended please consider running "sonar auth logout" and re-run the integrate command',
);
}

if (!!auth.orgKey && !!project.organization && auth.orgKey != project.organization) {
warn(
'Detected an organization mismatch between the current project configuration and the auth logged in configuration. If this is not intended please consider running "sonar auth logout" and re-run the integrate command',
);
}

return {
serverURL: auth.serverUrl,
organization: auth.orgKey,
projectKey: options.project || project.projectKey,
token: auth.token,
};
}

/**
* Validate Cloud org and print summary lines. `noProjectKeyMessage` customizes the line when no key.
*/
export function validateIntegrateConfiguration(
project: DiscoveredProject,
config: ConfigurationData,
noProjectKeyMessage: string = DEFAULT_NO_PROJECT_KEY_MESSAGE,
): void {
if (isSonarQubeCloud(config.serverURL) && !config.organization) {
throw new CommandFailedError(
'SonarQube Cloud requires an organization. Please run "sonar auth logout" and re-authenticate with an organization.',
);
}

blank();
text(`Server: ${config.serverURL}`);

if (config.organization) {
text(`Organization: ${config.organization}`);
}

if (project.isGitRepo) {
text('Git repository detected');
}

text(`Project root: ${project.rootDir}`);

if (config.projectKey) {
text(`Project: ${config.projectKey}`);
} else {
text(noProjectKeyMessage);
}
}
Loading
Loading