Files
mcp-sonarqube-proxy/CLAUDE.md
T
marcus a6fd188c14 feat: initial v0.1.0 of mcp-sonarqube-proxy
Stdio MCP server that proxies tools from an upstream SonarQube MCP server
over streamable HTTP. Tools are forwarded 1:1 with full schema preservation
(inputSchema, outputSchema, annotations, title, _meta); CallToolResult is
forwarded including isError and structuredContent.

- proxy.py: persistent upstream ClientSession, low-level Server with
  @list_tools and @call_tool(validate_input=False) handlers — the upstream
  is the sole schema authority.
- cli.py: Click-based 'serve' (stdio) and 'check' (probe) commands;
  logging strictly on stderr (stdout reserved for JSON-RPC).
- Targets mcp 1.27.x decorator API (pinned <2 to guard against the
  unreleased constructor-API rewrite on main).
- pytest suite (14 tests) covering env-var resolution, schema passthrough,
  CallToolResult forwarding, registration, dispatch end-to-end, and CLI
  success/error paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 20:26:38 +02:00

91 lines
3.6 KiB
Markdown

# 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` — eine `upstream_session()` (async context
manager) haelt die persistente `ClientSession` zum Upstream. `build_proxy_server()`
registriert zwei Handler auf einem `mcp.server.lowlevel.Server`, die Aufrufe
transparent weiterleiten.
- `src/mcp_sonarqube_proxy/cli.py` — Click-CLI mit `serve` (stdio) und `check`
(einmaliger Verbindungstest).
### Zentrale Architekturentscheidungen
1. **Low-Level `Server`, nicht `FastMCP`.** FastMCP introspiziert die
Handler-Signatur, um ein `inputSchema` zu 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 das
`Tool`-Objekt mit allen Feldern (`inputSchema`, `outputSchema`,
`annotations`, `title`, `_meta`) unveraendert vom Upstream.
2. **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, bei `serve`-Lifetime offenhalten, beim Beenden sauber schliessen.
3. **`validate_input=False` auf 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.
4. **`server.create_initialization_options()` statt manueller Capabilities.**
Die Helper-Methode liefert die korrekten `NotificationOptions()` und
`experimental_capabilities={}` und ueberlebt SDK-Updates besser als ein
handgeschriebener Aufruf.
5. **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.
## Lokale Entwicklung
```bash
# 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
```bash
uv tool install git+https://gitea.gecheckt.de/marcus/mcp-sonarqube-proxy.git
```
## Bekannte offene Punkte
- **Reconnect bei Verbindungsabbruch:** Wenn der Upstream waehrend `serve`
weggeht, scheitern nachfolgende Tool-Calls bis zum Neustart des Proxies.
Eine Reconnect-Logik mit Retry on `anyio.ClosedResourceError`/`httpx.ReadError`
waere ein sinnvoller naechster Schritt, ist fuer v0.1 aber bewusst weggelassen.
- **Concurrent Tool-Calls:** Die `ClientSession` korreliert Antworten ueber
Request-IDs und sollte parallele Calls verkraften. Nicht explizit getestet.
- **SDK-Versionssprung:** Auf `main` der python-sdk wurden die Decorators
durch Konstruktor-Kwargs ersetzt. Bei Migration auf eine neue Major-Version
muss `build_proxy_server()` angepasst werden.