#
tokens: 49556/50000 99/236 files (page 1/8)
lines: off (toggle) GitHub
raw markdown copy
This is page 1 of 8. Use http://codebase.md/sapientpants/sonarqube-mcp-server?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:
--------------------------------------------------------------------------------

```
docs/architecture/decisions

```

--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------

```
dist/
node_modules/
coverage/
pnpm-lock.yaml
package-lock.json
*.d.ts
sbom.cdx.json 
```

--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------

```
engine-strict=true
save-exact=true
fund=false
audit=true
@your-scope:registry=https://registry.npmjs.org/

```

--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------

```
{
  "semi": true,
  "trailingComma": "es5",
  "singleQuote": true,
  "printWidth": 100,
  "tabWidth": 2,
  "useTabs": false,
  "endOfLine": "lf"
}

```

--------------------------------------------------------------------------------
/.markdownlintignore:
--------------------------------------------------------------------------------

```
# Ignore CHANGELOG as it's auto-generated
CHANGELOG.md

# Ignore node_modules
node_modules/

# Ignore coverage reports
coverage/

# Ignore build output
dist/
```

--------------------------------------------------------------------------------
/.yamllintignore:
--------------------------------------------------------------------------------

```
# Ignore node_modules
node_modules/

# Ignore GitHub workflows (linted by actionlint)
.github/workflows/

# Ignore build output
dist/
coverage/

# Ignore lock files (auto-generated)
pnpm-lock.yaml
yarn.lock
package-lock.json
```

--------------------------------------------------------------------------------
/.yaml-lint.yml:
--------------------------------------------------------------------------------

```yaml
# yaml-lint configuration
# https://github.com/rasshofer/yaml-lint

# Schema to use for validation
schema: 'FAILSAFE_SCHEMA'

# Ignore certain paths
ignore:
  - node_modules/**
  - .github/workflows/**
  - dist/**
  - coverage/**
  - pnpm-lock.yaml
  - yarn.lock
  - package-lock.json

```

--------------------------------------------------------------------------------
/.trivyignore:
--------------------------------------------------------------------------------

```
# Trivy Ignore File
# This file contains CVEs to ignore during container security scanning
# Format: CVE-ID or CVE-ID with expiry date
# 
# Example entries:
# CVE-2022-12345
# CVE-2022-12345 exp:2025-01-01
#
# Add CVEs here that are false positives or accepted risks
# Each exclusion should be documented with a comment explaining why

# Example: False positive in test dependency
# CVE-2024-XXXXX

# Note: Regularly review and update this file to remove outdated exclusions
```

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
# Dependencies
node_modules/
.pnpm-store/

# Build output
dist/
coverage/

# IDE and editor files
.vscode/
.idea/
*.swp
*.swo

# Logs
*.log
npm-debug.log*
pnpm-debug.log*

# Environment variables
.env
.env.local
.env.*.local

# OS files
.DS_Store
Thumbs.db 
**/.claude/settings.local.json

# Terraform
terraform/.terraform/
terraform/**/.terraform/
terraform/**/terraform.tfstate
terraform/**/terraform.tfstate.*
terraform/**/*.tfvars
!terraform/**/*.tfvars.example
terraform/**/.terraform.lock.hcl

# Kubernetes secrets
k8s/overlays/production/secrets.env

# Helm
helm/**/charts/
helm/**/Chart.lock

```

--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------

```
# Git files
.git
.gitignore
.gitattributes

# GitHub files
.github

# Documentation
*.md
LICENSE
docs
doc

# Development files
.husky
.changeset
.eslintrc.json
.prettierrc
.prettierignore
.editorconfig
.vscode
.idea

# Test files
tests
test
*.test.ts
*.spec.ts
coverage
.nyc_output
junit.xml

# Build artifacts that will be created during Docker build
dist
*.tsbuildinfo

# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*

# OS files
.DS_Store
Thumbs.db

# Environment files (should not be in image)
.env
.env.*

# CI/CD files
.travis.yml
.gitlab-ci.yml
Jenkinsfile

# Docker files
Dockerfile
.dockerignore
docker-compose*.yml

# Temporary files
tmp
temp
*.tmp
*.bak
*.swp

# SBOM and security scan results
sbom.cdx.json
trivy-results.sarif
results.sarif

# Archives
*.tar.gz
*.zip
```

--------------------------------------------------------------------------------
/.markdownlint.yaml:
--------------------------------------------------------------------------------

```yaml
# Markdownlint configuration
# https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md

# Default state for all rules
default: true

# MD003/heading-style - Heading style
MD003:
  style: 'atx' # Use # style headers

# MD004/ul-style - Unordered list style
MD004:
  style: 'dash' # Use - for unordered lists

# MD007/ul-indent - Unordered list indentation
MD007:
  indent: 2 # 2 spaces for nested lists

# MD013/line-length - Line length
# Disabled as many lines contain URLs or complex content
MD013: false

# MD022/blanks-around-headings - Headings should be surrounded by blank lines
# Allow some flexibility in heading spacing
MD022: false

# MD024/no-duplicate-heading - Multiple headings with the same content
MD024:
  siblings_only: true # Allow same heading if not siblings

# MD026/no-trailing-punctuation - Trailing punctuation in heading
# Allow colons in headings for clarity
MD026: false

# MD031/blanks-around-fences - Fenced code blocks should be surrounded by blank lines
# Sometimes inline code blocks make sense
MD031: false

# MD034/no-bare-urls - Bare URLs
# Sometimes bare URLs are intentional for clarity
MD034: false

# MD025/single-title - Multiple top-level headings
# Disabled because we might have multiple H1s in different sections
MD025: false

# MD033/no-inline-html - Inline HTML
MD033:
  allowed_elements:
    - details
    - summary
    - sub
    - sup
    - br
    - img
    - a
    - code
    - pre
    - table
    - thead
    - tbody
    - tr
    - td
    - th
    - div
    - span
    - kbd

# MD040/fenced-code-language - Fenced code blocks should have a language specified
# Disabled as we sometimes have plain text blocks
MD040: false

# MD041/first-line-heading - First line in a file should be a top-level heading
# Disabled as not all markdown files need to start with a heading
MD041: false

# MD045/no-alt-text - Images should have alternate text
# Disabled as decorative images don't always need alt text
MD045: false

# MD046/code-block-style - Code block style
MD046:
  style: 'fenced' # Use ``` for code blocks

# MD048/code-fence-style - Code fence style
MD048:
  style: 'backtick' # Use backticks for code fences

# MD049/emphasis-style - Emphasis style
MD049:
  style: 'underscore' # Use _ for italic

# MD050/strong-style - Strong style
MD050:
  style: 'asterisk' # Use ** for bold

```

--------------------------------------------------------------------------------
/.changeset/README.md:
--------------------------------------------------------------------------------

```markdown
# Changesets

Use `pnpm changeset` to create version entries. On release, run `pnpm release`.

```

--------------------------------------------------------------------------------
/scripts/README.md:
--------------------------------------------------------------------------------

```markdown
# SonarQube MCP Server Test Scripts

This directory contains comprehensive test scripts for validating the SonarQube MCP Server deployment artifacts, including Kubernetes manifests, Helm charts, Terraform modules, and documentation.

## Overview

The test suite ensures that all deployment artifacts are:

- Syntactically correct
- Follow security best practices
- Work as expected in different configurations
- Scale properly under load
- Integrate correctly with monitoring systems

## Test Scripts

### 🚀 Master Test Runner

**Script:** `run-all-tests.sh`

Orchestrates all test suites in the correct order.

```bash
# Run all tests
./scripts/run-all-tests.sh

# Run specific test suite
./scripts/run-all-tests.sh --only helm

# Skip cleanup for debugging
./scripts/run-all-tests.sh --skip-cleanup
```

### 📚 Documentation Validation

**Script:** `validate-docs.sh`

Validates documentation for:

- Broken internal links
- Invalid code examples
- Missing required sections
- Orphaned images
- TODO markers

```bash
./scripts/validate-docs.sh
```

### 🔧 Terraform Validation

**Script:** `validate-terraform.sh`

Tests Terraform modules for:

- Syntax validation
- Formatting standards
- Security issues (hardcoded secrets)
- Required variables
- Plan generation

```bash
./scripts/validate-terraform.sh
```

### 🎯 Helm Chart Testing

**Script:** `test-helm-values.sh`

Tests Helm chart with various configurations:

- Minimal deployment
- Production settings
- High availability
- Monitoring enabled
- Ingress with TLS

```bash
./scripts/test-helm-values.sh
```

### ☸️ Kubernetes Deployment Testing

**Script:** `test-k8s-deployment.sh`

Basic Kubernetes deployment test using kind cluster.

```bash
export SONARQUBE_TOKEN="your-token"
./scripts/test-k8s-deployment.sh
```

**Script:** `test-k8s-helm-deployment.sh`

Extended test that validates both Kustomize and Helm deployments.

```bash
# Test both Kustomize and Helm
./scripts/test-k8s-helm-deployment.sh

# Test only Helm
./scripts/test-k8s-helm-deployment.sh --skip-kustomize

# Keep cluster for debugging
./scripts/test-k8s-helm-deployment.sh --keep-cluster
```

### 🔒 Security Scanning

**Script:** `security-scan.sh`

Comprehensive security scanning using:

- Kubesec for Kubernetes manifests
- Trivy for container vulnerabilities
- Polaris for policy violations
- Custom security checks

```bash
./scripts/security-scan.sh
```

### 📊 Monitoring Integration Tests

**Script:** `test-monitoring-integration.sh`

Tests monitoring endpoints and integration:

- Health and readiness checks
- Prometheus metrics format
- Circuit breaker functionality
- OpenTelemetry support
- Performance metrics

```bash
# Requires running service
npm run dev

# In another terminal
./scripts/test-monitoring-integration.sh
```

### ⚡ Load Testing

**Script:** `load-test.sh`

Tests auto-scaling behavior under load using k6 or Apache Bench.

```bash
# Default configuration
./scripts/load-test.sh

# Custom parameters
CONCURRENT_USERS=100 DURATION=600 ./scripts/load-test.sh
```

### 🌐 Network Fixes

**Script:** `fix-kind-dns.sh`

Fixes DNS resolution issues in kind clusters (common on macOS).

```bash
./scripts/fix-kind-dns.sh cluster-name
```

## Helm Test Hooks

Located in `helm/sonarqube-mcp/templates/tests/`:

- `deployment-test.yaml` - Tests deployment readiness
- `service-test.yaml` - Tests service connectivity
- `config-test.yaml` - Tests configuration validity

Run with:

```bash
helm test release-name -n namespace
```

## Prerequisites

### Required Tools

- Docker
- kubectl
- Helm 3+

### Optional Tools

- kind (for Kubernetes tests)
- Terraform (for Terraform validation)
- k6 or Apache Bench (for load testing)
- trivy (for container scanning)
- Node.js (for documentation validation)

### Tool Installation

```bash
# macOS with Homebrew
brew install kubectl helm kind terraform k6 trivy

# Install Polaris
brew install FairwindsOps/tap/polaris

# Install kubesec (downloaded automatically if not present)
```

## CI/CD Integration

These scripts are designed to be integrated into CI/CD pipelines:

```yaml
# Example GitHub Actions
- name: Run all tests
  run: ./scripts/run-all-tests.sh

# Example GitLab CI
test:
  script:
    - ./scripts/validate-docs.sh
    - ./scripts/validate-terraform.sh
    - ./scripts/test-helm-values.sh
    - ./scripts/security-scan.sh
```

## Troubleshooting

### Common Issues

1. **Script not executable**

   ```bash
   chmod +x scripts/*.sh
   ```

2. **Kind cluster issues**

   ```bash
   kind delete cluster --name sonarqube-mcp-test
   ./scripts/test-k8s-deployment.sh
   ```

3. **DNS resolution in kind**

   ```bash
   ./scripts/fix-kind-dns.sh sonarqube-mcp-test
   ```

4. **Missing tools**
   Check prerequisites and install required tools.

## Best Practices

1. **Run tests before commits**

   ```bash
   ./scripts/run-all-tests.sh --only docs,helm,security
   ```

2. **Use in CI/CD**
   Integrate appropriate tests in your pipeline.

3. **Regular security scans**

   ```bash
   ./scripts/security-scan.sh
   ```

4. **Test configuration changes**
   Always test Helm values changes:
   ```bash
   ./scripts/test-helm-values.sh
   ```

## Contributing

When adding new deployment artifacts:

1. Update relevant test scripts
2. Add new test cases if needed
3. Document in this README
4. Ensure tests pass before PR

```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
# SonarQube MCP Server

[![Main](https://github.com/sapientpants/sonarqube-mcp-server/actions/workflows/main.yml/badge.svg)](https://github.com/sapientpants/sonarqube-mcp-server/actions/workflows/main.yml)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=sonarqube-mcp-server&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=sonarqube-mcp-server)
[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=sonarqube-mcp-server&metric=bugs)](https://sonarcloud.io/summary/new_code?id=sonarqube-mcp-server)
[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=sonarqube-mcp-server&metric=code_smells)](https://sonarcloud.io/summary/new_code?id=sonarqube-mcp-server)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=sonarqube-mcp-server&metric=coverage)](https://sonarcloud.io/summary/new_code?id=sonarqube-mcp-server)
[![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=sonarqube-mcp-server&metric=duplicated_lines_density)](https://sonarcloud.io/summary/new_code?id=sonarqube-mcp-server)
[![npm version](https://img.shields.io/npm/v/sonarqube-mcp-server.svg)](https://www.npmjs.com/package/sonarqube-mcp-server)
[![npm downloads](https://img.shields.io/npm/dm/sonarqube-mcp-server.svg)](https://www.npmjs.com/package/sonarqube-mcp-server)
[![License](https://img.shields.io/npm/l/sonarqube-mcp-server.svg)](https://github.com/sapientpants/sonarqube-mcp-server/blob/main/LICENSE)

A Model Context Protocol (MCP) server that integrates with SonarQube to provide AI assistants with access to code quality metrics, issues, and analysis results.

## Table of Contents

- [Overview](#overview)
- [Documentation](#documentation)
- [Compatibility](#compatibility)
- [Quick Start](#quick-start)
- [Installation](#installation)
  - [NPX](#npx-recommended)
  - [Docker](#docker-recommended-for-production)
  - [Local Development](#local-development)
- [Configuration](#configuration)
  - [Environment Variables](#environment-variables)
  - [Authentication Methods](#authentication-methods)
- [Available Tools](#available-tools)
- [Usage Examples](#usage-examples)
- [Architecture](#architecture)
- [Development](#development)
- [Troubleshooting](#troubleshooting)
- [Contributing](#contributing)
- [License](#license)
- [External Resources](#external-resources)

## Overview

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:

- 📊 **Retrieve code metrics and analysis results** - Access detailed quality metrics for your projects
- 🐛 **Access and filter issues** - Search and filter code issues by severity, type, status, and more
- 🔒 **Review security hotspots** - Find and manage security vulnerabilities with dedicated workflows
- 🌿 **Analyze branches and PRs** - Review code quality in feature branches and pull requests
- 📦 **Multi-project analysis** - Query issues and metrics across multiple projects simultaneously
- ✅ **Check quality gates** - Monitor whether projects meet quality standards
- 📈 **Analyze project quality over time** - Track metrics history and trends
- 🔍 **View source code with issues** - See problematic code with highlighted issues
- 🏥 **Monitor system health** - Check SonarQube instance status and availability
- 🔄 **Enhanced error handling** - Clear error messages with solutions and automatic retry for transient failures

## Documentation

### Core Guides

- **[Architecture Guide](docs/architecture.md)** - System architecture, design decisions, and component overview
- **[Troubleshooting Guide](docs/troubleshooting.md)** - Common issues, debugging, and solutions

### Security & Authentication

- **[Security Guide](docs/security.md)** - Authentication, authorization, and security best practices

## Compatibility

For detailed information about MCP protocol version support and SDK compatibility, see [COMPATIBILITY.md](COMPATIBILITY.md).

## Quick Start

### Prerequisites

- [Claude Desktop](https://claude.ai/download) installed
- A SonarQube instance or [SonarCloud](https://sonarcloud.io) account
- A SonarQube/SonarCloud authentication token

### 1. Get Your SonarQube Token

**For SonarCloud:**

1. Log in to [SonarCloud](https://sonarcloud.io)
2. Go to **My Account** → **Security**
3. Generate a new token

**For SonarQube:**

1. Log in to your SonarQube instance
2. Go to **My Account** → **Security**
3. Generate a new token

### 2. Configure Claude Desktop

1. Open Claude Desktop
2. Go to **Settings** → **Developer** → **Edit Config**
3. Add the SonarQube server configuration:

```json
{
  "mcpServers": {
    "sonarqube": {
      "command": "npx",
      "args": ["-y", "sonarqube-mcp-server@latest"],
      "env": {
        "SONARQUBE_URL": "https://sonarcloud.io",
        "SONARQUBE_TOKEN": "your-token-here",
        "SONARQUBE_ORGANIZATION": "your-org (for SonarCloud)"
      }
    }
  }
}
```

**Alternative authentication methods:**

Using Basic Authentication:

```json
{
  "mcpServers": {
    "sonarqube": {
      "command": "npx",
      "args": ["-y", "sonarqube-mcp-server@latest"],
      "env": {
        "SONARQUBE_URL": "https://your-sonarqube.com",
        "SONARQUBE_USERNAME": "your-username",
        "SONARQUBE_PASSWORD": "your-password"
      }
    }
  }
}
```

Using System Passcode:

```json
{
  "mcpServers": {
    "sonarqube": {
      "command": "npx",
      "args": ["-y", "sonarqube-mcp-server@latest"],
      "env": {
        "SONARQUBE_URL": "https://your-sonarqube.com",
        "SONARQUBE_PASSCODE": "your-system-passcode"
      }
    }
  }
}
```

1. Restart Claude Desktop

### 3. Start Using

Ask Claude to analyze your SonarQube projects:

```
"List all my SonarQube projects"
"Show me critical issues in project xyz"
"What's the code coverage for project xyz?"
"Check the quality gate status for project xyz"
"Retrieve security hotspots in project xyz and create a plan to address them"
"Retrieve the issues for pr 123 in project xyz and create a plan to address them"
```

## Installation

### NPX (Recommended)

The simplest way to use the SonarQube MCP Server is through npx:

```json
{
  "mcpServers": {
    "sonarqube": {
      "command": "npx",
      "args": ["-y", "sonarqube-mcp-server@latest"],
      "env": {
        "SONARQUBE_URL": "https://sonarqube.example.com",
        "SONARQUBE_TOKEN": "your-sonarqube-token",
        "SONARQUBE_ORGANIZATION": "your-organization-key"
      }
    }
  }
}
```

### Docker (Recommended for Production)

Docker provides the most reliable deployment method by packaging all dependencies and ensuring consistent behavior across different environments.

> **Enterprise Deployment**: For production deployments with Kubernetes, Helm charts, and cloud-specific configurations, see our comprehensive [Deployment Guide](docs/deployment.md).

#### Quick Start with Docker

**For stdio transport (Claude Desktop):**

```json
{
  "mcpServers": {
    "sonarqube": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "-e",
        "SONARQUBE_URL",
        "-e",
        "SONARQUBE_TOKEN",
        "-e",
        "SONARQUBE_ORGANIZATION",
        "sapientpants/sonarqube-mcp-server:latest"
      ],
      "env": {
        "SONARQUBE_URL": "https://sonarqube.example.com",
        "SONARQUBE_TOKEN": "your-sonarqube-token",
        "SONARQUBE_ORGANIZATION": "your-organization-key"
      }
    }
  }
}
```

#### Docker Hub Images

Official images are available on Docker Hub: [`sapientpants/sonarqube-mcp-server`](https://hub.docker.com/r/sapientpants/sonarqube-mcp-server)

**Available tags:**

- `latest` - Latest stable release
- `1.6.0` - Specific version (recommended for production)
- `1.6` - Latest patch version of 1.6.x
- `1` - Latest minor version of 1.x.x

**Pull the image:**

```bash
docker pull sapientpants/sonarqube-mcp-server:latest
```

#### Advanced Docker Configuration

**With logging enabled:**

```json
{
  "mcpServers": {
    "sonarqube": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "-v",
        "/tmp/sonarqube-logs:/logs",
        "-e",
        "SONARQUBE_URL",
        "-e",
        "SONARQUBE_TOKEN",
        "-e",
        "SONARQUBE_ORGANIZATION",
        "-e",
        "LOG_FILE=/logs/sonarqube-mcp.log",
        "-e",
        "LOG_LEVEL=INFO",
        "sapientpants/sonarqube-mcp-server:latest"
      ],
      "env": {
        "SONARQUBE_URL": "https://sonarqube.example.com",
        "SONARQUBE_TOKEN": "your-sonarqube-token",
        "SONARQUBE_ORGANIZATION": "your-organization-key"
      }
    }
  }
}
```

**Using Docker Compose:**

```yaml
version: '3.8'
services:
  sonarqube-mcp:
    image: sapientpants/sonarqube-mcp-server:latest
    environment:
      - SONARQUBE_URL=https://sonarqube.example.com
      - SONARQUBE_TOKEN=${SONARQUBE_TOKEN}
      - SONARQUBE_ORGANIZATION=${SONARQUBE_ORGANIZATION}
      - LOG_FILE=/logs/sonarqube-mcp.log
      - LOG_LEVEL=INFO
    volumes:
      - ./logs:/logs
    stdin_open: true
    tty: true
```

#### Building Your Own Docker Image

If you need to customize the server, you can build your own image:

```bash
# Clone the repository
git clone https://github.com/sapientpants/sonarqube-mcp-server.git
cd sonarqube-mcp-server

# Build the Docker image
docker build -t my-sonarqube-mcp-server .

# Run your custom image
docker run -i --rm \
  -e SONARQUBE_URL="https://sonarqube.example.com" \
  -e SONARQUBE_TOKEN="your-token" \
  my-sonarqube-mcp-server
