Files

11 KiB

dependencyTrack-MCP

MCP-Server fuer OWASP Dependency-Track, 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)

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"):

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:

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

docker load -i dtrack-stdio-0.1.0-amd64.tar.gz
{
  "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:

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:

[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):

{
  "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).

[[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-<tag>-amd64, dtrack-http-<tag>-amd64, dtrack-http-<tag>-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)