This is page 1 of 11. Use http://codebase.md/sapientpants/sonarqube-mcp-server?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .adr-dir
├── .changeset
│ ├── config.json
│ └── README.md
├── .claude
│ ├── commands
│ │ ├── analyze-and-fix-github-issue.md
│ │ ├── fix-sonarqube-issues.md
│ │ ├── implement-github-issue.md
│ │ ├── release.md
│ │ ├── spec-feature.md
│ │ └── update-dependencies.md
│ ├── hooks
│ │ └── block-git-no-verify.ts
│ └── settings.json
├── .dockerignore
├── .github
│ ├── actionlint.yaml
│ ├── changeset.yml
│ ├── dependabot.yml
│ ├── ISSUE_TEMPLATE
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ ├── pull_request_template.md
│ ├── scripts
│ │ ├── determine-artifact.sh
│ │ └── version-and-release.js
│ ├── workflows
│ │ ├── codeql.yml
│ │ ├── main.yml
│ │ ├── pr.yml
│ │ ├── publish.yml
│ │ ├── reusable-docker.yml
│ │ ├── reusable-security.yml
│ │ └── reusable-validate.yml
│ └── WORKFLOWS.md
├── .gitignore
├── .husky
│ ├── commit-msg
│ └── pre-commit
├── .markdownlint.yaml
├── .markdownlintignore
├── .npmrc
├── .prettierignore
├── .prettierrc
├── .trivyignore
├── .yaml-lint.yml
├── .yamllintignore
├── CHANGELOG.md
├── changes.md
├── CLAUDE.md
├── CODE_OF_CONDUCT.md
├── commitlint.config.js
├── COMPATIBILITY.md
├── CONTRIBUTING.md
├── docker-compose.yml
├── Dockerfile
├── docs
│ ├── architecture
│ │ └── decisions
│ │ ├── 0001-record-architecture-decisions.md
│ │ ├── 0002-use-node-js-with-typescript.md
│ │ ├── 0003-adopt-model-context-protocol-for-sonarqube-integration.md
│ │ ├── 0004-use-sonarqube-web-api-client-for-all-sonarqube-interactions.md
│ │ ├── 0005-domain-driven-design-of-sonarqube-modules.md
│ │ ├── 0006-expose-sonarqube-features-as-mcp-tools.md
│ │ ├── 0007-support-multiple-authentication-methods-for-sonarqube.md
│ │ ├── 0008-use-environment-variables-for-configuration.md
│ │ ├── 0009-file-based-logging-to-avoid-stdio-conflicts.md
│ │ ├── 0010-use-stdio-transport-for-mcp-communication.md
│ │ ├── 0011-docker-containerization-for-deployment.md
│ │ ├── 0012-add-elicitation-support-for-interactive-user-input.md
│ │ ├── 0014-current-security-model-and-future-oauth2-considerations.md
│ │ ├── 0015-transport-architecture-refactoring.md
│ │ ├── 0016-http-transport-with-oauth-2-0-metadata-endpoints.md
│ │ ├── 0017-comprehensive-audit-logging-system.md
│ │ ├── 0018-add-comprehensive-monitoring-and-observability.md
│ │ ├── 0019-simplify-to-stdio-only-transport-for-mcp-gateway-deployment.md
│ │ ├── 0020-testing-framework-and-strategy-vitest-with-property-based-testing.md
│ │ ├── 0021-code-quality-toolchain-eslint-prettier-strict-typescript.md
│ │ ├── 0022-package-manager-choice-pnpm.md
│ │ ├── 0023-release-management-with-changesets.md
│ │ ├── 0024-ci-cd-platform-github-actions.md
│ │ ├── 0025-container-and-security-scanning-strategy.md
│ │ ├── 0026-circuit-breaker-pattern-with-opossum.md
│ │ ├── 0027-docker-image-publishing-strategy-ghcr-to-docker-hub.md
│ │ └── 0028-session-based-http-transport-with-server-sent-events.md
│ ├── architecture.md
│ ├── security.md
│ └── troubleshooting.md
├── eslint.config.js
├── examples
│ └── http-client.ts
├── jest.config.js
├── LICENSE
├── LICENSES.md
├── osv-scanner.toml
├── package.json
├── pnpm-lock.yaml
├── README.md
├── scripts
│ ├── actionlint.sh
│ ├── ci-local.sh
│ ├── load-test.sh
│ ├── README.md
│ ├── run-all-tests.sh
│ ├── scan-container.sh
│ ├── security-scan.sh
│ ├── setup.sh
│ ├── test-monitoring-integration.sh
│ └── validate-docs.sh
├── SECURITY.md
├── sonar-project.properties
├── src
│ ├── __tests__
│ │ ├── additional-coverage.test.ts
│ │ ├── advanced-index.test.ts
│ │ ├── assign-issue.test.ts
│ │ ├── auth-methods.test.ts
│ │ ├── boolean-string-transform.test.ts
│ │ ├── components.test.ts
│ │ ├── config
│ │ │ └── service-accounts.test.ts
│ │ ├── dependency-injection.test.ts
│ │ ├── direct-handlers.test.ts
│ │ ├── direct-lambdas.test.ts
│ │ ├── direct-schema-validation.test.ts
│ │ ├── domains
│ │ │ ├── components-domain-full.test.ts
│ │ │ ├── components-domain.test.ts
│ │ │ ├── hotspots-domain.test.ts
│ │ │ └── source-code-domain.test.ts
│ │ ├── environment-validation.test.ts
│ │ ├── error-handler.test.ts
│ │ ├── error-handling.test.ts
│ │ ├── errors.test.ts
│ │ ├── function-tests.test.ts
│ │ ├── handlers
│ │ │ ├── components-handler-integration.test.ts
│ │ │ └── projects-authorization.test.ts
│ │ ├── handlers.test.ts
│ │ ├── handlers.test.ts.skip
│ │ ├── index.test.ts
│ │ ├── issue-resolution-elicitation.test.ts
│ │ ├── issue-resolution.test.ts
│ │ ├── issue-transitions.test.ts
│ │ ├── issues-enhanced-search.test.ts
│ │ ├── issues-new-parameters.test.ts
│ │ ├── json-array-transform.test.ts
│ │ ├── lambda-functions.test.ts
│ │ ├── lambda-handlers.test.ts.skip
│ │ ├── logger.test.ts
│ │ ├── mapping-functions.test.ts
│ │ ├── mocked-environment.test.ts
│ │ ├── null-to-undefined.test.ts
│ │ ├── parameter-transformations-advanced.test.ts
│ │ ├── parameter-transformations.test.ts
│ │ ├── protocol-version.test.ts
│ │ ├── pull-request-transform.test.ts
│ │ ├── quality-gates.test.ts
│ │ ├── schema-parameter-transforms.test.ts
│ │ ├── schema-transformation-mocks.test.ts
│ │ ├── schema-transforms.test.ts
│ │ ├── schema-validators.test.ts
│ │ ├── schemas
│ │ │ ├── components-schema.test.ts
│ │ │ ├── hotspots-tools-schema.test.ts
│ │ │ └── issues-schema.test.ts
│ │ ├── sonarqube-elicitation.test.ts
│ │ ├── sonarqube.test.ts
│ │ ├── source-code.test.ts
│ │ ├── standalone-handlers.test.ts
│ │ ├── string-to-number-transform.test.ts
│ │ ├── tool-handler-lambdas.test.ts
│ │ ├── tool-handlers.test.ts
│ │ ├── tool-registration-schema.test.ts
│ │ ├── tool-registration-transforms.test.ts
│ │ ├── transformation-util.test.ts
│ │ ├── transports
│ │ │ ├── base.test.ts
│ │ │ ├── factory.test.ts
│ │ │ ├── http.test.ts
│ │ │ ├── session-manager.test.ts
│ │ │ └── stdio.test.ts
│ │ ├── utils
│ │ │ ├── retry.test.ts
│ │ │ └── transforms.test.ts
│ │ ├── zod-boolean-transform.test.ts
│ │ ├── zod-schema-transforms.test.ts
│ │ └── zod-transforms.test.ts
│ ├── config
│ │ ├── service-accounts.ts
│ │ └── versions.ts
│ ├── domains
│ │ ├── base.ts
│ │ ├── components.ts
│ │ ├── hotspots.ts
│ │ ├── index.ts
│ │ ├── issues.ts
│ │ ├── measures.ts
│ │ ├── metrics.ts
│ │ ├── projects.ts
│ │ ├── quality-gates.ts
│ │ ├── source-code.ts
│ │ └── system.ts
│ ├── errors.ts
│ ├── handlers
│ │ ├── components.ts
│ │ ├── hotspots.ts
│ │ ├── index.ts
│ │ ├── issues.ts
│ │ ├── measures.ts
│ │ ├── metrics.ts
│ │ ├── projects.ts
│ │ ├── quality-gates.ts
│ │ ├── source-code.ts
│ │ └── system.ts
│ ├── index.ts
│ ├── monitoring
│ │ ├── __tests__
│ │ │ └── circuit-breaker.test.ts
│ │ ├── circuit-breaker.ts
│ │ ├── health.ts
│ │ └── metrics.ts
│ ├── schemas
│ │ ├── common.ts
│ │ ├── components.ts
│ │ ├── hotspots-tools.ts
│ │ ├── hotspots.ts
│ │ ├── index.ts
│ │ ├── issues.ts
│ │ ├── measures.ts
│ │ ├── metrics.ts
│ │ ├── projects.ts
│ │ ├── quality-gates.ts
│ │ ├── source-code.ts
│ │ └── system.ts
│ ├── sonarqube.ts
│ ├── transports
│ │ ├── base.ts
│ │ ├── factory.ts
│ │ ├── http.ts
│ │ ├── index.ts
│ │ ├── session-manager.ts
│ │ └── stdio.ts
│ ├── types
│ │ ├── common.ts
│ │ ├── components.ts
│ │ ├── hotspots.ts
│ │ ├── index.ts
│ │ ├── issues.ts
│ │ ├── measures.ts
│ │ ├── metrics.ts
│ │ ├── projects.ts
│ │ ├── quality-gates.ts
│ │ ├── source-code.ts
│ │ └── system.ts
│ └── utils
│ ├── __tests__
│ │ ├── elicitation.test.ts
│ │ ├── pattern-matcher.test.ts
│ │ └── structured-response.test.ts
│ ├── client-factory.ts
│ ├── elicitation.ts
│ ├── error-handler.ts
│ ├── logger.ts
│ ├── parameter-mappers.ts
│ ├── pattern-matcher.ts
│ ├── retry.ts
│ ├── structured-response.ts
│ └── transforms.ts
├── test-http-transport.sh
├── tmp
│ └── .gitkeep
├── tsconfig.build.json
├── tsconfig.json
├── vitest.config.d.ts
├── vitest.config.js
├── vitest.config.js.map
└── vitest.config.ts
```
# Files
--------------------------------------------------------------------------------
/.adr-dir:
--------------------------------------------------------------------------------
```
1 | docs/architecture/decisions
2 |
```
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
```
1 | dist/
2 | node_modules/
3 | coverage/
4 | pnpm-lock.yaml
5 | package-lock.json
6 | *.d.ts
7 | sbom.cdx.json
```
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
```
1 | engine-strict=true
2 | save-exact=true
3 | fund=false
4 | audit=true
5 | @your-scope:registry=https://registry.npmjs.org/
6 |
```
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
```
1 | {
2 | "semi": true,
3 | "trailingComma": "es5",
4 | "singleQuote": true,
5 | "printWidth": 100,
6 | "tabWidth": 2,
7 | "useTabs": false,
8 | "endOfLine": "lf"
9 | }
10 |
```
--------------------------------------------------------------------------------
/.markdownlintignore:
--------------------------------------------------------------------------------
```
1 | # Ignore CHANGELOG as it's auto-generated
2 | CHANGELOG.md
3 |
4 | # Ignore node_modules
5 | node_modules/
6 |
7 | # Ignore coverage reports
8 | coverage/
9 |
10 | # Ignore build output
11 | dist/
```
--------------------------------------------------------------------------------
/.yamllintignore:
--------------------------------------------------------------------------------
```
1 | # Ignore node_modules
2 | node_modules/
3 |
4 | # Ignore GitHub workflows (linted by actionlint)
5 | .github/workflows/
6 |
7 | # Ignore build output
8 | dist/
9 | coverage/
10 |
11 | # Ignore lock files (auto-generated)
12 | pnpm-lock.yaml
13 | yarn.lock
14 | package-lock.json
```
--------------------------------------------------------------------------------
/.yaml-lint.yml:
--------------------------------------------------------------------------------
```yaml
1 | # yaml-lint configuration
2 | # https://github.com/rasshofer/yaml-lint
3 |
4 | # Schema to use for validation
5 | schema: 'FAILSAFE_SCHEMA'
6 |
7 | # Ignore certain paths
8 | ignore:
9 | - node_modules/**
10 | - .github/workflows/**
11 | - dist/**
12 | - coverage/**
13 | - pnpm-lock.yaml
14 | - yarn.lock
15 | - package-lock.json
16 |
```
--------------------------------------------------------------------------------
/.trivyignore:
--------------------------------------------------------------------------------
```
1 | # Trivy Ignore File
2 | # This file contains CVEs to ignore during container security scanning
3 | # Format: CVE-ID or CVE-ID with expiry date
4 | #
5 | # Example entries:
6 | # CVE-2022-12345
7 | # CVE-2022-12345 exp:2025-01-01
8 | #
9 | # Add CVEs here that are false positives or accepted risks
10 | # Each exclusion should be documented with a comment explaining why
11 |
12 | # Example: False positive in test dependency
13 | # CVE-2024-XXXXX
14 |
15 | # Note: Regularly review and update this file to remove outdated exclusions
```
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
1 | # Dependencies
2 | node_modules/
3 | .pnpm-store/
4 |
5 | # Build output
6 | dist/
7 | coverage/
8 |
9 | # IDE and editor files
10 | .vscode/
11 | .idea/
12 | *.swp
13 | *.swo
14 |
15 | # Logs
16 | *.log
17 | npm-debug.log*
18 | pnpm-debug.log*
19 |
20 | # Environment variables
21 | .env
22 | .env.local
23 | .env.*.local
24 |
25 | # OS files
26 | .DS_Store
27 | Thumbs.db
28 | **/.claude/settings.local.json
29 |
30 | # Terraform
31 | terraform/.terraform/
32 | terraform/**/.terraform/
33 | terraform/**/terraform.tfstate
34 | terraform/**/terraform.tfstate.*
35 | terraform/**/*.tfvars
36 | !terraform/**/*.tfvars.example
37 | terraform/**/.terraform.lock.hcl
38 |
39 | # Kubernetes secrets
40 | k8s/overlays/production/secrets.env
41 |
42 | # Helm
43 | helm/**/charts/
44 | helm/**/Chart.lock
45 |
```
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
```
1 | # Git files
2 | .git
3 | .gitignore
4 | .gitattributes
5 |
6 | # GitHub files
7 | .github
8 |
9 | # Documentation
10 | *.md
11 | LICENSE
12 | docs
13 | doc
14 |
15 | # Development files
16 | .husky
17 | .changeset
18 | .eslintrc.json
19 | .prettierrc
20 | .prettierignore
21 | .editorconfig
22 | .vscode
23 | .idea
24 |
25 | # Test files
26 | tests
27 | test
28 | *.test.ts
29 | *.spec.ts
30 | coverage
31 | .nyc_output
32 | junit.xml
33 |
34 | # Build artifacts that will be created during Docker build
35 | dist
36 | *.tsbuildinfo
37 |
38 | # Logs
39 | logs
40 | *.log
41 | npm-debug.log*
42 | yarn-debug.log*
43 | yarn-error.log*
44 | pnpm-debug.log*
45 |
46 | # OS files
47 | .DS_Store
48 | Thumbs.db
49 |
50 | # Environment files (should not be in image)
51 | .env
52 | .env.*
53 |
54 | # CI/CD files
55 | .travis.yml
56 | .gitlab-ci.yml
57 | Jenkinsfile
58 |
59 | # Docker files
60 | Dockerfile
61 | .dockerignore
62 | docker-compose*.yml
63 |
64 | # Temporary files
65 | tmp
66 | temp
67 | *.tmp
68 | *.bak
69 | *.swp
70 |
71 | # SBOM and security scan results
72 | sbom.cdx.json
73 | trivy-results.sarif
74 | results.sarif
75 |
76 | # Archives
77 | *.tar.gz
78 | *.zip
```
--------------------------------------------------------------------------------
/.markdownlint.yaml:
--------------------------------------------------------------------------------
```yaml
1 | # Markdownlint configuration
2 | # https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md
3 |
4 | # Default state for all rules
5 | default: true
6 |
7 | # MD003/heading-style - Heading style
8 | MD003:
9 | style: 'atx' # Use # style headers
10 |
11 | # MD004/ul-style - Unordered list style
12 | MD004:
13 | style: 'dash' # Use - for unordered lists
14 |
15 | # MD007/ul-indent - Unordered list indentation
16 | MD007:
17 | indent: 2 # 2 spaces for nested lists
18 |
19 | # MD013/line-length - Line length
20 | # Disabled as many lines contain URLs or complex content
21 | MD013: false
22 |
23 | # MD022/blanks-around-headings - Headings should be surrounded by blank lines
24 | # Allow some flexibility in heading spacing
25 | MD022: false
26 |
27 | # MD024/no-duplicate-heading - Multiple headings with the same content
28 | MD024:
29 | siblings_only: true # Allow same heading if not siblings
30 |
31 | # MD026/no-trailing-punctuation - Trailing punctuation in heading
32 | # Allow colons in headings for clarity
33 | MD026: false
34 |
35 | # MD031/blanks-around-fences - Fenced code blocks should be surrounded by blank lines
36 | # Sometimes inline code blocks make sense
37 | MD031: false
38 |
39 | # MD034/no-bare-urls - Bare URLs
40 | # Sometimes bare URLs are intentional for clarity
41 | MD034: false
42 |
43 | # MD025/single-title - Multiple top-level headings
44 | # Disabled because we might have multiple H1s in different sections
45 | MD025: false
46 |
47 | # MD033/no-inline-html - Inline HTML
48 | MD033:
49 | allowed_elements:
50 | - details
51 | - summary
52 | - sub
53 | - sup
54 | - br
55 | - img
56 | - a
57 | - code
58 | - pre
59 | - table
60 | - thead
61 | - tbody
62 | - tr
63 | - td
64 | - th
65 | - div
66 | - span
67 | - kbd
68 |
69 | # MD040/fenced-code-language - Fenced code blocks should have a language specified
70 | # Disabled as we sometimes have plain text blocks
71 | MD040: false
72 |
73 | # MD041/first-line-heading - First line in a file should be a top-level heading
74 | # Disabled as not all markdown files need to start with a heading
75 | MD041: false
76 |
77 | # MD045/no-alt-text - Images should have alternate text
78 | # Disabled as decorative images don't always need alt text
79 | MD045: false
80 |
81 | # MD046/code-block-style - Code block style
82 | MD046:
83 | style: 'fenced' # Use ``` for code blocks
84 |
85 | # MD048/code-fence-style - Code fence style
86 | MD048:
87 | style: 'backtick' # Use backticks for code fences
88 |
89 | # MD049/emphasis-style - Emphasis style
90 | MD049:
91 | style: 'underscore' # Use _ for italic
92 |
93 | # MD050/strong-style - Strong style
94 | MD050:
95 | style: 'asterisk' # Use ** for bold
96 |
```
--------------------------------------------------------------------------------
/.changeset/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # Changesets
2 |
3 | Use `pnpm changeset` to create version entries. On release, run `pnpm release`.
4 |
```
--------------------------------------------------------------------------------
/scripts/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # SonarQube MCP Server Test Scripts
2 |
3 | This directory contains comprehensive test scripts for validating the SonarQube MCP Server deployment artifacts, including Kubernetes manifests, Helm charts, Terraform modules, and documentation.
4 |
5 | ## Overview
6 |
7 | The test suite ensures that all deployment artifacts are:
8 |
9 | - Syntactically correct
10 | - Follow security best practices
11 | - Work as expected in different configurations
12 | - Scale properly under load
13 | - Integrate correctly with monitoring systems
14 |
15 | ## Test Scripts
16 |
17 | ### 🚀 Master Test Runner
18 |
19 | **Script:** `run-all-tests.sh`
20 |
21 | Orchestrates all test suites in the correct order.
22 |
23 | ```bash
24 | # Run all tests
25 | ./scripts/run-all-tests.sh
26 |
27 | # Run specific test suite
28 | ./scripts/run-all-tests.sh --only helm
29 |
30 | # Skip cleanup for debugging
31 | ./scripts/run-all-tests.sh --skip-cleanup
32 | ```
33 |
34 | ### 📚 Documentation Validation
35 |
36 | **Script:** `validate-docs.sh`
37 |
38 | Validates documentation for:
39 |
40 | - Broken internal links
41 | - Invalid code examples
42 | - Missing required sections
43 | - Orphaned images
44 | - TODO markers
45 |
46 | ```bash
47 | ./scripts/validate-docs.sh
48 | ```
49 |
50 | ### 🔧 Terraform Validation
51 |
52 | **Script:** `validate-terraform.sh`
53 |
54 | Tests Terraform modules for:
55 |
56 | - Syntax validation
57 | - Formatting standards
58 | - Security issues (hardcoded secrets)
59 | - Required variables
60 | - Plan generation
61 |
62 | ```bash
63 | ./scripts/validate-terraform.sh
64 | ```
65 |
66 | ### 🎯 Helm Chart Testing
67 |
68 | **Script:** `test-helm-values.sh`
69 |
70 | Tests Helm chart with various configurations:
71 |
72 | - Minimal deployment
73 | - Production settings
74 | - High availability
75 | - Monitoring enabled
76 | - Ingress with TLS
77 |
78 | ```bash
79 | ./scripts/test-helm-values.sh
80 | ```
81 |
82 | ### ☸️ Kubernetes Deployment Testing
83 |
84 | **Script:** `test-k8s-deployment.sh`
85 |
86 | Basic Kubernetes deployment test using kind cluster.
87 |
88 | ```bash
89 | export SONARQUBE_TOKEN="your-token"
90 | ./scripts/test-k8s-deployment.sh
91 | ```
92 |
93 | **Script:** `test-k8s-helm-deployment.sh`
94 |
95 | Extended test that validates both Kustomize and Helm deployments.
96 |
97 | ```bash
98 | # Test both Kustomize and Helm
99 | ./scripts/test-k8s-helm-deployment.sh
100 |
101 | # Test only Helm
102 | ./scripts/test-k8s-helm-deployment.sh --skip-kustomize
103 |
104 | # Keep cluster for debugging
105 | ./scripts/test-k8s-helm-deployment.sh --keep-cluster
106 | ```
107 |
108 | ### 🔒 Security Scanning
109 |
110 | **Script:** `security-scan.sh`
111 |
112 | Comprehensive security scanning using:
113 |
114 | - Kubesec for Kubernetes manifests
115 | - Trivy for container vulnerabilities
116 | - Polaris for policy violations
117 | - Custom security checks
118 |
119 | ```bash
120 | ./scripts/security-scan.sh
121 | ```
122 |
123 | ### 📊 Monitoring Integration Tests
124 |
125 | **Script:** `test-monitoring-integration.sh`
126 |
127 | Tests monitoring endpoints and integration:
128 |
129 | - Health and readiness checks
130 | - Prometheus metrics format
131 | - Circuit breaker functionality
132 | - OpenTelemetry support
133 | - Performance metrics
134 |
135 | ```bash
136 | # Requires running service
137 | npm run dev
138 |
139 | # In another terminal
140 | ./scripts/test-monitoring-integration.sh
141 | ```
142 |
143 | ### ⚡ Load Testing
144 |
145 | **Script:** `load-test.sh`
146 |
147 | Tests auto-scaling behavior under load using k6 or Apache Bench.
148 |
149 | ```bash
150 | # Default configuration
151 | ./scripts/load-test.sh
152 |
153 | # Custom parameters
154 | CONCURRENT_USERS=100 DURATION=600 ./scripts/load-test.sh
155 | ```
156 |
157 | ### 🌐 Network Fixes
158 |
159 | **Script:** `fix-kind-dns.sh`
160 |
161 | Fixes DNS resolution issues in kind clusters (common on macOS).
162 |
163 | ```bash
164 | ./scripts/fix-kind-dns.sh cluster-name
165 | ```
166 |
167 | ## Helm Test Hooks
168 |
169 | Located in `helm/sonarqube-mcp/templates/tests/`:
170 |
171 | - `deployment-test.yaml` - Tests deployment readiness
172 | - `service-test.yaml` - Tests service connectivity
173 | - `config-test.yaml` - Tests configuration validity
174 |
175 | Run with:
176 |
177 | ```bash
178 | helm test release-name -n namespace
179 | ```
180 |
181 | ## Prerequisites
182 |
183 | ### Required Tools
184 |
185 | - Docker
186 | - kubectl
187 | - Helm 3+
188 |
189 | ### Optional Tools
190 |
191 | - kind (for Kubernetes tests)
192 | - Terraform (for Terraform validation)
193 | - k6 or Apache Bench (for load testing)
194 | - trivy (for container scanning)
195 | - Node.js (for documentation validation)
196 |
197 | ### Tool Installation
198 |
199 | ```bash
200 | # macOS with Homebrew
201 | brew install kubectl helm kind terraform k6 trivy
202 |
203 | # Install Polaris
204 | brew install FairwindsOps/tap/polaris
205 |
206 | # Install kubesec (downloaded automatically if not present)
207 | ```
208 |
209 | ## CI/CD Integration
210 |
211 | These scripts are designed to be integrated into CI/CD pipelines:
212 |
213 | ```yaml
214 | # Example GitHub Actions
215 | - name: Run all tests
216 | run: ./scripts/run-all-tests.sh
217 |
218 | # Example GitLab CI
219 | test:
220 | script:
221 | - ./scripts/validate-docs.sh
222 | - ./scripts/validate-terraform.sh
223 | - ./scripts/test-helm-values.sh
224 | - ./scripts/security-scan.sh
225 | ```
226 |
227 | ## Troubleshooting
228 |
229 | ### Common Issues
230 |
231 | 1. **Script not executable**
232 |
233 | ```bash
234 | chmod +x scripts/*.sh
235 | ```
236 |
237 | 2. **Kind cluster issues**
238 |
239 | ```bash
240 | kind delete cluster --name sonarqube-mcp-test
241 | ./scripts/test-k8s-deployment.sh
242 | ```
243 |
244 | 3. **DNS resolution in kind**
245 |
246 | ```bash
247 | ./scripts/fix-kind-dns.sh sonarqube-mcp-test
248 | ```
249 |
250 | 4. **Missing tools**
251 | Check prerequisites and install required tools.
252 |
253 | ## Best Practices
254 |
255 | 1. **Run tests before commits**
256 |
257 | ```bash
258 | ./scripts/run-all-tests.sh --only docs,helm,security
259 | ```
260 |
261 | 2. **Use in CI/CD**
262 | Integrate appropriate tests in your pipeline.
263 |
264 | 3. **Regular security scans**
265 |
266 | ```bash
267 | ./scripts/security-scan.sh
268 | ```
269 |
270 | 4. **Test configuration changes**
271 | Always test Helm values changes:
272 | ```bash
273 | ./scripts/test-helm-values.sh
274 | ```
275 |
276 | ## Contributing
277 |
278 | When adding new deployment artifacts:
279 |
280 | 1. Update relevant test scripts
281 | 2. Add new test cases if needed
282 | 3. Document in this README
283 | 4. Ensure tests pass before PR
284 |
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # SonarQube MCP Server
2 |
3 | [](https://github.com/sapientpants/sonarqube-mcp-server/actions/workflows/main.yml)
4 | [](https://sonarcloud.io/summary/new_code?id=sonarqube-mcp-server)
5 | [](https://sonarcloud.io/summary/new_code?id=sonarqube-mcp-server)
6 | [](https://sonarcloud.io/summary/new_code?id=sonarqube-mcp-server)
7 | [](https://sonarcloud.io/summary/new_code?id=sonarqube-mcp-server)
8 | [](https://sonarcloud.io/summary/new_code?id=sonarqube-mcp-server)
9 | [](https://www.npmjs.com/package/sonarqube-mcp-server)
10 | [](https://www.npmjs.com/package/sonarqube-mcp-server)
11 | [](https://github.com/sapientpants/sonarqube-mcp-server/blob/main/LICENSE)
12 |
13 | A Model Context Protocol (MCP) server that integrates with SonarQube to provide AI assistants with access to code quality metrics, issues, and analysis results.
14 |
15 | ## Table of Contents
16 |
17 | - [Overview](#overview)
18 | - [Documentation](#documentation)
19 | - [Compatibility](#compatibility)
20 | - [Quick Start](#quick-start)
21 | - [Installation](#installation)
22 | - [NPX](#npx-recommended)
23 | - [Docker](#docker-recommended-for-production)
24 | - [Local Development](#local-development)
25 | - [Configuration](#configuration)
26 | - [Environment Variables](#environment-variables)
27 | - [Authentication Methods](#authentication-methods)
28 | - [Available Tools](#available-tools)
29 | - [Usage Examples](#usage-examples)
30 | - [Architecture](#architecture)
31 | - [Development](#development)
32 | - [Troubleshooting](#troubleshooting)
33 | - [Contributing](#contributing)
34 | - [License](#license)
35 | - [External Resources](#external-resources)
36 |
37 | ## Overview
38 |
39 | The SonarQube MCP Server enables AI assistants to interact with SonarQube's code quality analysis capabilities through the Model Context Protocol. This integration allows AI assistants to:
40 |
41 | - 📊 **Retrieve code metrics and analysis results** - Access detailed quality metrics for your projects
42 | - 🐛 **Access and filter issues** - Search and filter code issues by severity, type, status, and more
43 | - 🔒 **Review security hotspots** - Find and manage security vulnerabilities with dedicated workflows
44 | - 🌿 **Analyze branches and PRs** - Review code quality in feature branches and pull requests
45 | - 📦 **Multi-project analysis** - Query issues and metrics across multiple projects simultaneously
46 | - ✅ **Check quality gates** - Monitor whether projects meet quality standards
47 | - 📈 **Analyze project quality over time** - Track metrics history and trends
48 | - 🔍 **View source code with issues** - See problematic code with highlighted issues
49 | - 🏥 **Monitor system health** - Check SonarQube instance status and availability
50 | - 🔄 **Enhanced error handling** - Clear error messages with solutions and automatic retry for transient failures
51 |
52 | ## Documentation
53 |
54 | ### Core Guides
55 |
56 | - **[Architecture Guide](docs/architecture.md)** - System architecture, design decisions, and component overview
57 | - **[Troubleshooting Guide](docs/troubleshooting.md)** - Common issues, debugging, and solutions
58 |
59 | ### Security & Authentication
60 |
61 | - **[Security Guide](docs/security.md)** - Authentication, authorization, and security best practices
62 |
63 | ## Compatibility
64 |
65 | For detailed information about MCP protocol version support and SDK compatibility, see [COMPATIBILITY.md](COMPATIBILITY.md).
66 |
67 | ## Quick Start
68 |
69 | ### Prerequisites
70 |
71 | - [Claude Desktop](https://claude.ai/download) installed
72 | - A SonarQube instance or [SonarCloud](https://sonarcloud.io) account
73 | - A SonarQube/SonarCloud authentication token
74 |
75 | ### 1. Get Your SonarQube Token
76 |
77 | **For SonarCloud:**
78 |
79 | 1. Log in to [SonarCloud](https://sonarcloud.io)
80 | 2. Go to **My Account** → **Security**
81 | 3. Generate a new token
82 |
83 | **For SonarQube:**
84 |
85 | 1. Log in to your SonarQube instance
86 | 2. Go to **My Account** → **Security**
87 | 3. Generate a new token
88 |
89 | ### 2. Configure Claude Desktop
90 |
91 | 1. Open Claude Desktop
92 | 2. Go to **Settings** → **Developer** → **Edit Config**
93 | 3. Add the SonarQube server configuration:
94 |
95 | ```json
96 | {
97 | "mcpServers": {
98 | "sonarqube": {
99 | "command": "npx",
100 | "args": ["-y", "sonarqube-mcp-server@latest"],
101 | "env": {
102 | "SONARQUBE_URL": "https://sonarcloud.io",
103 | "SONARQUBE_TOKEN": "your-token-here",
104 | "SONARQUBE_ORGANIZATION": "your-org (for SonarCloud)"
105 | }
106 | }
107 | }
108 | }
109 | ```
110 |
111 | **Alternative authentication methods:**
112 |
113 | Using Basic Authentication:
114 |
115 | ```json
116 | {
117 | "mcpServers": {
118 | "sonarqube": {
119 | "command": "npx",
120 | "args": ["-y", "sonarqube-mcp-server@latest"],
121 | "env": {
122 | "SONARQUBE_URL": "https://your-sonarqube.com",
123 | "SONARQUBE_USERNAME": "your-username",
124 | "SONARQUBE_PASSWORD": "your-password"
125 | }
126 | }
127 | }
128 | }
129 | ```
130 |
131 | Using System Passcode:
132 |
133 | ```json
134 | {
135 | "mcpServers": {
136 | "sonarqube": {
137 | "command": "npx",
138 | "args": ["-y", "sonarqube-mcp-server@latest"],
139 | "env": {
140 | "SONARQUBE_URL": "https://your-sonarqube.com",
141 | "SONARQUBE_PASSCODE": "your-system-passcode"
142 | }
143 | }
144 | }
145 | }
146 | ```
147 |
148 | 1. Restart Claude Desktop
149 |
150 | ### 3. Start Using
151 |
152 | Ask Claude to analyze your SonarQube projects:
153 |
154 | ```
155 | "List all my SonarQube projects"
156 | "Show me critical issues in project xyz"
157 | "What's the code coverage for project xyz?"
158 | "Check the quality gate status for project xyz"
159 | "Retrieve security hotspots in project xyz and create a plan to address them"
160 | "Retrieve the issues for pr 123 in project xyz and create a plan to address them"
161 | ```
162 |
163 | ## Installation
164 |
165 | ### NPX (Recommended)
166 |
167 | The simplest way to use the SonarQube MCP Server is through npx:
168 |
169 | ```json
170 | {
171 | "mcpServers": {
172 | "sonarqube": {
173 | "command": "npx",
174 | "args": ["-y", "sonarqube-mcp-server@latest"],
175 | "env": {
176 | "SONARQUBE_URL": "https://sonarqube.example.com",
177 | "SONARQUBE_TOKEN": "your-sonarqube-token",
178 | "SONARQUBE_ORGANIZATION": "your-organization-key"
179 | }
180 | }
181 | }
182 | }
183 | ```
184 |
185 | ### Docker (Recommended for Production)
186 |
187 | Docker provides the most reliable deployment method by packaging all dependencies and ensuring consistent behavior across different environments.
188 |
189 | > **Enterprise Deployment**: For production deployments with Kubernetes, Helm charts, and cloud-specific configurations, see our comprehensive [Deployment Guide](docs/deployment.md).
190 |
191 | #### Quick Start with Docker
192 |
193 | **For stdio transport (Claude Desktop):**
194 |
195 | ```json
196 | {
197 | "mcpServers": {
198 | "sonarqube": {
199 | "command": "docker",
200 | "args": [
201 | "run",
202 | "-i",
203 | "--rm",
204 | "-e",
205 | "SONARQUBE_URL",
206 | "-e",
207 | "SONARQUBE_TOKEN",
208 | "-e",
209 | "SONARQUBE_ORGANIZATION",
210 | "sapientpants/sonarqube-mcp-server:latest"
211 | ],
212 | "env": {
213 | "SONARQUBE_URL": "https://sonarqube.example.com",
214 | "SONARQUBE_TOKEN": "your-sonarqube-token",
215 | "SONARQUBE_ORGANIZATION": "your-organization-key"
216 | }
217 | }
218 | }
219 | }
220 | ```
221 |
222 | #### Docker Hub Images
223 |
224 | Official images are available on Docker Hub: [`sapientpants/sonarqube-mcp-server`](https://hub.docker.com/r/sapientpants/sonarqube-mcp-server)
225 |
226 | **Available tags:**
227 |
228 | - `latest` - Latest stable release
229 | - `1.6.0` - Specific version (recommended for production)
230 | - `1.6` - Latest patch version of 1.6.x
231 | - `1` - Latest minor version of 1.x.x
232 |
233 | **Pull the image:**
234 |
235 | ```bash
236 | docker pull sapientpants/sonarqube-mcp-server:latest
237 | ```
238 |
239 | #### Advanced Docker Configuration
240 |
241 | **With logging enabled:**
242 |
243 | ```json
244 | {
245 | "mcpServers": {
246 | "sonarqube": {
247 | "command": "docker",
248 | "args": [
249 | "run",
250 | "-i",
251 | "--rm",
252 | "-v",
253 | "/tmp/sonarqube-logs:/logs",
254 | "-e",
255 | "SONARQUBE_URL",
256 | "-e",
257 | "SONARQUBE_TOKEN",
258 | "-e",
259 | "SONARQUBE_ORGANIZATION",
260 | "-e",
261 | "LOG_FILE=/logs/sonarqube-mcp.log",
262 | "-e",
263 | "LOG_LEVEL=INFO",
264 | "sapientpants/sonarqube-mcp-server:latest"
265 | ],
266 | "env": {
267 | "SONARQUBE_URL": "https://sonarqube.example.com",
268 | "SONARQUBE_TOKEN": "your-sonarqube-token",
269 | "SONARQUBE_ORGANIZATION": "your-organization-key"
270 | }
271 | }
272 | }
273 | }
274 | ```
275 |
276 | **Using Docker Compose:**
277 |
278 | ```yaml
279 | version: '3.8'
280 | services:
281 | sonarqube-mcp:
282 | image: sapientpants/sonarqube-mcp-server:latest
283 | environment:
284 | - SONARQUBE_URL=https://sonarqube.example.com
285 | - SONARQUBE_TOKEN=${SONARQUBE_TOKEN}
286 | - SONARQUBE_ORGANIZATION=${SONARQUBE_ORGANIZATION}
287 | - LOG_FILE=/logs/sonarqube-mcp.log
288 | - LOG_LEVEL=INFO
289 | volumes:
290 | - ./logs:/logs
291 | stdin_open: true
292 | tty: true
293 | ```
294 |
295 | #### Building Your Own Docker Image
296 |
297 | If you need to customize the server, you can build your own image:
298 |
299 | ```bash
300 | # Clone the repository
301 | git clone https://github.com/sapientpants/sonarqube-mcp-server.git
302 | cd sonarqube-mcp-server
303 |
304 | # Build the Docker image
305 | docker build -t my-sonarqube-mcp-server .
306 |
307 | # Run your custom image
308 | docker run -i --rm \
309 | -e SONARQUBE_URL="https://sonarqube.example.com" \
310 | -e SONARQUBE_TOKEN="your-token" \
311 | my-sonarqube-mcp-server
312 | ```
313 |
314 | #### Docker Best Practices
315 |
316 | 1. **Version Pinning**: Always use specific version tags in production:
317 |
318 | ```bash
319 | sapientpants/sonarqube-mcp-server:1.6.0
320 | ```
321 |
322 | 2. **Resource Limits**: Set appropriate resource limits:
323 |
324 | ```bash
325 | docker run -i --rm \
326 | --memory="256m" \
327 | --cpus="0.5" \
328 | sapientpants/sonarqube-mcp-server:1.6.0
329 | ```
330 |
331 | 3. **Security**: Run as non-root user (default in our image):
332 |
333 | ```bash
334 | docker run -i --rm \
335 | --user node \
336 | sapientpants/sonarqube-mcp-server:1.6.0
337 | ```
338 |
339 | 4. **Health Checks**: The container includes a health check that verifies the Node.js process is running
340 |
341 | ### Local Development
342 |
343 | For development or customization:
344 |
345 | ```json
346 | {
347 | "mcpServers": {
348 | "sonarqube": {
349 | "command": "node",
350 | "args": ["/path/to/sonarqube-mcp-server/dist/index.js"],
351 | "env": {
352 | "SONARQUBE_URL": "https://sonarqube.example.com",
353 | "SONARQUBE_TOKEN": "your-sonarqube-token",
354 | "SONARQUBE_ORGANIZATION": "your-organization-key"
355 | }
356 | }
357 | }
358 | }
359 | ```
360 |
361 | ## Configuration
362 |
363 | ### Environment Variables
364 |
365 | #### Authentication (choose one method)
366 |
367 | | Variable | Description | Required | Default |
368 | | ------------------------ | --------------------------------------------- | -------- | ------- |
369 | | **Token Authentication** | | | |
370 | | `SONARQUBE_TOKEN` | Authentication token for SonarQube API access | ✅ Yes\* | - |
371 | | **Basic Authentication** | | | |
372 | | `SONARQUBE_USERNAME` | Username for Basic authentication | ✅ Yes\* | - |
373 | | `SONARQUBE_PASSWORD` | Password for Basic authentication | ✅ Yes\* | - |
374 | | **System Passcode** | | | |
375 | | `SONARQUBE_PASSCODE` | System passcode for SonarQube authentication | ✅ Yes\* | - |
376 |
377 | \*One authentication method is required. Token authentication takes priority if multiple methods are configured.
378 |
379 | #### Connection Settings
380 |
381 | | Variable | Description | Required | Default |
382 | | ------------------------ | -------------------------------------------------------- | --------- | ----------------------- |
383 | | `SONARQUBE_URL` | URL of your SonarQube instance | ❌ No | `https://sonarcloud.io` |
384 | | `SONARQUBE_ORGANIZATION` | Organization key (required for SonarCloud) | ❌ No\*\* | - |
385 | | `LOG_FILE` | Path to write log files (e.g., `/tmp/sonarqube-mcp.log`) | ❌ No | - |
386 | | `LOG_LEVEL` | Minimum log level (DEBUG, INFO, WARN, ERROR) | ❌ No | `DEBUG` |
387 |
388 | \*\*Required when using SonarCloud
389 |
390 | #### HTTP Transport Settings (Advanced)
391 |
392 | By default, the server uses stdio transport for communication with Claude Desktop. For programmatic access or running as a web service, HTTP transport is available:
393 |
394 | | Variable | Description | Required | Default |
395 | | ------------------------------------------ | -------------------------------------------- | -------- | ----------- |
396 | | `MCP_TRANSPORT_TYPE` | Transport type (`stdio` or `http`) | ❌ No | `stdio` |
397 | | `MCP_HTTP_PORT` | Port for HTTP server | ❌ No | `3000` |
398 | | `MCP_HTTP_SESSION_TIMEOUT` | Session timeout in milliseconds | ❌ No | `1800000` |
399 | | `MCP_HTTP_ALLOWED_HOSTS` | Comma-separated list of allowed hosts | ❌ No | `localhost` |
400 | | `MCP_HTTP_ALLOWED_ORIGINS` | Comma-separated list of allowed CORS origins | ❌ No | `*` |
401 | | `MCP_HTTP_ENABLE_DNS_REBINDING_PROTECTION` | Enable DNS rebinding protection | ❌ No | `false` |
402 |
403 | ### Authentication Methods
404 |
405 | The server supports three authentication methods, with important differences between SonarQube versions:
406 |
407 | #### 1. Token Authentication (Recommended)
408 |
409 | ##### SonarQube 10.0+ (Bearer Token)
410 |
411 | - Starting with SonarQube 10.0, Bearer token authentication is the recommended approach
412 | - Most secure and flexible option
413 | - Tokens can have limited permissions
414 | - Configuration:
415 | ```json
416 | {
417 | "env": {
418 | "SONARQUBE_TOKEN": "your-token-here"
419 | }
420 | }
421 | ```
422 |
423 | ##### SonarQube < 10.0 (Token as Username)
424 |
425 | - For versions before 10.0, tokens must be sent as the username in Basic authentication
426 | - No password is required when using a token as username
427 | - The server automatically handles this based on your SonarQube version
428 | - Configuration remains the same - just use `SONARQUBE_USERNAME` with the token value:
429 | ```json
430 | {
431 | "env": {
432 | "SONARQUBE_USERNAME": "your-token-here"
433 | }
434 | }
435 | ```
436 |
437 | #### 2. Basic Authentication
438 |
439 | - Traditional username and password authentication
440 | - Suitable for self-hosted SonarQube instances
441 | - May not work with SonarCloud if 2FA is enabled
442 | - Configuration:
443 | ```json
444 | {
445 | "env": {
446 | "SONARQUBE_USERNAME": "your-username",
447 | "SONARQUBE_PASSWORD": "your-password"
448 | }
449 | }
450 | ```
451 |
452 | #### 3. System Passcode
453 |
454 | - Special authentication for SonarQube system administration
455 | - Typically used for automated deployment scenarios
456 | - Configuration:
457 | ```json
458 | {
459 | "env": {
460 | "SONARQUBE_PASSCODE": "your-system-passcode"
461 | }
462 | }
463 | ```
464 |
465 | **Note:** Token authentication takes priority if multiple authentication methods are configured. The server will automatically use the appropriate authentication strategy based on your SonarQube version.
466 |
467 | ### SonarCloud vs SonarQube
468 |
469 | **For SonarCloud:**
470 |
471 | - Set `SONARQUBE_URL` to `https://sonarcloud.io`
472 | - `SONARQUBE_ORGANIZATION` is required
473 | - Token authentication is recommended
474 |
475 | **For SonarQube Server:**
476 |
477 | - Set `SONARQUBE_URL` to your instance URL
478 | - `SONARQUBE_ORGANIZATION` is typically not needed
479 | - All authentication methods are supported
480 |
481 | ### HTTP Transport Mode
482 |
483 | The server supports HTTP transport for programmatic access and web service deployments. This enables integration with custom clients and web applications.
484 |
485 | #### Running as an HTTP Server
486 |
487 | Start the server with HTTP transport:
488 |
489 | ```bash
490 | # Using environment variables
491 | MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3000 npx sonarqube-mcp-server
492 |
493 | # With Docker
494 | docker run -i --rm \
495 | -p 3000:3000 \
496 | -e MCP_TRANSPORT_TYPE=http \
497 | -e MCP_HTTP_PORT=3000 \
498 | -e SONARQUBE_URL=https://sonarcloud.io \
499 | -e SONARQUBE_TOKEN=your-token \
500 | sapientpants/sonarqube-mcp-server:latest
501 | ```
502 |
503 | #### HTTP API Endpoints
504 |
505 | When running in HTTP mode, the server exposes the following endpoints:
506 |
507 | - `GET /health` - Health check endpoint
508 | - `POST /session` - Create a new session
509 | - `DELETE /session/:sessionId` - Close a session
510 | - `POST /mcp` - Execute MCP requests
511 | - `GET /events/:sessionId` - Server-sent events for notifications
512 |
513 | #### Example HTTP Client
514 |
515 | See [examples/http-client.ts](examples/http-client.ts) for a complete TypeScript client example.
516 |
517 | Basic usage with curl:
518 |
519 | ```bash
520 | # Health check
521 | curl http://localhost:3000/health
522 |
523 | # Create session
524 | SESSION_ID=$(curl -X POST http://localhost:3000/session | jq -r .sessionId)
525 |
526 | # Execute MCP request
527 | curl -X POST http://localhost:3000/mcp \
528 | -H "Content-Type: application/json" \
529 | -d "{
530 | \"sessionId\": \"$SESSION_ID\",
531 | \"method\": \"tools/list\",
532 | \"params\": {}
533 | }"
534 |
535 | # Close session
536 | curl -X DELETE http://localhost:3000/session/$SESSION_ID
537 | ```
538 |
539 | #### Security Considerations
540 |
541 | When running in HTTP mode:
542 |
543 | 1. **Enable DNS rebinding protection** for public deployments:
544 |
545 | ```bash
546 | MCP_HTTP_ENABLE_DNS_REBINDING_PROTECTION=true
547 | ```
548 |
549 | 2. **Configure CORS** for browser-based clients:
550 |
551 | ```bash
552 | MCP_HTTP_ALLOWED_ORIGINS=https://yourapp.com,https://anotherapp.com
553 | ```
554 |
555 | 3. **Set session timeouts** appropriately:
556 |
557 | ```bash
558 | MCP_HTTP_SESSION_TIMEOUT=900000 # 15 minutes
559 | ```
560 |
561 | 4. **Use HTTPS** in production (configure through a reverse proxy like nginx)
562 |
563 | ### Elicitation Configuration (Experimental)
564 |
565 | The server supports interactive user input through MCP's elicitation capability. This feature is opt-in and requires compatible MCP clients.
566 |
567 | **Environment Variables:**
568 |
569 | - `SONARQUBE_MCP_ELICITATION`: Set to `true` to enable elicitation
570 | - `SONARQUBE_MCP_BULK_THRESHOLD`: Number of items before confirmation (default: 5)
571 | - `SONARQUBE_MCP_REQUIRE_COMMENTS`: Set to `true` to require comments for resolutions
572 | - `SONARQUBE_MCP_INTERACTIVE_SEARCH`: Set to `true` for interactive disambiguation
573 |
574 | **Example Configuration:**
575 |
576 | ```json
577 | {
578 | "mcpServers": {
579 | "sonarqube": {
580 | "command": "npx",
581 | "args": ["-y", "sonarqube-mcp-server@latest"],
582 | "env": {
583 | "SONARQUBE_URL": "https://sonarcloud.io",
584 | "SONARQUBE_TOKEN": "your-token",
585 | "SONARQUBE_MCP_ELICITATION": "true",
586 | "SONARQUBE_MCP_BULK_THRESHOLD": "10",
587 | "SONARQUBE_MCP_REQUIRE_COMMENTS": "true"
588 | }
589 | }
590 | }
591 | }
592 | ```
593 |
594 | **Features When Enabled:**
595 |
596 | 1. **Bulk Operation Confirmation**: Prompts for confirmation before marking multiple issues
597 | 2. **Comment Collection**: Collects explanatory comments when marking issues as false positive or won't fix
598 | 3. **Authentication Setup**: Guides through authentication setup when credentials are missing
599 | 4. **Search Disambiguation**: Helps select from multiple matching components or projects
600 |
601 | **Note:** This feature requires MCP clients that support elicitation. Not all clients may support this capability.
602 |
603 | ### Logging Configuration
604 |
605 | The server supports file-based logging for debugging and monitoring. Since MCP servers use stdout for protocol communication, logs are written to a file instead of stdout/stderr to avoid interference.
606 |
607 | **Enable Logging:**
608 |
609 | ```json
610 | {
611 | "mcpServers": {
612 | "sonarqube": {
613 | "command": "npx",
614 | "args": ["-y", "sonarqube-mcp-server@latest"],
615 | "env": {
616 | "SONARQUBE_URL": "https://sonarcloud.io",
617 | "SONARQUBE_TOKEN": "your-token-here",
618 | "SONARQUBE_ORGANIZATION": "your-org",
619 | "LOG_FILE": "/tmp/sonarqube-mcp.log",
620 | "LOG_LEVEL": "INFO"
621 | }
622 | }
623 | }
624 | }
625 | ```
626 |
627 | **Log Levels:**
628 |
629 | - `DEBUG`: Detailed information for debugging
630 | - `INFO`: General information about server operation
631 | - `WARN`: Warning events that might lead to issues
632 | - `ERROR`: Error events (server continues running)
633 |
634 | **Example Log Output:**
635 |
636 | ```
637 | 2024-01-15T10:30:45.123Z INFO [index] Starting SonarQube MCP server
638 | 2024-01-15T10:30:45.234Z INFO [index] Environment variables validated successfully
639 | 2024-01-15T10:30:45.345Z INFO [index] SonarQube client created successfully
640 | 2024-01-15T10:30:45.456Z INFO [index] SonarQube MCP server started successfully
641 | 2024-01-15T10:30:50.123Z DEBUG [index] Handling SonarQube projects request
642 | 2024-01-15T10:30:50.567Z INFO [index] Successfully retrieved projects {"count": 5}
643 | ```
644 |
645 | ## Available Tools
646 |
647 | ### Permission Requirements
648 |
649 | Different SonarQube tools require different permission levels:
650 |
651 | **Tools requiring Admin permissions:**
652 |
653 | - `projects` - Lists all SonarQube projects with metadata (visibility, lastAnalysisDate, revision)
654 |
655 | **Tools accessible to all users:**
656 |
657 | - `components` - Search and navigate projects, directories, and files (requires 'Browse' permission on at least one project)
658 | - All other tools require appropriate permissions based on the resources being accessed
659 |
660 | #### Listing Projects
661 |
662 | **For Administrators:**
663 | Use the `projects` tool to get full project metadata including visibility, last analysis date, and revision info.
664 |
665 | **For All Users:**
666 | Use the `components` tool with project qualifier:
667 |
668 | - "List all projects I have access to" → `components` with `qualifiers: ['TRK']`
669 | - "Search for projects containing 'mobile'" → `components` with `query: 'mobile', qualifiers: ['TRK']`
670 |
671 | The `components` tool provides a more accessible alternative for non-admin users to discover projects they have access to.
672 |
673 | ### Project Management
674 |
675 | #### `projects`
676 |
677 | List all SonarQube projects with pagination support.
678 |
679 | **Parameters:**
680 |
681 | - `page` (optional): Page number for results pagination
682 | - `page_size` (optional): Number of items per page
683 |
684 | ### Metrics and Measures
685 |
686 | #### `metrics`
687 |
688 | Get available metrics from SonarQube.
689 |
690 | **Parameters:**
691 |
692 | - `page` (optional): Page number for results pagination
693 | - `page_size` (optional): Number of items per page
694 |
695 | #### `measures_component`
696 |
697 | Get measures for a specific component.
698 |
699 | **Parameters:**
700 |
701 | - `component` (required): Component key
702 | - `metric_keys` (required): Array of metric keys
703 | - `additional_fields` (optional): Additional fields to return
704 | - `branch` (optional): Branch name
705 | - `pull_request` (optional): Pull request key
706 | - `period` (optional): Period index
707 |
708 | #### `measures_components`
709 |
710 | Get measures for multiple components.
711 |
712 | **Parameters:**
713 |
714 | - `component_keys` (required): Array of component keys
715 | - `metric_keys` (required): Array of metric keys
716 | - Additional parameters same as `measures_component`
717 | - `page` (optional): Page number
718 | - `page_size` (optional): Items per page
719 |
720 | #### `measures_history`
721 |
722 | Get measures history for a component.
723 |
724 | **Parameters:**
725 |
726 | - `component` (required): Component key
727 | - `metrics` (required): Array of metric keys
728 | - `from` (optional): Start date (YYYY-MM-DD)
729 | - `to` (optional): End date (YYYY-MM-DD)
730 | - `branch` (optional): Branch name
731 | - `pull_request` (optional): Pull request key
732 | - `page` (optional): Page number
733 | - `page_size` (optional): Items per page
734 |
735 | ### Issue Management
736 |
737 | #### `issues`
738 |
739 | Search and filter SonarQube issues by severity, status, assignee, tag, file path, and more. Critical for dashboards, targeted clean-up sprints, security audits, and regression testing. Supports faceted search for aggregations.
740 |
741 | **Component/File Path Filters:**
742 |
743 | - `project_key` (optional): Single project key (backward compatible)
744 | - `projects` (optional): Array of project keys for multi-project analysis
745 | - `component_keys` (optional): Array of component keys (file paths, directories, or modules) - use this to filter issues by specific files or folders
746 | - `components` (optional): Alias for component_keys
747 | - `on_component_only` (optional): Boolean to return only issues on specified components, not sub-components
748 |
749 | **Branch/PR Support:**
750 |
751 | - `branch` (optional): Branch name for branch analysis
752 | - `pull_request` (optional): Pull request ID for PR analysis
753 |
754 | **Issue Filters:**
755 |
756 | - `issues` (optional): Array of specific issue keys to retrieve
757 | - `severity` (optional): Single severity (deprecated, use severities)
758 | - `severities` (optional): Array of severities (INFO, MINOR, MAJOR, CRITICAL, BLOCKER)
759 | - `statuses` (optional): Array of statuses (OPEN, CONFIRMED, REOPENED, RESOLVED, CLOSED)
760 | - `resolutions` (optional): Array of resolutions (FALSE-POSITIVE, WONTFIX, FIXED, REMOVED)
761 | - `resolved` (optional): Boolean filter for resolved/unresolved
762 | - `types` (optional): Array of types (CODE_SMELL, BUG, VULNERABILITY, SECURITY_HOTSPOT)
763 |
764 | **Clean Code Taxonomy (SonarQube 10.x+):**
765 |
766 | - `clean_code_attribute_categories` (optional): Array (ADAPTABLE, CONSISTENT, INTENTIONAL, RESPONSIBLE)
767 | - `impact_severities` (optional): Array (HIGH, MEDIUM, LOW)
768 | - `impact_software_qualities` (optional): Array (MAINTAINABILITY, RELIABILITY, SECURITY)
769 | - `issue_statuses` (optional): Array of new issue status values
770 |
771 | **Rules and Tags:**
772 |
773 | - `rules` (optional): Array of rule keys
774 | - `tags` (optional): Array of issue tags - essential for security audits, regression testing, and categorized analysis
775 |
776 | **Date Filters:**
777 |
778 | - `created_after` (optional): Issues created after date (YYYY-MM-DD)
779 | - `created_before` (optional): Issues created before date (YYYY-MM-DD)
780 | - `created_at` (optional): Issues created on date (YYYY-MM-DD)
781 | - `created_in_last` (optional): Issues created in last period (e.g., "30d", "1m")
782 |
783 | **Assignment:**
784 |
785 | - `assigned` (optional): Boolean filter for assigned/unassigned
786 | - `assignees` (optional): Array of assignee logins - critical for targeted clean-up sprints and workload analysis
787 | - `author` (optional): Single author login
788 | - `authors` (optional): Array of author logins
789 |
790 | **Security Standards:**
791 |
792 | - `cwe` (optional): Array of CWE identifiers
793 | - `owasp_top10` (optional): Array of OWASP Top 10 categories
794 | - `owasp_top10_v2021` (optional): Array of OWASP Top 10 2021 categories
795 | - `sans_top25` (optional): Array of SANS Top 25 categories
796 | - `sonarsource_security` (optional): Array of SonarSource security categories
797 | - `sonarsource_security_category` (optional): Additional security categories
798 |
799 | **Other Filters:**
800 |
801 | - `languages` (optional): Array of programming languages
802 | - `facets` (optional): Array of facets to aggregate
803 | - `facet_mode` (optional): Facet aggregation mode ('effort' or 'count')
804 | - `since_leak_period` (optional): Boolean for leak period filter (deprecated)
805 | - `in_new_code_period` (optional): Boolean for new code period filter
806 |
807 | **Sorting:**
808 |
809 | - `s` (optional): Sort field (e.g., 'SEVERITY', 'CREATION_DATE', 'UPDATE_DATE')
810 | - `asc` (optional): Boolean for ascending sort direction (default: false)
811 |
812 | **Response Control:**
813 |
814 | - `additional_fields` (optional): Array of additional fields to include
815 | - `page` (optional): Page number for pagination
816 | - `page_size` (optional): Number of items per page
817 |
818 | **Faceted Search (Dashboard Support):**
819 |
820 | - `facets` (optional): Array of facets to compute for aggregations. Available facets: severities, statuses, resolutions, rules, tags, types, authors, assignees, languages, etc.
821 | - `facet_mode` (optional): Mode for facet computation: 'count' (number of issues) or 'effort' (remediation effort)
822 |
823 | **Example Use Cases:**
824 |
825 | 1. **Dashboard Query** - Get issue counts by severity and assignee:
826 |
827 | ```json
828 | {
829 | "project_key": "my-project",
830 | "facets": ["severities", "assignees", "tags"],
831 | "facet_mode": "count"
832 | }
833 | ```
834 |
835 | 1. **Security Audit** - Find critical security issues in authentication modules:
836 |
837 | ```json
838 | {
839 | "project_key": "my-project",
840 | "component_keys": ["src/auth/", "src/security/"],
841 | "tags": ["security", "vulnerability"],
842 | "severities": ["CRITICAL", "BLOCKER"],
843 | "statuses": ["OPEN", "REOPENED"]
844 | }
845 | ```
846 |
847 | 1. **Sprint Planning** - Get open issues for specific team members:
848 |
849 | ```json
850 | {
851 | "project_key": "my-project",
852 | "assignees": ["[email protected]", "[email protected]"],
853 | "statuses": ["OPEN", "CONFIRMED"],
854 | "facets": ["severities", "types"],
855 | "facet_mode": "effort"
856 | }
857 | ```
858 |
859 | 1. **File-Specific Analysis** - Issues in a specific file:
860 |
861 | ```json
862 | {
863 | "project_key": "my-project",
864 | "component_keys": ["src/main/java/com/example/PaymentService.java"],
865 | "on_component_only": true
866 | }
867 | ```
868 |
869 | ### Component Navigation
870 |
871 | #### `components`
872 |
873 | Search and navigate SonarQube components (projects, directories, files). Supports text search, filtering by type/language, and tree navigation.
874 |
875 | **Search Parameters:**
876 |
877 | - `query` (optional): Text search query
878 | - `qualifiers` (optional): Array of component types (TRK, DIR, FIL, UTS, BRC, APP, VW, SVW, LIB)
879 | - `language` (optional): Programming language filter
880 |
881 | **Tree Navigation Parameters:**
882 |
883 | - `component` (optional): Component key for tree navigation
884 | - `strategy` (optional): Tree traversal strategy ('all', 'children', 'leaves')
885 |
886 | **Common Parameters:**
887 |
888 | - `asc` (optional): Sort ascending/descending
889 | - `ps` (optional): Page size (default: 100, max: 500)
890 | - `p` (optional): Page number
891 | - `branch` (optional): Branch name
892 | - `pullRequest` (optional): Pull request ID
893 |
894 | **Component Qualifiers:**
895 |
896 | - `TRK`: Project
897 | - `DIR`: Directory
898 | - `FIL`: File
899 | - `UTS`: Unit Test
900 | - `BRC`: Branch
901 | - `APP`: Application
902 | - `VW`: View
903 | - `SVW`: Sub-view
904 | - `LIB`: Library
905 |
906 | **Example Use Cases:**
907 |
908 | 1. **Find specific files:**
909 |
910 | ```json
911 | {
912 | "query": "UserService",
913 | "qualifiers": ["FIL"]
914 | }
915 | ```
916 |
917 | 1. **List all test files in a project:**
918 |
919 | ```json
920 | {
921 | "component": "my-project",
922 | "qualifiers": ["UTS"]
923 | }
924 | ```
925 |
926 | 1. **Navigate directory structure:**
927 |
928 | ```json
929 | {
930 | "component": "my-project:src/main",
931 | "strategy": "children",
932 | "qualifiers": ["DIR", "FIL"]
933 | }
934 | ```
935 |
936 | 1. **Search for components by language:**
937 |
938 | ```json
939 | {
940 | "language": "java",
941 | "qualifiers": ["FIL"],
942 | "query": "Controller"
943 | }
944 | ```
945 |
946 | 1. **Get project list:**
947 |
948 | ```json
949 | {
950 | "qualifiers": ["TRK"]
951 | }
952 | ```
953 |
954 | ### Security Hotspots
955 |
956 | #### `hotspots`
957 |
958 | Search for security hotspots with specialized filters for security review workflows.
959 |
960 | **Parameters:**
961 |
962 | - `project_key` (optional): Project key to filter hotspots
963 | - `branch` (optional): Branch name for branch analysis
964 | - `pull_request` (optional): Pull request ID for PR analysis
965 | - `status` (optional): Hotspot status (TO_REVIEW, REVIEWED)
966 | - `resolution` (optional): Hotspot resolution (FIXED, SAFE)
967 | - `files` (optional): Array of file paths to filter
968 | - `assigned_to_me` (optional): Boolean to show only assigned hotspots
969 | - `since_leak_period` (optional): Boolean for leak period filter
970 | - `in_new_code_period` (optional): Boolean for new code period filter
971 | - `page` (optional): Page number for pagination
972 | - `page_size` (optional): Number of items per page
973 |
974 | #### `hotspot`
975 |
976 | Get detailed information about a specific security hotspot including security context.
977 |
978 | **Parameters:**
979 |
980 | - `hotspot_key` (required): The unique key of the hotspot
981 |
982 | **Returns:**
983 |
984 | - Detailed hotspot information including:
985 | - Security category and vulnerability probability
986 | - Rule information and security context
987 | - Changelog and comments
988 | - Code flows and locations
989 |
990 | #### `update_hotspot_status`
991 |
992 | Update the status of a security hotspot (requires appropriate permissions).
993 |
994 | **Parameters:**
995 |
996 | - `hotspot_key` (required): The unique key of the hotspot
997 | - `status` (required): New status (TO_REVIEW, REVIEWED)
998 | - `resolution` (optional): Resolution when status is REVIEWED (FIXED, SAFE)
999 | - `comment` (optional): Comment explaining the status change
1000 |
1001 | ### Quality Gates
1002 |
1003 | #### `quality_gates`
1004 |
1005 | List available quality gates.
1006 |
1007 | No parameters required.
1008 |
1009 | #### `quality_gate`
1010 |
1011 | Get quality gate conditions.
1012 |
1013 | **Parameters:**
1014 |
1015 | - `id` (required): Quality gate ID
1016 |
1017 | #### `quality_gate_status`
1018 |
1019 | Get project quality gate status.
1020 |
1021 | **Parameters:**
1022 |
1023 | - `project_key` (required): Project key
1024 | - `branch` (optional): Branch name
1025 | - `pull_request` (optional): Pull request key
1026 |
1027 | ### Source Code
1028 |
1029 | #### `source_code`
1030 |
1031 | View source code with issues highlighted.
1032 |
1033 | **Parameters:**
1034 |
1035 | - `key` (required): File key
1036 | - `from` (optional): Start line
1037 | - `to` (optional): End line
1038 | - `branch` (optional): Branch name
1039 | - `pull_request` (optional): Pull request key
1040 |
1041 | #### `scm_blame`
1042 |
1043 | Get SCM blame information for source code.
1044 |
1045 | **Parameters:**
1046 |
1047 | - Same as `source_code`
1048 |
1049 | ### System Monitoring
1050 |
1051 | #### `system_health`
1052 |
1053 | Get the health status of the SonarQube instance.
1054 |
1055 | No parameters required.
1056 |
1057 | #### `system_status`
1058 |
1059 | Get the status of the SonarQube instance.
1060 |
1061 | No parameters required.
1062 |
1063 | #### `system_ping`
1064 |
1065 | Ping the SonarQube instance to check if it is up.
1066 |
1067 | No parameters required.
1068 |
1069 | ### Issue Resolution and Management
1070 |
1071 | #### `markIssueFalsePositive`
1072 |
1073 | Mark an issue as false positive.
1074 |
1075 | **Parameters:**
1076 |
1077 | - `issue_key` (required): The key of the issue to mark
1078 | - `comment` (optional): Comment explaining why it's a false positive
1079 |
1080 | #### `markIssueWontFix`
1081 |
1082 | Mark an issue as won't fix.
1083 |
1084 | **Parameters:**
1085 |
1086 | - `issue_key` (required): The key of the issue to mark
1087 | - `comment` (optional): Comment explaining why it won't be fixed
1088 |
1089 | #### `markIssuesFalsePositive`
1090 |
1091 | Mark multiple issues as false positive in bulk.
1092 |
1093 | **Parameters:**
1094 |
1095 | - `issue_keys` (required): Array of issue keys to mark
1096 | - `comment` (optional): Comment applying to all issues
1097 |
1098 | #### `markIssuesWontFix`
1099 |
1100 | Mark multiple issues as won't fix in bulk.
1101 |
1102 | **Parameters:**
1103 |
1104 | - `issue_keys` (required): Array of issue keys to mark
1105 | - `comment` (optional): Comment applying to all issues
1106 |
1107 | #### `addCommentToIssue`
1108 |
1109 | Add a comment to a SonarQube issue.
1110 |
1111 | **Parameters:**
1112 |
1113 | - `issue_key` (required): The key of the issue to comment on
1114 | - `text` (required): The comment text (supports markdown formatting)
1115 |
1116 | #### `assignIssue`
1117 |
1118 | Assign a SonarQube issue to a user or unassign it.
1119 |
1120 | **Parameters:**
1121 |
1122 | - `issueKey` (required): The key of the issue to assign
1123 | - `assignee` (optional): Username of the assignee. Leave empty to unassign the issue
1124 |
1125 | **Example usage:**
1126 |
1127 | ```json
1128 | {
1129 | "issueKey": "PROJECT-123",
1130 | "assignee": "john.doe"
1131 | }
1132 | ```
1133 |
1134 | ## Usage Examples
1135 |
1136 | ### Basic Project Analysis
1137 |
1138 | ```
1139 | "List all my SonarQube projects"
1140 | "Show me the code coverage for project xyz"
1141 | "What metrics are available for analysis?"
1142 | ```
1143 |
1144 | ### Issue Investigation
1145 |
1146 | ```
1147 | "Show me all critical bugs in project abc"
1148 | "Find security vulnerabilities in the main branch"
1149 | "List all code smells created in the last week"
1150 | "Show unresolved issues assigned to john.doe"
1151 | "Analyze issues in the feature/new-login branch"
1152 | "Compare issues between main and develop branches"
1153 | "Find issues across multiple projects: proj1, proj2, proj3"
1154 | "Show me issues sorted by severity in descending order"
1155 | "Find all issues with clean code impact on reliability"
1156 | ```
1157 |
1158 | ### Component Navigation
1159 |
1160 | ```
1161 | "Find all files containing 'UserService' in their name"
1162 | "List all test files in my project"
1163 | "Show me the directory structure of src/main"
1164 | "Find all Java controller files"
1165 | "List all projects in SonarQube"
1166 | "Navigate to the authentication module"
1167 | "Search for TypeScript files in the frontend directory"
1168 | "Show me all directories under src/components"
1169 | ```
1170 |
1171 | ### Issue Management
1172 |
1173 | ```
1174 | "Assign issue PROJECT-123 to john.doe"
1175 | "Unassign issue PROJECT-456"
1176 | "Mark issue ABC-789 as false positive with comment: 'Test code only'"
1177 | "Add comment to issue XYZ-111: 'Fixed in commit abc123'"
1178 | "Bulk mark issues DEF-222, DEF-223 as won't fix"
1179 | ```
1180 |
1181 | ### Quality Monitoring
1182 |
1183 | ```
1184 | "Check the quality gate status for my main project"
1185 | "Show me the code coverage history for the last month"
1186 | "What are the quality gate conditions?"
1187 | "Compare metrics between develop and main branches"
1188 | ```
1189 |
1190 | ### Security Hotspot Review
1191 |
1192 | ```
1193 | "Find all security hotspots that need review in project xyz"
1194 | "Show me hotspots in the authentication module"
1195 | "Get details for hotspot HSP-12345"
1196 | "List all hotspots assigned to me"
1197 | "Mark hotspot HSP-12345 as safe with explanation"
1198 | "Find hotspots in the new code period"
1199 | "Show security hotspots in pull request #42"
1200 | ```
1201 |
1202 | ### Source Code Analysis
1203 |
1204 | ```
1205 | "Show me the source code for file xyz with issues highlighted"
1206 | "Get blame information for the problematic file"
1207 | "View issues in the authentication module"
1208 | ```
1209 |
1210 | ### System Health
1211 |
1212 | ```
1213 | "Check if SonarQube is running"
1214 | "What's the health status of the SonarQube instance?"
1215 | "Show me the system status"
1216 | ```
1217 |
1218 | ## Architecture
1219 |
1220 | The SonarQube MCP Server follows a modular architecture:
1221 |
1222 | ```
1223 | ┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
1224 | │ Claude Desktop │────▶│ MCP Server │────▶│ SonarQube API │
1225 | │ (MCP Client) │◀────│ (index.ts) │◀────│ │
1226 | └─────────────────┘ └──────────────────┘ └─────────────────┘
1227 | │
1228 | ▼
1229 | ┌──────────────────┐
1230 | │ SonarQube │
1231 | │ Client │
1232 | │ (sonarqube.ts) │
1233 | └──────────────────┘
1234 | │
1235 | ▼
1236 | ┌──────────────────┐
1237 | │ API Module │
1238 | │ (api.ts) │
1239 | └──────────────────┘
1240 | ```
1241 |
1242 | ### Key Components
1243 |
1244 | 1. **MCP Server (`index.ts`)**: Main entry point that initializes the MCP server and registers all available tools
1245 | 2. **SonarQube Client (`sonarqube.ts`)**: Handles business logic and parameter transformation
1246 | 3. **API Module (`api.ts`)**: Manages HTTP requests to the SonarQube API
1247 | 4. **Type Definitions**: TypeScript interfaces for type safety
1248 |
1249 | ### Data Flow
1250 |
1251 | 1. MCP clients make requests through registered tools
1252 | 2. Tool handlers validate and transform parameters
1253 | 3. SonarQube client methods process the requests
1254 | 4. API module executes HTTP requests
1255 | 5. Responses are formatted and returned to the client
1256 |
1257 | ## Development
1258 |
1259 | ### Prerequisites
1260 |
1261 | - Node.js 22 or higher
1262 | - pnpm 10.17.0 or higher
1263 | - Docker (for container builds)
1264 |
1265 | ### Setup
1266 |
1267 | 1. Clone the repository:
1268 |
1269 | ```bash
1270 | git clone https://github.com/sapientpants/sonarqube-mcp-server.git
1271 | cd sonarqube-mcp-server
1272 | ```
1273 |
1274 | 1. Install dependencies:
1275 |
1276 | ```bash
1277 | pnpm install
1278 | ```
1279 |
1280 | 1. Build the project:
1281 |
1282 | ```bash
1283 | pnpm build
1284 | ```
1285 |
1286 | ### Development Commands
1287 |
1288 | ```bash
1289 | # Install dependencies
1290 | pnpm install
1291 |
1292 | # Build the project
1293 | pnpm build
1294 |
1295 | # Run in development mode with auto-reload
1296 | pnpm dev
1297 |
1298 | # Run tests
1299 | pnpm test
1300 |
1301 | # Run tests with coverage
1302 | pnpm test:coverage
1303 |
1304 | # Lint the code
1305 | pnpm lint
1306 |
1307 | # Fix linting issues
1308 | pnpm lint:fix
1309 |
1310 | # Check types
1311 | pnpm check-types
1312 |
1313 | # Format code
1314 | pnpm format
1315 |
1316 | # Run all validations
1317 | pnpm validate
1318 |
1319 | # Inspect MCP schema
1320 | pnpm inspect
1321 | ```
1322 |
1323 | ### Testing
1324 |
1325 | The project uses Jest for testing with:
1326 |
1327 | - Unit tests for all major components
1328 | - Mocked HTTP responses using `nock`
1329 | - Coverage reporting
1330 | - TypeScript support
1331 |
1332 | Run specific test files:
1333 |
1334 | ```bash
1335 | NODE_ENV=test NODE_OPTIONS='--experimental-vm-modules --no-warnings' jest src/__tests__/file-name.test.ts
1336 | ```
1337 |
1338 | ### Code Quality
1339 |
1340 | The project maintains high code quality through:
1341 |
1342 | - TypeScript for type safety
1343 | - ESLint for code linting
1344 | - Prettier for code formatting
1345 | - Jest for testing
1346 | - SonarCloud for continuous code analysis
1347 |
1348 | ## Common Issues and Solutions
1349 |
1350 | ### Quick Fixes
1351 |
1352 | #### "Authentication failed"
1353 |
1354 | - **Cause**: Invalid or expired token
1355 | - **Solution**: Generate a new token in SonarQube/SonarCloud
1356 |
1357 | #### "Project not found"
1358 |
1359 | - **Cause**: Incorrect project key or insufficient permissions
1360 | - **Solution**: Verify the project key and check token permissions
1361 |
1362 | #### "Organization required"
1363 |
1364 | - **Cause**: Using SonarCloud without organization parameter
1365 | - **Solution**: Add `SONARQUBE_ORGANIZATION` to your configuration
1366 |
1367 | #### "Connection refused"
1368 |
1369 | - **Cause**: Incorrect URL or network issues
1370 | - **Solution**: Verify `SONARQUBE_URL` and network connectivity
1371 |
1372 | #### "No output or errors visible"
1373 |
1374 | - **Cause**: Errors might be happening but not visible in Claude Desktop
1375 | - **Solution**: Enable logging with `LOG_FILE` and check the log file for detailed error messages
1376 |
1377 | ### FAQ
1378 |
1379 | **Q: Can I use this with both SonarQube and SonarCloud?**
1380 | A: Yes! Set the appropriate `SONARQUBE_URL` and include `SONARQUBE_ORGANIZATION` for SonarCloud.
1381 |
1382 | **Q: What permissions does my token need?**
1383 | A: The token needs "Execute Analysis" permission and access to the projects you want to analyze.
1384 |
1385 | **Q: How do I filter issues by multiple criteria?**
1386 | A: The `issues` tool supports extensive filtering. You can combine multiple parameters like severity, type, status, and date ranges.
1387 |
1388 | **Q: Can I analyze pull requests?**
1389 | A: Yes! Many tools support `branch` and `pull_request` parameters for branch and PR analysis.
1390 |
1391 | ## Troubleshooting
1392 |
1393 | ### Common Error Messages and Solutions
1394 |
1395 | #### Authentication Errors
1396 |
1397 | ##### Error: "Authentication failed"
1398 |
1399 | - **Solution**: Check that your SONARQUBE_TOKEN is valid and not expired. Generate a new token from your SonarQube user profile.
1400 |
1401 | ##### Error: "No SonarQube authentication configured"
1402 |
1403 | - **Solution**: Set one of the following authentication methods:
1404 | - `SONARQUBE_TOKEN` for token-based authentication (recommended)
1405 | - `SONARQUBE_USERNAME` and `SONARQUBE_PASSWORD` for basic authentication
1406 | - `SONARQUBE_PASSCODE` for system passcode authentication
1407 |
1408 | #### Authorization Errors
1409 |
1410 | ##### Error: "Access denied"
1411 |
1412 | - **Solution**: Ensure your token has the required permissions for the operation. Common required permissions:
1413 | - "Execute Analysis" for code analysis
1414 | - "Browse" for reading project data
1415 | - "Administer Issues" for issue management operations
1416 |
1417 | #### Resource Not Found Errors
1418 |
1419 | ##### Error: "Resource not found"
1420 |
1421 | - **Solution**: Verify that:
1422 | - The project key/component exists in SonarQube
1423 | - You have access to the resource
1424 | - The URL path is correct (no typos in project keys)
1425 |
1426 | #### Network and Connection Errors
1427 |
1428 | ##### Error: "Connection refused"
1429 |
1430 | - **Solution**: Check that:
1431 | - The SonarQube server is running
1432 | - The SONARQUBE_URL is correct
1433 | - There are no firewall rules blocking the connection
1434 |
1435 | ##### Error: "Network error" or timeout errors
1436 |
1437 | - **Solution**:
1438 | - Verify your network connection
1439 | - Check if the SonarQube server is accessible
1440 | - Ensure the URL doesn't have a trailing slash
1441 | - For self-hosted instances, verify SSL certificates
1442 |
1443 | #### Rate Limiting
1444 |
1445 | ##### Error: "Rate limit exceeded"
1446 |
1447 | - **Solution**: The server automatically retries rate-limited requests with exponential backoff. If you continue to hit rate limits:
1448 | - Reduce the frequency of your requests
1449 | - Implement request batching where possible
1450 | - Contact your SonarQube administrator to increase rate limits
1451 |
1452 | #### Configuration Errors
1453 |
1454 | ##### Error: "Invalid SONARQUBE_URL"
1455 |
1456 | - **Solution**: Provide a valid URL including the protocol:
1457 | - ✅ Correct: `https://sonarcloud.io`
1458 | - ✅ Correct: `https://sonarqube.example.com`
1459 | - ❌ Wrong: `sonarcloud.io` (missing protocol)
1460 | - ❌ Wrong: `https://sonarqube.example.com/` (trailing slash)
1461 |
1462 | ### Debugging Tips
1463 |
1464 | 1. **Enable Debug Logging**:
1465 |
1466 | ```bash
1467 | export LOG_LEVEL=DEBUG
1468 | ```
1469 |
1470 | 2. **Check Environment Variables**:
1471 |
1472 | ```bash
1473 | echo $SONARQUBE_URL
1474 | echo $SONARQUBE_TOKEN
1475 | echo $SONARQUBE_ORGANIZATION
1476 | ```
1477 |
1478 | 3. **Test Connection**:
1479 | Use the `ping` tool to verify connectivity:
1480 |
1481 | ```bash
1482 | # In your MCP client
1483 | sonarqube.ping
1484 | ```
1485 |
1486 | 4. **Verify Permissions**:
1487 | Use the `projects` tool to list accessible projects:
1488 | ```bash
1489 | # In your MCP client
1490 | sonarqube.projects
1491 | ```
1492 |
1493 | ### Retry Behavior
1494 |
1495 | The server automatically retries failed requests for transient errors:
1496 |
1497 | - **Network errors**: Retried up to 3 times
1498 | - **Rate limiting**: Retried with exponential backoff
1499 | - **Server errors (5xx)**: Retried up to 3 times
1500 |
1501 | Retry delays: 1s → 2s → 4s (capped at 10s)
1502 |
1503 | ### Getting Help
1504 |
1505 | If you continue to experience issues:
1506 |
1507 | 1. Check the [GitHub Issues](https://github.com/sapientpants/sonarqube-mcp-server/issues) for similar problems
1508 | 2. Enable debug logging and collect error details
1509 | 3. Create a new issue with:
1510 | - Error messages
1511 | - Environment details (OS, Node version)
1512 | - SonarQube version
1513 | - Steps to reproduce
1514 |
1515 | ## Contributing
1516 |
1517 | We welcome contributions! Please see our [Contributing Guidelines](CONTRIBUTING.md) for details.
1518 |
1519 | ### How to Contribute
1520 |
1521 | 1. Fork the repository
1522 | 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
1523 | 3. Commit your changes (`git commit -m 'Add amazing feature'`)
1524 | 4. Push to the branch (`git push origin feature/amazing-feature`)
1525 | 5. Open a Pull Request
1526 |
1527 | ### Development Guidelines
1528 |
1529 | - Write tests for new features
1530 | - Update documentation as needed
1531 | - Follow the existing code style
1532 | - Ensure all tests pass
1533 | - Add appropriate error handling
1534 |
1535 | ## License
1536 |
1537 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
1538 |
1539 | ## External Resources
1540 |
1541 | ### SonarQube Documentation
1542 |
1543 | - [SonarQube Documentation](https://docs.sonarqube.org/latest/)
1544 | - [SonarCloud Documentation](https://docs.sonarcloud.io/)
1545 | - [Web API Documentation](https://docs.sonarqube.org/latest/extend/web-api/)
1546 |
1547 | ### Model Context Protocol
1548 |
1549 | - [MCP Documentation](https://modelcontextprotocol.io/)
1550 | - [MCP Specification](https://github.com/modelcontextprotocol/specification)
1551 | - [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)
1552 |
1553 | ---
1554 |
1555 | Made with ❤️ by the SonarQube MCP Server community
1556 |
```
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
```markdown
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | The latest minor release will receive support for security updates. Users of older versions are requested to upgrade to the latest minor version.
6 |
7 | | Version | Supported |
8 | | ------- | ------------------ |
9 | | 1.4.x | :white_check_mark: |
10 | | < 1.4.0 | :x: |
11 |
12 | ## Authentication Security Best Practices
13 |
14 | The SonarQube MCP Server uses environment variables for authentication, which is appropriate for its design as a single-user local tool. Follow these best practices to ensure secure usage:
15 |
16 | ### 1. Use Token Authentication
17 |
18 | Token authentication is the most secure option and is strongly recommended:
19 |
20 | - **Generate dedicated tokens** for the MCP server rather than reusing existing tokens
21 | - **Use minimal permissions** - create tokens with only the necessary read permissions
22 | - **Rotate tokens regularly** - establish a schedule for token rotation
23 | - **Revoke unused tokens** - immediately revoke tokens that are no longer needed
24 |
25 | ### 2. Protect Configuration Files
26 |
27 | Since credentials are stored in the MCP client's configuration file:
28 |
29 | - **Check file permissions** - ensure configuration files are readable only by your user account
30 | - **Use secure storage** - on macOS, Claude Desktop stores config in `~/Library/Application Support/Claude/claude_desktop_config.json`
31 | - **Don't commit configs** - never commit configuration files containing credentials to version control
32 | - **Use password managers** - store tokens in a password manager and copy them when needed
33 |
34 | ### 3. Authentication Method Security
35 |
36 | Listed from most to least secure:
37 |
38 | 1. **Token Authentication** (Recommended)
39 | - Tokens can be scoped with limited permissions
40 | - Can be revoked without changing passwords
41 | - Works with all SonarQube versions
42 |
43 | 2. **Basic Authentication**
44 | - Use only when token auth is not available
45 | - Requires transmitting username and password
46 | - May not work with SonarCloud if 2FA is enabled
47 |
48 | 3. **System Passcode**
49 | - Only for special administrative scenarios
50 | - Provides system-level access
51 | - Use with extreme caution
52 |
53 | ### 4. SonarQube-Specific Considerations
54 |
55 | - **SonarQube 10.0+**: Uses Bearer token authentication (most secure)
56 | - **SonarQube < 10.0**: Tokens sent as username in Basic auth
57 | - **SonarCloud**: Always use token authentication, especially with 2FA enabled
58 |
59 | ### 5. Environment Variable Security
60 |
61 | When setting environment variables:
62 |
63 | - **Avoid command history** - Use configuration files instead of export commands
64 | - **Clear sensitive variables** - Unset variables after use if set temporarily
65 | - **Use dedicated shells** - Run the MCP server in a dedicated terminal session
66 |
67 | ### 6. Network Security
68 |
69 | - **Use HTTPS** - Always connect to SonarQube instances over HTTPS
70 | - **Verify certificates** - Ensure SSL certificates are valid
71 | - **Use VPN** - When accessing internal SonarQube instances, use VPN
72 |
73 | ## Security Architecture
74 |
75 | The SonarQube MCP Server operates with the following security model:
76 |
77 | - **Single-user design**: Each instance runs with one user's credentials
78 | - **Local execution**: Runs on the user's machine, not as a shared service
79 | - **Direct authentication**: Uses SonarQube's native authentication mechanisms
80 | - **No credential storage**: The server itself doesn't store credentials
81 |
82 | ## Enterprise Security Considerations
83 |
84 | While the current model is appropriate for local usage, enterprise deployments using MCP gateways provide:
85 |
86 | - OAuth 2.1 resource server implementation at the gateway layer
87 | - Token validation and scoping per RFC8707
88 | - Multi-client authorization mechanisms
89 | - Enterprise authentication and authorization
90 |
91 | ## Reporting a Vulnerability
92 |
93 | To report a security issue, please email [email protected] with a description of the issue, the steps you took to create the issue, affected versions, and if known, mitigations for the issue. Our vulnerability management team will acknowledge receiving your email within 3 working days. This project follows a 90 day disclosure timeline.
94 |
```
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
```markdown
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | - Demonstrating empathy and kindness toward other people
21 | - Being respectful of differing opinions, viewpoints, and experiences
22 | - Giving and gracefully accepting constructive feedback
23 | - Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | - Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | - The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | - Trolling, insulting or derogatory comments, and personal or political attacks
33 | - Public or private harassment
34 | - Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | - Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | [email protected].
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series
86 | of actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or
93 | permanent ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within
113 | the community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 | enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
129 |
```
--------------------------------------------------------------------------------
/docs/security.md:
--------------------------------------------------------------------------------
```markdown
1 | # Security Guide
2 |
3 | ## Overview
4 |
5 | This guide covers security best practices for the SonarQube MCP Server, focusing on authentication, credential management, and secure deployment patterns.
6 |
7 | ## Authentication Methods
8 |
9 | The server supports three authentication methods for connecting to SonarQube:
10 |
11 | ### 1. Token Authentication (Recommended)
12 |
13 | The most secure method for API access.
14 |
15 | ```json
16 | {
17 | "env": {
18 | "SONARQUBE_TOKEN": "squ_xxxxxxxxxxxxxxxx"
19 | }
20 | }
21 | ```
22 |
23 | **Benefits:**
24 |
25 | - Tokens can be scoped with limited permissions
26 | - Easy to revoke without changing passwords
27 | - No password exposure in logs or configs
28 | - Works with both SonarCloud and SonarQube
29 |
30 | **SonarQube Version Differences:**
31 |
32 | - **SonarQube 10.0+**: Uses Bearer token authentication
33 | - **SonarQube < 10.0**: Automatically uses token as username in Basic auth
34 |
35 | ### 2. Basic Authentication
36 |
37 | Traditional username/password authentication.
38 |
39 | ```json
40 | {
41 | "env": {
42 | "SONARQUBE_USERNAME": "your-username",
43 | "SONARQUBE_PASSWORD": "your-password"
44 | }
45 | }
46 | ```
47 |
48 | **Considerations:**
49 |
50 | - Suitable for self-hosted SonarQube instances
51 | - May not work with SonarCloud if 2FA is enabled
52 | - Passwords visible in environment variables
53 |
54 | ### 3. System Passcode
55 |
56 | Special authentication for system administration.
57 |
58 | ```json
59 | {
60 | "env": {
61 | "SONARQUBE_PASSCODE": "system-passcode"
62 | }
63 | }
64 | ```
65 |
66 | **Use Cases:**
67 |
68 | - Automated deployment scenarios
69 | - System-level operations
70 | - Emergency access
71 |
72 | ## Credential Security Best Practices
73 |
74 | ### 1. Token Management
75 |
76 | **Creating Secure Tokens:**
77 |
78 | 1. Log into SonarQube/SonarCloud
79 | 2. Navigate to **My Account** → **Security**
80 | 3. Generate tokens with minimal required permissions:
81 | - Read-only tokens for analysis
82 | - Write tokens only when needed
83 | - Project-specific tokens when possible
84 |
85 | **Token Rotation:**
86 |
87 | - Rotate tokens every 90 days
88 | - Immediately revoke compromised tokens
89 | - Use different tokens for different environments
90 |
91 | ### 2. Credential Storage
92 |
93 | **Local Development (Claude Desktop):**
94 |
95 | - Credentials stored in Claude Desktop's config file
96 | - Ensure proper file permissions (600 on Unix)
97 | - Consider using OS keychain integration
98 |
99 | **Docker Deployment:**
100 |
101 | ```bash
102 | # Use Docker secrets
103 | docker secret create sonarqube-token token.txt
104 | docker run --secret sonarqube-token ...
105 |
106 | # Or use environment file with restricted permissions
107 | chmod 600 .env
108 | docker run --env-file .env ...
109 | ```
110 |
111 | **MCP Gateway Deployment:**
112 |
113 | - Use gateway's secret management
114 | - Leverage vault integration if available
115 | - Rotate credentials at gateway level
116 |
117 | ### 3. Environment Variable Security
118 |
119 | **DO:**
120 |
121 | - Use `.env` files with restricted permissions
122 | - Load secrets from secure sources at runtime
123 | - Use placeholder values in version control
124 |
125 | **DON'T:**
126 |
127 | - Commit credentials to version control
128 | - Log environment variables
129 | - Pass secrets via command line arguments
130 |
131 | ## Network Security
132 |
133 | ### 1. TLS/SSL Configuration
134 |
135 | Always use HTTPS for SonarQube connections:
136 |
137 | ```json
138 | {
139 | "env": {
140 | "SONARQUBE_URL": "https://sonarqube.example.com",
141 | "NODE_TLS_REJECT_UNAUTHORIZED": "1"
142 | }
143 | }
144 | ```
145 |
146 | ### 2. Certificate Validation
147 |
148 | For self-signed certificates:
149 |
150 | ```bash
151 | # Add CA certificate to trusted store
152 | export NODE_EXTRA_CA_CERTS=/path/to/ca-cert.pem
153 | ```
154 |
155 | Never disable certificate validation in production:
156 |
157 | ```bash
158 | # INSECURE - Development only
159 | NODE_TLS_REJECT_UNAUTHORIZED=0
160 | ```
161 |
162 | ## Deployment Security
163 |
164 | ### 1. Container Security
165 |
166 | **Image Security:**
167 |
168 | - Use specific version tags, not `latest`
169 | - Scan images for vulnerabilities
170 | - Use minimal base images (Alpine)
171 |
172 | **Runtime Security:**
173 |
174 | ```yaml
175 | # docker-compose.yml
176 | services:
177 | sonarqube-mcp:
178 | image: sapientpants/sonarqube-mcp-server:1.7.0
179 | user: '1001:1001' # Non-root user
180 | read_only: true # Read-only filesystem
181 | tmpfs:
182 | - /tmp
183 | security_opt:
184 | - no-new-privileges:true
185 | cap_drop:
186 | - ALL
187 | ```
188 |
189 | ### 2. Resource Limits
190 |
191 | Prevent resource exhaustion:
192 |
193 | ```yaml
194 | resources:
195 | limits:
196 | memory: '256M'
197 | cpus: '0.5'
198 | requests:
199 | memory: '128M'
200 | cpus: '0.1'
201 | ```
202 |
203 | ### 3. Network Isolation
204 |
205 | For stdio transport, no network exposure needed:
206 |
207 | ```yaml
208 | networks:
209 | internal:
210 | driver: bridge
211 | internal: true # No external access
212 | ```
213 |
214 | ## Logging and Monitoring
215 |
216 | ### 1. Secure Logging
217 |
218 | Configure logging to avoid credential exposure:
219 |
220 | ```json
221 | {
222 | "env": {
223 | "LOG_LEVEL": "INFO",
224 | "LOG_FILE": "/logs/sonarqube-mcp.log"
225 | }
226 | }
227 | ```
228 |
229 | **Log Security:**
230 |
231 | - Never log authentication tokens
232 | - Redact sensitive data in logs
233 | - Rotate logs regularly
234 | - Restrict log file access
235 |
236 | ### 2. Monitoring Access
237 |
238 | Track usage patterns:
239 |
240 | - Monitor failed authentication attempts
241 | - Track unusual API usage patterns
242 | - Alert on circuit breaker activations
243 |
244 | ## Security Checklist
245 |
246 | ### Pre-Deployment
247 |
248 | - [ ] Use token authentication instead of passwords
249 | - [ ] Create tokens with minimal required permissions
250 | - [ ] Store credentials securely
251 | - [ ] Use HTTPS for all SonarQube connections
252 | - [ ] Review and apply container security settings
253 |
254 | ### Deployment
255 |
256 | - [ ] Use specific version tags for images
257 | - [ ] Run containers as non-root user
258 | - [ ] Apply resource limits
259 | - [ ] Enable secure logging
260 | - [ ] Restrict file permissions on configs
261 |
262 | ### Post-Deployment
263 |
264 | - [ ] Regularly rotate tokens
265 | - [ ] Monitor logs for security events
266 | - [ ] Keep server and dependencies updated
267 | - [ ] Review access patterns periodically
268 | - [ ] Test disaster recovery procedures
269 |
270 | ## Incident Response
271 |
272 | ### Compromised Credentials
273 |
274 | 1. **Immediate Actions:**
275 | - Revoke compromised tokens in SonarQube
276 | - Generate new tokens
277 | - Update all deployments
278 |
279 | 2. **Investigation:**
280 | - Review access logs
281 | - Check for unauthorized changes
282 | - Identify exposure source
283 |
284 | 3. **Prevention:**
285 | - Implement stricter access controls
286 | - Increase rotation frequency
287 | - Add additional monitoring
288 |
289 | ### Security Updates
290 |
291 | Stay informed about security updates:
292 |
293 | - Watch the [GitHub repository](https://github.com/sapientpants/sonarqube-mcp-server)
294 | - Subscribe to security advisories
295 | - Test updates in non-production first
296 |
297 | ## Compliance Considerations
298 |
299 | ### Data Privacy
300 |
301 | - The server doesn't store any SonarQube data
302 | - All data remains in SonarQube
303 | - Consider data residency requirements
304 |
305 | ### Audit Requirements
306 |
307 | When deployed with MCP gateways:
308 |
309 | - Leverage gateway audit capabilities
310 | - Track all access at gateway level
311 | - Maintain audit logs per compliance needs
312 |
313 | ## Support
314 |
315 | For security concerns:
316 |
317 | - Report vulnerabilities via GitHub Security Advisory
318 | - Contact maintainers for sensitive issues
319 | - Check documentation for updates
320 |
```
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
```markdown
1 | # Contributing to SonarQube MCP Server
2 |
3 | Thank you for your interest in contributing to the SonarQube MCP Server! This document provides guidelines and instructions for contributing to the project.
4 |
5 | ## Code of Conduct
6 |
7 | By participating in this project, you agree to abide by our code of conduct: be respectful, inclusive, and constructive in all interactions.
8 |
9 | ## How to Contribute
10 |
11 | ### Reporting Issues
12 |
13 | 1. **Check existing issues** first to avoid duplicates
14 | 2. **Use issue templates** when available
15 | 3. **Provide detailed information**:
16 | - Steps to reproduce
17 | - Expected behavior
18 | - Actual behavior
19 | - Environment details (OS, Node version, etc.)
20 | - Error messages and logs
21 |
22 | ### Submitting Pull Requests
23 |
24 | 1. **Fork the repository** and create your branch from `main`
25 | 2. **Install dependencies** with `pnpm install`
26 | 3. **Make your changes**:
27 | - Write clear, concise commit messages
28 | - Follow the existing code style
29 | - Add tests for new features
30 | - Update documentation as needed
31 | 4. **Test your changes**:
32 | - Run `pnpm test` to ensure all tests pass
33 | - Run `pnpm lint` to check code style
34 | - Run `pnpm check-types` for TypeScript validation
35 | - Run `pnpm validate` to run all checks
36 | 5. **Submit the pull request**:
37 | - Provide a clear description of the changes
38 | - Reference any related issues
39 | - Ensure CI checks pass
40 |
41 | ## Development Setup
42 |
43 | ### Prerequisites
44 |
45 | - Node.js 22 or higher
46 | - pnpm 10.17.0 or higher
47 | - Git
48 |
49 | ### Setup Steps
50 |
51 | ```bash
52 | # Clone your fork
53 | git clone https://github.com/your-username/sonarqube-mcp-server.git
54 | cd sonarqube-mcp-server
55 |
56 | # Install dependencies
57 | pnpm install
58 |
59 | # Build the project
60 | pnpm build
61 |
62 | # Run tests
63 | pnpm test
64 |
65 | # Start development mode
66 | pnpm dev
67 | ```
68 |
69 | ### Development Commands
70 |
71 | ```bash
72 | # Build the project
73 | pnpm build
74 |
75 | # Run in development mode with watch
76 | pnpm dev
77 |
78 | # Run all tests
79 | pnpm test
80 |
81 | # Run tests with coverage
82 | pnpm test:coverage
83 |
84 | # Run a specific test file
85 | NODE_ENV=test NODE_OPTIONS='--experimental-vm-modules --no-warnings' jest src/__tests__/file-name.test.ts
86 |
87 | # Lint code
88 | pnpm lint
89 |
90 | # Fix linting issues
91 | pnpm lint:fix
92 |
93 | # Check TypeScript types
94 | pnpm check-types
95 |
96 | # Format code
97 | pnpm format
98 |
99 | # Check formatting
100 | pnpm format:check
101 |
102 | # Run all validations
103 | pnpm validate
104 |
105 | # Inspect MCP schema
106 | pnpm inspect
107 | ```
108 |
109 | ## Coding Standards
110 |
111 | ### TypeScript
112 |
113 | - Use TypeScript for all new code
114 | - Provide proper type definitions
115 | - Avoid using `any` type
116 | - Use interfaces for object shapes
117 | - Export types that might be used by consumers
118 |
119 | ### Code Style
120 |
121 | - Follow the existing code style
122 | - Use ESLint and Prettier configurations
123 | - Write self-documenting code
124 | - Add comments for complex logic
125 | - Keep functions small and focused
126 |
127 | ### Testing
128 |
129 | - Write tests for all new features
130 | - Maintain or improve code coverage
131 | - Use descriptive test names
132 | - Mock external dependencies
133 | - Test error cases and edge conditions
134 |
135 | ### Git Commit Messages
136 |
137 | Follow conventional commit format:
138 |
139 | ```
140 | type(scope): subject
141 |
142 | body
143 |
144 | footer
145 | ```
146 |
147 | Types:
148 |
149 | - `feat`: New feature
150 | - `fix`: Bug fix
151 | - `docs`: Documentation changes
152 | - `style`: Code style changes (formatting, etc.)
153 | - `refactor`: Code refactoring
154 | - `test`: Test additions or changes
155 | - `chore`: Build process or auxiliary tool changes
156 |
157 | Example:
158 |
159 | ```
160 | feat(api): add support for branch filtering
161 |
162 | Add branch parameter to issues endpoint to allow filtering
163 | issues by specific branch
164 |
165 | Closes #123
166 | ```
167 |
168 | ## Project Structure
169 |
170 | ```
171 | sonarqube-mcp-server/
172 | ├── src/
173 | │ ├── __tests__/ # Test files
174 | │ ├── api.ts # API module for HTTP requests
175 | │ ├── index.ts # MCP server entry point
176 | │ └── sonarqube.ts # SonarQube client implementation
177 | ├── dist/ # Compiled output
178 | ├── package.json # Project configuration
179 | ├── tsconfig.json # TypeScript configuration
180 | ├── jest.config.js # Jest test configuration
181 | └── eslint.config.js # ESLint configuration
182 | ```
183 |
184 | ## Testing Guidelines
185 |
186 | ### Unit Tests
187 |
188 | - Test individual functions and methods
189 | - Mock external dependencies
190 | - Cover edge cases and error scenarios
191 | - Use descriptive test names
192 |
193 | ### Integration Tests
194 |
195 | - Test API endpoints with mocked HTTP responses
196 | - Verify parameter transformation
197 | - Test error handling and retries
198 |
199 | ### Test Structure
200 |
201 | ```typescript
202 | describe('ComponentName', () => {
203 | describe('methodName', () => {
204 | it('should handle normal case', () => {
205 | // Test implementation
206 | });
207 |
208 | it('should handle error case', () => {
209 | // Test implementation
210 | });
211 | });
212 | });
213 | ```
214 |
215 | ## Documentation
216 |
217 | - Update README.md for user-facing changes
218 | - Add JSDoc comments for public APIs
219 | - Include examples for new features
220 | - Keep documentation clear and concise
221 |
222 | ## MCP SDK Update Process
223 |
224 | When updating the Model Context Protocol SDK (`@modelcontextprotocol/sdk`), follow these steps:
225 |
226 | ### Before Updating
227 |
228 | 1. **Check the SDK Release Notes**:
229 | - Visit the [MCP SDK releases page](https://github.com/modelcontextprotocol/sdk/releases)
230 | - Review the changelog for breaking changes
231 | - Note any new protocol versions supported
232 |
233 | 2. **Assess Impact**:
234 | - Check if the new SDK version adds support for new protocol versions
235 | - Review any deprecated features or APIs
236 | - Evaluate compatibility with existing functionality
237 |
238 | ### Update Process
239 |
240 | 1. **Update the Dependency**:
241 |
242 | ```bash
243 | pnpm add @modelcontextprotocol/sdk@latest
244 | ```
245 |
246 | 2. **Update Version References**:
247 | - Update SDK version in `src/index.ts` startup logging
248 | - Update supported protocol versions in `COMPATIBILITY.md`
249 | - Update SDK version in `README.md` if referenced
250 |
251 | 3. **Test Protocol Compatibility**:
252 |
253 | ```bash
254 | # Run all tests
255 | pnpm test
256 |
257 | # Test with Claude Desktop or other MCP clients
258 | # Verify all tools work correctly
259 | ```
260 |
261 | 4. **Update Documentation**:
262 | - Update `COMPATIBILITY.md` with new protocol versions
263 | - Document any behavior changes
264 | - Update examples if APIs have changed
265 |
266 | 5. **Test with Multiple Clients**:
267 | - Test with Claude Desktop
268 | - Test with other MCP clients if available
269 | - Verify backward compatibility with older protocol versions
270 |
271 | ### Post-Update Checklist
272 |
273 | - [ ] All tests pass (`pnpm test`)
274 | - [ ] Type checking passes (`pnpm check-types`)
275 | - [ ] Linting passes (`pnpm lint`)
276 | - [ ] Documentation is updated
277 | - [ ] COMPATIBILITY.md reflects new protocol support
278 | - [ ] Manual testing confirms all tools work
279 | - [ ] No deprecated SDK features are being used
280 |
281 | ### Monitoring SDK Updates
282 |
283 | To stay informed about SDK updates:
284 |
285 | - Watch the [MCP SDK repository](https://github.com/modelcontextprotocol/sdk)
286 | - Subscribe to release notifications
287 | - Review the [MCP specification](https://modelcontextprotocol.io) for protocol changes
288 |
289 | ## Release Process
290 |
291 | 1. Ensure all tests pass
292 | 2. Update version in package.json
293 | 3. Create a pull request
294 | 4. After merge, create a release tag
295 | 5. GitHub Actions will automatically publish to npm and DockerHub
296 |
297 | ## Need Help?
298 |
299 | - Check the [README](README.md) for general information
300 | - Look at existing code for examples
301 | - Ask questions in GitHub issues
302 | - Review closed PRs for similar changes
303 |
304 | ## Recognition
305 |
306 | Contributors will be recognized in the project documentation. Thank you for helping improve the SonarQube MCP Server!
307 |
```
--------------------------------------------------------------------------------
/CLAUDE.md:
--------------------------------------------------------------------------------
```markdown
1 | # CLAUDE.md
2 |
3 | This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4 |
5 | ## Project Overview
6 |
7 | See [README.md](./README.md) for the complete project overview, features, and usage documentation.
8 |
9 | ## Architecture Decision Records (ADRs)
10 |
11 | This project uses adr-tools to document architectural decisions. ADRs are stored in `doc/architecture/decisions/`.
12 |
13 | ```bash
14 | # Create a new ADR without opening an editor (prevents timeout in Claude Code)
15 | EDITOR=true adr-new "Title of the decision"
16 |
17 | # Then edit the created file manually
18 | ```
19 |
20 | All architectural decisions are documented in Architecture Decision Records (ADRs) located in `doc/architecture/decisions/`. These ADRs are the single source of truth for understanding the design and architecture of this library.
21 |
22 | Refer to the ADRs for detailed information about design rationale, implementation details, and consequences of each architectural decision.
23 |
24 | ## Key Architectural Decisions
25 |
26 | **Testing** (ADR-0020):
27 |
28 | - Vitest as test framework with native ES modules support
29 | - fast-check for property-based testing
30 | - 80% minimum coverage required (lines, functions, branches, statements)
31 |
32 | **Code Quality** (ADR-0021):
33 |
34 | - TypeScript strict mode enabled (all strict flags)
35 | - ESLint flat config with cognitive complexity limit (15)
36 | - Prettier for consistent formatting
37 | - Husky + lint-staged for pre-commit automation
38 |
39 | **Package Manager** (ADR-0022):
40 |
41 | - pnpm for disk efficiency and strict dependency management
42 | - Version MUST match across: package.json, Dockerfile, all workflows
43 | - Uses Corepack for automatic version management
44 |
45 | **Release Management** (ADR-0023):
46 |
47 | - Changesets for versioning and changelog generation
48 | - Changeset validation enforced in CI/CD
49 | - Automated GitHub releases via workflows
50 |
51 | **CI/CD** (ADR-0024):
52 |
53 | - GitHub Actions with 7 workflows (main, PR, publish, 4 reusable)
54 | - Parallel execution for validation, security, build
55 | - Quality gates block merge on failures
56 |
57 | **Security** (ADR-0025):
58 |
59 | - Multi-layered: Trivy (containers), CodeQL (SAST), OSV-Scanner (deps), SonarCloud
60 | - SLSA provenance attestations on all artifacts
61 | - SBOM (CycloneDX) generated for Docker images
62 |
63 | **Resilience** (ADR-0026):
64 |
65 | - Circuit breaker pattern via opossum library
66 | - Default: 50% error threshold, 10s timeout, 30s reset
67 | - Integrated with metrics/monitoring system
68 |
69 | **Docker Publishing** (ADR-0027):
70 |
71 | - Multi-platform: linux/amd64, linux/arm64
72 | - Two-stage: build→GHCR (with security scan), copy→Docker Hub
73 | - Build-once-deploy-many strategy
74 |
75 | **Transport** (ADR-0010, ADR-0028):
76 |
77 | - stdio: Default transport (recommended)
78 | - HTTP: Optional session-based transport with SSE
79 | - HTTP uses session management (not OAuth), delegates auth to gateways
80 |
81 | ## Code Quality Conventions
82 |
83 | Follow these conventions to maintain code quality:
84 |
85 | ### TypeScript Best Practices
86 |
87 | 1. **Use Type Aliases for Union Types**
88 |
89 | ```typescript
90 | // ❌ Avoid repeated union types
91 | function foo(param: 'option1' | 'option2' | 'option3') {}
92 | function bar(param: 'option1' | 'option2' | 'option3') {}
93 |
94 | // ✅ Use type alias
95 | type MyOptions = 'option1' | 'option2' | 'option3';
96 | function foo(param: MyOptions) {}
97 | function bar(param: MyOptions) {}
98 | ```
99 |
100 | 2. **Use Nullish Coalescing Operator**
101 |
102 | ```typescript
103 | // ❌ Avoid logical OR for defaults (can fail with falsy values)
104 | const value = input || 'default';
105 |
106 | // ✅ Use nullish coalescing (only replaces null/undefined)
107 | const value = input ?? 'default';
108 | ```
109 |
110 | 3. **Use Object Spread Instead of Object.assign**
111 |
112 | ```typescript
113 | // ❌ Avoid Object.assign
114 | const merged = Object.assign({}, obj1, obj2);
115 |
116 | // ✅ Use object spread
117 | const merged = { ...obj1, ...obj2 };
118 | ```
119 |
120 | 4. **Avoid Deprecated APIs**
121 | - Check for deprecation warnings in the IDE
122 | - Use recommended replacements (e.g., `getHealthV2()` instead of `health()`)
123 | - Update to newer API versions when available
124 |
125 | ### Code Complexity
126 |
127 | 1. **Keep Cognitive Complexity Low**
128 | - Maximum cognitive complexity: 15
129 | - Break complex functions into smaller, focused functions
130 | - Reduce nesting levels
131 | - Simplify conditional logic
132 |
133 | 2. **Remove Redundant Code**
134 | - Don't create type aliases for primitive types
135 | - Remove unused variable assignments
136 | - Eliminate dead code
137 |
138 | ### Regular Expressions
139 |
140 | 1. **Make Regex Operator Precedence Explicit**
141 |
142 | ```typescript
143 | // ❌ Ambiguous precedence
144 | /abc|def+/
145 |
146 | // ✅ Clear precedence with grouping
147 | /abc|(def+)/
148 | ```
149 |
150 | ### General Guidelines
151 |
152 | 1. **Follow Existing Patterns**
153 | - Check how similar functionality is implemented in the codebase
154 | - Maintain consistency with existing code style
155 | - Use the same libraries and utilities as the rest of the project
156 |
157 | 2. **Run Validation Before Committing**
158 |
159 | ```bash
160 | # Run all checks before committing
161 | pnpm run precommit
162 |
163 | # This includes:
164 | # - Format checking (prettier)
165 | # - Linting (eslint)
166 | # - Type checking (tsc)
167 | # - Tests
168 | ```
169 |
170 | ## Claude-Specific Tips
171 |
172 | - Remember to use the ADR creation command with `EDITOR=true` to prevent timeouts in Claude Code
173 | - Never use `--no-verify` when committing code. This bypasses pre-commit hooks which run important validation checks
174 | - Run `pnpm format` to format code before committing
175 | - Run `pnpm run precommit` before finalizing any code changes
176 | - Documentation for sonarqube-web-api-client can be retrieved from <https://raw.githubusercontent.com/sapientpants/sonarqube-web-api-client/refs/heads/main/docs/LLM.md>
177 |
178 | ## Updating pnpm Version
179 |
180 | When updating the pnpm version in this project, you MUST update it in ALL of the following locations to maintain consistency:
181 |
182 | 1. **package.json**: Update the `packageManager` field (e.g., `"packageManager": "[email protected]"`)
183 | 2. **Dockerfile**: Update the version in `RUN npm install -g [email protected]`
184 | 3. **Documentation files**:
185 | - **README.md**: Update the Prerequisites section
186 | - **CONTRIBUTING.md**: Update the Prerequisites section
187 | 4. **Setup script**: Update `scripts/setup.sh` for mise installation
188 | 5. **GitHub Actions workflows** (CRITICAL - these must match package.json exactly):
189 | - `.github/workflows/main.yml`: Line with `version: 10.17.0`
190 | - `.github/workflows/pr.yml`: Line with `version: 10.17.0`
191 | - `.github/workflows/publish.yml`: `PNPM_VERSION` environment variable
192 | - `.github/workflows/reusable-security.yml`: Default value for `pnpm-version` input
193 | - `.github/workflows/reusable-validate.yml`: Default value for `pnpm-version` input
194 |
195 | **Important**: If package.json and GitHub workflows have different pnpm versions, the CI/CD pipeline will fail with an error like:
196 |
197 | ```
198 | Error: Multiple versions of pnpm specified:
199 | - version X in the GitHub Action config with the key "version"
200 | - version pnpm@Y in the package.json with the key "packageManager"
201 | ```
202 |
203 | Always search for the old version number across all files to ensure no location is missed.
204 |
205 | ## Memory
206 |
207 | Follow these steps for each interaction:
208 |
209 | 1. User Identification:
210 | - You should assume that you are interacting with default_user
211 | - If you have not identified default_user, proactively try to do so.
212 |
213 | 2. Memory Retrieval:
214 | - Always begin your chat by saying only "Remembering..." and retrieve all relevant information from your knowledge graph
215 | - Always refer to your knowledge graph as your "memory"
216 |
217 | 3. Memory Gathering:
218 | - While conversing with the user, be attentive to any new information that falls into these categories:
219 | a) Basic Identity (age, gender, location, job title, education level, etc.)
220 | b) Behaviors (interests, habits, etc.)
221 | c) Preferences (communication style, preferred language, etc.)
222 | d) Goals (goals, targets, aspirations, etc.)
223 | e) Relationships (personal and professional relationships up to 3 degrees of separation)
224 |
225 | 4. Memory Update:
226 | - If any new information was gathered during the interaction, update your memory as follows:
227 | a) Create entities for recurring entities (people, places, organizations, modules, concepts, etc.)
228 | b) Connect them to the current entities using relations
229 | c) Store facts about them as observations
230 |
```
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
```javascript
1 | export default {
2 | extends: ['@commitlint/config-conventional'],
3 | };
4 |
```
--------------------------------------------------------------------------------
/vitest.config.d.ts:
--------------------------------------------------------------------------------
```typescript
1 | declare const _default: import("vite").UserConfig;
2 | export default _default;
3 |
```
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "noEmit": false,
5 | "emitDeclarationOnly": false
6 | },
7 | "exclude": ["src/__tests__/**/*", "**/*.test.ts", "**/*.spec.ts"]
8 | }
9 |
```
--------------------------------------------------------------------------------
/src/schemas/system.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Schemas for system tools
3 | */
4 |
5 | // Empty schemas for system tools that don't require parameters
6 | export const systemHealthToolSchema = {};
7 | export const systemStatusToolSchema = {};
8 | export const systemPingToolSchema = {};
9 |
```
--------------------------------------------------------------------------------
/.claude/settings.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "hooks": {
3 | "PreToolUse": [
4 | {
5 | "matcher": "Bash",
6 | "hooks": [
7 | {
8 | "type": "command",
9 | "command": "./.claude/hooks/block-git-no-verify.ts"
10 | }
11 | ]
12 | }
13 | ]
14 | }
15 | }
16 |
```
--------------------------------------------------------------------------------
/src/types/common.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Type alias for severity levels
3 | */
4 | export type SeverityLevel = 'HIGH' | 'MEDIUM' | 'LOW';
5 |
6 | /**
7 | * Interface for pagination parameters
8 | */
9 | export interface PaginationParams {
10 | page: number | undefined;
11 | pageSize: number | undefined;
12 | }
13 |
```
--------------------------------------------------------------------------------
/src/schemas/hotspots.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { z } from 'zod';
2 |
3 | /**
4 | * Schemas for security hotspots
5 | */
6 |
7 | export const hotspotStatusSchema = z.enum(['TO_REVIEW', 'REVIEWED']).nullable().optional();
8 |
9 | export const hotspotResolutionSchema = z.enum(['FIXED', 'SAFE']).nullable().optional();
10 |
```
--------------------------------------------------------------------------------
/sonar-project.properties:
--------------------------------------------------------------------------------
```
1 | sonar.projectKey=sonarqube-mcp-server
2 | sonar.organization=sapientpants
3 | sonar.sources=src
4 | sonar.exclusions=node_modules/**,**/__tests__/**
5 | sonar.tests=src
6 | sonar.test.inclusions=**/__tests__/*.test.ts
7 | sonar.typescript.lcov.reportPaths=coverage/lcov.info
8 |
```
--------------------------------------------------------------------------------
/src/schemas/metrics.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { z } from 'zod';
2 | import { stringToNumberTransform } from '../utils/transforms.js';
3 |
4 | /**
5 | * Schema for metrics tool
6 | */
7 | export const metricsToolSchema = {
8 | page: z.string().optional().transform(stringToNumberTransform),
9 | page_size: z.string().optional().transform(stringToNumberTransform),
10 | };
11 |
```
--------------------------------------------------------------------------------
/src/schemas/projects.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { z } from 'zod';
2 | import { stringToNumberTransform } from '../utils/transforms.js';
3 |
4 | /**
5 | * Schema for projects tool
6 | */
7 | export const projectsToolSchema = {
8 | page: z.string().optional().transform(stringToNumberTransform),
9 | page_size: z.string().optional().transform(stringToNumberTransform),
10 | };
11 |
```
--------------------------------------------------------------------------------
/.github/changeset.yml:
--------------------------------------------------------------------------------
```yaml
1 | # Configuration for Changeset Bot
2 | # This bot will comment on PRs to remind contributors to add changesets
3 |
4 | # The message to display when a PR is missing a changeset
5 | # Default message will be used if not specified
6 | commit: false
7 | # Whether to exclude certain PR labels from changeset requirements
8 | # excluded:
9 | # - "dependencies"
10 | # - "documentation"
11 |
```
--------------------------------------------------------------------------------
/src/schemas/quality-gates.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { z } from 'zod';
2 | import { pullRequestSchema } from './common.js';
3 |
4 | /**
5 | * Schemas for quality gates tools
6 | */
7 |
8 | export const qualityGatesToolSchema = {};
9 |
10 | export const qualityGateToolSchema = {
11 | id: z.string(),
12 | };
13 |
14 | export const qualityGateStatusToolSchema = {
15 | project_key: z.string(),
16 | branch: z.string().optional(),
17 | pull_request: pullRequestSchema,
18 | };
19 |
```
--------------------------------------------------------------------------------
/src/types/system.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Interface for SonarQube health status
3 | */
4 | export interface SonarQubeHealthStatus {
5 | health: 'GREEN' | 'YELLOW' | 'RED';
6 | causes: string[];
7 | }
8 |
9 | /**
10 | * Interface for SonarQube system status
11 | */
12 | export interface SonarQubeSystemStatus {
13 | id: string;
14 | version: string;
15 | status:
16 | | 'UP'
17 | | 'DOWN'
18 | | 'STARTING'
19 | | 'RESTARTING'
20 | | 'DB_MIGRATION_NEEDED'
21 | | 'DB_MIGRATION_RUNNING';
22 | }
23 |
```
--------------------------------------------------------------------------------
/.changeset/config.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "$schema": "https://unpkg.com/@changesets/[email protected]/schema.json",
3 | "changelog": [
4 | "changelog-github-custom",
5 | {
6 | "repo": "sapientpants/sonarqube-mcp-server"
7 | }
8 | ],
9 | "commit": false,
10 | "fixed": [],
11 | "linked": [],
12 | "access": "public",
13 | "baseBranch": "main",
14 | "updateInternalDependencies": "patch",
15 | "ignore": [],
16 | "privatePackages": {
17 | "version": true,
18 | "tag": false
19 | }
20 | }
21 |
```
--------------------------------------------------------------------------------
/osv-scanner.toml:
--------------------------------------------------------------------------------
```toml
1 | # OSV Scanner Configuration
2 | # Documentation: https://google.github.io/osv-scanner/configuration/
3 |
4 | # Ignored vulnerabilities
5 | # These vulnerabilities are acknowledged and accepted as risk
6 | [[IgnoredVulns]]
7 | id = "GHSA-9965-vmph-33xx"
8 | # Reason: validator package is dev-only dependency (not in production)
9 | # Impact: Medium severity (CVSS 6.1)
10 | # Status: No fix available yet
11 | # Review date: 2025-10-14
12 | reason = "Dev-only dependency, not exposed in production"
13 |
```
--------------------------------------------------------------------------------
/src/types/metrics.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Interface for SonarQube metric
3 | */
4 | export interface SonarQubeMetric {
5 | id: string;
6 | key: string;
7 | name: string;
8 | description: string;
9 | domain: string;
10 | type: string;
11 | direction: number;
12 | qualitative: boolean;
13 | hidden: boolean;
14 | custom: boolean;
15 | }
16 |
17 | /**
18 | * Interface for SonarQube metrics result
19 | */
20 | export interface SonarQubeMetricsResult {
21 | metrics: SonarQubeMetric[];
22 | paging: {
23 | pageIndex: number;
24 | pageSize: number;
25 | total: number;
26 | };
27 | }
28 |
```
--------------------------------------------------------------------------------
/docs/architecture/decisions/0001-record-architecture-decisions.md:
--------------------------------------------------------------------------------
```markdown
1 | # 1. Record architecture decisions
2 |
3 | Date: 2025-06-13
4 |
5 | ## Status
6 |
7 | Accepted
8 |
9 | ## Context
10 |
11 | We need to record the architectural decisions made on this project.
12 |
13 | ## Decision
14 |
15 | We will use Architecture Decision Records, as [described by Michael Nygard](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions).
16 |
17 | ## Consequences
18 |
19 | See Michael Nygard's article, linked above. For a lightweight ADR toolset, see Nat Pryce's [adr-tools](https://github.com/npryce/adr-tools).
20 |
```
--------------------------------------------------------------------------------
/src/types/projects.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Interface for SonarQube project
3 | */
4 | export interface SonarQubeProject {
5 | key: string;
6 | name: string;
7 | qualifier: string;
8 | visibility: string;
9 | lastAnalysisDate: string | undefined;
10 | revision: string | undefined;
11 | managed: boolean | undefined;
12 | }
13 |
14 | /**
15 | * Interface for SonarQube projects result - Clean abstraction for consumers
16 | */
17 | export interface SonarQubeProjectsResult {
18 | projects: SonarQubeProject[];
19 | paging: {
20 | pageIndex: number;
21 | pageSize: number;
22 | total: number;
23 | };
24 | }
25 |
```
--------------------------------------------------------------------------------
/src/domains/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | // Export all domain modules
2 | export { BaseDomain } from './base.js';
3 | export { ProjectsDomain } from './projects.js';
4 | export { IssuesDomain } from './issues.js';
5 | export { MetricsDomain } from './metrics.js';
6 | export { MeasuresDomain } from './measures.js';
7 | export { SystemDomain } from './system.js';
8 | export { QualityGatesDomain } from './quality-gates.js';
9 | export { SourceCodeDomain } from './source-code.js';
10 | export { HotspotsDomain } from './hotspots.js';
11 | export { ComponentsDomain } from './components.js';
12 |
```
--------------------------------------------------------------------------------
/src/transports/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Transport module exports.
3 | * This module provides the transport abstraction layer for the MCP server.
4 | */
5 |
6 | export type { ITransport, ITransportConfig, IHttpTransportConfig } from './base.js';
7 | export { isStdioTransport } from './base.js';
8 | export { StdioTransport } from './stdio.js';
9 | export { HttpTransport } from './http.js';
10 | export { SessionManager } from './session-manager.js';
11 | export type { ISession, ISessionManagerConfig } from './session-manager.js';
12 | export { TransportFactory } from './factory.js';
13 |
```
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
```yaml
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: 'npm' # See documentation for possible values
9 | directory: '/' # Location of package manifests
10 | schedule:
11 | interval: 'weekly'
12 |
```
--------------------------------------------------------------------------------
/src/__tests__/null-to-undefined.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { describe, it, expect } from 'vitest';
2 | import { nullToUndefined } from '../index.js';
3 | describe('nullToUndefined', () => {
4 | it('should convert null to undefined', () => {
5 | expect(nullToUndefined(null)).toBeUndefined();
6 | });
7 | it('should pass through non-null values', () => {
8 | expect(nullToUndefined('value')).toBe('value');
9 | expect(nullToUndefined(123)).toBe(123);
10 | expect(nullToUndefined(0)).toBe(0);
11 | expect(nullToUndefined(false)).toBe(false);
12 | expect(nullToUndefined(undefined)).toBeUndefined();
13 | });
14 | });
15 |
```
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 | ---
8 |
9 | **Is your feature request related to a problem? Please describe.**
10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
11 |
12 | **Describe the solution you'd like**
13 | A clear and concise description of what you want to happen.
14 |
15 | **Describe alternatives you've considered**
16 | A clear and concise description of any alternative solutions or features you've considered.
17 |
18 | **Additional context**
19 | Add any other context or screenshots about the feature request here.
20 |
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "compilerOptions": {
3 | "target": "ES2022",
4 | "lib": ["ES2022"],
5 | "module": "NodeNext",
6 | "moduleResolution": "NodeNext",
7 | "strict": true,
8 | "noUncheckedIndexedAccess": true,
9 | "exactOptionalPropertyTypes": true,
10 | "noImplicitOverride": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "noFallthroughCasesInSwitch": true,
13 | "resolveJsonModule": true,
14 | "declaration": true,
15 | "sourceMap": true,
16 | "rootDir": "./src",
17 | "outDir": "dist",
18 | "esModuleInterop": true,
19 | "allowSyntheticDefaultImports": true,
20 | "downlevelIteration": true
21 | },
22 | "include": ["src", "tests"]
23 | }
24 |
```
--------------------------------------------------------------------------------
/src/schemas/source-code.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { z } from 'zod';
2 | import { stringToNumberTransform } from '../utils/transforms.js';
3 | import { pullRequestSchema } from './common.js';
4 |
5 | /**
6 | * Schemas for source code tools
7 | */
8 |
9 | export const sourceCodeToolSchema = {
10 | key: z.string(),
11 | from: z.string().optional().transform(stringToNumberTransform),
12 | to: z.string().optional().transform(stringToNumberTransform),
13 | branch: z.string().optional(),
14 | pull_request: pullRequestSchema,
15 | };
16 |
17 | export const scmBlameToolSchema = {
18 | key: z.string(),
19 | from: z.string().optional().transform(stringToNumberTransform),
20 | to: z.string().optional().transform(stringToNumberTransform),
21 | branch: z.string().optional(),
22 | pull_request: pullRequestSchema,
23 | };
24 |
```
--------------------------------------------------------------------------------
/.github/actionlint.yaml:
--------------------------------------------------------------------------------
```yaml
1 | # actionlint configuration
2 | # https://github.com/rhysd/actionlint/blob/main/docs/config.md
3 |
4 | # Self-hosted runner labels used in this project
5 | # Add any custom runner labels here if using self-hosted runners
6 | self-hosted-runner:
7 | labels: []
8 |
9 | # Configuration variables used in workflows
10 | # This helps actionlint validate variable references
11 | config-variables:
12 | - ENABLE_DOCKER_RELEASE
13 | - ENABLE_NPM_RELEASE
14 | - ENABLE_GITHUB_PACKAGES
15 |
16 | # Ignore specific shellcheck warnings
17 | # SC2086: Double quote to prevent globbing and word splitting
18 | # These are often false positives in GitHub Actions contexts
19 | paths:
20 | .github/workflows/**/*.{yml,yaml}:
21 | ignore:
22 | - 'shellcheck reported issue in this script: SC2086:.+'
23 |
```
--------------------------------------------------------------------------------
/src/__tests__/transports/base.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
2 | import { isStdioTransport } from '../../transports/base.js';
3 |
4 | describe('Transport Base', () => {
5 | describe('isStdioTransport', () => {
6 | it('should return true for StdioServerTransport instance', () => {
7 | const transport = new StdioServerTransport();
8 | expect(isStdioTransport(transport)).toBe(true);
9 | });
10 |
11 | it('should return false for non-StdioServerTransport instances', () => {
12 | expect(isStdioTransport({})).toBe(false);
13 | expect(isStdioTransport(null)).toBe(false);
14 | expect(isStdioTransport(undefined)).toBe(false);
15 | expect(isStdioTransport('string')).toBe(false);
16 | expect(isStdioTransport(123)).toBe(false);
17 | });
18 | });
19 | });
20 |
```
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
```markdown
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 | ---
8 |
9 | **Describe the bug**
10 | A clear and concise description of what the bug is.
11 |
12 | **To Reproduce**
13 | Steps to reproduce the behavior:
14 |
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 |
28 | - OS: [e.g. iOS]
29 | - Browser [e.g. chrome, safari]
30 | - Version [e.g. 22]
31 |
32 | **Smartphone (please complete the following information):**
33 |
34 | - Device: [e.g. iPhone6]
35 | - OS: [e.g. iOS8.1]
36 | - Browser [e.g. stock browser, safari]
37 | - Version [e.g. 22]
38 |
39 | **Additional context**
40 | Add any other context about the problem here.
41 |
```
--------------------------------------------------------------------------------
/vitest.config.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { defineConfig } from 'vitest/config';
2 |
3 | export default defineConfig({
4 | test: {
5 | environment: 'node',
6 | globals: true,
7 | coverage: {
8 | provider: 'v8',
9 | reporter: ['text', 'html', 'json-summary', 'lcov'],
10 | exclude: [
11 | 'coverage/**',
12 | 'dist/**',
13 | '*.config.js',
14 | '*.config.ts',
15 | '.*.js',
16 | '**/*.d.ts',
17 | 'tests/**',
18 | 'test/**',
19 | '**/*.spec.ts',
20 | '**/*.test.ts',
21 | 'docs/**',
22 | '.github/**',
23 | '.changeset/**',
24 | '.claude/**',
25 | 'node_modules/**',
26 | 'src/dev/**', // Development utilities - no coverage required
27 | '**/*.example.ts', // Example files - not part of production code
28 | ],
29 | thresholds: {
30 | branches: 80,
31 | functions: 80,
32 | lines: 80,
33 | statements: 80,
34 | },
35 | },
36 | },
37 | });
38 |
```
--------------------------------------------------------------------------------
/vitest.config.js:
--------------------------------------------------------------------------------
```javascript
1 | import { defineConfig } from 'vitest/config';
2 | export default defineConfig({
3 | test: {
4 | environment: 'node',
5 | globals: true,
6 | coverage: {
7 | provider: 'v8',
8 | reporter: ['text', 'html', 'json-summary', 'lcov'],
9 | exclude: [
10 | 'coverage/**',
11 | 'dist/**',
12 | '*.config.js',
13 | '*.config.ts',
14 | '.*.js',
15 | '**/*.d.ts',
16 | 'tests/**',
17 | 'test/**',
18 | '**/*.spec.ts',
19 | '**/*.test.ts',
20 | 'docs/**',
21 | '.github/**',
22 | '.changeset/**',
23 | '.claude/**',
24 | 'node_modules/**',
25 | 'src/dev/**', // Development utilities - no coverage required
26 | '**/*.example.ts', // Example files - not part of production code
27 | ],
28 | thresholds: {
29 | branches: 80,
30 | functions: 80,
31 | lines: 80,
32 | statements: 80,
33 | },
34 | },
35 | },
36 | });
37 | //# sourceMappingURL=vitest.config.js.map
38 |
```
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
```markdown
1 | <!--- Please provide a general summary of your changes in the title above -->
2 |
3 | ## Pull request type
4 |
5 | <!-- Please try to limit your pull request to one type, submit multiple pull requests if needed. -->
6 |
7 | Please check the type of change your PR introduces:
8 |
9 | - [ ] Bugfix
10 | - [ ] Feature
11 | - [ ] Code style update (formatting, renaming)
12 | - [ ] Refactoring (no functional changes, no api changes)
13 | - [ ] Build related changes
14 | - [ ] Documentation content changes
15 | - [ ] Other (please describe):
16 |
17 | ## What is the current behavior?
18 |
19 | <!-- Please describe the current behavior that you are modifying, or link to a relevant issue. -->
20 |
21 | Issue Number: N/A
22 |
23 | ## What is the new behavior?
24 |
25 | <!-- Please describe the behavior or changes that are being added by this PR. -->
26 |
27 | -
28 | -
29 | -
30 |
31 | ## Other information
32 |
33 | <!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. -->
34 |
```
--------------------------------------------------------------------------------
/src/handlers/metrics.ts:
--------------------------------------------------------------------------------
```typescript
1 | import type { PaginationParams, ISonarQubeClient } from '../types/index.js';
2 | import { getDefaultClient } from '../utils/client-factory.js';
3 | import { createStructuredResponse } from '../utils/structured-response.js';
4 |
5 | /**
6 | * Handler for getting SonarQube metrics
7 | * @param params Parameters for the metrics request
8 | * @param client Optional SonarQube client instance
9 | * @returns Promise with the metrics result
10 | */
11 | export async function handleSonarQubeGetMetrics(
12 | params: PaginationParams,
13 | client: ISonarQubeClient = getDefaultClient()
14 | ) {
15 | const result = await client.getMetrics(params);
16 |
17 | // Create a properly structured response matching the expected format
18 | const response = {
19 | metrics: result.metrics ?? [],
20 | paging: result.paging ?? {
21 | pageIndex: params.page ?? 1,
22 | pageSize: params.pageSize ?? 100,
23 | total: (result.metrics ?? []).length,
24 | },
25 | };
26 |
27 | return createStructuredResponse(response);
28 | }
29 |
```
--------------------------------------------------------------------------------
/.claude/commands/analyze-and-fix-github-issue.md:
--------------------------------------------------------------------------------
```markdown
1 | # Analyze and fix GitHub Issue
2 |
3 | Please analyze and fix the GitHub issue: $ARGUMENTS.
4 |
5 | Follow these steps:
6 |
7 | 1. Use `gh issue view` to get the issue details
8 | 2. Understand the problem described in the issue
9 | 3. Search the codebase for relevant files
10 | 4. Create a detailed plan to address the issue
11 | 5. Create a new branch for the fix, e.g., `fix/issue-123`
12 | 6. Implement the necessary changes to fix the issue
13 | 7. Ensure that any new code is well-documented and follows the project's coding standards
14 | 8. Write tests to cover the changes made, if applicable
15 | 9. Ensure code passes formatting, linting, type checking, and tests using `pnpm run precommit`
16 | 10. Create a descriptive commit message
17 | 11. Push and create a PR
18 | 12. Wait for code review and address any feedback provided by reviewers.
19 | 13. Merge the pull request once it has been approved. Use the "Squash and merge" option to keep the commit history clean.
20 |
21 | Remember to use the GitHub CLI (`gh`) for all GitHub-related tasks.
22 |
```
--------------------------------------------------------------------------------
/src/handlers/source-code.ts:
--------------------------------------------------------------------------------
```typescript
1 | import type { SourceCodeParams, ScmBlameParams, ISonarQubeClient } from '../types/index.js';
2 | import { getDefaultClient } from '../utils/client-factory.js';
3 | import { createStructuredResponse } from '../utils/structured-response.js';
4 |
5 | /**
6 | * Handler for getting source code
7 | * @param params Parameters for the source code request
8 | * @param client Optional SonarQube client instance
9 | * @returns Promise with the source code result
10 | */
11 | export async function handleSonarQubeGetSourceCode(
12 | params: SourceCodeParams,
13 | client: ISonarQubeClient = getDefaultClient()
14 | ) {
15 | const result = await client.getSourceCode(params);
16 |
17 | return createStructuredResponse(result);
18 | }
19 |
20 | /**
21 | * Handler for getting SCM blame information
22 | * @param params Parameters for the SCM blame request
23 | * @param client Optional SonarQube client instance
24 | * @returns Promise with the SCM blame result
25 | */
26 | export async function handleSonarQubeGetScmBlame(
27 | params: ScmBlameParams,
28 | client: ISonarQubeClient = getDefaultClient()
29 | ) {
30 | const result = await client.getScmBlame(params);
31 |
32 | return createStructuredResponse(result);
33 | }
34 |
```
--------------------------------------------------------------------------------
/src/config/versions.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Centralized version configuration for the SonarQube MCP Server
3 | */
4 |
5 | /**
6 | * Server version - the version of the SonarQube MCP Server itself
7 | */
8 | export const SERVER_VERSION = '1.7.0';
9 |
10 | /**
11 | * MCP SDK version currently in use
12 | */
13 | export const SDK_VERSION = '1.13.0';
14 |
15 | /**
16 | * MCP protocol versions supported by the current SDK
17 | * Listed in order from newest to oldest
18 | */
19 | export const SUPPORTED_PROTOCOL_VERSIONS = [
20 | '2025-06-18',
21 | '2025-03-26',
22 | '2024-11-05',
23 | '2024-10-07',
24 | ] as const;
25 |
26 | /**
27 | * Latest MCP protocol version
28 | */
29 | export const LATEST_PROTOCOL_VERSION = SUPPORTED_PROTOCOL_VERSIONS[0];
30 |
31 | /**
32 | * Default negotiated protocol version (what most clients will use)
33 | */
34 | export const DEFAULT_NEGOTIATED_PROTOCOL_VERSION = '2025-03-26';
35 |
36 | /**
37 | * Version information object for logging and display
38 | */
39 | export const VERSION_INFO = {
40 | serverVersion: SERVER_VERSION,
41 | sdkVersion: SDK_VERSION,
42 | supportedProtocolVersions: SUPPORTED_PROTOCOL_VERSIONS,
43 | latestProtocolVersion: LATEST_PROTOCOL_VERSION,
44 | defaultNegotiatedProtocolVersion: DEFAULT_NEGOTIATED_PROTOCOL_VERSION,
45 | } as const;
46 |
```
--------------------------------------------------------------------------------
/src/handlers/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Re-export all handlers from their respective modules
3 | */
4 |
5 | export { handleSonarQubeProjects } from './projects.js';
6 |
7 | export {
8 | handleSonarQubeGetIssues,
9 | handleMarkIssueFalsePositive,
10 | handleMarkIssueWontFix,
11 | handleMarkIssuesFalsePositive,
12 | handleMarkIssuesWontFix,
13 | handleAddCommentToIssue,
14 | handleAssignIssue,
15 | handleConfirmIssue,
16 | handleUnconfirmIssue,
17 | handleResolveIssue,
18 | handleReopenIssue,
19 | setElicitationManager,
20 | } from './issues.js';
21 |
22 | export { handleSonarQubeGetMetrics } from './metrics.js';
23 |
24 | export {
25 | handleSonarQubeGetHealth,
26 | handleSonarQubeGetStatus,
27 | handleSonarQubePing,
28 | } from './system.js';
29 |
30 | export {
31 | handleSonarQubeComponentMeasures,
32 | handleSonarQubeComponentsMeasures,
33 | handleSonarQubeMeasuresHistory,
34 | } from './measures.js';
35 |
36 | export {
37 | handleSonarQubeListQualityGates,
38 | handleSonarQubeGetQualityGate,
39 | handleSonarQubeQualityGateStatus,
40 | } from './quality-gates.js';
41 |
42 | export { handleSonarQubeGetSourceCode, handleSonarQubeGetScmBlame } from './source-code.js';
43 |
44 | export {
45 | handleSonarQubeHotspots,
46 | handleSonarQubeHotspot,
47 | handleSonarQubeUpdateHotspotStatus,
48 | } from './hotspots.js';
49 |
50 | export { handleSonarQubeComponents } from './components.js';
51 |
```
--------------------------------------------------------------------------------
/src/config/service-accounts.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Service account configuration utilities
3 | */
4 |
5 | export interface ServiceAccountConfig {
6 | id: string;
7 | token: string;
8 | url: string | undefined;
9 | organization: string | undefined;
10 | }
11 |
12 | /**
13 | * Get service account configuration
14 | */
15 | export function getServiceAccountConfig(accountId: string): ServiceAccountConfig | null {
16 | // For default account, check standard environment variables
17 | if (accountId === 'default') {
18 | const token = process.env.SONARQUBE_TOKEN;
19 | if (!token) {
20 | return null;
21 | }
22 |
23 | const config: ServiceAccountConfig = {
24 | id: 'default',
25 | token,
26 | url: process.env.SONARQUBE_URL,
27 | organization: process.env.SONARQUBE_ORGANIZATION,
28 | };
29 | return config;
30 | }
31 |
32 | // For numbered accounts (SA1-SA10)
33 | const regex = /^SA(\d+)$/;
34 | const match = regex.exec(accountId);
35 | if (match) {
36 | const num = match[1];
37 | const token = process.env[`SONARQUBE_SA${num}_TOKEN`];
38 | if (!token) {
39 | return null;
40 | }
41 |
42 | const config: ServiceAccountConfig = {
43 | id: accountId,
44 | token,
45 | url: process.env[`SONARQUBE_SA${num}_URL`],
46 | organization: process.env[`SONARQUBE_SA${num}_ORGANIZATION`],
47 | };
48 | return config;
49 | }
50 |
51 | return null;
52 | }
53 |
```
--------------------------------------------------------------------------------
/src/schemas/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | // Re-export all schemas from their respective files
2 |
3 | // Common schemas
4 | export * from './common.js';
5 |
6 | // Domain schemas
7 | export * from './hotspots.js';
8 |
9 | // Tool schemas
10 | export { projectsToolSchema } from './projects.js';
11 | export { metricsToolSchema } from './metrics.js';
12 | export {
13 | issuesToolSchema,
14 | markIssueFalsePositiveToolSchema,
15 | markIssueWontFixToolSchema,
16 | markIssuesFalsePositiveToolSchema,
17 | markIssuesWontFixToolSchema,
18 | addCommentToIssueToolSchema,
19 | assignIssueToolSchema,
20 | confirmIssueToolSchema,
21 | unconfirmIssueToolSchema,
22 | resolveIssueToolSchema,
23 | reopenIssueToolSchema,
24 | } from './issues.js';
25 | export { systemHealthToolSchema, systemStatusToolSchema, systemPingToolSchema } from './system.js';
26 | export {
27 | componentMeasuresToolSchema,
28 | componentsMeasuresToolSchema,
29 | measuresHistoryToolSchema,
30 | } from './measures.js';
31 | export {
32 | qualityGatesToolSchema,
33 | qualityGateToolSchema,
34 | qualityGateStatusToolSchema,
35 | } from './quality-gates.js';
36 | export { sourceCodeToolSchema, scmBlameToolSchema } from './source-code.js';
37 | export {
38 | hotspotsToolSchema,
39 | hotspotToolSchema,
40 | updateHotspotStatusToolSchema,
41 | } from './hotspots-tools.js';
42 | export { componentsToolSchema } from './components.js';
43 |
```
--------------------------------------------------------------------------------
/src/schemas/measures.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { z } from 'zod';
2 | import { stringToNumberTransform } from '../utils/transforms.js';
3 | import { pullRequestSchema } from './common.js';
4 |
5 | /**
6 | * Schemas for measures tools
7 | */
8 |
9 | export const componentMeasuresToolSchema = {
10 | component: z.string(),
11 | metric_keys: z.array(z.string()),
12 | additional_fields: z.array(z.string()).optional(),
13 | branch: z.string().optional(),
14 | pull_request: pullRequestSchema,
15 | period: z.string().optional(),
16 | };
17 |
18 | export const componentsMeasuresToolSchema = {
19 | component_keys: z.array(z.string()),
20 | metric_keys: z.array(z.string()),
21 | additional_fields: z.array(z.string()).optional(),
22 | branch: z.string().optional(),
23 | pull_request: pullRequestSchema,
24 | period: z.string().optional(),
25 | page: z.string().optional().transform(stringToNumberTransform),
26 | page_size: z.string().optional().transform(stringToNumberTransform),
27 | };
28 |
29 | export const measuresHistoryToolSchema = {
30 | component: z.string(),
31 | metrics: z.array(z.string()),
32 | from: z.string().optional(),
33 | to: z.string().optional(),
34 | branch: z.string().optional(),
35 | pull_request: pullRequestSchema,
36 | page: z.string().optional().transform(stringToNumberTransform),
37 | page_size: z.string().optional().transform(stringToNumberTransform),
38 | };
39 |
```
--------------------------------------------------------------------------------
/src/types/quality-gates.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Interface for SonarQube quality gate condition
3 | */
4 | export interface SonarQubeQualityGateCondition {
5 | id: string;
6 | metric: string;
7 | op: string;
8 | error: string;
9 | }
10 |
11 | /**
12 | * Interface for SonarQube quality gate
13 | */
14 | export interface SonarQubeQualityGate {
15 | id: string;
16 | name: string;
17 | isDefault: boolean | undefined;
18 | isBuiltIn: boolean | undefined;
19 | conditions: SonarQubeQualityGateCondition[] | undefined;
20 | }
21 |
22 | /**
23 | * Interface for SonarQube quality gates list result
24 | */
25 | export interface SonarQubeQualityGatesResult {
26 | qualitygates: SonarQubeQualityGate[];
27 | default: string;
28 | actions:
29 | | {
30 | create: boolean | undefined;
31 | }
32 | | undefined;
33 | }
34 |
35 | /**
36 | * Interface for SonarQube quality gate status
37 | */
38 | export interface SonarQubeQualityGateStatus {
39 | projectStatus: {
40 | status: 'OK' | 'WARN' | 'ERROR' | 'NONE';
41 | conditions: Array<{
42 | status: 'OK' | 'WARN' | 'ERROR';
43 | metricKey: string;
44 | comparator: string;
45 | errorThreshold: string;
46 | actualValue: string;
47 | }>;
48 | periods?: Array<{
49 | index: number;
50 | mode: string;
51 | date: string;
52 | parameter?: string;
53 | }>;
54 | ignoredConditions: boolean;
55 | };
56 | }
57 |
58 | /**
59 | * Interface for project quality gate params
60 | */
61 | export interface ProjectQualityGateParams {
62 | projectKey: string;
63 | branch?: string;
64 | pullRequest?: string;
65 | }
66 |
```
--------------------------------------------------------------------------------
/src/utils/error-handler.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { SonarQubeAPIError, formatErrorForMCP } from '../errors.js';
2 | import { createLogger } from './logger.js';
3 |
4 | const logger = createLogger('utils/error-handler');
5 |
6 | /**
7 | * Custom error class for MCP errors that includes a code property
8 | */
9 | class MCPError extends Error {
10 | code: number;
11 |
12 | constructor(message: string, code: number) {
13 | super(message);
14 | this.name = 'MCPError';
15 | this.code = code;
16 | }
17 | }
18 |
19 | /**
20 | * Wraps an async MCP handler function with error handling that converts
21 | * SonarQubeAPIError instances to MCP-formatted errors
22 | *
23 | * @param fn The async function to wrap
24 | * @returns A wrapped function that handles errors appropriately
25 | */
26 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
27 | export function withMCPErrorHandling<T extends (...args: any[]) => Promise<any>>(fn: T): T {
28 | return (async (
29 | ...args: Parameters<T>
30 | ): Promise<ReturnType<T> extends Promise<infer U> ? U : never> => {
31 | try {
32 | return (await fn(...args)) as ReturnType<T> extends Promise<infer U> ? U : never;
33 | } catch (error) {
34 | if (error instanceof SonarQubeAPIError) {
35 | logger.error('SonarQube API error occurred', error);
36 | const mcpError = formatErrorForMCP(error);
37 | throw new MCPError(mcpError.message, mcpError.code);
38 | }
39 | // Re-throw non-SonarQubeAPIError errors as-is
40 | throw error;
41 | }
42 | }) as T;
43 | }
44 |
```
--------------------------------------------------------------------------------
/src/schemas/hotspots-tools.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { z } from 'zod';
2 | import { stringToNumberTransform } from '../utils/transforms.js';
3 | import { pullRequestNullableSchema } from './common.js';
4 | import { hotspotStatusSchema, hotspotResolutionSchema } from './hotspots.js';
5 |
6 | /**
7 | * Schemas for hotspot tools
8 | */
9 |
10 | export const hotspotsToolSchema = {
11 | project_key: z.string().optional(),
12 | branch: z.string().nullable().optional(),
13 | pull_request: pullRequestNullableSchema,
14 | status: hotspotStatusSchema,
15 | resolution: hotspotResolutionSchema,
16 | files: z.array(z.string()).nullable().optional(),
17 | assigned_to_me: z
18 | .union([z.boolean(), z.string().transform((val) => val === 'true')])
19 | .nullable()
20 | .optional(),
21 | since_leak_period: z
22 | .union([z.boolean(), z.string().transform((val) => val === 'true')])
23 | .nullable()
24 | .optional(),
25 | in_new_code_period: z
26 | .union([z.boolean(), z.string().transform((val) => val === 'true')])
27 | .nullable()
28 | .optional(),
29 | page: z.string().optional().transform(stringToNumberTransform),
30 | page_size: z.string().optional().transform(stringToNumberTransform),
31 | };
32 |
33 | export const hotspotToolSchema = {
34 | hotspot_key: z.string(),
35 | };
36 |
37 | export const updateHotspotStatusToolSchema = {
38 | hotspot_key: z.string(),
39 | status: z.enum(['TO_REVIEW', 'REVIEWED']),
40 | resolution: z.enum(['FIXED', 'SAFE']).nullable().optional(),
41 | comment: z.string().nullable().optional(),
42 | };
43 |
```
--------------------------------------------------------------------------------
/test-http-transport.sh:
--------------------------------------------------------------------------------
```bash
1 | #!/bin/bash
2 |
3 | echo "Starting SonarQube MCP Server with HTTP transport..."
4 |
5 | # Start the server in the background with HTTP transport
6 | MCP_TRANSPORT_TYPE=http \
7 | MCP_HTTP_PORT=3000 \
8 | SONARQUBE_URL=https://sonarcloud.io \
9 | SONARQUBE_TOKEN=dummy_token \
10 | LOG_LEVEL=INFO \
11 | LOG_FILE=/tmp/http-transport-test.log \
12 | pnpm start &
13 |
14 | SERVER_PID=$!
15 | echo "Server started with PID: $SERVER_PID"
16 |
17 | # Wait for server to start
18 | echo "Waiting for server to start..."
19 | sleep 3
20 |
21 | # Test health endpoint
22 | echo ""
23 | echo "Testing health endpoint..."
24 | curl -s http://localhost:3000/health | jq .
25 |
26 | # Create a session
27 | echo ""
28 | echo "Creating session..."
29 | SESSION_RESPONSE=$(curl -s -X POST http://localhost:3000/session)
30 | SESSION_ID=$(echo "$SESSION_RESPONSE" | jq -r .sessionId)
31 | echo "Session created: $SESSION_ID"
32 |
33 | # Test MCP endpoint (will fail with placeholder response, but tests the endpoint)
34 | echo ""
35 | echo "Testing MCP endpoint..."
36 | curl -s -X POST http://localhost:3000/mcp \
37 | -H "Content-Type: application/json" \
38 | -d "{\"sessionId\": \"$SESSION_ID\", \"method\": \"tools/list\", \"params\": {}}" | jq .
39 |
40 | # Close session
41 | echo ""
42 | echo "Closing session..."
43 | curl -s -X DELETE "http://localhost:3000/session/$SESSION_ID" | jq .
44 |
45 | # Kill the server
46 | echo ""
47 | echo "Stopping server..."
48 | kill $SERVER_PID
49 | wait $SERVER_PID 2>/dev/null
50 |
51 | echo ""
52 | echo "Test completed!"
53 | echo "Check logs at /tmp/http-transport-test.log for server logs."
```
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
```javascript
1 | /** @type {import('jest').Config} */
2 | export default {
3 | preset: 'ts-jest/presets/default-esm',
4 | testEnvironment: 'node',
5 | testMatch: ['**/__tests__/**/*.test.ts'],
6 | moduleNameMapper: {
7 | '^(\\.{1,2}/.*)\\.js$': '$1',
8 | '^(\\.{1,2}/.*)\\.ts$': '$1',
9 | },
10 | transform: {
11 | '^.+\\.tsx?$': [
12 | 'ts-jest',
13 | {
14 | useESM: true,
15 | tsconfig: {
16 | moduleResolution: 'NodeNext',
17 | },
18 | },
19 | ],
20 | },
21 | extensionsToTreatAsEsm: ['.ts'],
22 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
23 | transformIgnorePatterns: ['node_modules/(?!(@modelcontextprotocol)/)'],
24 | collectCoverageFrom: [
25 | 'src/**/*.ts',
26 | '!src/**/*.d.ts',
27 | '!src/**/*.test.ts',
28 | '!src/__tests__/mocks/**/*.ts',
29 | ],
30 | coverageReporters: ['text', 'lcov'],
31 | testPathIgnorePatterns: [
32 | '/node_modules/',
33 | '/src/__tests__/lambda-functions.test.ts',
34 | '/src/__tests__/handlers.test.ts',
35 | '/src/__tests__/tool-handlers.test.ts',
36 | '/src/__tests__/mocked-environment.test.ts',
37 | '/src/__tests__/direct-lambdas.test.ts',
38 | ],
39 | // Focusing on total coverage, with sonarqube.ts at 100%
40 | coverageThreshold: {
41 | 'src/sonarqube.ts': {
42 | statements: 81,
43 | branches: 60,
44 | functions: 100,
45 | lines: 81,
46 | },
47 | global: {
48 | statements: 68,
49 | branches: 8,
50 | functions: 40,
51 | lines: 68,
52 | },
53 | },
54 | bail: 0, // Run all tests regardless of failures
55 | };
56 |
```
--------------------------------------------------------------------------------
/src/__tests__/transports/stdio.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { describe, it, expect, vi } from 'vitest';
2 | import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3 | import { StdioTransport } from '../../transports/stdio.js';
4 |
5 | describe('StdioTransport', () => {
6 | it('should create instance with transport', () => {
7 | const transport = new StdioTransport();
8 | expect(transport).toBeDefined();
9 | expect(transport.getName()).toBe('stdio');
10 | });
11 |
12 | it('should return underlying transport', () => {
13 | const transport = new StdioTransport();
14 | const underlying = transport.getUnderlyingTransport();
15 | expect(underlying).toBeDefined();
16 | expect(underlying.constructor.name).toBe('StdioServerTransport');
17 | });
18 |
19 | it('should connect to server', async () => {
20 | const transport = new StdioTransport();
21 | const mockServer = {
22 | connect: vi.fn<() => Promise<any>>().mockResolvedValue(undefined as never),
23 | } as unknown as Server;
24 |
25 | await transport.connect(mockServer);
26 | expect(mockServer.connect).toHaveBeenCalledWith(transport.getUnderlyingTransport());
27 | });
28 |
29 | it('should add connect method to underlying transport', () => {
30 | const transport = new StdioTransport();
31 | const underlying = transport.getUnderlyingTransport();
32 | // The transport should have a connect method added via our workaround
33 | expect('connect' in underlying).toBe(true);
34 | expect(typeof (underlying as { connect?: unknown }).connect).toBe('function');
35 | });
36 | });
37 |
```
--------------------------------------------------------------------------------
/.claude/commands/release.md:
--------------------------------------------------------------------------------
```markdown
1 | # Release a new version
2 |
3 | You are about to make a release of the project. Please follow these steps:
4 |
5 | 1. **Create a new branch** for the release, e.g., `release/v1.0.0`.
6 | 2. **Update the version number** in `package.json` according to [Semantic Versioning](https://semver.org/) and based on the changes made since the last release.
7 | 3. **Update the changelog** in `CHANGELOG.md` to reflect the changes made in this release. Use the `date` command to get the current date in the format `YYYY-MM-DD`.
8 | Use the following format for the changelog entry:
9 |
10 | ```
11 | ## [v1.0.0] - YYYY-MM-DD
12 | - Description of changes
13 | - Another change
14 | ```
15 |
16 | 4. Update the README.md file if necessary, ensuring it reflects the latest changes and version number.
17 | 5. **Commit the changes** making sure that the pre-commit hook passes without warnings or errors. ^:wq
18 | 6. **Push the branch** to the remote repository.
19 | 7. **Create a pull request** to trigger the CI/CD pipeline.
20 | 8. **Wait for the CI/CD pipeline to complete** successfully. Ensure that all tests pass and the build is successful.
21 | 9. **Merge the pull request** into the main branch once the CI/CD pipeline has passed. The merge should be done using the "Squash and merge" option to keep the commit history clean and the branch should be deleted after merging.
22 | 10. **Make the release** by using the `gh` CLI tool:
23 |
24 | ```bash
25 | gh release create v1.0.0 --title "Release v1.0.0" --notes "Release notes for v1.0.0"
26 | ```
27 |
```
--------------------------------------------------------------------------------
/src/domains/metrics.ts:
--------------------------------------------------------------------------------
```typescript
1 | import type { PaginationParams, SonarQubeMetricsResult } from '../types/index.js';
2 | import { BaseDomain } from './base.js';
3 |
4 | /**
5 | * Domain module for metrics-related operations
6 | */
7 | export class MetricsDomain extends BaseDomain {
8 | /**
9 | * Gets available metrics from SonarQube
10 | * @param params Parameters including pagination
11 | * @returns Promise with the list of metrics
12 | */
13 | async getMetrics(params?: PaginationParams): Promise<SonarQubeMetricsResult> {
14 | const { page, pageSize } = params ?? {};
15 |
16 | const request: {
17 | p?: number;
18 | ps?: number;
19 | } = {
20 | ...(page && { p: page }),
21 | ...(pageSize && { ps: pageSize }),
22 | };
23 |
24 | const response = await this.webApiClient.metrics.search(request);
25 |
26 | // The API might return paging info
27 | const paging = (
28 | response as unknown as {
29 | paging?: { pageIndex: number; pageSize: number; total: number };
30 | }
31 | ).paging;
32 |
33 | return {
34 | metrics: response.metrics.map((metric) => ({
35 | id: metric.id ?? '',
36 | key: metric.key,
37 | name: metric.name,
38 | description: metric.description ?? '',
39 | domain: metric.domain ?? '',
40 | type: metric.type,
41 | direction: metric.direction ?? 0,
42 | qualitative: metric.qualitative ?? false,
43 | hidden: metric.hidden ?? false,
44 | custom: metric.custom ?? false,
45 | })),
46 | paging: paging ?? {
47 | pageIndex: page ?? 1,
48 | pageSize: pageSize ?? 100,
49 | total: response.metrics.length,
50 | },
51 | };
52 | }
53 | }
54 |
```
--------------------------------------------------------------------------------
/src/handlers/quality-gates.ts:
--------------------------------------------------------------------------------
```typescript
1 | import type { ProjectQualityGateParams, ISonarQubeClient } from '../types/index.js';
2 | import { getDefaultClient } from '../utils/client-factory.js';
3 | import { createStructuredResponse } from '../utils/structured-response.js';
4 |
5 | /**
6 | * Handler for listing quality gates
7 | * @param client Optional SonarQube client instance
8 | * @returns Promise with the quality gates list
9 | */
10 | export async function handleSonarQubeListQualityGates(
11 | client: ISonarQubeClient = getDefaultClient()
12 | ) {
13 | const result = await client.listQualityGates();
14 |
15 | return createStructuredResponse(result);
16 | }
17 |
18 | /**
19 | * Handler for getting a specific quality gate
20 | * @param params Parameters containing the quality gate ID
21 | * @param client Optional SonarQube client instance
22 | * @returns Promise with the quality gate details
23 | */
24 | export async function handleSonarQubeGetQualityGate(
25 | params: { id: string },
26 | client: ISonarQubeClient = getDefaultClient()
27 | ) {
28 | const result = await client.getQualityGate(params.id);
29 |
30 | return createStructuredResponse(result);
31 | }
32 |
33 | /**
34 | * Handler for getting quality gate status for a project
35 | * @param params Parameters for the quality gate status request
36 | * @param client Optional SonarQube client instance
37 | * @returns Promise with the quality gate status
38 | */
39 | export async function handleSonarQubeQualityGateStatus(
40 | params: ProjectQualityGateParams,
41 | client: ISonarQubeClient = getDefaultClient()
42 | ) {
43 | const result = await client.getProjectQualityGateStatus(params);
44 |
45 | return createStructuredResponse(result);
46 | }
47 |
```
--------------------------------------------------------------------------------
/src/__tests__/string-to-number-transform.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { describe, it, expect } from 'vitest';
2 | import { nullToUndefined, stringToNumberTransform } from '../index.js';
3 | describe('String to Number Transform', () => {
4 | describe('nullToUndefined', () => {
5 | it('should transform null to undefined', () => {
6 | expect(nullToUndefined(null)).toBeUndefined();
7 | });
8 | it('should not transform undefined', () => {
9 | expect(nullToUndefined(undefined)).toBeUndefined();
10 | });
11 | it('should not transform non-null values', () => {
12 | expect(nullToUndefined('test')).toBe('test');
13 | expect(nullToUndefined(123)).toBe(123);
14 | expect(nullToUndefined(true)).toBe(true);
15 | expect(nullToUndefined(false)).toBe(false);
16 | expect(nullToUndefined(0)).toBe(0);
17 | expect(nullToUndefined('')).toBe('');
18 | });
19 | });
20 | describe('stringToNumberTransform', () => {
21 | it('should transform valid string numbers to integers', () => {
22 | expect(stringToNumberTransform('123')).toBe(123);
23 | expect(stringToNumberTransform('0')).toBe(0);
24 | expect(stringToNumberTransform('-10')).toBe(-10);
25 | });
26 | it('should return null for invalid number strings', () => {
27 | expect(stringToNumberTransform('abc')).toBeNull();
28 | expect(stringToNumberTransform('')).toBeNull();
29 | expect(stringToNumberTransform('123abc')).toBe(123); // parseInt behavior
30 | });
31 | it('should pass through null and undefined values', () => {
32 | expect(stringToNumberTransform(null)).toBeNull();
33 | expect(stringToNumberTransform(undefined)).toBeUndefined();
34 | });
35 | });
36 | });
37 |
```
--------------------------------------------------------------------------------
/src/domains/projects.ts:
--------------------------------------------------------------------------------
```typescript
1 | import type { PaginationParams, SonarQubeProjectsResult } from '../types/index.js';
2 | import { BaseDomain } from './base.js';
3 |
4 | /**
5 | * Domain module for project-related operations
6 | */
7 | export class ProjectsDomain extends BaseDomain {
8 | /**
9 | * Lists all projects in SonarQube
10 | * @param params Pagination and organization parameters
11 | * @returns Promise with the list of projects
12 | */
13 | async listProjects(params?: PaginationParams): Promise<SonarQubeProjectsResult> {
14 | const { page, pageSize } = params ?? {};
15 | this.logger.debug('Listing projects', { page, pageSize, organization: this.organization });
16 |
17 | try {
18 | const builder = this.webApiClient.projects.search();
19 |
20 | if (page !== undefined) {
21 | builder.page(page);
22 | }
23 | if (pageSize !== undefined) {
24 | builder.pageSize(pageSize);
25 | }
26 |
27 | const response = await builder.execute();
28 | this.logger.debug('Projects retrieved successfully', { count: response.components.length });
29 |
30 | // Transform to our interface
31 | return {
32 | projects: response.components.map((component) => ({
33 | key: component.key,
34 | name: component.name,
35 | qualifier: component.qualifier,
36 | visibility: component.visibility,
37 | lastAnalysisDate: component.lastAnalysisDate,
38 | revision: component.revision,
39 | managed: component.managed,
40 | })),
41 | paging: response.paging,
42 | };
43 | } catch (error) {
44 | this.logger.error('Failed to list projects', error);
45 | throw error;
46 | }
47 | }
48 | }
49 |
```
--------------------------------------------------------------------------------
/src/handlers/hotspots.ts:
--------------------------------------------------------------------------------
```typescript
1 | import type {
2 | HotspotSearchParams,
3 | HotspotStatusUpdateParams,
4 | ISonarQubeClient,
5 | } from '../types/index.js';
6 | import { getDefaultClient } from '../utils/client-factory.js';
7 | import { createStructuredResponse, createTextResponse } from '../utils/structured-response.js';
8 |
9 | /**
10 | * Handler for searching security hotspots
11 | * @param params Parameters for the hotspots search
12 | * @param client Optional SonarQube client instance
13 | * @returns Promise with the hotspots search result
14 | */
15 | export async function handleSonarQubeHotspots(
16 | params: HotspotSearchParams,
17 | client: ISonarQubeClient = getDefaultClient()
18 | ) {
19 | const result = await client.hotspots(params);
20 |
21 | return createStructuredResponse(result);
22 | }
23 |
24 | /**
25 | * Handler for getting hotspot details
26 | * @param hotspotKey The key of the hotspot
27 | * @param client Optional SonarQube client instance
28 | * @returns Promise with the hotspot details
29 | */
30 | export async function handleSonarQubeHotspot(
31 | hotspotKey: string,
32 | client: ISonarQubeClient = getDefaultClient()
33 | ) {
34 | const result = await client.hotspot(hotspotKey);
35 |
36 | return createStructuredResponse(result);
37 | }
38 |
39 | /**
40 | * Handler for updating hotspot status
41 | * @param params Parameters for the hotspot status update
42 | * @param client Optional SonarQube client instance
43 | * @returns Promise with success message
44 | */
45 | export async function handleSonarQubeUpdateHotspotStatus(
46 | params: HotspotStatusUpdateParams,
47 | client: ISonarQubeClient = getDefaultClient()
48 | ) {
49 | await client.updateHotspotStatus(params);
50 |
51 | return createTextResponse('Hotspot status updated successfully');
52 | }
53 |
```
--------------------------------------------------------------------------------
/src/utils/structured-response.ts:
--------------------------------------------------------------------------------
```typescript
1 | import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2 |
3 | /**
4 | * Creates a structured response for MCP tools that includes both text content
5 | * (for backward compatibility) and structured content (for better machine readability)
6 | *
7 | * @param data The structured data to return
8 | * @returns A CallToolResult with both text and structured content
9 | */
10 | export function createStructuredResponse(data: unknown): CallToolResult {
11 | return {
12 | content: [
13 | {
14 | type: 'text' as const,
15 | text: JSON.stringify(data, null, 2),
16 | },
17 | ],
18 | structuredContent: data as Record<string, unknown>,
19 | };
20 | }
21 |
22 | /**
23 | * Creates a simple text response without structured content
24 | *
25 | * @param text The text content to return
26 | * @returns A CallToolResult with only text content
27 | */
28 | export function createTextResponse(text: string): CallToolResult {
29 | return {
30 | content: [
31 | {
32 | type: 'text' as const,
33 | text,
34 | },
35 | ],
36 | };
37 | }
38 |
39 | /**
40 | * Creates an error response with optional structured error details
41 | *
42 | * @param message The error message
43 | * @param details Optional structured error details
44 | * @returns A CallToolResult marked as an error
45 | */
46 | export function createErrorResponse(message: string, details?: unknown): CallToolResult {
47 | const errorData: Record<string, unknown> = {
48 | error: message,
49 | };
50 |
51 | if (details !== undefined) {
52 | errorData.details = details;
53 | }
54 |
55 | return {
56 | content: [
57 | {
58 | type: 'text' as const,
59 | text: message,
60 | },
61 | ],
62 | structuredContent: errorData,
63 | isError: true,
64 | };
65 | }
66 |
```
--------------------------------------------------------------------------------
/src/handlers/system.ts:
--------------------------------------------------------------------------------
```typescript
1 | import type { ISonarQubeClient } from '../types/index.js';
2 | import { getDefaultClient } from '../utils/client-factory.js';
3 | import { createLogger } from '../utils/logger.js';
4 | import { withErrorHandling } from '../errors.js';
5 | import { withMCPErrorHandling } from '../utils/error-handler.js';
6 | import { createStructuredResponse, createTextResponse } from '../utils/structured-response.js';
7 |
8 | const logger = createLogger('handlers/system');
9 |
10 | /**
11 | * Handler for getting SonarQube system health status
12 | * @param client Optional SonarQube client instance
13 | * @returns Promise with the health status result
14 | */
15 | export const handleSonarQubeGetHealth = withMCPErrorHandling(
16 | async (client: ISonarQubeClient = getDefaultClient()) => {
17 | logger.debug('Handling get health request');
18 |
19 | const result = await withErrorHandling('Get SonarQube health status', () => client.getHealth());
20 | logger.info('Successfully retrieved health status', { health: result.health });
21 |
22 | return createStructuredResponse(result);
23 | }
24 | );
25 |
26 | /**
27 | * Handler for getting SonarQube system status
28 | * @param client Optional SonarQube client instance
29 | * @returns Promise with the system status result
30 | */
31 | export async function handleSonarQubeGetStatus(client: ISonarQubeClient = getDefaultClient()) {
32 | const result = await client.getStatus();
33 |
34 | return createStructuredResponse(result);
35 | }
36 |
37 | /**
38 | * Handler for pinging SonarQube system
39 | * @param client Optional SonarQube client instance
40 | * @returns Promise with the ping result
41 | */
42 | export async function handleSonarQubePing(client: ISonarQubeClient = getDefaultClient()) {
43 | const result = await client.ping();
44 |
45 | return createTextResponse(result);
46 | }
47 |
```
--------------------------------------------------------------------------------
/src/schemas/components.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { z } from 'zod';
2 | import { stringToNumberTransform } from '../utils/transforms.js';
3 | import { pullRequestSchema } from './common.js';
4 |
5 | /**
6 | * Valid component qualifiers based on SonarQube API
7 | */
8 | const componentQualifierSchema = z.enum([
9 | 'TRK', // Project
10 | 'DIR', // Directory
11 | 'FIL', // File
12 | 'UTS', // Unit Test
13 | 'BRC', // Branch
14 | 'APP', // Application
15 | 'VW', // View
16 | 'SVW', // Sub-view
17 | 'LIB', // Library
18 | ]);
19 |
20 | /**
21 | * Schema for components tool
22 | */
23 | export const componentsToolSchema = {
24 | // Search parameters
25 | query: z.string().optional().describe('Text search query'),
26 | qualifiers: z
27 | .array(componentQualifierSchema)
28 | .optional()
29 | .describe('Component types: TRK, DIR, FIL, UTS, BRC, APP, VW, SVW, LIB'),
30 | language: z.string().optional().describe('Programming language filter'),
31 |
32 | // Tree navigation parameters
33 | component: z.string().optional().describe('Component key for tree navigation'),
34 | strategy: z.enum(['all', 'children', 'leaves']).optional().describe('Tree traversal strategy'),
35 |
36 | // Show component parameter
37 | key: z.string().optional().describe('Component key to show details for'),
38 |
39 | // Common parameters
40 | asc: z
41 | .union([z.boolean(), z.string().transform((val) => val === 'true')])
42 | .optional()
43 | .describe('Sort ascending/descending'),
44 | ps: z
45 | .string()
46 | .optional()
47 | .transform(stringToNumberTransform)
48 | .describe('Page size (default: 100, max: 500)'),
49 | p: z.string().optional().transform(stringToNumberTransform).describe('Page number'),
50 |
51 | // Additional filters
52 | branch: z.string().optional().describe('Branch name'),
53 | pullRequest: pullRequestSchema.describe('Pull request ID'),
54 | };
55 |
```