```

#### Docker Best Practices

1. **Version Pinning**: Always use specific version tags in production:

   ```bash
   sapientpants/sonarqube-mcp-server:1.6.0
   ```

2. **Resource Limits**: Set appropriate resource limits:

   ```bash
   docker run -i --rm \
     --memory="256m" \
     --cpus="0.5" \
     sapientpants/sonarqube-mcp-server:1.6.0
   ```

3. **Security**: Run as non-root user (default in our image):

   ```bash
   docker run -i --rm \
     --user node \
     sapientpants/sonarqube-mcp-server:1.6.0
   ```

4. **Health Checks**: The container includes a health check that verifies the Node.js process is running

### Local Development

For development or customization:

```json
{
  "mcpServers": {
    "sonarqube": {
      "command": "node",
      "args": ["/path/to/sonarqube-mcp-server/dist/index.js"],
      "env": {
        "SONARQUBE_URL": "https://sonarqube.example.com",
        "SONARQUBE_TOKEN": "your-sonarqube-token",
        "SONARQUBE_ORGANIZATION": "your-organization-key"
      }
    }
  }
}
```

## Configuration

### Environment Variables

#### Authentication (choose one method)

| Variable                 | Description                                   | Required | Default |
| ------------------------ | --------------------------------------------- | -------- | ------- |
| **Token Authentication** |                                               |          |         |
| `SONARQUBE_TOKEN`        | Authentication token for SonarQube API access | ✅ Yes\* | -       |
| **Basic Authentication** |                                               |          |         |
| `SONARQUBE_USERNAME`     | Username for Basic authentication             | ✅ Yes\* | -       |
| `SONARQUBE_PASSWORD`     | Password for Basic authentication             | ✅ Yes\* | -       |
| **System Passcode**      |                                               |          |         |
| `SONARQUBE_PASSCODE`     | System passcode for SonarQube authentication  | ✅ Yes\* | -       |

\*One authentication method is required. Token authentication takes priority if multiple methods are configured.

#### Connection Settings

| Variable                 | Description                                              | Required  | Default                 |
| ------------------------ | -------------------------------------------------------- | --------- | ----------------------- |
| `SONARQUBE_URL`          | URL of your SonarQube instance                           | ❌ No     | `https://sonarcloud.io` |
| `SONARQUBE_ORGANIZATION` | Organization key (required for SonarCloud)               | ❌ No\*\* | -                       |
| `LOG_FILE`               | Path to write log files (e.g., `/tmp/sonarqube-mcp.log`) | ❌ No     | -                       |
| `LOG_LEVEL`              | Minimum log level (DEBUG, INFO, WARN, ERROR)             | ❌ No     | `DEBUG`                 |

\*\*Required when using SonarCloud

#### HTTP Transport Settings (Advanced)

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:

| Variable                                   | Description                                  | Required | Default     |
| ------------------------------------------ | -------------------------------------------- | -------- | ----------- |
| `MCP_TRANSPORT_TYPE`                       | Transport type (`stdio` or `http`)           | ❌ No    | `stdio`     |
| `MCP_HTTP_PORT`                            | Port for HTTP server                         | ❌ No    | `3000`      |
| `MCP_HTTP_SESSION_TIMEOUT`                 | Session timeout in milliseconds              | ❌ No    | `1800000`   |
| `MCP_HTTP_ALLOWED_HOSTS`                   | Comma-separated list of allowed hosts        | ❌ No    | `localhost` |
| `MCP_HTTP_ALLOWED_ORIGINS`                 | Comma-separated list of allowed CORS origins | ❌ No    | `*`         |
| `MCP_HTTP_ENABLE_DNS_REBINDING_PROTECTION` | Enable DNS rebinding protection              | ❌ No    | `false`     |

### Authentication Methods

The server supports three authentication methods, with important differences between SonarQube versions:

#### 1. Token Authentication (Recommended)

##### SonarQube 10.0+ (Bearer Token)

- Starting with SonarQube 10.0, Bearer token authentication is the recommended approach
- Most secure and flexible option
- Tokens can have limited permissions
- Configuration:
  ```json
  {
    "env": {
      "SONARQUBE_TOKEN": "your-token-here"
    }
  }
  ```

##### SonarQube < 10.0 (Token as Username)

- For versions before 10.0, tokens must be sent as the username in Basic authentication
- No password is required when using a token as username
- The server automatically handles this based on your SonarQube version
- Configuration remains the same - just use `SONARQUBE_USERNAME` with the token value:
  ```json
  {
    "env": {
      "SONARQUBE_USERNAME": "your-token-here"
    }
  }
  ```

#### 2. Basic Authentication

- Traditional username and password authentication
- Suitable for self-hosted SonarQube instances
- May not work with SonarCloud if 2FA is enabled
- Configuration:
  ```json
  {
    "env": {
      "SONARQUBE_USERNAME": "your-username",
      "SONARQUBE_PASSWORD": "your-password"
    }
  }
  ```

#### 3. System Passcode

- Special authentication for SonarQube system administration
- Typically used for automated deployment scenarios
- Configuration:
  ```json
  {
    "env": {
      "SONARQUBE_PASSCODE": "your-system-passcode"
    }
  }
  ```

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

### SonarCloud vs SonarQube

**For SonarCloud:**

- Set `SONARQUBE_URL` to `https://sonarcloud.io`
- `SONARQUBE_ORGANIZATION` is required
- Token authentication is recommended

**For SonarQube Server:**

- Set `SONARQUBE_URL` to your instance URL
- `SONARQUBE_ORGANIZATION` is typically not needed
- All authentication methods are supported

### HTTP Transport Mode

The server supports HTTP transport for programmatic access and web service deployments. This enables integration with custom clients and web applications.

#### Running as an HTTP Server

Start the server with HTTP transport:

```bash
# Using environment variables
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3000 npx sonarqube-mcp-server

# With Docker
docker run -i --rm \
  -p 3000:3000 \
  -e MCP_TRANSPORT_TYPE=http \
  -e MCP_HTTP_PORT=3000 \
  -e SONARQUBE_URL=https://sonarcloud.io \
  -e SONARQUBE_TOKEN=your-token \
  sapientpants/sonarqube-mcp-server:latest
```

#### HTTP API Endpoints

When running in HTTP mode, the server exposes the following endpoints:

- `GET /health` - Health check endpoint
- `POST /session` - Create a new session
- `DELETE /session/:sessionId` - Close a session
- `POST /mcp` - Execute MCP requests
- `GET /events/:sessionId` - Server-sent events for notifications

#### Example HTTP Client

See [examples/http-client.ts](examples/http-client.ts) for a complete TypeScript client example.

Basic usage with curl:

```bash
# Health check
curl http://localhost:3000/health

# Create session
SESSION_ID=$(curl -X POST http://localhost:3000/session | jq -r .sessionId)

# Execute MCP request
curl -X POST http://localhost:3000/mcp \
  -H "Content-Type: application/json" \
  -d "{
    \"sessionId\": \"$SESSION_ID\",
    \"method\": \"tools/list\",
    \"params\": {}
  }"

# Close session
curl -X DELETE http://localhost:3000/session/$SESSION_ID
```

#### Security Considerations

When running in HTTP mode:

1. **Enable DNS rebinding protection** for public deployments:

   ```bash
   MCP_HTTP_ENABLE_DNS_REBINDING_PROTECTION=true
   ```

2. **Configure CORS** for browser-based clients:

   ```bash
   MCP_HTTP_ALLOWED_ORIGINS=https://yourapp.com,https://anotherapp.com
   ```

3. **Set session timeouts** appropriately:

   ```bash
   MCP_HTTP_SESSION_TIMEOUT=900000  # 15 minutes
   ```

4. **Use HTTPS** in production (configure through a reverse proxy like nginx)

### Elicitation Configuration (Experimental)

The server supports interactive user input through MCP's elicitation capability. This feature is opt-in and requires compatible MCP clients.

**Environment Variables:**

- `SONARQUBE_MCP_ELICITATION`: Set to `true` to enable elicitation
- `SONARQUBE_MCP_BULK_THRESHOLD`: Number of items before confirmation (default: 5)
- `SONARQUBE_MCP_REQUIRE_COMMENTS`: Set to `true` to require comments for resolutions
- `SONARQUBE_MCP_INTERACTIVE_SEARCH`: Set to `true` for interactive disambiguation

**Example Configuration:**

```json
{
  "mcpServers": {
    "sonarqube": {
      "command": "npx",
      "args": ["-y", "sonarqube-mcp-server@latest"],
      "env": {
        "SONARQUBE_URL": "https://sonarcloud.io",
        "SONARQUBE_TOKEN": "your-token",
        "SONARQUBE_MCP_ELICITATION": "true",
        "SONARQUBE_MCP_BULK_THRESHOLD": "10",
        "SONARQUBE_MCP_REQUIRE_COMMENTS": "true"
      }
    }
  }
}
```

**Features When Enabled:**

1. **Bulk Operation Confirmation**: Prompts for confirmation before marking multiple issues
2. **Comment Collection**: Collects explanatory comments when marking issues as false positive or won't fix
3. **Authentication Setup**: Guides through authentication setup when credentials are missing
4. **Search Disambiguation**: Helps select from multiple matching components or projects

**Note:** This feature requires MCP clients that support elicitation. Not all clients may support this capability.

### Logging Configuration

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.

**Enable Logging:**

```json
{
  "mcpServers": {
    "sonarqube": {
      "command": "npx",
      "args": ["-y", "sonarqube-mcp-server@latest"],
      "env": {
        "SONARQUBE_URL": "https://sonarcloud.io",
        "SONARQUBE_TOKEN": "your-token-here",
        "SONARQUBE_ORGANIZATION": "your-org",
        "LOG_FILE": "/tmp/sonarqube-mcp.log",
        "LOG_LEVEL": "INFO"
      }
    }
  }
}
```

**Log Levels:**

- `DEBUG`: Detailed information for debugging
- `INFO`: General information about server operation
- `WARN`: Warning events that might lead to issues
- `ERROR`: Error events (server continues running)

**Example Log Output:**

```
2024-01-15T10:30:45.123Z INFO [index] Starting SonarQube MCP server
2024-01-15T10:30:45.234Z INFO [index] Environment variables validated successfully
2024-01-15T10:30:45.345Z INFO [index] SonarQube client created successfully
2024-01-15T10:30:45.456Z INFO [index] SonarQube MCP server started successfully
2024-01-15T10:30:50.123Z DEBUG [index] Handling SonarQube projects request
2024-01-15T10:30:50.567Z INFO [index] Successfully retrieved projects {"count": 5}
```

## Available Tools

### Permission Requirements

Different SonarQube tools require different permission levels:

**Tools requiring Admin permissions:**

- `projects` - Lists all SonarQube projects with metadata (visibility, lastAnalysisDate, revision)

**Tools accessible to all users:**

- `components` - Search and navigate projects, directories, and files (requires 'Browse' permission on at least one project)
- All other tools require appropriate permissions based on the resources being accessed

#### Listing Projects

**For Administrators:**
Use the `projects` tool to get full project metadata including visibility, last analysis date, and revision info.

**For All Users:**
Use the `components` tool with project qualifier:

- "List all projects I have access to" → `components` with `qualifiers: ['TRK']`
- "Search for projects containing 'mobile'" → `components` with `query: 'mobile', qualifiers: ['TRK']`

The `components` tool provides a more accessible alternative for non-admin users to discover projects they have access to.

### Project Management

#### `projects`

List all SonarQube projects with pagination support.

**Parameters:**

- `page` (optional): Page number for results pagination
- `page_size` (optional): Number of items per page

### Metrics and Measures

#### `metrics`

Get available metrics from SonarQube.

**Parameters:**

- `page` (optional): Page number for results pagination
- `page_size` (optional): Number of items per page

#### `measures_component`

Get measures for a specific component.

**Parameters:**

- `component` (required): Component key
- `metric_keys` (required): Array of metric keys
- `additional_fields` (optional): Additional fields to return
- `branch` (optional): Branch name
- `pull_request` (optional): Pull request key
- `period` (optional): Period index

#### `measures_components`

Get measures for multiple components.

**Parameters:**

- `component_keys` (required): Array of component keys
- `metric_keys` (required): Array of metric keys
- Additional parameters same as `measures_component`
- `page` (optional): Page number
- `page_size` (optional): Items per page

#### `measures_history`

Get measures history for a component.

**Parameters:**

- `component` (required): Component key
- `metrics` (required): Array of metric keys
- `from` (optional): Start date (YYYY-MM-DD)
- `to` (optional): End date (YYYY-MM-DD)
- `branch` (optional): Branch name
- `pull_request` (optional): Pull request key
- `page` (optional): Page number
- `page_size` (optional): Items per page

### Issue Management

#### `issues`

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.

**Component/File Path Filters:**

- `project_key` (optional): Single project key (backward compatible)
- `projects` (optional): Array of project keys for multi-project analysis
- `component_keys` (optional): Array of component keys (file paths, directories, or modules) - use this to filter issues by specific files or folders
- `components` (optional): Alias for component_keys
- `on_component_only` (optional): Boolean to return only issues on specified components, not sub-components

**Branch/PR Support:**

- `branch` (optional): Branch name for branch analysis
- `pull_request` (optional): Pull request ID for PR analysis

**Issue Filters:**

- `issues` (optional): Array of specific issue keys to retrieve
- `severity` (optional): Single severity (deprecated, use severities)
- `severities` (optional): Array of severities (INFO, MINOR, MAJOR, CRITICAL, BLOCKER)
- `statuses` (optional): Array of statuses (OPEN, CONFIRMED, REOPENED, RESOLVED, CLOSED)
- `resolutions` (optional): Array of resolutions (FALSE-POSITIVE, WONTFIX, FIXED, REMOVED)
- `resolved` (optional): Boolean filter for resolved/unresolved
- `types` (optional): Array of types (CODE_SMELL, BUG, VULNERABILITY, SECURITY_HOTSPOT)

**Clean Code Taxonomy (SonarQube 10.x+):**

- `clean_code_attribute_categories` (optional): Array (ADAPTABLE, CONSISTENT, INTENTIONAL, RESPONSIBLE)
- `impact_severities` (optional): Array (HIGH, MEDIUM, LOW)
- `impact_software_qualities` (optional): Array (MAINTAINABILITY, RELIABILITY, SECURITY)
- `issue_statuses` (optional): Array of new issue status values

**Rules and Tags:**

- `rules` (optional): Array of rule keys
- `tags` (optional): Array of issue tags - essential for security audits, regression testing, and categorized analysis

**Date Filters:**

- `created_after` (optional): Issues created after date (YYYY-MM-DD)
- `created_before` (optional): Issues created before date (YYYY-MM-DD)
- `created_at` (optional): Issues created on date (YYYY-MM-DD)
- `created_in_last` (optional): Issues created in last period (e.g., "30d", "1m")

**Assignment:**

- `assigned` (optional): Boolean filter for assigned/unassigned
- `assignees` (optional): Array of assignee logins - critical for targeted clean-up sprints and workload analysis
- `author` (optional): Single author login
- `authors` (optional): Array of author logins

**Security Standards:**

- `cwe` (optional): Array of CWE identifiers
- `owasp_top10` (optional): Array of OWASP Top 10 categories
- `owasp_top10_v2021` (optional): Array of OWASP Top 10 2021 categories
- `sans_top25` (optional): Array of SANS Top 25 categories
- `sonarsource_security` (optional): Array of SonarSource security categories
- `sonarsource_security_category` (optional): Additional security categories

**Other Filters:**

- `languages` (optional): Array of programming languages
- `facets` (optional): Array of facets to aggregate
- `facet_mode` (optional): Facet aggregation mode ('effort' or 'count')
- `since_leak_period` (optional): Boolean for leak period filter (deprecated)
- `in_new_code_period` (optional): Boolean for new code period filter

**Sorting:**

- `s` (optional): Sort field (e.g., 'SEVERITY', 'CREATION_DATE', 'UPDATE_DATE')
- `asc` (optional): Boolean for ascending sort direction (default: false)

**Response Control:**

- `additional_fields` (optional): Array of additional fields to include
- `page` (optional): Page number for pagination
- `page_size` (optional): Number of items per page

**Faceted Search (Dashboard Support):**

- `facets` (optional): Array of facets to compute for aggregations. Available facets: severities, statuses, resolutions, rules, tags, types, authors, assignees, languages, etc.
- `facet_mode` (optional): Mode for facet computation: 'count' (number of issues) or 'effort' (remediation effort)

**Example Use Cases:**

1. **Dashboard Query** - Get issue counts by severity and assignee:

```json
{
  "project_key": "my-project",
  "facets": ["severities", "assignees", "tags"],
  "facet_mode": "count"
}
```

1. **Security Audit** - Find critical security issues in authentication modules:

```json
{
  "project_key": "my-project",
  "component_keys": ["src/auth/", "src/security/"],
  "tags": ["security", "vulnerability"],
  "severities": ["CRITICAL", "BLOCKER"],
  "statuses": ["OPEN", "REOPENED"]
}
```

1. **Sprint Planning** - Get open issues for specific team members:

```json
{
  "project_key": "my-project",
  "assignees": ["[email protected]", "[email protected]"],
  "statuses": ["OPEN", "CONFIRMED"],
  "facets": ["severities", "types"],
  "facet_mode": "effort"
}
```

1. **File-Specific Analysis** - Issues in a specific file:

```json
{
  "project_key": "my-project",
  "component_keys": ["src/main/java/com/example/PaymentService.java"],
  "on_component_only": true
}
```

### Component Navigation

#### `components`

Search and navigate SonarQube components (projects, directories, files). Supports text search, filtering by type/language, and tree navigation.

**Search Parameters:**

- `query` (optional): Text search query
- `qualifiers` (optional): Array of component types (TRK, DIR, FIL, UTS, BRC, APP, VW, SVW, LIB)
- `language` (optional): Programming language filter

**Tree Navigation Parameters:**

- `component` (optional): Component key for tree navigation
- `strategy` (optional): Tree traversal strategy ('all', 'children', 'leaves')

**Common Parameters:**

- `asc` (optional): Sort ascending/descending
- `ps` (optional): Page size (default: 100, max: 500)
- `p` (optional): Page number
- `branch` (optional): Branch name
- `pullRequest` (optional): Pull request ID

**Component Qualifiers:**

- `TRK`: Project
- `DIR`: Directory
- `FIL`: File
- `UTS`: Unit Test
- `BRC`: Branch
- `APP`: Application
- `VW`: View
- `SVW`: Sub-view
- `LIB`: Library

**Example Use Cases:**

1. **Find specific files:**

```json
{
  "query": "UserService",
  "qualifiers": ["FIL"]
}
```

1. **List all test files in a project:**

```json
{
  "component": "my-project",
  "qualifiers": ["UTS"]
}
```

1. **Navigate directory structure:**

```json
{
  "component": "my-project:src/main",
  "strategy": "children",
  "qualifiers": ["DIR", "FIL"]
}
```

1. **Search for components by language:**

```json
{
  "language": "java",
  "qualifiers": ["FIL"],
  "query": "Controller"
}
```

1. **Get project list:**

```json
{
  "qualifiers": ["TRK"]
}
```

### Security Hotspots

#### `hotspots`

Search for security hotspots with specialized filters for security review workflows.

**Parameters:**

- `project_key` (optional): Project key to filter hotspots
- `branch` (optional): Branch name for branch analysis
- `pull_request` (optional): Pull request ID for PR analysis
- `status` (optional): Hotspot status (TO_REVIEW, REVIEWED)
- `resolution` (optional): Hotspot resolution (FIXED, SAFE)
- `files` (optional): Array of file paths to filter
- `assigned_to_me` (optional): Boolean to show only assigned hotspots
- `since_leak_period` (optional): Boolean for leak period filter
- `in_new_code_period` (optional): Boolean for new code period filter
- `page` (optional): Page number for pagination
- `page_size` (optional): Number of items per page

#### `hotspot`

Get detailed information about a specific security hotspot including security context.

**Parameters:**

- `hotspot_key` (required): The unique key of the hotspot

**Returns:**

- Detailed hotspot information including:
  - Security category and vulnerability probability
  - Rule information and security context
  - Changelog and comments
  - Code flows and locations

#### `update_hotspot_status`

Update the status of a security hotspot (requires appropriate permissions).

**Parameters:**

- `hotspot_key` (required): The unique key of the hotspot
- `status` (required): New status (TO_REVIEW, REVIEWED)
- `resolution` (optional): Resolution when status is REVIEWED (FIXED, SAFE)
- `comment` (optional): Comment explaining the status change

### Quality Gates

#### `quality_gates`

List available quality gates.

No parameters required.

#### `quality_gate`

Get quality gate conditions.

**Parameters:**

- `id` (required): Quality gate ID

#### `quality_gate_status`

Get project quality gate status.

**Parameters:**

- `project_key` (required): Project key
- `branch` (optional): Branch name
- `pull_request` (optional): Pull request key

### Source Code

#### `source_code`

View source code with issues highlighted.

**Parameters:**

- `key` (required): File key
- `from` (optional): Start line
- `to` (optional): End line
- `branch` (optional): Branch name
- `pull_request` (optional): Pull request key

#### `scm_blame`

Get SCM blame information for source code.

**Parameters:**

- Same as `source_code`

### System Monitoring

#### `system_health`

Get the health status of the SonarQube instance.

No parameters required.

#### `system_status`

Get the status of the SonarQube instance.

No parameters required.

#### `system_ping`

Ping the SonarQube instance to check if it is up.

No parameters required.

### Issue Resolution and Management

#### `markIssueFalsePositive`

Mark an issue as false positive.

**Parameters:**

- `issue_key` (required): The key of the issue to mark
- `comment` (optional): Comment explaining why it's a false positive

#### `markIssueWontFix`

Mark an issue as won't fix.

**Parameters:**

- `issue_key` (required): The key of the issue to mark
- `comment` (optional): Comment explaining why it won't be fixed

#### `markIssuesFalsePositive`

Mark multiple issues as false positive in bulk.

**Parameters:**

- `issue_keys` (required): Array of issue keys to mark
- `comment` (optional): Comment applying to all issues

#### `markIssuesWontFix`

Mark multiple issues as won't fix in bulk.

**Parameters:**

- `issue_keys` (required): Array of issue keys to mark
- `comment` (optional): Comment applying to all issues

#### `addCommentToIssue`

Add a comment to a SonarQube issue.

**Parameters:**

- `issue_key` (required): The key of the issue to comment on
- `text` (required): The comment text (supports markdown formatting)

#### `assignIssue`

Assign a SonarQube issue to a user or unassign it.

**Parameters:**

- `issueKey` (required): The key of the issue to assign
- `assignee` (optional): Username of the assignee. Leave empty to unassign the issue

**Example usage:**

```json
{
  "issueKey": "PROJECT-123",
  "assignee": "john.doe"
}
```

## Usage Examples

### Basic Project Analysis

```
"List all my SonarQube projects"
"Show me the code coverage for project xyz"
"What metrics are available for analysis?"
```

### Issue Investigation

```
"Show me all critical bugs in project abc"
"Find security vulnerabilities in the main branch"
"List all code smells created in the last week"
"Show unresolved issues assigned to john.doe"
"Analyze issues in the feature/new-login branch"
"Compare issues between main and develop branches"
"Find issues across multiple projects: proj1, proj2, proj3"
"Show me issues sorted by severity in descending order"
"Find all issues with clean code impact on reliability"
```

