# dependencyTrack-MCP MCP-Server fuer [OWASP Dependency-Track](https://dependencytrack.org/), in Rust. Ueberwiegend lesender Zugriff (Projekte, Findings, Metriken, VEX/SBOM u.a.) plus eine geschuetzte Schreib-Operation: Finding-Suppression/Analyse. Zwei Frontends, eine gemeinsame Tool-Logik: | Modus | Binary | Einsatz | |---|---|---| | **stdio** | `dtrack-stdio` | Lokal/Arbeitsrechner: von Claude Desktop gestartet (`docker run -i` oder native `.exe`) | | **HTTP** | `dtrack-http` | Server: StreamableHTTP `/mcp`, Multi-Client, Bearer-Auth/RBAC, Admin-UI | > **Status: Stage 3** -- HTTP-Frontend, Bearer-Auth, persistente Config, > Admin-UI und RBAC-Enforcement stehen. Tool-Sichtbarkeit pro Client (Stufe 2) > und SSO folgen (siehe Roadmap). ## Architektur ``` crates/dtrack-config Config (TOML laden/speichern, Secret-Masking, Persistenz) crates/dtrack-perms RBAC-Policy (Rollen/Clients, Deny-overrides-Allow) crates/dtrack-core DT-REST-Client (reqwest+rustls) crates/dtrack-tools rmcp-Server (die Tools) -- transport-unabhaengig, geteilt crates/dtrack-stdio Bin: stdio-Frontend crates/dtrack-http Bin: HTTP-Frontend (axum + StreamableHTTP + Admin-UI) ``` ## Tools Lesend: | Tool | Argument | DT-Endpoint | |---|---|---| | `list_projects` | -- | `GET /project` | | `lookup_project` | `name`, `version?` | `GET /project/lookup` | | `project_components` | `uuid` | `GET /component/project/{uuid}` | | `project_findings` | `uuid` | `GET /finding/project/{uuid}` | | `project_metrics` | `uuid` | `GET /metrics/project/{uuid}/current` | | `project_violations` | `uuid` | `GET /violation/project/{uuid}` (braucht `VIEW_POLICY_VIOLATION`) | | `project_vex` | `uuid` | `GET /vex/cyclonedx/project/{uuid}` | | `project_bom` | `uuid` | `GET /bom/cyclonedx/project/{uuid}` | Schreibend (braucht das DT-Recht `VULNERABILITY_ANALYSIS`): | Tool | Argument | DT-Endpoint | |---|---|---| | `suppress_finding` | `project`, `component`, `vulnerability`, `suppressed?`, `state?`, `justification?`, `comment?` | `PUT /analysis` | > `suppress_finding` ist die einzige schreibende Operation. Die drei UUIDs > (project/component/vulnerability) stammen aus der `project_findings`-Antwort. > `suppressed` ist standardmaessig `true` (`false` hebt die Unterdrueckung auf). > Wer nur lesen soll, bekommt einen Key **ohne** `VULNERABILITY_ANALYSIS` -- > dann scheitert der Write serverseitig mit HTTP 403. Im HTTP-Modus zusaetzlich > per RBAC eingrenzbar (eigene Rolle / `deny`). ## Konfiguration (Umgebungsvariablen) | Var | Pflicht | Bedeutung | |---|---|---| | `DTRACK_URL` | ja* | Basis-URL der DT-Instanz (ohne `/api/v1`, wird angehaengt) | | `DTRACK_API_KEY` | ja* | API-Key eines Teams. Lesen: `VIEW_PORTFOLIO` + `VIEW_VULNERABILITY` (Policy-Verstoesse: `VIEW_POLICY_VIOLATION`); fuer `suppress_finding`: `VULNERABILITY_ANALYSIS` | | `DTRACK_INSECURE` | nein | `1`/`true` -> TLS-Pruefung aus (Notbehelf bei internem CA-Cert) | | `DTRACK_CONFIG` | nein | Pfad zu einer TOML-Config; einmal aus env gebootstrappt, danach massgeblich | | `DTRACK_PERMISSIONS` | nein | (nur HTTP) Pfad zur RBAC-Policy; gesetzt -> RBAC statt flacher Token-Liste | | `DTRACK_HTTP_TOKENS` | nein | (nur HTTP) kommagetrennte erlaubte Bearer-Tokens; leer = ungeschuetzt | | `DTRACK_HTTP_ADDR` | nein | (nur HTTP) Bind-Adresse, Default `0.0.0.0:8080` | | `DTRACK_ADMIN_USER` | nein | (nur HTTP) Basic-Auth-User der Admin-UI; leer = UI deaktiviert | | `DTRACK_ADMIN_PASSWORD` | nein | (nur HTTP) Basic-Auth-Passwort der Admin-UI | \* oder ueber `DTRACK_CONFIG`-Datei. --- ## Schnellstart: lokal testen (Mac/Linux mit Docker) > Beispiele mit `docker`; `podman` funktioniert identisch. ### Variante A -- aus dem Quellcode bauen (baut nativ fuer deine Architektur) ```sh cp .env.example .env # DTRACK_URL / DTRACK_API_KEY ausfuellen docker compose up --build ``` ### Variante B -- vorgebautes Release-Image laden Am Release haengen pro Architektur Tarballs. Passenden waehlen (**arm64** = Apple Silicon, **amd64** = Intel / "nicht Mac"): ```sh docker load -i dtrack-http-0.1.0-arm64.tar.gz docker run --rm -p 8080:8080 \ -e DTRACK_URL=http://host.docker.internal:8081 \ -e DTRACK_API_KEY=odt_dein_key \ -e DTRACK_ADMIN_USER=admin -e DTRACK_ADMIN_PASSWORD=geheim \ dtrack-http:0.1.0-arm64 ``` Pruefen: - `curl http://localhost:8080/health` -> `OK` - `http://localhost:8080/admin` -> Admin-UI (Basic-Auth) > Der Server bootet auch ohne erreichbare DT -- nur die Tool-Calls brauchen eine > echte Instanz. Tipp fuer Persistenz: `-v ~/dtrack-data:/data -e DTRACK_CONFIG=/data/config.toml` > ergaenzen, dann ueberleben Admin-Aenderungen den Neustart. ### (optional) Dummy-DependencyTrack zum Testen `docker-compose.yml` in einem eigenen Ordner: ```yaml services: dtrack-apiserver: image: dependencytrack/apiserver deploy: resources: limits: memory: 4608m ports: - "8081:8080" # API -> Host-Port 8081 volumes: - dtrack-data:/data dtrack-frontend: image: dependencytrack/frontend environment: - API_BASE_URL=http://localhost:8081 ports: - "8082:8080" # Web-UI -> Host-Port 8082 volumes: dtrack-data: {} ``` `docker compose up -d` (apiserver braucht ~4 GB RAM + ein paar Minuten beim Erststart). Dann `http://localhost:8082` (Login `admin`/`admin`), unter **Administration -> Access Management -> Teams** einen API-Key mit `VIEW_PORTFOLIO` + `VIEW_VULNERABILITY` erzeugen (fuer den Suppression-Test zusaetzlich `VULNERABILITY_ANALYSIS`) und ein Projekt anlegen. Aus dem MCP-Container ist die DT unter `http://host.docker.internal:8081` erreichbar (nicht `localhost`). --- ## Mit Claude Desktop verbinden > **Wichtig:** Der UI-Weg *"Add custom connector"* (URL eingeben) funktioniert > fuer einen **lokalen** Server **nicht** -- Claude verbindet sich dabei aus > Anthropics Cloud, die deinen `localhost` nicht erreicht. Fuer lokal geht der > Weg ueber die `claude_desktop_config.json`. Config-Datei (macOS): `~/Library/Application Support/Claude/claude_desktop_config.json` (oder in Desktop: **Settings -> Developer -> Edit Config**). Den `mcpServers`-Block neben `preferences` einfuegen, danach Desktop **komplett neu starten** (Cmd+Q). ### Empfohlen lokal: stdio stdio hat keinen HTTP-Session-Handshake -> zuverlaessig. Den HTTP-Container braucht man dafuer nicht (nur eine laufende DT). ```sh docker load -i dtrack-stdio-0.1.0-amd64.tar.gz ``` ```json { "mcpServers": { "dependency-track": { "command": "docker", "args": ["run","-i","--rm", "-e","DTRACK_URL","-e","DTRACK_API_KEY", "dtrack-stdio:0.1.0-amd64"], "env": { "DTRACK_URL": "http://host.docker.internal:8081", "DTRACK_API_KEY": "odt_dein_key" } } } } ``` > Findet Desktop `docker` nicht (eingeschraenkter PATH), `which docker` ausfuehren > und den vollen Pfad als `command` setzen (z.B. `/usr/local/bin/docker`). > Das stdio-Release-Image ist amd64 -> laeuft auf Apple Silicon emuliert (ok zum Testen). ### Windows (Arbeitsrechner): native `.exe` -- empfohlen hinter Firmen-VPN Auf Windows ist die native `.exe` der zuverlaessigste Weg: sie laeuft im Netz-Kontext des Windows-Hosts, also greift die **VPN-Route zur Firmen-DT direkt**. Container (Docker/Podman Desktop) laufen dagegen in einer WSL2-VM -- `--network=host` bindet dort an die VM, und die DT ueber die Host-VPN ist von da drin oft nicht erreichbar. **1. `.exe` bauen** -- Cross-Compile von CachyOS aus. reqwest nutzt rustls (kein OpenSSL), darum keine nativen TLS-Libs noetig, nur der MinGW-Linker: ```sh rustup target add x86_64-pc-windows-gnu sudo pacman -S --needed mingw-w64-gcc cargo build --release --target x86_64-pc-windows-gnu --bin dtrack-stdio # -> target/x86_64-pc-windows-gnu/release/dtrack-stdio.exe ``` Findet cargo den Linker nicht, in `.cargo/config.toml`: ```toml [target.x86_64-pc-windows-gnu] linker = "x86_64-w64-mingw32-gcc" ``` > **Fallback**, falls der rustls-Crypto-Provider beim `windows-gnu`-Cross zickt: > direkt auf dem Windows-Rechner mit rustup/MSVC bauen -- > `cargo build --release --bin dtrack-stdio` erzeugt dieselbe `.exe`. Die `dtrack-stdio.exe` dann auf den Arbeitsrechner ziehen, z.B. nach `C:\Tools\dtrack-mcp\dtrack-stdio.exe`. **2. Claude Desktop einbinden** -- Config-Datei unter Windows: `%APPDATA%\Claude\claude_desktop_config.json` (oder **Settings -> Developer -> Edit Config**): ```json { "mcpServers": { "dependency-track": { "command": "C:\\Tools\\dtrack-mcp\\dtrack-stdio.exe", "env": { "DTRACK_URL": "https://dtrack.firma.intern", "DTRACK_API_KEY": "odt_...", "DTRACK_INSECURE": "0" } } } } ``` Pfad mit doppelten Backslashes (oder Forward-Slashes). Bei internem CA-Cert `DTRACK_INSECURE` auf `"1"` setzen. Danach Desktop **komplett neu starten**. **3. Testen** -- VPN verbinden, im Chat z.B. *"liste die DT-Projekte"* -> `list_projects` muss Treffer liefern. Kommt nichts: `DTRACK_URL`/Key pruefen, bei TLS-Fehler `DTRACK_INSECURE=1`, VPN-Verbindung pruefen. Logs: `%APPDATA%\Claude\logs\mcp*.log`. ### HTTP als echter Remote-Connector Der HTTP-Modus ist fuer den Betrieb als **oeffentlich erreichbarer** Remote-MCP gedacht (Custom Connector ueber eine echte URL, z.B. hinter Reverse-Proxy/Tunnel). Der Bridge-Versuch `npx mcp-remote http://localhost:8080/mcp` fuer lokal scheitert aktuell am rmcp-StreamableHTTP-Handshake (`tools/list` vor `initialized`) -- darum lokal stdio nutzen. Logs bei Problemen: `~/Library/Logs/Claude/mcp*.log`. --- ## RBAC (optional, nur HTTP) Standardmaessig regelt `DTRACK_HTTP_TOKENS` den Zugang (flache Token-Liste, leer = offen). Fuer feinere Rechte `permissions.toml.example` nach `./data/permissions.toml` kopieren, Tokens/Rollen eintragen und `DTRACK_PERMISSIONS=/data/permissions.toml` setzen. Modell: benannte Rollen mit `allow`/`deny` (`"*"` = alle Tools, **deny schlaegt allow**), Clients ordnen einen Bearer-Token einer Rolle zu. Ein nicht erlaubter `tools/call` wird mit **403** abgewiesen. Tipp: das schreibende `suppress_finding` gezielt nur Auditor-Rollen erlauben (oder breit per `deny` ausschliessen). ```toml [[roles]] name = "read-only" allow = ["*"] deny = ["suppress_finding"] [[roles]] name = "auditor" allow = ["*"] [[clients]] name = "dashboard-bot" token = "GEHEIM" role = "read-only" ``` ## Admin-UI Unter `/admin` (Basic-Auth via `DTRACK_ADMIN_USER`/`_PASSWORD`): zeigt URL + maskierten API-Key, erlaubt Aendern (Client wird **live** neu gebaut) und persistiert bei gesetztem `DTRACK_CONFIG`. Aktuell bewusst minimal -- Styling und Client-/Rollen-Verwaltung folgen. ## Release bauen (Maintainer) In Gitea einen Release veroeffentlichen (oder **Actions -> Release Images -> Run workflow** mit Tag). Der Workflow baut via buildx Multi-Arch und haengt an: `dtrack-stdio--amd64`, `dtrack-http--amd64`, `dtrack-http--arm64` (je `.tar.gz`). Auth laeuft ueber das automatische `gitea.token`. ## Roadmap | Stage | Inhalt | Status | |---|---|---| | 0 | stdio, read-only, 3 Tools, CI, Container | ✅ | | 1 | Config-Layer + geteiltes Tool-Crate | ✅ | | 2 | HTTP-Frontend + Bearer-Auth | ✅ | | 3 | Admin-UI + Persistenz + RBAC-Enforcement | ✅ | | 3.2 | Tool-Sichtbarkeit pro Client (`tools/list` filtern) | … | | 4 | SSO (Gitea-OIDC) | … |