The upstream MCP container requires a SonarQube user token in the Authorization header. Without one, every call returns 401. - proxy: read SONARQUBE_TOKEN via sonarqube_token() at session-open time; raise TokenMissingError when unset/blank. upstream_session() attaches the token as "Authorization: Bearer <token>" via streamablehttp_client(headers=...). - cli: fail fast in serve and check with a clear stderr message and exit 1 when the token is missing, before any network attempt. All exception text written to stderr passes through _redact() so an accidentally-leaked token from a third-party exception is replaced with [REDACTED] before display. - The token is never stored on any object, never logged, and the TokenMissingError message contains no token material (it only describes how to generate one in SonarQube). - Tests: header forwarding via mocked streamablehttp_client, missing- token exit code, redaction in CLI error paths, whitespace stripping on the token. Total: 25 tests. - Docs: README/CLAUDE updated with the new env-var, Claude Desktop config snippet, and the security guarantees. CHANGELOG added. Bumps version to 0.2.0. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4.3 KiB
CLAUDE.md
Entwicklungshinweise fuer mcp-sonarqube-proxy.
Projekt
Stdio-MCP-Server, der beim Start eine Streamable-HTTP-Verbindung zum Upstream SonarQube-MCP-Server oeffnet, dessen Tools 1:1 weiterreicht und Aufrufe an diesen weiterleitet. Claude Desktop / Claude App spawnt diesen Prozess via stdio.
Architektur
src/mcp_sonarqube_proxy/proxy.py— eineupstream_session()(async context manager) haelt die persistenteClientSessionzum Upstream.build_proxy_server()registriert zwei Handler auf einemmcp.server.lowlevel.Server, die Aufrufe transparent weiterleiten.src/mcp_sonarqube_proxy/cli.py— Click-CLI mitserve(stdio) undcheck(einmaliger Verbindungstest).
Zentrale Architekturentscheidungen
-
Low-Level
Server, nichtFastMCP. FastMCP introspiziert die Handler-Signatur, um eininputSchemazu generieren. Bei einem generischen**kwargs-Handler ist das Schema leer und der Client weiss nicht, wie er das Tool aufrufen soll. Der Low-Level-Server uebernimmt dagegen dasTool-Objekt mit allen Feldern (inputSchema,outputSchema,annotations,title,_meta) unveraendert vom Upstream. -
Eine persistente Upstream-Session, kein Per-Call-Reconnect. Die fruehere Variante hat fuer jeden Tool-Call eine neue HTTP-Session geoeffnet. Das bricht die Streamable-HTTP-Session-Id, multipliziert die Latenz und macht
initialize()zur teuersten Operation im Hot Path. Jetzt: einmal beim Start verbinden, beiserve-Lifetime offenhalten, beim Beenden sauber schliessen. -
validate_input=Falseauf dem@call_tool-Decorator. Der Upstream ist die einzige Wahrheit ueber Schemata. Lokale Validierung wuerde nur duplizieren und koennte bei Schema-Drift zu falschen Ablehnungen fuehren. -
server.create_initialization_options()statt manueller Capabilities. Die Helper-Methode liefert die korrektenNotificationOptions()undexperimental_capabilities={}und ueberlebt SDK-Updates besser als ein handgeschriebener Aufruf. -
Stateless Tool-Liste — kein Caching. Jeder
tools/list-Request re-fetcht den Upstream. Weniger Code, immer aktuell, kein Cache-Coherency- Problem.
Logging und stdout/stderr
stdout ist fuer JSON-RPC reserviert. Jegliche Ausgabe (Logging, Startmeldungen,
Fehlermeldungen) muss auf stderr. _configure_logging setzt das fuer das
Logging-Modul, _stderr() ist der Helper fuer direkte Meldungen.
SONARQUBE_TOKEN — Sicherheit
Der Upstream verlangt einen User-Token im Authorization-Header. Wir lesen
den Token in proxy.sonarqube_token() aus der Env-Var, leiten ihn als
Bearer <token> an streamablehttp_client(headers=...) weiter, und speichern
ihn nirgends. Vor jedem stderr-Output mit potenziell ungeprueftem
Exception-Text laeuft cli._redact(), die den live-Token (falls in der Env)
durch [REDACTED] ersetzt — defensive Schicht gegen Drittanbieter-Libraries,
die in Fehlermeldungen Header einbetten koennten.
TokenMissingError enthaelt niemals Token-Material, weil dieser Fall genau
heisst: es gibt keinen Token. Die Meldung enthaelt nur die operator-gerichtete
Anleitung "My Account -> Security".
Lokale Entwicklung
# Editable install + Dev-Group
uv sync
# Upstream-Verbindung testen
SONARQUBE_MCP_URL=http://192.168.0.2:8765/mcp uv run mcp-sonarqube-proxy check
# Server lokal starten (stdio — z.B. via MCP Inspector)
SONARQUBE_MCP_URL=http://192.168.0.2:8765/mcp uv run mcp-sonarqube-proxy serve
# Tests
uv run pytest
# Lint / Format
uv run ruff check .
uv run ruff format .
Installation als Tool
uv tool install git+https://gitea.gecheckt.de/marcus/mcp-sonarqube-proxy.git
Bekannte offene Punkte
- Reconnect bei Verbindungsabbruch: Wenn der Upstream waehrend
serveweggeht, scheitern nachfolgende Tool-Calls bis zum Neustart des Proxies. Eine Reconnect-Logik mit Retry onanyio.ClosedResourceError/httpx.ReadErrorwaere ein sinnvoller naechster Schritt, ist fuer v0.1 aber bewusst weggelassen. - Concurrent Tool-Calls: Die
ClientSessionkorreliert Antworten ueber Request-IDs und sollte parallele Calls verkraften. Nicht explizit getestet. - SDK-Versionssprung: Auf
mainder python-sdk wurden die Decorators durch Konstruktor-Kwargs ersetzt. Bei Migration auf eine neue Major-Version mussbuild_proxy_server()angepasst werden.