### Component Navigation

```
"Find all files containing 'UserService' in their name"
"List all test files in my project"
"Show me the directory structure of src/main"
"Find all Java controller files"
"List all projects in SonarQube"
"Navigate to the authentication module"
"Search for TypeScript files in the frontend directory"
"Show me all directories under src/components"
```

### Issue Management

```
"Assign issue PROJECT-123 to john.doe"
"Unassign issue PROJECT-456"
"Mark issue ABC-789 as false positive with comment: 'Test code only'"
"Add comment to issue XYZ-111: 'Fixed in commit abc123'"
"Bulk mark issues DEF-222, DEF-223 as won't fix"
```

### Quality Monitoring

```
"Check the quality gate status for my main project"
"Show me the code coverage history for the last month"
"What are the quality gate conditions?"
"Compare metrics between develop and main branches"
```

### Security Hotspot Review

```
"Find all security hotspots that need review in project xyz"
"Show me hotspots in the authentication module"
"Get details for hotspot HSP-12345"
"List all hotspots assigned to me"
"Mark hotspot HSP-12345 as safe with explanation"
"Find hotspots in the new code period"
"Show security hotspots in pull request #42"
```

### Source Code Analysis

```
"Show me the source code for file xyz with issues highlighted"
"Get blame information for the problematic file"
"View issues in the authentication module"
```

### System Health

```
"Check if SonarQube is running"
"What's the health status of the SonarQube instance?"
"Show me the system status"
```

## Architecture

The SonarQube MCP Server follows a modular architecture:

```
┌─────────────────┐     ┌──────────────────┐     ┌─────────────────┐
│  Claude Desktop │────▶│  MCP Server      │────▶│  SonarQube API  │
│  (MCP Client)   │◀────│  (index.ts)      │◀────│                 │
└─────────────────┘     └──────────────────┘     └─────────────────┘
                               │
                               ▼
                        ┌──────────────────┐
                        │  SonarQube       │
                        │  Client          │
                        │  (sonarqube.ts)  │
                        └──────────────────┘
                               │
                               ▼
                        ┌──────────────────┐
                        │  API Module      │
                        │  (api.ts)        │
                        └──────────────────┘
```

### Key Components

1. **MCP Server (`index.ts`)**: Main entry point that initializes the MCP server and registers all available tools
2. **SonarQube Client (`sonarqube.ts`)**: Handles business logic and parameter transformation
3. **API Module (`api.ts`)**: Manages HTTP requests to the SonarQube API
4. **Type Definitions**: TypeScript interfaces for type safety

### Data Flow

1. MCP clients make requests through registered tools
2. Tool handlers validate and transform parameters
3. SonarQube client methods process the requests
4. API module executes HTTP requests
5. Responses are formatted and returned to the client

## Development

### Prerequisites

- Node.js 22 or higher
- pnpm 10.17.0 or higher
- Docker (for container builds)

### Setup

1. Clone the repository:

```bash
git clone https://github.com/sapientpants/sonarqube-mcp-server.git
cd sonarqube-mcp-server
```

1. Install dependencies:

```bash
pnpm install
```

1. Build the project:

```bash
pnpm build
```

### Development Commands

```bash
# Install dependencies
pnpm install

# Build the project
pnpm build

# Run in development mode with auto-reload
pnpm dev

# Run tests
pnpm test

# Run tests with coverage
pnpm test:coverage

# Lint the code
pnpm lint

# Fix linting issues
pnpm lint:fix

# Check types
pnpm check-types

# Format code
pnpm format

# Run all validations
pnpm validate

# Inspect MCP schema
pnpm inspect
```

### Testing

The project uses Jest for testing with:

- Unit tests for all major components
- Mocked HTTP responses using `nock`
- Coverage reporting
- TypeScript support

Run specific test files:

```bash
NODE_ENV=test NODE_OPTIONS='--experimental-vm-modules --no-warnings' jest src/__tests__/file-name.test.ts
```

### Code Quality

The project maintains high code quality through:

- TypeScript for type safety
- ESLint for code linting
- Prettier for code formatting
- Jest for testing
- SonarCloud for continuous code analysis

## Common Issues and Solutions

### Quick Fixes

#### "Authentication failed"

- **Cause**: Invalid or expired token
- **Solution**: Generate a new token in SonarQube/SonarCloud

#### "Project not found"

- **Cause**: Incorrect project key or insufficient permissions
- **Solution**: Verify the project key and check token permissions

#### "Organization required"

- **Cause**: Using SonarCloud without organization parameter
- **Solution**: Add `SONARQUBE_ORGANIZATION` to your configuration

#### "Connection refused"

- **Cause**: Incorrect URL or network issues
- **Solution**: Verify `SONARQUBE_URL` and network connectivity

#### "No output or errors visible"

- **Cause**: Errors might be happening but not visible in Claude Desktop
- **Solution**: Enable logging with `LOG_FILE` and check the log file for detailed error messages

### FAQ

**Q: Can I use this with both SonarQube and SonarCloud?**
A: Yes! Set the appropriate `SONARQUBE_URL` and include `SONARQUBE_ORGANIZATION` for SonarCloud.

**Q: What permissions does my token need?**
A: The token needs "Execute Analysis" permission and access to the projects you want to analyze.

**Q: How do I filter issues by multiple criteria?**
A: The `issues` tool supports extensive filtering. You can combine multiple parameters like severity, type, status, and date ranges.

**Q: Can I analyze pull requests?**
A: Yes! Many tools support `branch` and `pull_request` parameters for branch and PR analysis.

## Troubleshooting

### Common Error Messages and Solutions

#### Authentication Errors

##### Error: "Authentication failed"

- **Solution**: Check that your SONARQUBE_TOKEN is valid and not expired. Generate a new token from your SonarQube user profile.

##### Error: "No SonarQube authentication configured"

- **Solution**: Set one of the following authentication methods:
  - `SONARQUBE_TOKEN` for token-based authentication (recommended)
  - `SONARQUBE_USERNAME` and `SONARQUBE_PASSWORD` for basic authentication
  - `SONARQUBE_PASSCODE` for system passcode authentication

#### Authorization Errors

##### Error: "Access denied"

- **Solution**: Ensure your token has the required permissions for the operation. Common required permissions:
  - "Execute Analysis" for code analysis
  - "Browse" for reading project data
  - "Administer Issues" for issue management operations

#### Resource Not Found Errors

##### Error: "Resource not found"

- **Solution**: Verify that:
  - The project key/component exists in SonarQube
  - You have access to the resource
  - The URL path is correct (no typos in project keys)

#### Network and Connection Errors

##### Error: "Connection refused"

- **Solution**: Check that:
  - The SonarQube server is running
  - The SONARQUBE_URL is correct
  - There are no firewall rules blocking the connection

##### Error: "Network error" or timeout errors

- **Solution**:
  - Verify your network connection
  - Check if the SonarQube server is accessible
  - Ensure the URL doesn't have a trailing slash
  - For self-hosted instances, verify SSL certificates

#### Rate Limiting

##### Error: "Rate limit exceeded"

- **Solution**: The server automatically retries rate-limited requests with exponential backoff. If you continue to hit rate limits:
  - Reduce the frequency of your requests
  - Implement request batching where possible
  - Contact your SonarQube administrator to increase rate limits

#### Configuration Errors

##### Error: "Invalid SONARQUBE_URL"

- **Solution**: Provide a valid URL including the protocol:
  - ✅ Correct: `https://sonarcloud.io`
  - ✅ Correct: `https://sonarqube.example.com`
  - ❌ Wrong: `sonarcloud.io` (missing protocol)
  - ❌ Wrong: `https://sonarqube.example.com/` (trailing slash)

### Debugging Tips

1. **Enable Debug Logging**:

   ```bash
   export LOG_LEVEL=DEBUG
   ```

2. **Check Environment Variables**:

   ```bash
   echo $SONARQUBE_URL
   echo $SONARQUBE_TOKEN
   echo $SONARQUBE_ORGANIZATION
   ```

3. **Test Connection**:
   Use the `ping` tool to verify connectivity:

   ```bash
   # In your MCP client
   sonarqube.ping
   ```

4. **Verify Permissions**:
   Use the `projects` tool to list accessible projects:
   ```bash
   # In your MCP client
   sonarqube.projects
   ```

### Retry Behavior

The server automatically retries failed requests for transient errors:

- **Network errors**: Retried up to 3 times
- **Rate limiting**: Retried with exponential backoff
- **Server errors (5xx)**: Retried up to 3 times

Retry delays: 1s → 2s → 4s (capped at 10s)

### Getting Help

If you continue to experience issues:

