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>
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user