1. Check the [GitHub Issues](https://github.com/sapientpants/sonarqube-mcp-server/issues) for similar problems
2. Enable debug logging and collect error details
3. Create a new issue with:
   - Error messages
   - Environment details (OS, Node version)
   - SonarQube version
   - Steps to reproduce

## Contributing

We welcome contributions! Please see our [Contributing Guidelines](CONTRIBUTING.md) for details.

### How to Contribute

1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

### Development Guidelines

- Write tests for new features
- Update documentation as needed
- Follow the existing code style
- Ensure all tests pass
- Add appropriate error handling

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## External Resources

### SonarQube Documentation

- [SonarQube Documentation](https://docs.sonarqube.org/latest/)
- [SonarCloud Documentation](https://docs.sonarcloud.io/)
- [Web API Documentation](https://docs.sonarqube.org/latest/extend/web-api/)

### Model Context Protocol

- [MCP Documentation](https://modelcontextprotocol.io/)
- [MCP Specification](https://github.com/modelcontextprotocol/specification)
- [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)

---

Made with ❤️ by the SonarQube MCP Server community

```

--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------

```markdown
# Security Policy

## Supported Versions

The latest minor release will receive support for security updates. Users of older versions are requested to upgrade to the latest minor version.

| Version | Supported          |
| ------- | ------------------ |
| 1.4.x   | :white_check_mark: |
| < 1.4.0 | :x:                |

## Authentication Security Best Practices

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:

### 1. Use Token Authentication

Token authentication is the most secure option and is strongly recommended:

- **Generate dedicated tokens** for the MCP server rather than reusing existing tokens
- **Use minimal permissions** - create tokens with only the necessary read permissions
- **Rotate tokens regularly** - establish a schedule for token rotation
- **Revoke unused tokens** - immediately revoke tokens that are no longer needed

### 2. Protect Configuration Files

Since credentials are stored in the MCP client's configuration file:

- **Check file permissions** - ensure configuration files are readable only by your user account
- **Use secure storage** - on macOS, Claude Desktop stores config in `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Don't commit configs** - never commit configuration files containing credentials to version control
- **Use password managers** - store tokens in a password manager and copy them when needed

### 3. Authentication Method Security

Listed from most to least secure:

1. **Token Authentication** (Recommended)
   - Tokens can be scoped with limited permissions
   - Can be revoked without changing passwords
   - Works with all SonarQube versions

2. **Basic Authentication**
   - Use only when token auth is not available
   - Requires transmitting username and password
   - May not work with SonarCloud if 2FA is enabled

3. **System Passcode**
   - Only for special administrative scenarios
   - Provides system-level access
   - Use with extreme caution

### 4. SonarQube-Specific Considerations

- **SonarQube 10.0+**: Uses Bearer token authentication (most secure)
- **SonarQube < 10.0**: Tokens sent as username in Basic auth
- **SonarCloud**: Always use token authentication, especially with 2FA enabled

### 5. Environment Variable Security

When setting environment variables:

- **Avoid command history** - Use configuration files instead of export commands
- **Clear sensitive variables** - Unset variables after use if set temporarily
- **Use dedicated shells** - Run the MCP server in a dedicated terminal session

### 6. Network Security

- **Use HTTPS** - Always connect to SonarQube instances over HTTPS
- **Verify certificates** - Ensure SSL certificates are valid
- **Use VPN** - When accessing internal SonarQube instances, use VPN

## Security Architecture

The SonarQube MCP Server operates with the following security model:

- **Single-user design**: Each instance runs with one user's credentials
- **Local execution**: Runs on the user's machine, not as a shared service
- **Direct authentication**: Uses SonarQube's native authentication mechanisms
- **No credential storage**: The server itself doesn't store credentials

## Enterprise Security Considerations

While the current model is appropriate for local usage, enterprise deployments using MCP gateways provide:

- OAuth 2.1 resource server implementation at the gateway layer
- Token validation and scoping per RFC8707
- Multi-client authorization mechanisms
- Enterprise authentication and authorization

## Reporting a Vulnerability

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.

```

--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------

```markdown
# Contributor Covenant Code of Conduct

## Our Pledge

We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.

## Our Standards

Examples of behavior that contributes to a positive environment for our
community include:

- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes,
  and learning from the experience
- Focusing on what is best not just for us as individuals, but for the
  overall community

Examples of unacceptable behavior include:

- The use of sexualized language or imagery, and sexual attention or
  advances of any kind
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email
  address, without their explicit permission
- Other conduct which could reasonably be considered inappropriate in a
  professional setting

## Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.

Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.

## Scope

This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
[email protected].
All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the
reporter of any incident.

## Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:

### 1. Correction

**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.

**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.

### 2. Warning

**Community Impact**: A violation through a single incident or series
of actions.

**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.

### 3. Temporary Ban

**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.

**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.

### 4. Permanent Ban

**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.

**Consequence**: A permanent ban from any sort of public interaction within
the community.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.

Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).

[homepage]: https://www.contributor-covenant.org

For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

```

--------------------------------------------------------------------------------
/docs/security.md:
--------------------------------------------------------------------------------

```markdown
# Security Guide

## Overview

This guide covers security best practices for the SonarQube MCP Server, focusing on authentication, credential management, and secure deployment patterns.

## Authentication Methods

The server supports three authentication methods for connecting to SonarQube:

### 1. Token Authentication (Recommended)

The most secure method for API access.

```json
{
  "env": {
    "SONARQUBE_TOKEN": "squ_xxxxxxxxxxxxxxxx"
  }
}
```

**Benefits:**

- Tokens can be scoped with limited permissions
- Easy to revoke without changing passwords
- No password exposure in logs or configs
- Works with both SonarCloud and SonarQube

**SonarQube Version Differences:**

- **SonarQube 10.0+**: Uses Bearer token authentication
- **SonarQube < 10.0**: Automatically uses token as username in Basic auth

### 2. Basic Authentication

Traditional username/password authentication.

```json
{
  "env": {
    "SONARQUBE_USERNAME": "your-username",
    "SONARQUBE_PASSWORD": "your-password"
  }
}
```

**Considerations:**

- Suitable for self-hosted SonarQube instances
- May not work with SonarCloud if 2FA is enabled
- Passwords visible in environment variables

### 3. System Passcode

Special authentication for system administration.

```json
{
  "env": {
    "SONARQUBE_PASSCODE": "system-passcode"
  }
}
```

**Use Cases:**

- Automated deployment scenarios
- System-level operations
- Emergency access

## Credential Security Best Practices

### 1. Token Management

**Creating Secure Tokens:**

1. Log into SonarQube/SonarCloud
2. Navigate to **My Account** → **Security**
3. Generate tokens with minimal required permissions:
   - Read-only tokens for analysis
   - Write tokens only when needed
   - Project-specific tokens when possible

**Token Rotation:**

- Rotate tokens every 90 days
- Immediately revoke compromised tokens
- Use different tokens for different environments

### 2. Credential Storage

**Local Development (Claude Desktop):**

- Credentials stored in Claude Desktop's config file
- Ensure proper file permissions (600 on Unix)
- Consider using OS keychain integration

**Docker Deployment:**

```bash
# Use Docker secrets
docker secret create sonarqube-token token.txt
docker run --secret sonarqube-token ...

# Or use environment file with restricted permissions
chmod 600 .env
docker run --env-file .env ...
```

**MCP Gateway Deployment:**

- Use gateway's secret management
- Leverage vault integration if available
- Rotate credentials at gateway level

### 3. Environment Variable Security

**DO:**

- Use `.env` files with restricted permissions
- Load secrets from secure sources at runtime
- Use placeholder values in version control

**DON'T:**

- Commit credentials to version control
- Log environment variables
- Pass secrets via command line arguments

## Network Security

### 1. TLS/SSL Configuration

Always use HTTPS for SonarQube connections:

```json
{
  "env": {
    "SONARQUBE_URL": "https://sonarqube.example.com",
    "NODE_TLS_REJECT_UNAUTHORIZED": "1"
  }
}
```

### 2. Certificate Validation

For self-signed certificates:

```bash
# Add CA certificate to trusted store
export NODE_EXTRA_CA_CERTS=/path/to/ca-cert.pem
```

Never disable certificate validation in production:

```bash
# INSECURE - Development only
NODE_TLS_REJECT_UNAUTHORIZED=0
```

## Deployment Security

### 1. Container Security

**Image Security:**

- Use specific version tags, not `latest`
- Scan images for vulnerabilities
- Use minimal base images (Alpine)

**Runtime Security:**

```yaml
# docker-compose.yml
services:
  sonarqube-mcp:
    image: sapientpants/sonarqube-mcp-server:1.7.0
    user: '1001:1001' # Non-root user
    read_only: true # Read-only filesystem
    tmpfs:
      - /tmp
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
```

### 2. Resource Limits

Prevent resource exhaustion:

```yaml
resources:
  limits:
    memory: '256M'
    cpus: '0.5'
  requests:
    memory: '128M'
    cpus: '0.1'
```

### 3. Network Isolation

For stdio transport, no network exposure needed:

```yaml
networks:
  internal:
    driver: bridge
    internal: true # No external access
```

## Logging and Monitoring

### 1. Secure Logging

Configure logging to avoid credential exposure:

```json
{
  "env": {
    "LOG_LEVEL": "INFO",
    "LOG_FILE": "/logs/sonarqube-mcp.log"
  }
}
```

**Log Security:**

- Never log authentication tokens
- Redact sensitive data in logs
- Rotate logs regularly
- Restrict log file access

### 2. Monitoring Access

Track usage patterns:

- Monitor failed authentication attempts
- Track unusual API usage patterns
- Alert on circuit breaker activations

## Security Checklist

### Pre-Deployment

- [ ] Use token authentication instead of passwords
- [ ] Create tokens with minimal required permissions
- [ ] Store credentials securely
- [ ] Use HTTPS for all SonarQube connections
- [ ] Review and apply container security settings

### Deployment

- [ ] Use specific version tags for images
- [ ] Run containers as non-root user
- [ ] Apply resource limits
- [ ] Enable secure logging
- [ ] Restrict file permissions on configs

### Post-Deployment

- [ ] Regularly rotate tokens
- [ ] Monitor logs for security events
- [ ] Keep server and dependencies updated
- [ ] Review access patterns periodically
- [ ] Test disaster recovery procedures

## Incident Response

### Compromised Credentials

1. **Immediate Actions:**
   - Revoke compromised tokens in SonarQube
   - Generate new tokens
   - Update all deployments

2. **Investigation:**
   - Review access logs
   - Check for unauthorized changes
   - Identify exposure source

3. **Prevention:**
   - Implement stricter access controls
   - Increase rotation frequency
   - Add additional monitoring

### Security Updates

Stay informed about security updates:

- Watch the [GitHub repository](https://github.com/sapientpants/sonarqube-mcp-server)
- Subscribe to security advisories
- Test updates in non-production first

## Compliance Considerations

### Data Privacy

- The server doesn't store any SonarQube data
- All data remains in SonarQube
- Consider data residency requirements

### Audit Requirements

When deployed with MCP gateways:

- Leverage gateway audit capabilities
- Track all access at gateway level
- Maintain audit logs per compliance needs

## Support

For security concerns:

- Report vulnerabilities via GitHub Security Advisory
- Contact maintainers for sensitive issues
- Check documentation for updates

```

--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------

```markdown
# Contributing to SonarQube MCP Server

Thank you for your interest in contributing to the SonarQube MCP Server! This document provides guidelines and instructions for contributing to the project.

## Code of Conduct

By participating in this project, you agree to abide by our code of conduct: be respectful, inclusive, and constructive in all interactions.

## How to Contribute

### Reporting Issues

1. **Check existing issues** first to avoid duplicates
2. **Use issue templates** when available
3. **Provide detailed information**:
   - Steps to reproduce
   - Expected behavior
   - Actual behavior
   - Environment details (OS, Node version, etc.)
   - Error messages and logs

### Submitting Pull Requests

1. **Fork the repository** and create your branch from `main`
2. **Install dependencies** with `pnpm install`
3. **Make your changes**:
   - Write clear, concise commit messages
   - Follow the existing code style
   - Add tests for new features
   - Update documentation as needed
4. **Test your changes**:
   - Run `pnpm test` to ensure all tests pass
   - Run `pnpm lint` to check code style
   - Run `pnpm check-types` for TypeScript validation
   - Run `pnpm validate` to run all checks
5. **Submit the pull request**:
   - Provide a clear description of the changes
   - Reference any related issues
   - Ensure CI checks pass

## Development Setup

### Prerequisites

- Node.js 22 or higher
- pnpm 10.17.0 or higher
- Git

### Setup Steps

```bash
# Clone your fork
git clone https://github.com/your-username/sonarqube-mcp-server.git
cd sonarqube-mcp-server

# Install dependencies
pnpm install

# Build the project
pnpm build

# Run tests
pnpm test

# Start development mode
pnpm dev
```

### Development Commands

```bash
# Build the project
pnpm build

# Run in development mode with watch
pnpm dev

# Run all tests
pnpm test

# Run tests with coverage
pnpm test:coverage

# Run a specific test file
NODE_ENV=test NODE_OPTIONS='--experimental-vm-modules --no-warnings' jest src/__tests__/file-name.test.ts

# Lint code
pnpm lint

# Fix linting issues
pnpm lint:fix

# Check TypeScript types
pnpm check-types

# Format code
pnpm format

# Check formatting
pnpm format:check

# Run all validations
pnpm validate

# Inspect MCP schema
pnpm inspect
```

## Coding Standards

### TypeScript

- Use TypeScript for all new code
- Provide proper type definitions
- Avoid using `any` type
- Use interfaces for object shapes
- Export types that might be used by consumers

### Code Style

- Follow the existing code style
- Use ESLint and Prettier configurations
- Write self-documenting code
- Add comments for complex logic
- Keep functions small and focused

### Testing

- Write tests for all new features
- Maintain or improve code coverage
- Use descriptive test names
- Mock external dependencies
- Test error cases and edge conditions

### Git Commit Messages

Follow conventional commit format:

```
type(scope): subject

body

footer
```

Types:

- `feat`: New feature
- `fix`: Bug fix
- `docs`: Documentation changes
- `style`: Code style changes (formatting, etc.)
- `refactor`: Code refactoring
- `test`: Test additions or changes
- `chore`: Build process or auxiliary tool changes

Example:

```
feat(api): add support for branch filtering

Add branch parameter to issues endpoint to allow filtering
issues by specific branch

Closes #123
```

## Project Structure

```
sonarqube-mcp-server/
├── src/
│   ├── __tests__/      # Test files
│   ├── api.ts          # API module for HTTP requests
│   ├── index.ts        # MCP server entry point
│   └── sonarqube.ts    # SonarQube client implementation
├── dist/               # Compiled output
├── package.json        # Project configuration
├── tsconfig.json       # TypeScript configuration
├── jest.config.js      # Jest test configuration
└── eslint.config.js    # ESLint configuration
```

## Testing Guidelines

### Unit Tests

- Test individual functions and methods
- Mock external dependencies
- Cover edge cases and error scenarios
- Use descriptive test names

### Integration Tests

- Test API endpoints with mocked HTTP responses
- Verify parameter transformation
- Test error handling and retries

### Test Structure

```typescript
describe('ComponentName', () => {
  describe('methodName', () => {
    it('should handle normal case', () => {
      // Test implementation
    });

    it('should handle error case', () => {
      // Test implementation
    });
  });
});
```

## Documentation

- Update README.md for user-facing changes
- Add JSDoc comments for public APIs
- Include examples for new features
- Keep documentation clear and concise

## MCP SDK Update Process

When updating the Model Context Protocol SDK (`@modelcontextprotocol/sdk`), follow these steps:

### Before Updating

1. **Check the SDK Release Notes**:
   - Visit the [MCP SDK releases page](https://github.com/modelcontextprotocol/sdk/releases)
   - Review the changelog for breaking changes
   - Note any new protocol versions supported

2. **Assess Impact**:
   - Check if the new SDK version adds support for new protocol versions
   - Review any deprecated features or APIs
   - Evaluate compatibility with existing functionality

### Update Process

1. **Update the Dependency**:

   ```bash
   pnpm add @modelcontextprotocol/sdk@latest
   ```

2. **Update Version References**:
   - Update SDK version in `src/index.ts` startup logging
   - Update supported protocol versions in `COMPATIBILITY.md`
   - Update SDK version in `README.md` if referenced

3. **Test Protocol Compatibility**:

   ```bash
   # Run all tests
   pnpm test

   # Test with Claude Desktop or other MCP clients
   # Verify all tools work correctly
   ```

4. **Update Documentation**:
   - Update `COMPATIBILITY.md` with new protocol versions
   - Document any behavior changes
   - Update examples if APIs have changed

5. **Test with Multiple Clients**:
   - Test with Claude Desktop
   - Test with other MCP clients if available
   - Verify backward compatibility with older protocol versions

### Post-Update Checklist

- [ ] All tests pass (`pnpm test`)
- [ ] Type checking passes (`pnpm check-types`)
- [ ] Linting passes (`pnpm lint`)
- [ ] Documentation is updated
- [ ] COMPATIBILITY.md reflects new protocol support
- [ ] Manual testing confirms all tools work
- [ ] No deprecated SDK features are being used

### Monitoring SDK Updates

To stay informed about SDK updates:

- Watch the [MCP SDK repository](https://github.com/modelcontextprotocol/sdk)
- Subscribe to release notifications
- Review the [MCP specification](https://modelcontextprotocol.io) for protocol changes

## Release Process

1. Ensure all tests pass
2. Update version in package.json
3. Create a pull request
4. After merge, create a release tag
5. GitHub Actions will automatically publish to npm and DockerHub

## Need Help?

- Check the [README](README.md) for general information
- Look at existing code for examples
- Ask questions in GitHub issues
- Review closed PRs for similar changes

## Recognition

Contributors will be recognized in the project documentation. Thank you for helping improve the SonarQube MCP Server!

```

--------------------------------------------------------------------------------
/CLAUDE.md:
--------------------------------------------------------------------------------

```markdown
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

See [README.md](./README.md) for the complete project overview, features, and usage documentation.

## Architecture Decision Records (ADRs)

This project uses adr-tools to document architectural decisions. ADRs are stored in `doc/architecture/decisions/`.

```bash
# Create a new ADR without opening an editor (prevents timeout in Claude Code)
EDITOR=true adr-new "Title of the decision"

# Then edit the created file manually
```

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.

Refer to the ADRs for detailed information about design rationale, implementation details, and consequences of each architectural decision.

## Key Architectural Decisions

**Testing** (ADR-0020):

- Vitest as test framework with native ES modules support
- fast-check for property-based testing
- 80% minimum coverage required (lines, functions, branches, statements)

**Code Quality** (ADR-0021):

- TypeScript strict mode enabled (all strict flags)
- ESLint flat config with cognitive complexity limit (15)
- Prettier for consistent formatting
- Husky + lint-staged for pre-commit automation

**Package Manager** (ADR-0022):

- pnpm for disk efficiency and strict dependency management
- Version MUST match across: package.json, Dockerfile, all workflows
- Uses Corepack for automatic version management

**Release Management** (ADR-0023):

- Changesets for versioning and changelog generation
- Changeset validation enforced in CI/CD
- Automated GitHub releases via workflows

**CI/CD** (ADR-0024):

- GitHub Actions with 7 workflows (main, PR, publish, 4 reusable)
- Parallel execution for validation, security, build
- Quality gates block merge on failures

**Security** (ADR-0025):

- Multi-layered: Trivy (containers), CodeQL (SAST), OSV-Scanner (deps), SonarCloud
- SLSA provenance attestations on all artifacts
- SBOM (CycloneDX) generated for Docker images

**Resilience** (ADR-0026):

- Circuit breaker pattern via opossum library
- Default: 50% error threshold, 10s timeout, 30s reset
- Integrated with metrics/monitoring system

**Docker Publishing** (ADR-0027):

- Multi-platform: linux/amd64, linux/arm64
- Two-stage: build→GHCR (with security scan), copy→Docker Hub
- Build-once-deploy-many strategy

**Transport** (ADR-0010, ADR-0028):

- stdio: Default transport (recommended)
- HTTP: Optional session-based transport with SSE
- HTTP uses session management (not OAuth), delegates auth to gateways

## Code Quality Conventions

Follow these conventions to maintain code quality:

### TypeScript Best Practices

1. **Use Type Aliases for Union Types**

   ```typescript
   // ❌ Avoid repeated union types
   function foo(param: 'option1' | 'option2' | 'option3') {}
   function bar(param: 'option1' | 'option2' | 'option3') {}

   // ✅ Use type alias
   type MyOptions = 'option1' | 'option2' | 'option3';
   function foo(param: MyOptions) {}
   function bar(param: MyOptions) {}
   ```

2. **Use Nullish Coalescing Operator**

   ```typescript
   // ❌ Avoid logical OR for defaults (can fail with falsy values)
   const value = input || 'default';

   // ✅ Use nullish coalescing (only replaces null/undefined)
   const value = input ?? 'default';
   ```

3. **Use Object Spread Instead of Object.assign**

   ```typescript
   // ❌ Avoid Object.assign
   const merged = Object.assign({}, obj1, obj2);

   // ✅ Use object spread
   const merged = { ...obj1, ...obj2 };
   ```

4. **Avoid Deprecated APIs**
   - Check for deprecation warnings in the IDE
   - Use recommended replacements (e.g., `getHealthV2()` instead of `health()`)
   - Update to newer API versions when available

### Code Complexity

1. **Keep Cognitive Complexity Low**
   - Maximum cognitive complexity: 15
   - Break complex functions into smaller, focused functions
   - Reduce nesting levels
   - Simplify conditional logic

2. **Remove Redundant Code**
   - Don't create type aliases for primitive types
   - Remove unused variable assignments
   - Eliminate dead code

### Regular Expressions

1. **Make Regex Operator Precedence Explicit**

   ```typescript
   // ❌ Ambiguous precedence
   /abc|def+/

   // ✅ Clear precedence with grouping
   /abc|(def+)/
   ```

### General Guidelines

1. **Follow Existing Patterns**
   - Check how similar functionality is implemented in the codebase
   - Maintain consistency with existing code style
   - Use the same libraries and utilities as the rest of the project

2. **Run Validation Before Committing**

   ```bash
   # Run all checks before committing
   pnpm run precommit

   # This includes:
   # - Format checking (prettier)
   # - Linting (eslint)
   # - Type checking (tsc)
   # - Tests
   ```

## Claude-Specific Tips

- Remember to use the ADR creation command with `EDITOR=true` to prevent timeouts in Claude Code
- Never use `--no-verify` when committing code. This bypasses pre-commit hooks which run important validation checks
- Run `pnpm format` to format code before committing
- Run `pnpm run precommit` before finalizing any code changes
- 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>

## Updating pnpm Version

When updating the pnpm version in this project, you MUST update it in ALL of the following locations to maintain consistency:

1. **package.json**: Update the `packageManager` field (e.g., `"packageManager": "[email protected]"`)
2. **Dockerfile**: Update the version in `RUN npm install -g [email protected]`
3. **Documentation files**:
   - **README.md**: Update the Prerequisites section
   - **CONTRIBUTING.md**: Update the Prerequisites section
4. **Setup script**: Update `scripts/setup.sh` for mise installation
5. **GitHub Actions workflows** (CRITICAL - these must match package.json exactly):
   - `.github/workflows/main.yml`: Line with `version: 10.17.0`
   - `.github/workflows/pr.yml`: Line with `version: 10.17.0`
   - `.github/workflows/publish.yml`: `PNPM_VERSION` environment variable
   - `.github/workflows/reusable-security.yml`: Default value for `pnpm-version` input
   - `.github/workflows/reusable-validate.yml`: Default value for `pnpm-version` input

**Important**: If package.json and GitHub workflows have different pnpm versions, the CI/CD pipeline will fail with an error like:

```
Error: Multiple versions of pnpm specified:
  - version X in the GitHub Action config with the key "version"
  - version pnpm@Y in the package.json with the key "packageManager"
```

Always search for the old version number across all files to ensure no location is missed.

## Memory

Follow these steps for each interaction:

1. User Identification:
   - You should assume that you are interacting with default_user
   - If you have not identified default_user, proactively try to do so.

2. Memory Retrieval:
   - Always begin your chat by saying only "Remembering..." and retrieve all relevant information from your knowledge graph
   - Always refer to your knowledge graph as your "memory"

3. Memory Gathering:
   - While conversing with the user, be attentive to any new information that falls into these categories:
     a) Basic Identity (age, gender, location, job title, education level, etc.)
     b) Behaviors (interests, habits, etc.)
     c) Preferences (communication style, preferred language, etc.)
     d) Goals (goals, targets, aspirations, etc.)
     e) Relationships (personal and professional relationships up to 3 degrees of separation)

4. Memory Update:
   - If any new information was gathered during the interaction, update your memory as follows:
     a) Create entities for recurring entities (people, places, organizations, modules, concepts, etc.)
     b) Connect them to the current entities using relations
     c) Store facts about them as observations

```

--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------

```javascript
export default {
  extends: ['@commitlint/config-conventional'],
};

```

--------------------------------------------------------------------------------
/vitest.config.d.ts:
--------------------------------------------------------------------------------

```typescript
declare const _default: import("vite").UserConfig;
export default _default;

```

--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------

```json
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "noEmit": false,
    "emitDeclarationOnly": false
  },
  "exclude": ["src/__tests__/**/*", "**/*.test.ts", "**/*.spec.ts"]
}

```

--------------------------------------------------------------------------------
/src/schemas/system.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Schemas for system tools
 */

// Empty schemas for system tools that don't require parameters
export const systemHealthToolSchema = {};
export const systemStatusToolSchema = {};
export const systemPingToolSchema = {};

```

--------------------------------------------------------------------------------
/.claude/settings.json:
--------------------------------------------------------------------------------

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "./.claude/hooks/block-git-no-verify.ts"
          }
        ]
      }
    ]
  }
}

```

--------------------------------------------------------------------------------
/src/types/common.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Type alias for severity levels
 */
export type SeverityLevel = 'HIGH' | 'MEDIUM' | 'LOW';

/**
 * Interface for pagination parameters
 */
export interface PaginationParams {
  page: number | undefined;
  pageSize: number | undefined;
}

```

--------------------------------------------------------------------------------
/src/schemas/hotspots.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from 'zod';

/**
 * Schemas for security hotspots
 */

export const hotspotStatusSchema = z.enum(['TO_REVIEW', 'REVIEWED']).nullable().optional();

export const hotspotResolutionSchema = z.enum(['FIXED', 'SAFE']).nullable().optional();

```

--------------------------------------------------------------------------------
/sonar-project.properties:
--------------------------------------------------------------------------------

```
sonar.projectKey=sonarqube-mcp-server
sonar.organization=sapientpants
sonar.sources=src
sonar.exclusions=node_modules/**,**/__tests__/**
sonar.tests=src
sonar.test.inclusions=**/__tests__/*.test.ts
sonar.typescript.lcov.reportPaths=coverage/lcov.info

```

--------------------------------------------------------------------------------
/src/schemas/metrics.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from 'zod';
import { stringToNumberTransform } from '../utils/transforms.js';

/**
 * Schema for metrics tool
 */
export const metricsToolSchema = {
  page: z.string().optional().transform(stringToNumberTransform),
  page_size: z.string().optional().transform(stringToNumberTransform),
};

```

--------------------------------------------------------------------------------
/src/schemas/projects.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from 'zod';
import { stringToNumberTransform } from '../utils/transforms.js';

/**
 * Schema for projects tool
 */
export const projectsToolSchema = {
  page: z.string().optional().transform(stringToNumberTransform),
  page_size: z.string().optional().transform(stringToNumberTransform),
};

```

--------------------------------------------------------------------------------
/.github/changeset.yml:
--------------------------------------------------------------------------------

```yaml
# Configuration for Changeset Bot
# This bot will comment on PRs to remind contributors to add changesets

# The message to display when a PR is missing a changeset
# Default message will be used if not specified
commit: false
# Whether to exclude certain PR labels from changeset requirements
# excluded:
#   - "dependencies"
#   - "documentation"

```

--------------------------------------------------------------------------------
/src/schemas/quality-gates.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from 'zod';
import { pullRequestSchema } from './common.js';

/**
 * Schemas for quality gates tools
 */

export const qualityGatesToolSchema = {};

export const qualityGateToolSchema = {
  id: z.string(),
};

export const qualityGateStatusToolSchema = {
  project_key: z.string(),
  branch: z.string().optional(),
  pull_request: pullRequestSchema,
};

```

--------------------------------------------------------------------------------
/src/types/system.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Interface for SonarQube health status
 */
export interface SonarQubeHealthStatus {
  health: 'GREEN' | 'YELLOW' | 'RED';
  causes: string[];
}

/**
 * Interface for SonarQube system status
 */
export interface SonarQubeSystemStatus {
  id: string;
  version: string;
  status:
    | 'UP'
    | 'DOWN'
    | 'STARTING'
    | 'RESTARTING'
    | 'DB_MIGRATION_NEEDED'
    | 'DB_MIGRATION_RUNNING';
}

```

--------------------------------------------------------------------------------
/.changeset/config.json:
--------------------------------------------------------------------------------

```json
{
  "$schema": "https://unpkg.com/@changesets/[email protected]/schema.json",
  "changelog": [
    "changelog-github-custom",
    {
      "repo": "sapientpants/sonarqube-mcp-server"
    }
  ],
  "commit": false,
  "fixed": [],
  "linked": [],
  "access": "public",
  "baseBranch": "main",
  "updateInternalDependencies": "patch",
  "ignore": [],
  "privatePackages": {
    "version": true,
    "tag": false
  }
}

```

--------------------------------------------------------------------------------
/osv-scanner.toml:
--------------------------------------------------------------------------------

```toml
# OSV Scanner Configuration
# Documentation: https://google.github.io/osv-scanner/configuration/

# Ignored vulnerabilities
# These vulnerabilities are acknowledged and accepted as risk
[[IgnoredVulns]]
id = "GHSA-9965-vmph-33xx"
# Reason: validator package is dev-only dependency (not in production)
# Impact: Medium severity (CVSS 6.1)
# Status: No fix available yet
# Review date: 2025-10-14
reason = "Dev-only dependency, not exposed in production"

```

--------------------------------------------------------------------------------
/src/types/metrics.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Interface for SonarQube metric
 */
export interface SonarQubeMetric {
  id: string;
  key: string;
  name: string;
  description: string;
  domain: string;
  type: string;
  direction: number;
  qualitative: boolean;
  hidden: boolean;
  custom: boolean;
}

/**
 * Interface for SonarQube metrics result
 */
export interface SonarQubeMetricsResult {
  metrics: SonarQubeMetric[];
  paging: {
    pageIndex: number;
    pageSize: number;
    total: number;
  };
}

```

--------------------------------------------------------------------------------
/docs/architecture/decisions/0001-record-architecture-decisions.md:
--------------------------------------------------------------------------------

```markdown
# 1. Record architecture decisions

Date: 2025-06-13

## Status

Accepted

## Context

We need to record the architectural decisions made on this project.

## Decision

We will use Architecture Decision Records, as [described by Michael Nygard](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions).

## Consequences

See Michael Nygard's article, linked above. For a lightweight ADR toolset, see Nat Pryce's [adr-tools](https://github.com/npryce/adr-tools).

```

--------------------------------------------------------------------------------
/src/types/projects.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Interface for SonarQube project
 */
export interface SonarQubeProject {
  key: string;
  name: string;
  qualifier: string;
  visibility: string;
  lastAnalysisDate: string | undefined;
  revision: string | undefined;
  managed: boolean | undefined;
}

/**
 * Interface for SonarQube projects result - Clean abstraction for consumers
 */
export interface SonarQubeProjectsResult {
  projects: SonarQubeProject[];
  paging: {
    pageIndex: number;
    pageSize: number;
    total: number;
  };
}

```

--------------------------------------------------------------------------------
/src/domains/index.ts:
--------------------------------------------------------------------------------

```typescript
// Export all domain modules
export { BaseDomain } from './base.js';
export { ProjectsDomain } from './projects.js';
export { IssuesDomain } from './issues.js';
export { MetricsDomain } from './metrics.js';
export { MeasuresDomain } from './measures.js';
export { SystemDomain } from './system.js';
export { QualityGatesDomain } from './quality-gates.js';
export { SourceCodeDomain } from './source-code.js';
export { HotspotsDomain } from './hotspots.js';
export { ComponentsDomain } from './components.js';

```

--------------------------------------------------------------------------------
/src/transports/index.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Transport module exports.
 * This module provides the transport abstraction layer for the MCP server.
 */

export type { ITransport, ITransportConfig, IHttpTransportConfig } from './base.js';
export { isStdioTransport } from './base.js';
export { StdioTransport } from './stdio.js';
export { HttpTransport } from './http.js';
export { SessionManager } from './session-manager.js';
export type { ISession, ISessionManagerConfig } from './session-manager.js';
export { TransportFactory } from './factory.js';

```

--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------

```yaml
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file

version: 2
updates:
  - package-ecosystem: 'npm' # See documentation for possible values
    directory: '/' # Location of package manifests
    schedule:
      interval: 'weekly'

```

--------------------------------------------------------------------------------
/src/__tests__/null-to-undefined.test.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, it, expect } from 'vitest';
import { nullToUndefined } from '../index.js';
describe('nullToUndefined', () => {
  it('should convert null to undefined', () => {
    expect(nullToUndefined(null)).toBeUndefined();
  });
  it('should pass through non-null values', () => {
    expect(nullToUndefined('value')).toBe('value');
    expect(nullToUndefined(123)).toBe(123);
    expect(nullToUndefined(0)).toBe(0);
    expect(nullToUndefined(false)).toBe(false);
    expect(nullToUndefined(undefined)).toBeUndefined();
  });
});

```

--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------

```markdown
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.

```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2022"],
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "noImplicitOverride": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "resolveJsonModule": true,
    "declaration": true,
    "sourceMap": true,
    "rootDir": "./src",
    "outDir": "dist",
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "downlevelIteration": true
  },
  "include": ["src", "tests"]
}

```

--------------------------------------------------------------------------------
/src/schemas/source-code.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from 'zod';
import { stringToNumberTransform } from '../utils/transforms.js';
import { pullRequestSchema } from './common.js';

/**
 * Schemas for source code tools
 */

export const sourceCodeToolSchema = {
  key: z.string(),
  from: z.string().optional().transform(stringToNumberTransform),
  to: z.string().optional().transform(stringToNumberTransform),
  branch: z.string().optional(),
  pull_request: pullRequestSchema,
};

export const scmBlameToolSchema = {
  key: z.string(),
  from: z.string().optional().transform(stringToNumberTransform),
  to: z.string().optional().transform(stringToNumberTransform),
  branch: z.string().optional(),
  pull_request: pullRequestSchema,
};

```

--------------------------------------------------------------------------------
/.github/actionlint.yaml:
--------------------------------------------------------------------------------

```yaml
# actionlint configuration
# https://github.com/rhysd/actionlint/blob/main/docs/config.md

# Self-hosted runner labels used in this project
# Add any custom runner labels here if using self-hosted runners
self-hosted-runner:
  labels: []

# Configuration variables used in workflows
# This helps actionlint validate variable references
config-variables:
  - ENABLE_DOCKER_RELEASE
  - ENABLE_NPM_RELEASE
  - ENABLE_GITHUB_PACKAGES

# Ignore specific shellcheck warnings
# SC2086: Double quote to prevent globbing and word splitting
# These are often false positives in GitHub Actions contexts
paths:
  .github/workflows/**/*.{yml,yaml}:
    ignore:
      - 'shellcheck reported issue in this script: SC2086:.+'

```

--------------------------------------------------------------------------------
/src/__tests__/transports/base.test.ts:
--------------------------------------------------------------------------------

```typescript
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { isStdioTransport } from '../../transports/base.js';

describe('Transport Base', () => {
  describe('isStdioTransport', () => {
    it('should return true for StdioServerTransport instance', () => {
      const transport = new StdioServerTransport();
      expect(isStdioTransport(transport)).toBe(true);
    });

    it('should return false for non-StdioServerTransport instances', () => {
      expect(isStdioTransport({})).toBe(false);
      expect(isStdioTransport(null)).toBe(false);
      expect(isStdioTransport(undefined)).toBe(false);
      expect(isStdioTransport('string')).toBe(false);
      expect(isStdioTransport(123)).toBe(false);
    });
  });
});

```

--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------

```markdown
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:

1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Desktop (please complete the following information):**

- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]

**Smartphone (please complete the following information):**

- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]

**Additional context**
Add any other context about the problem here.

```

--------------------------------------------------------------------------------
/vitest.config.ts:
--------------------------------------------------------------------------------

```typescript
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    environment: 'node',
    globals: true,
    coverage: {
      provider: 'v8',
      reporter: ['text', 'html', 'json-summary', 'lcov'],
      exclude: [
        'coverage/**',
        'dist/**',
        '*.config.js',
        '*.config.ts',
        '.*.js',
        '**/*.d.ts',
        'tests/**',
        'test/**',
        '**/*.spec.ts',
        '**/*.test.ts',
        'docs/**',
        '.github/**',
        '.changeset/**',
        '.claude/**',
        'node_modules/**',
        'src/dev/**', // Development utilities - no coverage required
        '**/*.example.ts', // Example files - not part of production code
      ],
      thresholds: {
        branches: 80,
        functions: 80,
        lines: 80,
        statements: 80,
      },
    },
  },
});

```

--------------------------------------------------------------------------------
/vitest.config.js:
--------------------------------------------------------------------------------

```javascript
import { defineConfig } from 'vitest/config';
export default defineConfig({
  test: {
    environment: 'node',
    globals: true,
    coverage: {
      provider: 'v8',
      reporter: ['text', 'html', 'json-summary', 'lcov'],
      exclude: [
        'coverage/**',
        'dist/**',
        '*.config.js',
        '*.config.ts',
        '.*.js',
        '**/*.d.ts',
        'tests/**',
        'test/**',
        '**/*.spec.ts',
        '**/*.test.ts',
        'docs/**',
        '.github/**',
        '.changeset/**',
        '.claude/**',
        'node_modules/**',
        'src/dev/**', // Development utilities - no coverage required
        '**/*.example.ts', // Example files - not part of production code
      ],
      thresholds: {
        branches: 80,
        functions: 80,
        lines: 80,
        statements: 80,
      },
    },
  },
});
//# sourceMappingURL=vitest.config.js.map

```

--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------

```markdown
<!--- Please provide a general summary of your changes in the title above -->

## Pull request type

<!-- Please try to limit your pull request to one type, submit multiple pull requests if needed. -->

Please check the type of change your PR introduces:

- [ ] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, renaming)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] Documentation content changes
- [ ] Other (please describe):

## What is the current behavior?

<!-- Please describe the current behavior that you are modifying, or link to a relevant issue. -->

Issue Number: N/A

## What is the new behavior?

<!-- Please describe the behavior or changes that are being added by this PR. -->

-
-
-

## Other information

<!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. -->

```

--------------------------------------------------------------------------------
/src/handlers/metrics.ts:
--------------------------------------------------------------------------------

```typescript
import type { PaginationParams, ISonarQubeClient } from '../types/index.js';
import { getDefaultClient } from '../utils/client-factory.js';
import { createStructuredResponse } from '../utils/structured-response.js';

/**
 * Handler for getting SonarQube metrics
 * @param params Parameters for the metrics request
 * @param client Optional SonarQube client instance
 * @returns Promise with the metrics result
 */
export async function handleSonarQubeGetMetrics(
  params: PaginationParams,
  client: ISonarQubeClient = getDefaultClient()
) {
  const result = await client.getMetrics(params);

  // Create a properly structured response matching the expected format
  const response = {
    metrics: result.metrics ?? [],
    paging: result.paging ?? {
      pageIndex: params.page ?? 1,
      pageSize: params.pageSize ?? 100,
      total: (result.metrics ?? []).length,
    },
  };

  return createStructuredResponse(response);
}

```

--------------------------------------------------------------------------------
/.claude/commands/analyze-and-fix-github-issue.md:
--------------------------------------------------------------------------------

```markdown
# Analyze and fix GitHub Issue

Please analyze and fix the GitHub issue: $ARGUMENTS.

Follow these steps:

1. Use `gh issue view` to get the issue details
2. Understand the problem described in the issue
3. Search the codebase for relevant files
4. Create a detailed plan to address the issue
5. Create a new branch for the fix, e.g., `fix/issue-123`
6. Implement the necessary changes to fix the issue
7. Ensure that any new code is well-documented and follows the project's coding standards
8. Write tests to cover the changes made, if applicable
9. Ensure code passes formatting, linting, type checking, and tests using `pnpm run precommit`
10. Create a descriptive commit message
11. Push and create a PR
12. Wait for code review and address any feedback provided by reviewers.
13. Merge the pull request once it has been approved. Use the "Squash and merge" option to keep the commit history clean.

Remember to use the GitHub CLI (`gh`) for all GitHub-related tasks.

```

--------------------------------------------------------------------------------
/src/handlers/source-code.ts:
--------------------------------------------------------------------------------

```typescript
import type { SourceCodeParams, ScmBlameParams, ISonarQubeClient } from '../types/index.js';
import { getDefaultClient } from '../utils/client-factory.js';
import { createStructuredResponse } from '../utils/structured-response.js';

/**
 * Handler for getting source code
 * @param params Parameters for the source code request
 * @param client Optional SonarQube client instance
 * @returns Promise with the source code result
 */
export async function handleSonarQubeGetSourceCode(
  params: SourceCodeParams,
  client: ISonarQubeClient = getDefaultClient()
) {
  const result = await client.getSourceCode(params);

  return createStructuredResponse(result);
}

/**
 * Handler for getting SCM blame information
 * @param params Parameters for the SCM blame request
 * @param client Optional SonarQube client instance
 * @returns Promise with the SCM blame result
 */
export async function handleSonarQubeGetScmBlame(
  params: ScmBlameParams,
  client: ISonarQubeClient = getDefaultClient()
) {
  const result = await client.getScmBlame(params);

  return createStructuredResponse(result);
}

```

--------------------------------------------------------------------------------
/src/config/versions.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Centralized version configuration for the SonarQube MCP Server
 */

/**
 * Server version - the version of the SonarQube MCP Server itself
 */
export const SERVER_VERSION = '1.7.0';

/**
 * MCP SDK version currently in use
 */
export const SDK_VERSION = '1.13.0';

/**
 * MCP protocol versions supported by the current SDK
 * Listed in order from newest to oldest
 */
export const SUPPORTED_PROTOCOL_VERSIONS = [
  '2025-06-18',
  '2025-03-26',
  '2024-11-05',
  '2024-10-07',
] as const;

/**
 * Latest MCP protocol version
 */
export const LATEST_PROTOCOL_VERSION = SUPPORTED_PROTOCOL_VERSIONS[0];

/**
 * Default negotiated protocol version (what most clients will use)
 */
export const DEFAULT_NEGOTIATED_PROTOCOL_VERSION = '2025-03-26';

/**
 * Version information object for logging and display
 */
export const VERSION_INFO = {
  serverVersion: SERVER_VERSION,
  sdkVersion: SDK_VERSION,
  supportedProtocolVersions: SUPPORTED_PROTOCOL_VERSIONS,
  latestProtocolVersion: LATEST_PROTOCOL_VERSION,
  defaultNegotiatedProtocolVersion: DEFAULT_NEGOTIATED_PROTOCOL_VERSION,
} as const;

```

--------------------------------------------------------------------------------
/src/handlers/index.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Re-export all handlers from their respective modules
 */

export { handleSonarQubeProjects } from './projects.js';

export {
  handleSonarQubeGetIssues,
  handleMarkIssueFalsePositive,
  handleMarkIssueWontFix,
  handleMarkIssuesFalsePositive,
  handleMarkIssuesWontFix,
  handleAddCommentToIssue,
  handleAssignIssue,
  handleConfirmIssue,
  handleUnconfirmIssue,
  handleResolveIssue,
  handleReopenIssue,
  setElicitationManager,
} from './issues.js';

export { handleSonarQubeGetMetrics } from './metrics.js';

export {
  handleSonarQubeGetHealth,
  handleSonarQubeGetStatus,
  handleSonarQubePing,
} from './system.js';

export {
  handleSonarQubeComponentMeasures,
  handleSonarQubeComponentsMeasures,
  handleSonarQubeMeasuresHistory,
} from './measures.js';

export {
  handleSonarQubeListQualityGates,
  handleSonarQubeGetQualityGate,
  handleSonarQubeQualityGateStatus,
} from './quality-gates.js';

export { handleSonarQubeGetSourceCode, handleSonarQubeGetScmBlame } from './source-code.js';

export {
  handleSonarQubeHotspots,
  handleSonarQubeHotspot,
  handleSonarQubeUpdateHotspotStatus,
} from './hotspots.js';

export { handleSonarQubeComponents } from './components.js';

```

--------------------------------------------------------------------------------
/src/config/service-accounts.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Service account configuration utilities
 */

export interface ServiceAccountConfig {
  id: string;
  token: string;
  url: string | undefined;
  organization: string | undefined;
}

/**
 * Get service account configuration
 */
export function getServiceAccountConfig(accountId: string): ServiceAccountConfig | null {
  // For default account, check standard environment variables
  if (accountId === 'default') {
    const token = process.env.SONARQUBE_TOKEN;
    if (!token) {
      return null;
    }

    const config: ServiceAccountConfig = {
      id: 'default',
      token,
      url: process.env.SONARQUBE_URL,
      organization: process.env.SONARQUBE_ORGANIZATION,
    };
    return config;
  }

  // For numbered accounts (SA1-SA10)
  const regex = /^SA(\d+)$/;
  const match = regex.exec(accountId);
  if (match) {
    const num = match[1];
    const token = process.env[`SONARQUBE_SA${num}_TOKEN`];
    if (!token) {
      return null;
    }

    const config: ServiceAccountConfig = {
      id: accountId,
      token,
      url: process.env[`SONARQUBE_SA${num}_URL`],
      organization: process.env[`SONARQUBE_SA${num}_ORGANIZATION`],
    };
    return config;
  }

  return null;
}

```

--------------------------------------------------------------------------------
/src/schemas/index.ts:
--------------------------------------------------------------------------------

```typescript
// Re-export all schemas from their respective files

// Common schemas
export * from './common.js';

// Domain schemas
export * from './hotspots.js';

// Tool schemas
export { projectsToolSchema } from './projects.js';
export { metricsToolSchema } from './metrics.js';
export {
  issuesToolSchema,
  markIssueFalsePositiveToolSchema,
  markIssueWontFixToolSchema,
  markIssuesFalsePositiveToolSchema,
  markIssuesWontFixToolSchema,
  addCommentToIssueToolSchema,
  assignIssueToolSchema,
  confirmIssueToolSchema,
  unconfirmIssueToolSchema,
  resolveIssueToolSchema,
  reopenIssueToolSchema,
} from './issues.js';
export { systemHealthToolSchema, systemStatusToolSchema, systemPingToolSchema } from './system.js';
export {
  componentMeasuresToolSchema,
  componentsMeasuresToolSchema,
  measuresHistoryToolSchema,
} from './measures.js';
export {
  qualityGatesToolSchema,
  qualityGateToolSchema,
  qualityGateStatusToolSchema,
} from './quality-gates.js';
export { sourceCodeToolSchema, scmBlameToolSchema } from './source-code.js';
export {
  hotspotsToolSchema,
  hotspotToolSchema,
  updateHotspotStatusToolSchema,
} from './hotspots-tools.js';
export { componentsToolSchema } from './components.js';

```

--------------------------------------------------------------------------------
/src/schemas/measures.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from 'zod';
import { stringToNumberTransform } from '../utils/transforms.js';
import { pullRequestSchema } from './common.js';

/**
 * Schemas for measures tools
 */

export const componentMeasuresToolSchema = {
  component: z.string(),
  metric_keys: z.array(z.string()),
  additional_fields: z.array(z.string()).optional(),
  branch: z.string().optional(),
  pull_request: pullRequestSchema,
  period: z.string().optional(),
};

export const componentsMeasuresToolSchema = {
  component_keys: z.array(z.string()),
  metric_keys: z.array(z.string()),
  additional_fields: z.array(z.string()).optional(),
  branch: z.string().optional(),
  pull_request: pullRequestSchema,
  period: z.string().optional(),
  page: z.string().optional().transform(stringToNumberTransform),
  page_size: z.string().optional().transform(stringToNumberTransform),
};

export const measuresHistoryToolSchema = {
  component: z.string(),
  metrics: z.array(z.string()),
  from: z.string().optional(),
  to: z.string().optional(),
  branch: z.string().optional(),
  pull_request: pullRequestSchema,
  page: z.string().optional().transform(stringToNumberTransform),
  page_size: z.string().optional().transform(stringToNumberTransform),
};

```

--------------------------------------------------------------------------------
/src/types/quality-gates.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Interface for SonarQube quality gate condition
 */
export interface SonarQubeQualityGateCondition {
  id: string;
  metric: string;
  op: string;
  error: string;
}

/**
 * Interface for SonarQube quality gate
 */
export interface SonarQubeQualityGate {
  id: string;
  name: string;
  isDefault: boolean | undefined;
  isBuiltIn: boolean | undefined;
  conditions: SonarQubeQualityGateCondition[] | undefined;
}

/**
 * Interface for SonarQube quality gates list result
 */
export interface SonarQubeQualityGatesResult {
  qualitygates: SonarQubeQualityGate[];
  default: string;
  actions:
    | {
        create: boolean | undefined;
      }
    | undefined;
}

/**
 * Interface for SonarQube quality gate status
 */
export interface SonarQubeQualityGateStatus {
  projectStatus: {
    status: 'OK' | 'WARN' | 'ERROR' | 'NONE';
    conditions: Array<{
      status: 'OK' | 'WARN' | 'ERROR';
      metricKey: string;
      comparator: string;
      errorThreshold: string;
      actualValue: string;
    }>;
    periods?: Array<{
      index: number;
      mode: string;
      date: string;
      parameter?: string;
    }>;
    ignoredConditions: boolean;
  };
}

/**
 * Interface for project quality gate params
 */
export interface ProjectQualityGateParams {
  projectKey: string;
  branch?: string;
  pullRequest?: string;
}

```

--------------------------------------------------------------------------------
/src/utils/error-handler.ts:
--------------------------------------------------------------------------------

```typescript
import { SonarQubeAPIError, formatErrorForMCP } from '../errors.js';
import { createLogger } from './logger.js';

const logger = createLogger('utils/error-handler');

/**
 * Custom error class for MCP errors that includes a code property
 */
class MCPError extends Error {
  code: number;

  constructor(message: string, code: number) {
    super(message);
    this.name = 'MCPError';
    this.code = code;
  }
}

/**
 * Wraps an async MCP handler function with error handling that converts
 * SonarQubeAPIError instances to MCP-formatted errors
 *
 * @param fn The async function to wrap
 * @returns A wrapped function that handles errors appropriately
 */
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function withMCPErrorHandling<T extends (...args: any[]) => Promise<any>>(fn: T): T {
  return (async (
    ...args: Parameters<T>
  ): Promise<ReturnType<T> extends Promise<infer U> ? U : never> => {
    try {
      return (await fn(...args)) as ReturnType<T> extends Promise<infer U> ? U : never;
    } catch (error) {
      if (error instanceof SonarQubeAPIError) {
        logger.error('SonarQube API error occurred', error);
        const mcpError = formatErrorForMCP(error);
        throw new MCPError(mcpError.message, mcpError.code);
      }
      // Re-throw non-SonarQubeAPIError errors as-is
      throw error;
    }
  }) as T;
}

```

--------------------------------------------------------------------------------
/src/schemas/hotspots-tools.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from 'zod';
import { stringToNumberTransform } from '../utils/transforms.js';
import { pullRequestNullableSchema } from './common.js';
import { hotspotStatusSchema, hotspotResolutionSchema } from './hotspots.js';

/**
 * Schemas for hotspot tools
 */

export const hotspotsToolSchema = {
  project_key: z.string().optional(),
  branch: z.string().nullable().optional(),
  pull_request: pullRequestNullableSchema,
  status: hotspotStatusSchema,
  resolution: hotspotResolutionSchema,
  files: z.array(z.string()).nullable().optional(),
  assigned_to_me: z
    .union([z.boolean(), z.string().transform((val) => val === 'true')])
    .nullable()
    .optional(),
  since_leak_period: z
    .union([z.boolean(), z.string().transform((val) => val === 'true')])
    .nullable()
    .optional(),
  in_new_code_period: z
    .union([z.boolean(), z.string().transform((val) => val === 'true')])
    .nullable()
    .optional(),
  page: z.string().optional().transform(stringToNumberTransform),
  page_size: z.string().optional().transform(stringToNumberTransform),
};

export const hotspotToolSchema = {
  hotspot_key: z.string(),
};

export const updateHotspotStatusToolSchema = {
  hotspot_key: z.string(),
  status: z.enum(['TO_REVIEW', 'REVIEWED']),
  resolution: z.enum(['FIXED', 'SAFE']).nullable().optional(),
  comment: z.string().nullable().optional(),
};

```

--------------------------------------------------------------------------------
/test-http-transport.sh:
--------------------------------------------------------------------------------

```bash
#!/bin/bash

echo "Starting SonarQube MCP Server with HTTP transport..."

# Start the server in the background with HTTP transport
MCP_TRANSPORT_TYPE=http \
MCP_HTTP_PORT=3000 \
SONARQUBE_URL=https://sonarcloud.io \
SONARQUBE_TOKEN=dummy_token \
LOG_LEVEL=INFO \
LOG_FILE=/tmp/http-transport-test.log \
pnpm start &

SERVER_PID=$!
echo "Server started with PID: $SERVER_PID"

# Wait for server to start
echo "Waiting for server to start..."
sleep 3

# Test health endpoint
echo ""
echo "Testing health endpoint..."
curl -s http://localhost:3000/health | jq .

# Create a session
echo ""
echo "Creating session..."
SESSION_RESPONSE=$(curl -s -X POST http://localhost:3000/session)
SESSION_ID=$(echo "$SESSION_RESPONSE" | jq -r .sessionId)
echo "Session created: $SESSION_ID"

# Test MCP endpoint (will fail with placeholder response, but tests the endpoint)
echo ""
echo "Testing MCP endpoint..."
curl -s -X POST http://localhost:3000/mcp \
  -H "Content-Type: application/json" \
  -d "{\"sessionId\": \"$SESSION_ID\", \"method\": \"tools/list\", \"params\": {}}" | jq .

# Close session
echo ""
echo "Closing session..."
curl -s -X DELETE "http://localhost:3000/session/$SESSION_ID" | jq .

# Kill the server
echo ""
echo "Stopping server..."
kill $SERVER_PID
wait $SERVER_PID 2>/dev/null

echo ""
echo "Test completed!"
echo "Check logs at /tmp/http-transport-test.log for server logs."
```

--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------

```javascript
/** @type {import('jest').Config} */
export default {
  preset: 'ts-jest/presets/default-esm',
  testEnvironment: 'node',
  testMatch: ['**/__tests__/**/*.test.ts'],
  moduleNameMapper: {
    '^(\\.{1,2}/.*)\\.js$': '$1',
    '^(\\.{1,2}/.*)\\.ts$': '$1',
  },
  transform: {
    '^.+\\.tsx?$': [
      'ts-jest',
      {
        useESM: true,
        tsconfig: {
          moduleResolution: 'NodeNext',
        },
      },
    ],
  },
  extensionsToTreatAsEsm: ['.ts'],
  moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
  transformIgnorePatterns: ['node_modules/(?!(@modelcontextprotocol)/)'],
  collectCoverageFrom: [
    'src/**/*.ts',
    '!src/**/*.d.ts',
    '!src/**/*.test.ts',
    '!src/__tests__/mocks/**/*.ts',
  ],
  coverageReporters: ['text', 'lcov'],
  testPathIgnorePatterns: [
    '/node_modules/',
    '/src/__tests__/lambda-functions.test.ts',
    '/src/__tests__/handlers.test.ts',
    '/src/__tests__/tool-handlers.test.ts',
    '/src/__tests__/mocked-environment.test.ts',
    '/src/__tests__/direct-lambdas.test.ts',
  ],
  // Focusing on total coverage, with sonarqube.ts at 100%
  coverageThreshold: {
    'src/sonarqube.ts': {
      statements: 81,
      branches: 60,
      functions: 100,
      lines: 81,
    },
    global: {
      statements: 68,
      branches: 8,
      functions: 40,
      lines: 68,
    },
  },
  bail: 0, // Run all tests regardless of failures
};

```

--------------------------------------------------------------------------------
/src/__tests__/transports/stdio.test.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, it, expect, vi } from 'vitest';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioTransport } from '../../transports/stdio.js';

describe('StdioTransport', () => {
  it('should create instance with transport', () => {
    const transport = new StdioTransport();
    expect(transport).toBeDefined();
    expect(transport.getName()).toBe('stdio');
  });

  it('should return underlying transport', () => {
    const transport = new StdioTransport();
    const underlying = transport.getUnderlyingTransport();
    expect(underlying).toBeDefined();
    expect(underlying.constructor.name).toBe('StdioServerTransport');
  });

  it('should connect to server', async () => {
    const transport = new StdioTransport();
    const mockServer = {
      connect: vi.fn<() => Promise<any>>().mockResolvedValue(undefined as never),
    } as unknown as Server;

    await transport.connect(mockServer);
    expect(mockServer.connect).toHaveBeenCalledWith(transport.getUnderlyingTransport());
  });

  it('should add connect method to underlying transport', () => {
    const transport = new StdioTransport();
    const underlying = transport.getUnderlyingTransport();
    // The transport should have a connect method added via our workaround
    expect('connect' in underlying).toBe(true);
    expect(typeof (underlying as { connect?: unknown }).connect).toBe('function');
  });
});

```

--------------------------------------------------------------------------------
/.claude/commands/release.md:
--------------------------------------------------------------------------------

```markdown
# Release a new version

You are about to make a release of the project. Please follow these steps:

1. **Create a new branch** for the release, e.g., `release/v1.0.0`.
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.
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`.
   Use the following format for the changelog entry:

   ```
   ## [v1.0.0] - YYYY-MM-DD
   - Description of changes
   - Another change
   ```

4. Update the README.md file if necessary, ensuring it reflects the latest changes and version number.
5. **Commit the changes** making sure that the pre-commit hook passes without warnings or errors. ^:wq
6. **Push the branch** to the remote repository.
7. **Create a pull request** to trigger the CI/CD pipeline.
8. **Wait for the CI/CD pipeline to complete** successfully. Ensure that all tests pass and the build is successful.
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.
10. **Make the release** by using the `gh` CLI tool:

```bash
gh release create v1.0.0 --title "Release v1.0.0" --notes "Release notes for v1.0.0"
```

```

--------------------------------------------------------------------------------
/src/domains/metrics.ts:
--------------------------------------------------------------------------------

```typescript
import type { PaginationParams, SonarQubeMetricsResult } from '../types/index.js';
import { BaseDomain } from './base.js';

/**
 * Domain module for metrics-related operations
 */
export class MetricsDomain extends BaseDomain {
  /**
   * Gets available metrics from SonarQube
   * @param params Parameters including pagination
   * @returns Promise with the list of metrics
   */
  async getMetrics(params?: PaginationParams): Promise<SonarQubeMetricsResult> {
    const { page, pageSize } = params ?? {};

    const request: {
      p?: number;
      ps?: number;
    } = {
      ...(page && { p: page }),
      ...(pageSize && { ps: pageSize }),
    };

    const response = await this.webApiClient.metrics.search(request);

    // The API might return paging info
    const paging = (
      response as unknown as {
        paging?: { pageIndex: number; pageSize: number; total: number };
      }
    ).paging;

    return {
      metrics: response.metrics.map((metric) => ({
        id: metric.id ?? '',
        key: metric.key,
        name: metric.name,
        description: metric.description ?? '',
        domain: metric.domain ?? '',
        type: metric.type,
        direction: metric.direction ?? 0,
        qualitative: metric.qualitative ?? false,
        hidden: metric.hidden ?? false,
        custom: metric.custom ?? false,
      })),
      paging: paging ?? {
        pageIndex: page ?? 1,
        pageSize: pageSize ?? 100,
        total: response.metrics.length,
      },
    };
  }
}

```

--------------------------------------------------------------------------------
/src/handlers/quality-gates.ts:
--------------------------------------------------------------------------------

```typescript
import type { ProjectQualityGateParams, ISonarQubeClient } from '../types/index.js';
import { getDefaultClient } from '../utils/client-factory.js';
import { createStructuredResponse } from '../utils/structured-response.js';

/**
 * Handler for listing quality gates
 * @param client Optional SonarQube client instance
 * @returns Promise with the quality gates list
 */
export async function handleSonarQubeListQualityGates(
  client: ISonarQubeClient = getDefaultClient()
) {
  const result = await client.listQualityGates();

  return createStructuredResponse(result);
}

/**
 * Handler for getting a specific quality gate
 * @param params Parameters containing the quality gate ID
 * @param client Optional SonarQube client instance
 * @returns Promise with the quality gate details
 */
export async function handleSonarQubeGetQualityGate(
  params: { id: string },
  client: ISonarQubeClient = getDefaultClient()
) {
  const result = await client.getQualityGate(params.id);

  return createStructuredResponse(result);
}

/**
 * Handler for getting quality gate status for a project
 * @param params Parameters for the quality gate status request
 * @param client Optional SonarQube client instance
 * @returns Promise with the quality gate status
 */
export async function handleSonarQubeQualityGateStatus(
  params: ProjectQualityGateParams,
  client: ISonarQubeClient = getDefaultClient()
) {
  const result = await client.getProjectQualityGateStatus(params);

  return createStructuredResponse(result);
}

```

--------------------------------------------------------------------------------
/src/__tests__/string-to-number-transform.test.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, it, expect } from 'vitest';
import { nullToUndefined, stringToNumberTransform } from '../index.js';
describe('String to Number Transform', () => {
  describe('nullToUndefined', () => {
    it('should transform null to undefined', () => {
      expect(nullToUndefined(null)).toBeUndefined();
    });
    it('should not transform undefined', () => {
      expect(nullToUndefined(undefined)).toBeUndefined();
    });
    it('should not transform non-null values', () => {
      expect(nullToUndefined('test')).toBe('test');
      expect(nullToUndefined(123)).toBe(123);
      expect(nullToUndefined(true)).toBe(true);
      expect(nullToUndefined(false)).toBe(false);
      expect(nullToUndefined(0)).toBe(0);
      expect(nullToUndefined('')).toBe('');
    });
  });
  describe('stringToNumberTransform', () => {
    it('should transform valid string numbers to integers', () => {
      expect(stringToNumberTransform('123')).toBe(123);
      expect(stringToNumberTransform('0')).toBe(0);
      expect(stringToNumberTransform('-10')).toBe(-10);
    });
    it('should return null for invalid number strings', () => {
      expect(stringToNumberTransform('abc')).toBeNull();
      expect(stringToNumberTransform('')).toBeNull();
      expect(stringToNumberTransform('123abc')).toBe(123); // parseInt behavior
    });
    it('should pass through null and undefined values', () => {
      expect(stringToNumberTransform(null)).toBeNull();
      expect(stringToNumberTransform(undefined)).toBeUndefined();
    });
  });
});

```

--------------------------------------------------------------------------------
/src/domains/projects.ts:
--------------------------------------------------------------------------------

```typescript
import type { PaginationParams, SonarQubeProjectsResult } from '../types/index.js';
import { BaseDomain } from './base.js';

/**
 * Domain module for project-related operations
 */
export class ProjectsDomain extends BaseDomain {
  /**
   * Lists all projects in SonarQube
   * @param params Pagination and organization parameters
   * @returns Promise with the list of projects
   */
  async listProjects(params?: PaginationParams): Promise<SonarQubeProjectsResult> {
    const { page, pageSize } = params ?? {};
    this.logger.debug('Listing projects', { page, pageSize, organization: this.organization });

    try {
      const builder = this.webApiClient.projects.search();

      if (page !== undefined) {
        builder.page(page);
      }
      if (pageSize !== undefined) {
        builder.pageSize(pageSize);
      }

      const response = await builder.execute();
      this.logger.debug('Projects retrieved successfully', { count: response.components.length });

      // Transform to our interface
      return {
        projects: response.components.map((component) => ({
          key: component.key,
          name: component.name,
          qualifier: component.qualifier,
          visibility: component.visibility,
          lastAnalysisDate: component.lastAnalysisDate,
          revision: component.revision,
          managed: component.managed,
        })),
        paging: response.paging,
      };
    } catch (error) {
      this.logger.error('Failed to list projects', error);
      throw error;
    }
  }
}

```

--------------------------------------------------------------------------------
/src/handlers/hotspots.ts:
--------------------------------------------------------------------------------

```typescript
import type {
  HotspotSearchParams,
  HotspotStatusUpdateParams,
  ISonarQubeClient,
} from '../types/index.js';
import { getDefaultClient } from '../utils/client-factory.js';
import { createStructuredResponse, createTextResponse } from '../utils/structured-response.js';

/**
 * Handler for searching security hotspots
 * @param params Parameters for the hotspots search
 * @param client Optional SonarQube client instance
 * @returns Promise with the hotspots search result
 */
export async function handleSonarQubeHotspots(
  params: HotspotSearchParams,
  client: ISonarQubeClient = getDefaultClient()
) {
  const result = await client.hotspots(params);

  return createStructuredResponse(result);
}

/**
 * Handler for getting hotspot details
 * @param hotspotKey The key of the hotspot
 * @param client Optional SonarQube client instance
 * @returns Promise with the hotspot details
 */
export async function handleSonarQubeHotspot(
  hotspotKey: string,
  client: ISonarQubeClient = getDefaultClient()
) {
  const result = await client.hotspot(hotspotKey);

  return createStructuredResponse(result);
}

/**
 * Handler for updating hotspot status
 * @param params Parameters for the hotspot status update
 * @param client Optional SonarQube client instance
 * @returns Promise with success message
 */
export async function handleSonarQubeUpdateHotspotStatus(
  params: HotspotStatusUpdateParams,
  client: ISonarQubeClient = getDefaultClient()
) {
  await client.updateHotspotStatus(params);

  return createTextResponse('Hotspot status updated successfully');
}

```

--------------------------------------------------------------------------------
/src/utils/structured-response.ts:
--------------------------------------------------------------------------------

```typescript
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';

/**
 * Creates a structured response for MCP tools that includes both text content
 * (for backward compatibility) and structured content (for better machine readability)
 *
 * @param data The structured data to return
 * @returns A CallToolResult with both text and structured content
 */
export function createStructuredResponse(data: unknown): CallToolResult {
  return {
    content: [
      {
        type: 'text' as const,
        text: JSON.stringify(data, null, 2),
      },
    ],
    structuredContent: data as Record<string, unknown>,
  };
}

/**
 * Creates a simple text response without structured content
 *
 * @param text The text content to return
 * @returns A CallToolResult with only text content
 */
export function createTextResponse(text: string): CallToolResult {
  return {
    content: [
      {
        type: 'text' as const,
        text,
      },
    ],
  };
}

/**
 * Creates an error response with optional structured error details
 *
 * @param message The error message
 * @param details Optional structured error details
 * @returns A CallToolResult marked as an error
 */
export function createErrorResponse(message: string, details?: unknown): CallToolResult {
  const errorData: Record<string, unknown> = {
    error: message,
  };

  if (details !== undefined) {
    errorData.details = details;
  }

  return {
    content: [
      {
        type: 'text' as const,
        text: message,
      },
    ],
    structuredContent: errorData,
    isError: true,
  };
}

```

--------------------------------------------------------------------------------
/src/handlers/system.ts:
--------------------------------------------------------------------------------

```typescript
import type { ISonarQubeClient } from '../types/index.js';
import { getDefaultClient } from '../utils/client-factory.js';
import { createLogger } from '../utils/logger.js';
import { withErrorHandling } from '../errors.js';
import { withMCPErrorHandling } from '../utils/error-handler.js';
import { createStructuredResponse, createTextResponse } from '../utils/structured-response.js';

const logger = createLogger('handlers/system');

/**
 * Handler for getting SonarQube system health status
 * @param client Optional SonarQube client instance
 * @returns Promise with the health status result
 */
export const handleSonarQubeGetHealth = withMCPErrorHandling(
  async (client: ISonarQubeClient = getDefaultClient()) => {
    logger.debug('Handling get health request');

    const result = await withErrorHandling('Get SonarQube health status', () => client.getHealth());
    logger.info('Successfully retrieved health status', { health: result.health });

    return createStructuredResponse(result);
  }
);

/**
 * Handler for getting SonarQube system status
 * @param client Optional SonarQube client instance
 * @returns Promise with the system status result
 */
export async function handleSonarQubeGetStatus(client: ISonarQubeClient = getDefaultClient()) {
  const result = await client.getStatus();

  return createStructuredResponse(result);
}

/**
 * Handler for pinging SonarQube system
 * @param client Optional SonarQube client instance
 * @returns Promise with the ping result
 */
export async function handleSonarQubePing(client: ISonarQubeClient = getDefaultClient()) {
  const result = await client.ping();

  return createTextResponse(result);
}

```

--------------------------------------------------------------------------------
/src/schemas/components.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from 'zod';
import { stringToNumberTransform } from '../utils/transforms.js';
import { pullRequestSchema } from './common.js';

/**
 * Valid component qualifiers based on SonarQube API
 */
const componentQualifierSchema = z.enum([
  'TRK', // Project
  'DIR', // Directory
  'FIL', // File
  'UTS', // Unit Test
  'BRC', // Branch
  'APP', // Application
  'VW', // View
  'SVW', // Sub-view
  'LIB', // Library
]);

/**
 * Schema for components tool
 */
export const componentsToolSchema = {
  // Search parameters
  query: z.string().optional().describe('Text search query'),
  qualifiers: z
    .array(componentQualifierSchema)
    .optional()
    .describe('Component types: TRK, DIR, FIL, UTS, BRC, APP, VW, SVW, LIB'),
  language: z.string().optional().describe('Programming language filter'),

  // Tree navigation parameters
  component: z.string().optional().describe('Component key for tree navigation'),
  strategy: z.enum(['all', 'children', 'leaves']).optional().describe('Tree traversal strategy'),

  // Show component parameter
  key: z.string().optional().describe('Component key to show details for'),

  // Common parameters
  asc: z
    .union([z.boolean(), z.string().transform((val) => val === 'true')])
    .optional()
    .describe('Sort ascending/descending'),
  ps: z
    .string()
    .optional()
    .transform(stringToNumberTransform)
    .describe('Page size (default: 100, max: 500)'),
  p: z.string().optional().transform(stringToNumberTransform).describe('Page number'),

  // Additional filters
  branch: z.string().optional().describe('Branch name'),
  pullRequest: pullRequestSchema.describe('Pull request ID'),
};

```

--------------------------------------------------------------------------------
/src/handlers/measures.ts:
--------------------------------------------------------------------------------

```typescript
import type {
  ComponentMeasuresParams,
  ComponentsMeasuresParams,
  MeasuresHistoryParams,
  ISonarQubeClient,
} from '../types/index.js';
import { getDefaultClient } from '../utils/client-factory.js';
import { createStructuredResponse } from '../utils/structured-response.js';

/**
 * Handler for getting measures for a specific component
 * @param params Parameters for the component measures request
 * @param client Optional SonarQube client instance
 * @returns Promise with the component measures result
 */
export async function handleSonarQubeComponentMeasures(
  params: ComponentMeasuresParams,
  client: ISonarQubeClient = getDefaultClient()
) {
  const result = await client.getComponentMeasures(params);

  return createStructuredResponse(result);
}

/**
 * Handler for getting measures for multiple components
 * @param params Parameters for the components measures request
 * @param client Optional SonarQube client instance
 * @returns Promise with the components measures result
 */
export async function handleSonarQubeComponentsMeasures(
  params: ComponentsMeasuresParams,
  client: ISonarQubeClient = getDefaultClient()
) {
  const result = await client.getComponentsMeasures(params);

  return createStructuredResponse(result);
}

/**
 * Handler for getting measures history
 * @param params Parameters for the measures history request
 * @param client Optional SonarQube client instance
 * @returns Promise with the measures history result
 */
export async function handleSonarQubeMeasuresHistory(
  params: MeasuresHistoryParams,
  client: ISonarQubeClient = getDefaultClient()
) {
  const result = await client.getMeasuresHistory(params);

  return createStructuredResponse(result);
}

```

--------------------------------------------------------------------------------
/src/transports/stdio.ts:
--------------------------------------------------------------------------------

```typescript
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { ITransport } from './base.js';

/**
 * Extended interface for StdioServerTransport with the connect method.
 * This is a temporary workaround until the MCP SDK types are updated.
 * @deprecated Remove this interface when MCP SDK includes proper connect method typing
 */
interface StdioServerTransportWithConnect extends StdioServerTransport {
  connect: () => Promise<void>;
}

/**
 * STDIO transport implementation for MCP server.
 * This transport uses standard input/output streams for communication.
 */
export class StdioTransport implements ITransport {
  private readonly transport: StdioServerTransportWithConnect;

  constructor() {
    this.transport = new StdioServerTransport() as StdioServerTransportWithConnect;

    // Add the connect method workaround for proper TypeScript compatibility
    // This is a known issue with the current MCP SDK types
    this.transport.connect = () => Promise.resolve();
  }

  /**
   * Connect the STDIO transport to the MCP server.
   *
   * @param server The MCP server instance to connect to
   * @returns Promise that resolves when the connection is established
   */
  async connect(server: Server): Promise<void> {
    await server.connect(this.transport);
  }

  /**
   * Get the name of this transport.
   *
   * @returns 'stdio'
   */
  getName(): string {
    return 'stdio';
  }

  /**
   * Get the underlying StdioServerTransport instance.
   * This is useful for backward compatibility and testing.
   *
   * @returns The underlying STDIO transport
   */
  getUnderlyingTransport(): StdioServerTransport {
    return this.transport;
  }
}

```

--------------------------------------------------------------------------------
/src/types/source-code.ts:
--------------------------------------------------------------------------------

```typescript
import type { SonarQubeIssue } from './issues.js';

/**
 * Interface for source code location parameters
 */
export interface SourceCodeParams {
  key: string;
  from?: number;
  to?: number;
  branch?: string;
  pullRequest?: string;
}

/**
 * Interface for SCM blame parameters
 */
export interface ScmBlameParams {
  key: string;
  from?: number;
  to?: number;
  branch?: string;
  pullRequest?: string;
}

/**
 * Interface for line issue in source code
 */
export interface SonarQubeLineIssue {
  line: number;
  issues: SonarQubeIssue[];
}

/**
 * Interface for SCM author information
 */
export interface SonarQubeScmAuthor {
  revision: string;
  date: string;
  author: string;
}

/**
 * Interface for source code line with annotations
 */
export interface SonarQubeSourceLine {
  line: number;
  code: string;
  scmAuthor: string | undefined;
  scmDate: string | undefined;
  scmRevision: string | undefined;
  duplicated: boolean | undefined;
  isNew: boolean | undefined;
  lineHits: number | undefined;
  conditions: number | undefined;
  coveredConditions: number | undefined;
  highlightedText: string | undefined;
  issues: SonarQubeIssue[] | undefined;
}

/**
 * Interface for source code result
 */
export interface SonarQubeSourceResult {
  component: {
    key: string;
    path: string | undefined;
    qualifier: string;
    name: string;
    longName: string | undefined;
    language: string | undefined;
  };
  sources: SonarQubeSourceLine[];
}

/**
 * Interface for SCM blame result
 */
export interface SonarQubeScmBlameResult {
  component: {
    key: string;
    path?: string;
    qualifier: string;
    name: string;
    longName?: string;
    language?: string;
  };
  sources: {
    [lineNumber: string]: SonarQubeScmAuthor;
  };
}

```

--------------------------------------------------------------------------------
/src/transports/base.ts:
--------------------------------------------------------------------------------

```typescript
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';

/**
 * Base transport interface for MCP server transports.
 * This interface defines the contract that all transport implementations must follow.
 */
export interface ITransport {
  /**
   * Connect the transport to the MCP server.
   * This method should establish the connection and start listening for messages.
   *
   * @param server The MCP server instance to connect to
   * @returns Promise that resolves when the connection is established
   */
  connect(server: Server): Promise<void>;

  /**
   * Get the name of the transport for logging and debugging purposes.
   *
   * @returns The transport name (e.g., 'stdio')
   */
  getName(): string;
}

/**
 * Configuration options for transport initialization.
 */
export interface ITransportConfig {
  /**
   * The type of transport to use.
   */
  type: 'stdio' | 'http';

  /**
   * Optional configuration specific to the transport type.
   */
  options?: Record<string, unknown>;
}

/**
 * HTTP-specific transport configuration.
 */
export interface IHttpTransportConfig extends ITransportConfig {
  type: 'http';
  options?: {
    /**
     * Port to listen on for HTTP requests.
     */
    port?: number;

    /**
     * Allowed hosts for DNS rebinding protection.
     */
    allowedHosts?: string[];

    /**
     * Allowed origins for CORS.
     */
    allowedOrigins?: string[];

    /**
     * Session timeout in milliseconds.
     */
    sessionTimeout?: number;

    /**
     * Enable DNS rebinding protection.
     */
    enableDnsRebindingProtection?: boolean;
  };
}

/**
 * Type guard to check if a transport instance is STDIO transport.
 * This is useful for maintaining backward compatibility.
 */
export function isStdioTransport(transport: unknown): transport is StdioServerTransport {
  return transport instanceof StdioServerTransport;
}

```

--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------

```yaml
# Docker Compose for SonarQube MCP Server (stdio-only)
# Suitable for development and testing with MCP gateways

services:
  sonarqube-mcp-server:
    build: .
    image: sonarqube-mcp-server:1.7.0-stdio
    container_name: sonarqube-mcp-stdio

    # Environment configuration
    environment:
      # SonarQube connection
      SONARQUBE_URL: ${SONARQUBE_URL:-https://sonarcloud.io}
      SONARQUBE_TOKEN: ${SONARQUBE_TOKEN}
      SONARQUBE_ORGANIZATION: ${SONARQUBE_ORGANIZATION}

      # Logging
      LOG_LEVEL: ${LOG_LEVEL:-INFO}
      NODE_ENV: production

    # Stdio transport - no ports needed
    # Network mode can be none for maximum isolation
    network_mode: none

    # Resource limits for stdio operation
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '0.5'
        reservations:
          memory: 256M
          cpus: '0.25'

    # Health check using command execution (no HTTP endpoint)
    healthcheck:
      test: ['CMD', 'node', '-e', 'process.exit(0)']
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s

    # Restart policy
    restart: unless-stopped

    # Security context
    security_opt:
      - no-new-privileges:true
    read_only: true
    tmpfs:
      - /tmp:size=100M,noexec,nosuid,nodev

    # Volume for logs (optional)
    volumes:
      - ./logs:/app/logs

    # Labels for container management
    labels:
      - 'com.sonarqube.mcp.transport=stdio'
      - 'com.sonarqube.mcp.version=1.7.0-stdio'
      - 'com.sonarqube.mcp.description=SonarQube MCP Server - stdio transport only'

  # Example: MCP Gateway integration (commented out - requires actual gateway)
  # mcp-gateway:
  #   image: docker/mcp-gateway:latest  # Hypothetical gateway
  #   container_name: mcp-gateway
  #   ports:
  #     - "8080:8080"
  #   environment:
  #     - MCP_SERVERS=sonarqube-mcp-stdio
  #   depends_on:
  #     - sonarqube-mcp-server
  #   volumes:
  #     - ./gateway-config:/config

```

--------------------------------------------------------------------------------
/src/__tests__/components.test.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
import { resetDefaultClient } from '../utils/client-factory.js';
// Mock environment variables
process.env.SONARQUBE_TOKEN = 'test-token';
process.env.SONARQUBE_URL = 'http://localhost:9000';
process.env.SONARQUBE_ORGANIZATION = 'test-org';
// Save environment variables
const originalEnv = process.env;
describe('Components Handler', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    resetDefaultClient();
    process.env = { ...originalEnv };
  });
  afterEach(() => {
    process.env = originalEnv;
    vi.clearAllMocks();
    resetDefaultClient();
  });
  describe('Integration', () => {
    it('should have components handler exported from index', async () => {
      const module = await import('../index.js');
      expect(module.componentsHandler).toBeDefined();
      expect(typeof module.componentsHandler).toBe('function');
    });
    it('should have handleSonarQubeComponents exported from handlers', async () => {
      const module = await import('../handlers/index.js');
      expect(module.handleSonarQubeComponents).toBeDefined();
      expect(typeof module.handleSonarQubeComponents).toBe('function');
    });
    it('should have ComponentsDomain exported from domains', async () => {
      const module = await import('../domains/index.js');
      expect(module.ComponentsDomain).toBeDefined();
      expect(typeof module.ComponentsDomain).toBe('function');
    });
    it('should have components schemas exported', async () => {
      const module = await import('../schemas/index.js');
      expect(module.componentsToolSchema).toBeDefined();
      expect(typeof module.componentsToolSchema).toBe('object');
    });
    it('should have components types exported', async () => {
      const module = await import('../types/index.js');
      // Check that types are available (will be stripped at runtime but validates imports work)
      expect(module).toBeDefined();
    });
  });
});

```

--------------------------------------------------------------------------------
/.claude/hooks/block-git-no-verify.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node
/**
 * Hook to prevent git commands with --no-verify flag from being executed.
 * This ensures all git hooks and verification steps are properly executed.
 */

import { readFileSync } from 'node:fs';
import process from 'node:process';

interface ToolInput {
  tool_name: string;
  tool_input: {
    command?: string;
    [key: string]: unknown;
  };
}

function main(): void {
  try {
    // Read JSON input from stdin
    const inputData = readFileSync(0, 'utf-8');
    const parsedInput: ToolInput = JSON.parse(inputData);

    // Extract tool information
    const toolName = parsedInput.tool_name ?? '';
    const toolInput = parsedInput.tool_input ?? {};

    // Only process Bash tool calls
    if (toolName !== 'Bash') {
      process.exit(0);
    }

    // Get the command from tool_input
    const command = toolInput.command ?? '';

    // Check if it's a git command
    if (!/\bgit\b/.test(command)) {
      process.exit(0);
    }

    // Remove quoted strings to avoid false positives
    // Remove single-quoted strings
    let cleanedCmd = command.replace(/'[^']*'/g, '');
    // Remove double-quoted strings
    cleanedCmd = cleanedCmd.replace(/"[^"]*"/g, '');

    // Check for --no-verify or -n flag
    const noVerifyPattern = /(^|\s)--no-verify($|=|\s)/;
    const shortNPattern = /(^|\s)-n($|\s)/;

    if (noVerifyPattern.test(cleanedCmd) || shortNPattern.test(cleanedCmd)) {
      // Block with error message (exit code 2)
      // eslint-disable-next-line no-console
      console.error('Error: Git commands with --no-verify flag are not allowed.');
      // eslint-disable-next-line no-console
      console.error('This ensures all git hooks and verification steps are properly executed.');
      // eslint-disable-next-line no-console
      console.error('Please run the git command without the --no-verify flag.');
      process.exit(2);
    }

    // Allow the command to proceed
    process.exit(0);
  } catch {
    // For any errors (including JSON parsing), allow the command (fail open)
    process.exit(0);
  }
}

main();

```

--------------------------------------------------------------------------------
/src/types/components.ts:
--------------------------------------------------------------------------------

```typescript
import type { SonarQubeComponent } from './issues.js';

/**
 * Component qualifier types based on SonarQube API
 */
export type ComponentQualifier =
  | 'TRK' // Project
  | 'DIR' // Directory
  | 'FIL' // File
  | 'UTS' // Unit Test
  | 'BRC' // Branch
  | 'APP' // Application
  | 'VW' // View
  | 'SVW' // Sub-view
  | 'LIB'; // Library

/**
 * Result of component search operation
 */
export interface ComponentsResult {
  components: SonarQubeComponent[];
  paging: {
    pageIndex: number;
    pageSize: number;
    total: number;
  };
}

/**
 * Result of component tree navigation
 */
export interface ComponentsTreeResult {
  components: SonarQubeComponent[];
  baseComponent: SonarQubeComponent | undefined;
  paging: {
    pageIndex: number;
    pageSize: number;
    total: number;
  };
}

/**
 * Result of component show operation
 */
export interface ComponentShowResult {
  component: SonarQubeComponent;
  ancestors: SonarQubeComponent[];
}

/**
 * Parameters for searching components
 */
export interface ComponentsSearchParams {
  query?: string;
  qualifiers?: ComponentQualifier[];
  language?: string;
  page?: number;
  pageSize?: number;
}

/**
 * Parameters for navigating component tree
 */
export interface ComponentsTreeParams {
  component: string;
  strategy?: 'all' | 'children' | 'leaves';
  qualifiers?: ComponentQualifier[];
  sort?: 'name' | 'path' | 'qualifier';
  asc?: boolean;
  page?: number;
  pageSize?: number;
  branch?: string;
  pullRequest?: string;
}

/**
 * Parameters for showing component details
 */
export interface ComponentShowParams {
  key: string;
  branch?: string;
  pullRequest?: string;
}

/**
 * Combined parameters for components action
 */
export interface ComponentsParams {
  // Search parameters
  query?: string;
  qualifiers?: ComponentQualifier[];
  language?: string;

  // Tree navigation parameters
  component?: string;
  strategy?: 'all' | 'children' | 'leaves';

  // Show component parameter
  key?: string;

  // Common parameters
  asc?: boolean;
  ps?: number;
  p?: number;
  branch?: string;
  pullRequest?: string;
}

```

--------------------------------------------------------------------------------
/src/utils/retry.ts:
--------------------------------------------------------------------------------

```typescript
import { createLogger } from './logger.js';

const logger = createLogger('Retry');

export interface RetryOptions {
  maxAttempts?: number;
  initialDelay?: number;
  maxDelay?: number;
  backoffMultiplier?: number;
  shouldRetry?: (error: Error, attempt: number) => boolean;
}

const DEFAULT_OPTIONS: Required<RetryOptions> = {
  maxAttempts: 3,
  initialDelay: 1000,
  maxDelay: 10000,
  backoffMultiplier: 2,
  shouldRetry: (error: Error) => {
    // Retry on network errors and 5xx server errors
    const message = error.message.toLowerCase();
    return (
      message.includes('econnrefused') ||
      message.includes('etimedout') ||
      message.includes('enotfound') ||
      message.includes('econnreset') ||
      message.includes('socket hang up') ||
      (message.includes('50') && !message.includes('40')) // 5xx errors
    );
  },
};

/**
 * Retry a function with exponential backoff
 */
export async function withRetry<T>(fn: () => Promise<T>, options: RetryOptions = {}): Promise<T> {
  const opts = { ...DEFAULT_OPTIONS, ...options };
  let lastError: Error | null = null;

  for (let attempt = 1; attempt <= opts.maxAttempts; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error as Error;

      if (attempt === opts.maxAttempts || !opts.shouldRetry(lastError, attempt)) {
        throw lastError;
      }

      const delay = Math.min(
        opts.initialDelay * Math.pow(opts.backoffMultiplier, attempt - 1),
        opts.maxDelay
      );

      logger.warn(`Attempt ${attempt} failed, retrying in ${delay}ms`, {
        error: lastError.message,
        attempt,
        nextDelay: delay,
      });

      await new Promise((resolve) => setTimeout(resolve, delay));
    }
  }

  throw new Error(`Max retry attempts reached: ${lastError?.message}`);
}

/**
 * Create a retryable version of a function
 */
export function makeRetryable<T extends unknown[], R>(
  fn: (...args: T) => Promise<R>,
  options: RetryOptions = {}
): (...args: T) => Promise<R> {
  return async (...args: T): Promise<R> => {
    return withRetry(() => fn(...args), options);
  };
}

```

--------------------------------------------------------------------------------
/src/utils/pattern-matcher.ts:
--------------------------------------------------------------------------------

```typescript
import { createLogger } from './logger.js';

const logger = createLogger('PatternMatcher');

/**
 * A safe pattern matcher that uses glob-style patterns instead of regex.
 * This eliminates regex injection vulnerabilities while providing
 * sufficient flexibility for matching users and issuers.
 *
 * Supported patterns:
 * - * matches zero or more characters
 * - ? matches exactly one character
 * - All other characters match literally
 *
 * Examples:
 * - *@example.com matches any email at example.com
 * - user-? matches user-1, user-a, etc.
 * - https://*.auth.com matches any subdomain of auth.com
 */
export class PatternMatcher {
  private readonly pattern: string;
  private readonly regex: RegExp;

  constructor(pattern: string) {
    this.pattern = pattern;
    this.regex = this.globToRegex(pattern);
  }

  /**
   * Convert a glob pattern to a safe regex
   */
  private globToRegex(pattern: string): RegExp {
    // Escape all regex special characters except * and ?
    const escaped = pattern
      .replaceAll(/[\\^$.()|[\]{}+]/g, '\\$&') // Escape regex special chars
      .replaceAll('*', '.*') // * matches any sequence
      .replaceAll('?', '.'); // ? matches any single character

    // Create regex with anchors for full string matching
    return new RegExp(`^${escaped}$`);
  }

  /**
   * Test if a string matches the pattern
   */
  test(value: string): boolean {
    return this.regex.test(value);
  }

  /**
   * Get the original pattern
   */
  getPattern(): string {
    return this.pattern;
  }

  /**
   * Create a pattern matcher from a string, with error handling
   */
  static create(pattern: string, context: string): PatternMatcher | undefined {
    try {
      const matcher = new PatternMatcher(pattern);
      logger.debug('Created pattern matcher', {
        context,
        pattern,
        regex: matcher.regex.source,
      });
      return matcher;
    } catch (error) {
      logger.warn('Failed to create pattern matcher', {
        pattern,
        context,
        error: error instanceof Error ? error.message : 'Unknown error',
      });
      return undefined;
    }
  }
}

```

--------------------------------------------------------------------------------
/docs/architecture/decisions/0002-use-node-js-with-typescript.md:
--------------------------------------------------------------------------------

```markdown
# 2. Use Node.js with TypeScript

Date: 2025-06-13

## Status

Accepted

## Context

The SonarQube MCP server needs to be implemented in a technology stack that provides:

- Strong type safety to prevent runtime errors
- Rich ecosystem for HTTP clients and server frameworks
- Easy distribution mechanism for end users
- Good developer experience with modern tooling
- Cross-platform compatibility
- Quick feedback loop during development

Alternative languages like Rust were considered but would introduce longer compilation times and a steeper learning curve.

## Decision

We will implement the server using Node.js with TypeScript, managed by pnpm.

Key aspects of this decision:

- **Runtime**: Node.js provides a stable, well-supported JavaScript runtime
- **Language**: TypeScript adds static typing, improving code reliability and developer experience
- **Package Manager**: pnpm for efficient dependency management
- **Distribution**: npm/npx for easy installation and execution by end users

## Consequences

### Positive

- **Quick Feedback Loop**: Fast compilation and hot-reloading enable rapid development iterations compared to compiled languages like Rust
- **Type Safety**: TypeScript's static typing catches errors at compile time, reducing runtime bugs
- **Developer Experience**: Modern IDE support, auto-completion, and refactoring tools
- **Ecosystem**: Access to npm's vast collection of packages for HTTP clients, testing, and utilities
- **Distribution**: Users can easily install and run the server via `npx @burgess01/sonarqube-mcp-server`
- **Community**: Large community support for both Node.js and TypeScript
- **Cross-platform**: Works on Windows, macOS, and Linux without modification

### Negative

- **Build Step**: TypeScript requires compilation to JavaScript before execution
- **Learning Curve**: Developers need familiarity with TypeScript's type system
- **Bundle Size**: TypeScript adds some overhead compared to plain JavaScript

### Neutral

- **Performance**: Node.js provides adequate performance for an MCP server that primarily makes HTTP requests
- **Maintenance**: Regular updates needed for Node.js, TypeScript, and dependencies

```

--------------------------------------------------------------------------------
/docs/architecture/decisions/0004-use-sonarqube-web-api-client-for-all-sonarqube-interactions.md:
--------------------------------------------------------------------------------

```markdown
# 4. Use SonarQube Web API Client for all SonarQube interactions

Date: 2025-06-13

## Status

Accepted

## Context

The SonarQube MCP server needs to interact with SonarQube's REST API to provide functionality for code quality analysis, issue management, and metrics retrieval. We need a reliable and maintainable approach for making these API calls that:

- Handles authentication consistently
- Manages API versioning and endpoint changes
- Provides type safety for API requests and responses
- Reduces boilerplate code for common operations
- Offers a clear abstraction layer between our server and SonarQube's API

## Decision

We will use the `sonarqube-web-api-client` library for all interactions with SonarQube. A custom `SonarQubeClient` class will wrap this API client to:

1. Encapsulate authentication logic (API tokens)
2. Provide higher-level methods for common operations (projects, issues, metrics, etc.)
3. Handle error responses consistently
4. Abstract away the complexity of the underlying API client

All SonarQube API interactions will go through this client wrapper rather than making direct HTTP requests or using the API client library directly in action implementations.

## Consequences

### Positive

- **Consistency**: All API interactions follow the same pattern and error handling approach
- **Maintainability**: Changes to authentication or API endpoints can be handled in one place
- **Type Safety**: The library provides TypeScript types for API requests and responses
- **Reduced Complexity**: Action implementations focus on business logic rather than HTTP details
- **Testability**: The client wrapper can be easily mocked for unit testing

### Negative

- **Dependency**: We're tied to the `sonarqube-web-api-client` library's maintenance and updates
- **Abstraction Overhead**: Adding another layer of abstraction may hide some API capabilities
- **Learning Curve**: Developers need to understand both our wrapper and the underlying library

### Neutral

- The client wrapper pattern is a common architectural approach that most developers will be familiar with
- Future changes to the SonarQube API may require updates to both the library and our wrapper

```

--------------------------------------------------------------------------------
/docs/architecture/decisions/0009-file-based-logging-to-avoid-stdio-conflicts.md:
--------------------------------------------------------------------------------

```markdown
# 9. File-based logging to avoid STDIO conflicts

Date: 2025-06-13

## Status

Accepted

## Context

The Model Context Protocol (MCP) uses standard input/output (STDIO) streams for communication between the client and server. This creates a fundamental conflict with traditional logging approaches that write to stdout or stderr.

When an MCP server writes anything to stdout, it must be valid JSON-RPC messages conforming to the MCP protocol. Any non-protocol output (such as log messages) written to stdout would corrupt the communication stream and cause protocol errors.

Similarly, stderr output could interfere with the client's ability to properly parse and handle MCP messages, potentially causing connection failures or undefined behavior.

## Decision

We will implement file-based logging for all diagnostic and debugging output in the MCP server.

The server will:

- Write all log messages to a file specified by the `LOG_FILE` environment variable
- Default to no logging if `LOG_FILE` is not set
- Never write log messages to stdout or stderr
- Ensure all stdout output consists only of valid MCP protocol messages

## Consequences

### Positive

- **Protocol integrity**: The MCP communication stream remains clean and uncorrupted
- **Reliable communication**: Prevents protocol errors caused by interleaved log messages
- **Debugging capability**: Developers can still access detailed logs for troubleshooting
- **Configuration flexibility**: Log file location can be customized via environment variable

### Negative

- **No console logging**: Developers cannot see logs in real-time in the console during development
- **File management**: Log files need to be managed (rotation, cleanup) to prevent disk space issues
- **Additional setup**: Developers must know where to find log files for debugging
- **Potential permissions issues**: The server must have write permissions to the log file location

### Mitigation

- Document the log file location clearly in README and error messages
- Consider implementing log rotation to manage file size
- Provide clear error messages if log file cannot be created
- Include log file location in any error output that is part of the protocol

```

--------------------------------------------------------------------------------
/docs/architecture/decisions/0010-use-stdio-transport-for-mcp-communication.md:
--------------------------------------------------------------------------------

```markdown
# 10. Use STDIO transport for MCP communication

Date: 2025-06-13

## Status

Accepted

## Context

The Model Context Protocol (MCP) server needs a transport mechanism to communicate with MCP clients. The transport layer determines how the server receives requests and sends responses.

Several transport options are available:

- STDIO (Standard Input/Output)
- WebSocket
- HTTP
- Named pipes

The server needs to be easily invokable from various environments (node, npx, Docker) without requiring complex network configuration or exposing network ports.

## Decision

We will use StdioServerTransport for MCP communication, with a dummy `connect()` method for compatibility with the MCP protocol requirements.

The server communicates with clients over standard input/output streams, making it a CLI tool that can be launched as a subprocess by MCP clients.

## Consequences

### Positive

- **Simple invocation**: The server can be launched via `node`, `npx`, or `docker run` commands without additional configuration
- **No network exposure**: Eliminates security concerns about open network ports or unauthorized access
- **Cross-platform compatibility**: STDIO works consistently across all operating systems
- **Process isolation**: Each client gets its own server process with isolated state
- **Easy debugging**: STDIO communication can be easily logged and inspected
- **Container-friendly**: Works seamlessly in Docker containers without port mapping

### Negative

- **Single client per process**: Each MCP client requires its own server process
- **No remote access**: Clients must run on the same machine or use SSH/container forwarding
- **Process lifecycle management**: The client is responsible for starting and stopping the server process
- **Resource overhead**: Multiple clients mean multiple Node.js processes

### Implementation Notes

- The dummy `connect()` method is required because the MCP protocol expects all transports to implement this interface, even though STDIO doesn't need an explicit connection step
- The server reads JSON-RPC messages from stdin and writes responses to stdout
- Error messages and logs should be written to stderr to avoid interfering with the JSON-RPC communication

```

--------------------------------------------------------------------------------
/src/__tests__/dependency-injection.test.ts:
--------------------------------------------------------------------------------

```typescript
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { createDefaultClient } from '../index.js';
import { createSonarQubeClient } from '../sonarqube.js';
// Save original environment variables
const originalEnv = process.env;
describe('Client Configuration Tests', () => {
  beforeEach(() => {
    vi.resetModules();
    process.env = { ...originalEnv };
    process.env.SONARQUBE_TOKEN = 'test-token';
    process.env.SONARQUBE_URL = 'http://localhost:9000';
    process.env.SONARQUBE_ORGANIZATION = 'test-org';
  });
  afterEach(() => {
    process.env = originalEnv;
    vi.clearAllMocks();
  });
  describe('Client Factory Functions', () => {
    it('should create a client with default parameters', () => {
      // Test the factory function
      const client = createSonarQubeClient('test-token');
      expect(client).toBeDefined();
    });
    it('should create a default client using environment variables', () => {
      // Test the default client creation function
      const client = createDefaultClient();
      expect(client).toBeDefined();
    });
    it('should create a client with custom base URL and organization', () => {
      const customUrl = 'https://custom-sonar.example.com';
      const customOrg = 'custom-org';
      const client = createSonarQubeClient('test-token', customUrl, customOrg);
      expect(client).toBeDefined();
    });
    it('should handle null organization parameter', () => {
      const client = createSonarQubeClient('test-token', undefined, null);
      expect(client).toBeDefined();
    });
  });
  describe('Environment Variable Configuration', () => {
    it('should use environment variables for default client creation', () => {
      process.env.SONARQUBE_TOKEN = 'env-token';
      process.env.SONARQUBE_URL = 'https://env-sonar.example.com';
      process.env.SONARQUBE_ORGANIZATION = 'env-org';
      const client = createDefaultClient();
      expect(client).toBeDefined();
    });
    it('should handle missing optional environment variables', () => {
      process.env.SONARQUBE_TOKEN = 'env-token';
      delete process.env.SONARQUBE_URL;
      delete process.env.SONARQUBE_ORGANIZATION;
      const client = createDefaultClient();
      expect(client).toBeDefined();
    });
  });
});

```

--------------------------------------------------------------------------------
/src/types/measures.ts:
--------------------------------------------------------------------------------

```typescript
import type { PaginationParams } from './common.js';
import type { SonarQubeMetric } from './metrics.js';

/**
 * Interface for component measures parameters
 */
export interface ComponentMeasuresParams {
  component: string;
  metricKeys: string[];
  additionalFields?: string[];
  branch?: string;
  pullRequest?: string;
  period?: string;
}

/**
 * Interface for components measures parameters
 */
export interface ComponentsMeasuresParams extends PaginationParams {
  componentKeys: string[] | string;
  metricKeys: string[] | string;
  additionalFields?: string[];
  branch?: string;
  pullRequest?: string;
  period?: string;
}

/**
 * Interface for measures history parameters
 */
export interface MeasuresHistoryParams extends PaginationParams {
  component: string;
  metrics: string[];
  from?: string;
  to?: string;
  branch?: string;
  pullRequest?: string;
}

/**
 * Interface for SonarQube measure
 */
export interface SonarQubeMeasure {
  metric: string;
  value?: string;
  period?: {
    index: number;
    value: string;
  };
  bestValue?: boolean;
}

/**
 * Interface for SonarQube measure component
 */
export interface SonarQubeMeasureComponent {
  key: string;
  name: string;
  qualifier: string;
  measures: SonarQubeMeasure[];
  periods?: Array<{
    index: number;
    mode: string;
    date: string;
    parameter?: string;
  }>;
}

/**
 * Interface for SonarQube component with measures result
 */
export interface SonarQubeComponentMeasuresResult {
  component: SonarQubeMeasureComponent;
  metrics: SonarQubeMetric[];
  period?: {
    index: number;
    mode: string;
    date: string;
    parameter?: string;
  };
}

/**
 * Interface for SonarQube components with measures result
 */
export interface SonarQubeComponentsMeasuresResult {
  components: SonarQubeMeasureComponent[];
  metrics: SonarQubeMetric[];
  paging: {
    pageIndex: number;
    pageSize: number;
    total: number;
  };
  period?: {
    index: number;
    mode: string;
    date: string;
    parameter?: string;
  };
}

/**
 * Interface for SonarQube measures history result
 */
export interface SonarQubeMeasuresHistoryResult {
  paging: {
    pageIndex: number;
    pageSize: number;
    total: number;
  };
  measures: {
    metric: string;
    history: {
      date: string;
      value?: string;
    }[];
  }[];
}

```

--------------------------------------------------------------------------------
/.github/workflows/codeql.yml:
--------------------------------------------------------------------------------

```yaml
# =============================================================================
# WORKFLOW: CodeQL Security Analysis
# PURPOSE: Continuous security analysis for the default branch and pull requests
# TRIGGERS: Push to main, Pull requests to main
# OUTPUTS: Security findings uploaded to GitHub Security tab
# =============================================================================

name: CodeQL

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  schedule:
    # Run daily at 00:00 UTC to catch new vulnerabilities
    - cron: '0 0 * * *'

# SECURITY: Required permissions for CodeQL analysis
permissions:
  actions: read # Read workflow metadata
  contents: read # Read source code
  security-events: write # Upload security findings to Security tab

jobs:
  analyze:
    name: Analyze Code
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0 # Full history for accurate analysis

      - name: Install pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 10.17.0
          run_install: false
          standalone: true

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: pnpm

      - name: Install dependencies
        # Dependencies needed for accurate CodeQL analysis
        run: pnpm install --frozen-lockfile

      # =============================================================================
      # CODEQL STATIC ANALYSIS
      # Scans for security vulnerabilities in source code
      # =============================================================================

      - name: Initialize CodeQL
        # Setup CodeQL for JavaScript/TypeScript analysis
        # Detects: XSS, SQL injection, path traversal, command injection, etc.
        uses: github/codeql-action/init@v3
        with:
          languages: javascript-typescript
          # Optionally specify additional queries to run
          # queries: security-extended,security-and-quality

      - name: Perform CodeQL Analysis
        # Analyze code and upload results to Security tab
        # Results viewable at: Security > Code scanning alerts
        uses: github/codeql-action/analyze@v3
        with:
          category: '/language:javascript-typescript'

```

--------------------------------------------------------------------------------
/src/domains/system.ts:
--------------------------------------------------------------------------------

```typescript
import type { SonarQubeHealthStatus, SonarQubeSystemStatus } from '../types/index.js';
import { BaseDomain } from './base.js';

/**
 * Domain module for system-related operations
 */
export class SystemDomain extends BaseDomain {
  /**
   * Gets the health status of the SonarQube instance using V2 API
   *
   * The V2 API response structure differs from V1:
   * - V2 returns `status` field instead of `health`
   * - V2 includes optional `nodes` array for clustered setups
   * - Each node can have its own `causes` array for health issues
   *
   * This method transforms the V2 response to maintain backward compatibility
   * with the existing SonarQubeHealthStatus interface.
   *
   * @returns Promise with the health status containing aggregated causes from all nodes
   */
  async getHealth(): Promise<SonarQubeHealthStatus> {
    return this.tracedApiCall('system/health', async () => {
      const response = await this.webApiClient.system.getHealthV2();
      const causes = this.extractCausesFromNodes(response.nodes);

      return {
        health: response.status,
        causes,
      };
    });
  }

  /**
   * Extracts and aggregates causes from all nodes in a clustered SonarQube setup
   *
   * @param nodes - Optional array of nodes from V2 health API response
   * @returns Array of all causes from all nodes, or empty array if no nodes/causes
   * @private
   */
  private extractCausesFromNodes(nodes?: Array<{ causes?: string[] }>): string[] {
    if (!nodes) {
      return [];
    }

    const causes: string[] = [];
    for (const node of nodes) {
      if (node.causes) {
        causes.push(...node.causes);
      }
    }

    return causes;
  }

  /**
   * Gets the system status of the SonarQube instance
   * @returns Promise with the system status
   */
  async getStatus(): Promise<SonarQubeSystemStatus> {
    return this.tracedApiCall('system/status', async () => {
      const response = await this.webApiClient.system.status();
      return {
        id: response.id,
        version: response.version,
        status: response.status,
      };
    });
  }

  /**
   * Pings the SonarQube instance
   * @returns Promise with the ping response
   */
  async ping(): Promise<string> {
    return this.tracedApiCall('system/ping', async () => {
      const response = await this.webApiClient.system.ping();
      return response;
    });
  }
}

```

--------------------------------------------------------------------------------
/docs/architecture/decisions/0007-support-multiple-authentication-methods-for-sonarqube.md:
--------------------------------------------------------------------------------

```markdown
# 7. Support Multiple Authentication Methods for SonarQube

Date: 2025-06-13

## Status

Accepted

## Context

SonarQube offers different deployment models (SonarQube Server and SonarCloud) that use different authentication mechanisms:

- SonarQube Server typically uses user tokens or username/password authentication
- SonarCloud uses bearer tokens
- Some installations may use system-level passcodes for authentication

To provide a flexible MCP server that works across all SonarQube deployment scenarios, we need to support multiple authentication methods.

## Decision

We will support three authentication methods for SonarQube API access:

1. **Bearer Token Authentication** (preferred)
   - Uses the `SONARQUBE_TOKEN` environment variable
   - Sent as `Authorization: Bearer <token>` header
   - Primary method for SonarCloud and modern SonarQube Server installations

2. **HTTP Basic Authentication**
   - Uses `SONARQUBE_USERNAME` and `SONARQUBE_PASSWORD` environment variables
   - Sent as `Authorization: Basic <base64(username:password)>` header
   - Fallback for older SonarQube Server installations

3. **System Passcode Authentication**
   - Uses the `SONARQUBE_PASSCODE` environment variable
   - Sent as `X-Sonar-Passcode` header
   - For system-level operations on certain SonarQube Server configurations

The authentication method is automatically selected based on which environment variables are present, with bearer token authentication taking precedence.

## Consequences

### Positive

- **Broad compatibility**: The MCP server works with all SonarQube deployment models
- **User flexibility**: Administrators can choose the authentication method that best fits their security policies
- **Future-proof**: Supporting token-based authentication aligns with modern security practices
- **Backward compatibility**: HTTP Basic auth ensures older installations remain supported

### Negative

- **Configuration complexity**: Users need to understand which authentication method to use for their setup
- **Security considerations**: Supporting multiple auth methods increases the attack surface
- **Maintenance burden**: Each authentication method needs to be tested and maintained

### Mitigation

- Clear documentation explaining which authentication method to use for different SonarQube deployments
- Security warnings in documentation about the relative security of each method
- Automated tests covering all authentication scenarios

```

--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------

```dockerfile
# Build stage - compile TypeScript to JavaScript
FROM node:22-alpine AS builder

WORKDIR /app

# Copy package files
COPY package.json pnpm-lock.yaml ./

# Install pnpm
RUN npm install -g [email protected]

# Install ALL dependencies (including dev) needed for build
RUN pnpm install --frozen-lockfile --ignore-scripts

# Copy source code and build configuration
COPY src/ ./src/
COPY tsconfig.json tsconfig.build.json ./

# Build the application
RUN pnpm run build

# Production dependencies stage - prepare clean node_modules
FROM node:22-alpine AS deps

WORKDIR /app

# Copy package files
COPY package.json pnpm-lock.yaml ./

# Install pnpm
RUN npm install -g [email protected]

# Install ONLY production dependencies
RUN pnpm install --prod --frozen-lockfile --ignore-scripts

# Production stage - minimal stdio-only runtime
FROM node:22-alpine

# Update OpenSSL to fix CVE-2025-9230, CVE-2025-9231, CVE-2025-9232
# Upgrade libcrypto3 and libssl3 from 3.5.1-r0 to 3.5.4-r0
RUN apk update && \
    apk upgrade --no-cache libcrypto3 libssl3 && \
    rm -rf /var/cache/apk/*

# Create non-root user upfront
RUN addgroup -g 1001 nodejs && \
    adduser -S -u 1001 -G nodejs nodejs

WORKDIR /app

# Copy production dependencies from deps stage (clean, no dev deps)
COPY --from=deps /app/node_modules ./node_modules

# Copy package.json for metadata
COPY --from=deps /app/package.json ./package.json

# Copy built application from builder stage
COPY --from=builder /app/dist ./dist

# Set default environment for production
ENV NODE_ENV=production
ENV LOG_LEVEL=INFO
# Default to stdio transport, can be overridden at runtime
ENV MCP_TRANSPORT=stdio

# Create logs directory with proper permissions
RUN mkdir -p logs && \
    chown -R nodejs:nodejs /app

# Switch to non-root user
USER nodejs

# Expose port for HTTP transport (ignored when using stdio)
EXPOSE 3000

# Health check for HTTP mode (no-op for stdio mode)
# Uses node's built-in http module for more secure health checking
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD if [ "$MCP_TRANSPORT" = "http" ]; then \
        node -e "require('http').get('http://127.0.0.1:3000/health', (res) => { \
          if (res.statusCode === 200) { process.exit(0); } else { process.exit(1); } \
        }).on('error', () => { process.exit(1); });" || exit 1; \
      else \
        exit 0; \
      fi

# Start the server with optimized flags
CMD ["node", "--experimental-specifier-resolution=node", "--max-old-space-size=512", "dist/index.js"] 
```

--------------------------------------------------------------------------------
/docs/architecture/decisions/0008-use-environment-variables-for-configuration.md:
--------------------------------------------------------------------------------

```markdown
# 8. Use environment variables for configuration

Date: 2025-06-13

## Status

Accepted

## Context

The SonarQube MCP server needs to be configured with various settings including:

- SonarQube server URL
- Authentication credentials (token or username/password)
- Organization key (for SonarCloud)
- Log file path
- Other operational settings

These configuration values need to be:

- Easily changeable without modifying code
- Secure (especially credentials)
- Container-friendly for modern deployment scenarios
- Simple to manage across different environments (development, staging, production)

## Decision

We will use environment variables exclusively for all configuration settings. Key settings will include:

- `SONARQUBE_URL`: The base URL of the SonarQube server
- `SONARQUBE_TOKEN`: Authentication token (preferred over username/password)
- `SONARQUBE_USERNAME` and `SONARQUBE_PASSWORD`: Alternative authentication method
- `SONARQUBE_ORGANIZATION`: Organization key for SonarCloud
- `MCP_LOG_FILE`: Path for log file output

No configuration values will be hard-coded in the source code. Default values may be provided where appropriate, but all settings must be overridable via environment variables.

## Consequences

### Positive

- **Security**: Credentials are kept out of the codebase and can be managed through secure environment variable management systems
- **Container-friendly**: Environment variables are the standard way to configure containerized applications
- **Simplicity**: No need for configuration file parsing or management
- **Flexibility**: Easy to change settings without rebuilding or modifying the application
- **12-Factor App compliance**: Follows the third factor of the twelve-factor app methodology
- **Platform agnostic**: Works consistently across different operating systems and deployment platforms

### Negative

- **Limited structure**: Environment variables are flat key-value pairs, making complex nested configuration more challenging
- **Type safety**: All environment variables are strings, requiring parsing and validation
- **Discovery**: Users need documentation to know which environment variables are available
- **No comments**: Unlike configuration files, environment variables cannot include inline documentation

### Mitigation

- Provide comprehensive documentation of all environment variables
- Implement robust validation and helpful error messages for missing or invalid configuration
- Consider supporting a `.env` file for local development convenience
- Log configuration values (excluding secrets) at startup for debugging

```

--------------------------------------------------------------------------------
/src/types/hotspots.ts:
--------------------------------------------------------------------------------

```typescript
import type { PaginationParams, SeverityLevel } from './common.js';

/**
 * Interface for hotspot search parameters
 */
export interface HotspotSearchParams extends PaginationParams {
  projectKey?: string;
  branch?: string;
  pullRequest?: string;
  status?: 'TO_REVIEW' | 'REVIEWED';
  resolution?: 'FIXED' | 'SAFE';
  files?: string[];
  assignedToMe?: boolean;
  sinceLeakPeriod?: boolean;
  inNewCodePeriod?: boolean;
}

/**
 * Interface for security hotspot
 */
export interface SonarQubeHotspot {
  key: string;
  component: string;
  project: string;
  securityCategory: string;
  vulnerabilityProbability: SeverityLevel;
  status: 'TO_REVIEW' | 'REVIEWED';
  resolution?: 'FIXED' | 'SAFE';
  line: number;
  message: string;
  assignee?: string;
  author?: string;
  creationDate: string;
  updateDate: string;
  textRange?: {
    startLine: number;
    endLine: number;
    startOffset: number;
    endOffset: number;
  };
  flows?: Array<{
    locations: Array<{
      component: string;
      textRange: {
        startLine: number;
        endLine: number;
        startOffset: number;
        endOffset: number;
      };
      msg: string;
    }>;
  }>;
  ruleKey?: string;
}

/**
 * Interface for hotspot search result
 */
export interface SonarQubeHotspotSearchResult {
  hotspots: SonarQubeHotspot[];
  components:
    | Array<{
        key: string;
        qualifier: string;
        name: string;
        longName: string | undefined;
        path: string | undefined;
      }>
    | undefined;
  paging: {
    pageIndex: number;
    pageSize: number;
    total: number;
  };
}

/**
 * Interface for hotspot details
 */
export interface SonarQubeHotspotDetails extends SonarQubeHotspot {
  rule: {
    key: string;
    name: string;
    securityCategory: string;
    vulnerabilityProbability: SeverityLevel;
  };
  changelog:
    | Array<{
        user: string;
        userName: string | undefined;
        creationDate: string;
        diffs: Array<{
          key: string;
          oldValue: string | undefined;
          newValue: string | undefined;
        }>;
      }>
    | undefined;
  comment:
    | Array<{
        key: string;
        login: string;
        htmlText: string;
        markdown: string | undefined;
        createdAt: string;
      }>
    | undefined;
  users:
    | Array<{
        login: string;
        name: string;
        active: boolean;
      }>
    | undefined;
}

/**
 * Interface for hotspot status update parameters
 */
export interface HotspotStatusUpdateParams {
  hotspot: string;
  status: 'TO_REVIEW' | 'REVIEWED';
  resolution?: 'FIXED' | 'SAFE';
  comment?: string;
}

```

--------------------------------------------------------------------------------
/docs/architecture/decisions/0005-domain-driven-design-of-sonarqube-modules.md:
--------------------------------------------------------------------------------

```markdown
# 5. Domain-driven design of SonarQube modules

Date: 2025-06-13

## Status

Accepted

## Context

The SonarQube API surface is extensive, covering various aspects of code quality management including projects, issues, metrics, measures, quality gates, security hotspots, and system administration. Managing all these API endpoints in a single monolithic client class would lead to:

- A large, difficult-to-maintain codebase
- Unclear separation of concerns
- Difficulty in finding and understanding specific functionality
- Challenges in testing individual API areas
- Risk of naming conflicts and confusion between similar operations

## Decision

We will organize the SonarQube client functionality using a domain-driven design approach, where each major API area is encapsulated in its own domain class:

- `ProjectsDomain`: Handles project listing and management
- `IssuesDomain`: Manages code issues and their lifecycle (search, comment, assign, resolve, etc.)
- `MetricsDomain`: Provides access to available metrics
- `MeasuresDomain`: Retrieves component measures and their history
- `QualityGatesDomain`: Manages quality gates and their status
- `HotspotsDomain`: Handles security hotspots
- `SourceDomain`: Provides access to source code and SCM information
- `SystemDomain`: Handles system health and status checks

Each domain class:

- Encapsulates all API methods related to its specific area
- Has its own dedicated test file
- Can evolve independently without affecting other domains
- Provides a clear, focused interface for its functionality

The main `SonarQubeClient` class acts as a facade, instantiating and providing access to all domain classes through properties.

## Consequences

### Positive

- **Clear separation of concerns**: Each domain has a well-defined responsibility
- **Improved maintainability**: Changes to one API area don't affect others
- **Better discoverability**: Developers can easily find functionality by domain
- **Focused testing**: Each domain can be tested in isolation
- **Scalability**: New domains can be added without modifying existing code
- **Type safety**: Domain-specific types and interfaces can be co-located with their domain

### Negative

- **Initial complexity**: More files and classes to manage
- **Potential duplication**: Some common functionality might be duplicated across domains
- **Navigation overhead**: Developers need to know which domain contains specific functionality

### Mitigation

- Use clear, consistent naming conventions for domains
- Document the responsibility of each domain in its class documentation
- Share common functionality through utility functions or base classes where appropriate

```

--------------------------------------------------------------------------------
/docs/architecture/decisions/0003-adopt-model-context-protocol-for-sonarqube-integration.md:
--------------------------------------------------------------------------------

```markdown
# 3. Adopt Model Context Protocol for SonarQube Integration

Date: 2025-06-13

## Status

Accepted

## Context

We need to integrate SonarQube's code quality and security analysis capabilities with AI assistants like Claude. This integration should allow AI clients to programmatically access SonarQube data and perform operations through a standardized interface.

The Model Context Protocol (MCP) is emerging as a standard protocol for AI assistants to interact with external tools and data sources. It provides:

- A standardized way to expose tools to AI assistants
- Built-in support for bidirectional communication
- A growing ecosystem of compatible AI clients
- SDKs that simplify server implementation

## Decision

We will adopt the Model Context Protocol (MCP) framework as the foundation for our SonarQube integration. Specifically:

1. The application will be designed as an MCP server using the `@modelcontextprotocol/sdk` package
2. The MCP server (implemented in `index.ts`) will register a comprehensive suite of SonarQube tools
3. We will use the Stdio transport for communication between the MCP server and AI clients
4. Each SonarQube operation will be exposed as a distinct MCP tool with proper input validation and error handling

## Consequences

### Positive Consequences

- **Standardization**: Using MCP ensures compatibility with any AI client that supports the protocol, not just Claude
- **Simplified Integration**: AI clients can discover and use SonarQube tools without custom integration code
- **Tool Discovery**: MCP's tool registration system allows AI clients to automatically discover available SonarQube operations
- **Type Safety**: The MCP SDK provides TypeScript support for defining tool inputs and outputs
- **Future-Proof**: As MCP evolves and gains adoption, our integration will benefit from ecosystem improvements

### Negative Consequences

- **Protocol Dependency**: We're tied to the MCP specification and any breaking changes it might introduce
- **Limited Transport Options**: While Stdio transport works well for local integrations, it may not be suitable for all deployment scenarios
- **Learning Curve**: Developers need to understand MCP concepts and patterns to maintain the integration
- **Debugging Complexity**: Troubleshooting issues requires understanding both SonarQube API and MCP protocol layers

### Mitigation Strategies

- Abstract the transport layer to allow future support for HTTP or WebSocket transports if needed
- Maintain comprehensive documentation for MCP-specific implementation details
- Implement robust error handling and logging to aid in debugging MCP communication issues
- Monitor MCP protocol evolution and plan for version upgrades

```

--------------------------------------------------------------------------------
/src/domains/quality-gates.ts:
--------------------------------------------------------------------------------

```typescript
import type {
  SonarQubeQualityGatesResult,
  SonarQubeQualityGate,
  SonarQubeQualityGateStatus,
  ProjectQualityGateParams,
} from '../types/index.js';
import { BaseDomain } from './base.js';

/**
 * Domain module for quality gates operations
 */
export class QualityGatesDomain extends BaseDomain {
  /**
   * Lists all quality gates
   * @returns Promise with the list of quality gates
   */
  async listQualityGates(): Promise<SonarQubeQualityGatesResult> {
    const response = await this.webApiClient.qualityGates.list();
    return {
      qualitygates: response.qualitygates.map((gate) => ({
        id: gate.id,
        name: gate.name,
        isDefault: gate.isDefault,
        isBuiltIn: gate.isBuiltIn,
        conditions: gate.conditions?.map((condition) => ({
          id: condition.id,
          metric: condition.metric,
          op: condition.operator ?? 'GT', // Default operator
          error: condition.error ?? '',
        })),
      })),
      default: response.default ?? '',
      actions: (response as { actions?: { create?: boolean } }).actions
        ? {
            create: (response as { actions?: { create?: boolean } }).actions?.create,
          }
        : undefined, // actions might not be in the type definition
    };
  }

  /**
   * Gets a specific quality gate by ID
   * @param id The quality gate ID
   * @returns Promise with the quality gate details
   */
  async getQualityGate(id: string): Promise<SonarQubeQualityGate> {
    const response = await this.webApiClient.qualityGates.get({ id });
    return {
      id: response.id,
      name: response.name,
      isDefault: response.isDefault,
      isBuiltIn: response.isBuiltIn,
      conditions: response.conditions?.map((condition) => ({
        id: condition.id,
        metric: condition.metric,
        op:
          (condition as { op?: string; operator?: string }).op ??
          (condition as { op?: string; operator?: string }).operator ??
          'GT',
        error: condition.error ?? '',
      })),
    };
  }

  /**
   * Gets the quality gate status for a project
   * @param params Parameters including project key and optional branch/PR
   * @returns Promise with the quality gate status
   */
  async getProjectQualityGateStatus(
    params: ProjectQualityGateParams
  ): Promise<SonarQubeQualityGateStatus> {
    const { projectKey, branch, pullRequest } = params;

    const request: {
      projectKey: string;
      branch?: string;
      pullRequest?: string;
    } = {
      projectKey,
      ...(branch && { branch }),
      ...(pullRequest && { pullRequest }),
    };

    const response = await this.webApiClient.qualityGates.getProjectStatus(request);

    return response as unknown as SonarQubeQualityGateStatus;
  }
}

```

--------------------------------------------------------------------------------
/src/handlers/projects.ts:
--------------------------------------------------------------------------------

```typescript
import type { PaginationParams, ISonarQubeClient, SonarQubeProject } from '../types/index.js';
import { getDefaultClient } from '../utils/client-factory.js';
import { nullToUndefined } from '../utils/transforms.js';
import { createLogger } from '../utils/logger.js';
import { withErrorHandling } from '../errors.js';
import { withMCPErrorHandling } from '../utils/error-handler.js';
import { createStructuredResponse } from '../utils/structured-response.js';

const logger = createLogger('handlers/projects');

/**
 * Fetches and returns a list of all SonarQube projects
 * @param params Parameters for listing projects, including pagination and organization
 * @param client Optional SonarQube client instance
 * @returns A response containing the list of projects with their details
 * @throws Error if no authentication environment variables are set (SONARQUBE_TOKEN, SONARQUBE_USERNAME/PASSWORD, or SONARQUBE_PASSCODE)
 */
export const handleSonarQubeProjects = withMCPErrorHandling(
  async (
    params: {
      page?: number | null;
      page_size?: number | null;
    },
    client: ISonarQubeClient = getDefaultClient()
  ) => {
    logger.debug('Handling SonarQube projects request', params);

    const projectsParams: PaginationParams = {
      page: nullToUndefined(params.page),
      pageSize: nullToUndefined(params.page_size),
    };

    let result;
    try {
      result = await withErrorHandling('List SonarQube projects', () =>
        client.listProjects(projectsParams)
      );
    } catch (error: unknown) {
      // Check if this is an authorization error and provide helpful guidance
      if (
        error instanceof Error &&
        (error.message.includes('Insufficient privileges') ||
          error.message.includes('requires authentication') ||
          error.message.includes('403') ||
          error.message.includes('Administer System'))
      ) {
        throw new Error(
          `${error.message}\n\nNote: The 'projects' tool requires admin permissions. ` +
            `Non-admin users can use the 'components' tool instead:\n` +
            `- To list all accessible projects: components with qualifiers: ['TRK']\n` +
            `- To search projects: components with query: 'search-term', qualifiers: ['TRK']`
        );
      }
      throw error;
    }
    logger.info('Successfully retrieved projects', { count: result.projects.length });
    return createStructuredResponse({
      projects: result.projects.map((project: SonarQubeProject) => ({
        key: project.key,
        name: project.name,
        qualifier: project.qualifier,
        visibility: project.visibility,
        lastAnalysisDate: project.lastAnalysisDate,
        revision: project.revision,
        managed: project.managed,
      })),
      paging: result.paging,
    });
  }
);

```
Page 1/8FirstPrevNextLast