dtrack-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@dtrack-mcp Show unanalysed CRITICAL findings in myapp v2.3.0"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
dtrack-mcp
MCP server that connects Claude (or any MCP-compatible LLM) to Dependency-Track.
Instead of clicking through the DT UI to triage hundreds of vulnerability findings, describe what you need in natural language — Claude pulls the data, reasons over it, and writes the verdict back.
Why
Dependency-Track accumulates findings fast. A product with 5 versions and 100+ vulnerabilities each means 500+ rows to triage — each requiring you to open a finding, read the CVE, check CVSS, look at EPSS/KEV signals, see what other projects decided, and pick a state. That's hours of browser clicking per release cycle.
dtrack-mcp exposes DT as an MCP server so Claude can do the data work:
Pull findings filtered by severity or state
Deduplicate CVE/GHSA/OSV aliases into one group per real issue
Check what was decided for the same vulnerability in other projects or versions
Write the triage decision back to DT with a comment
Related MCP server: CVE MCP Server
Key scenarios
1. Triage a batch of findings
Show me all CRITICAL and HIGH findings in project "myapp v2.3.0"
that haven't been analysed yet. Group by alias so I don't see
the same CVE twice. For each group tell me the CVSS vector,
EPSS score, and whether it's in CISA KEV.2. Carry triage forward when upgrading
We just uploaded the SBOM for myapp v2.4.0. Carry over all
triage decisions from v2.3.0 — dry run first, then apply.3. Propagate a decision across versions
A new CVE is found in v1.0, v1.1, v2.0, and v2.1 simultaneously. Triage it in one version, then carry the decision backward and forward to the others:
Set CVE-2024-12345 in myapp v2.1.0 as NOT_AFFECTED
(justification: CODE_NOT_REACHABLE). Then carry that decision
to v1.0, v1.1, and v2.0.Tools
Tool | Description |
| List projects with vulnerability counts |
| Find project by UUID or by exact name + version |
| Findings with severity / state / suppressed filters |
| Deduplicate CVE/GHSA/OSV via union-find |
| Full detail by id, optionally specifying source |
| Which projects are affected by a given CVE? |
| Current triage state + full comment history |
| Same vuln in other components / projects, with prior analyses |
| ⚠ WRITE — set state, justification, response, comment. Accepts raw UUIDs or a finding dict |
| All versions of a project, newest first |
| Carried / updated / new / gone between two versions |
| ⚠ WRITE — transfer decisions v1 → v2 (or v2 → v1) |
| ⚠ WRITE — fan out one decision to all versions of a project |
| ⚠ WRITE — upload CycloneDX/SPDX SBOM |
All tools are read-only except the four marked ⚠ WRITE. The HTTP layer
enforces this: any write path other than PUT /api/v1/analysis and
POST /api/v1/bom raises before reaching the network.
Requirements
Python 3.10+ (tested on 3.10–3.12).
Dependency-Track 4.11+. DT 4.14+ is recommended — earlier versions lack EPSS-for-GHSA data and CVSSv4 fields, and purl distro-qualifier matching degrades. The server logs a warning at startup when it detects an older DT.
An MCP-capable client — Claude Desktop, Claude Code, or any other stdio MCP runtime.
A DT account with
VIEW_PORTFOLIO+VULNERABILITY_ANALYSISpermissions (andBOM_UPLOADif you useupload_bom).
Linux and macOS are the primary targets; Windows works under WSL.
Installation
pip install dtrack-mcpOr from source (for development):
git clone https://github.com/drewrukin/dtrack-mcp.git
cd dtrack-mcp
pip install -e .After pip install, the dtrack-mcp command is on $PATH and starts
the MCP server on stdio. A quick local smoke test against your DT
instance:
DTRACK_URL=https://dt.example.com \
DTRACK_API_KEY=odt_... \
python scripts/smoke.pyscripts/smoke.py is read-only and safe to re-run — it exercises every
GET-based tool on one real project and prints a compact summary.
Claude Desktop — add to ~/Library/Application Support/Claude/claude_desktop_config.json
(macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"dtrack": {
"command": "dtrack-mcp",
"env": {
"DTRACK_URL": "https://dt.example.com",
"DTRACK_API_KEY": "odt_..."
}
}
}
}Claude Code — add to ~/.claude.json:
{
"mcpServers": {
"dtrack": {
"command": "dtrack-mcp",
"env": {
"DTRACK_URL": "https://dt.example.com",
"DTRACK_API_KEY": "odt_..."
}
}
}
}Restart Claude after editing the config.
Auth options
Variable | Description |
| Base URL of your DT instance |
| Preferred. Requires VIEW_PORTFOLIO + VULNERABILITY_ANALYSIS permissions. Add BOM_UPLOAD for |
| Alternative. Exchanges credentials for a JWT; re-fetches on 401. |
Optional tuning
Variable | Default | Description |
|
| HTTP timeout in seconds. |
|
| Set |
|
| Retries on HTTP 429/502/503/504 and transport errors (connect refused, read timeout). |
|
| Base for exponential backoff: |
|
| Sleep between writes in |
|
| Standard |
|
| Skip the one-shot |
Proxies are disabled inside the client (trust_env=False) because DT
is typically on an internal network and corporate proxies refuse to
tunnel it. Equivalent to curl --noproxy '*'.
Safety
Read-mostly by design. Writes are gated at the HTTP client level, not in application logic. The guard runs before any network call. The only allowed write paths are
PUT /api/v1/analysis(triage) andPOST /api/v1/bom(SBOM upload).Input validation. All tool parameters are validated against enum allowlists before hitting DT. Unknown parameters are rejected (
additionalProperties: falsein JSON schema).carry_over_triageandbroadcast_triagedefault todry_run. No writes happen unless you explicitly passmode="exact"after reviewing the plan.max_operationscap. Bulk write operations fail early if the plan exceeds 500 entries by default, preventing accidental mass-triage from a hallucinated call.Transient-failure retry. Connection refuseds, read timeouts, and HTTP 429/502/503/504 are retried with exponential backoff (see
DTRACK_RETRY_*); no other status is retried, so a broken caller never loops.No secrets in logs. Credentials come from env only; JWTs and API keys are never logged, and raw DT responses are never echoed verbatim into tool output.
Troubleshooting
no credentials: set DTRACK_API_KEY or DTRACK_USER+DTRACK_PASSWORDat startup — the env is not visible to the MCP subprocess. Put the vars in theenvblock of your client config (examples above), not just in your shell.DTRACK_URL is not set— same cause as above.HTTP 401afterapi_keyauth — the key was revoked or lacks the required permissions. API-key auth does not re-login; rotate the key in DT and update the client config.HTTP 403 on PUT /api/v1/analysis— your account lacksVULNERABILITY_ANALYSIS. Read-only tools work without it.dtrack-mcp: refused <METHOD> <path>— you hit the read-mostly guard. Either the call is outside the documented write allowlist (which is the whole point of the guard) or DT introduced a new endpoint and the spec needs updating — open an issue, don't relax the guard locally.Every call is slow on login+password auth — the JWT is cached per-process, so this only happens at startup. If it repeats, the client is restarting between calls; prefer
DTRACK_API_KEYfor long sessions.
Documentation
SPEC.md— full protocol specification: normalized schemas, per-tool input/output contracts, hard invariants, per-stage evolution.scripts/smoke.py— end-to-end read-only smoke test; mirrors the shape of a real triage session.scripts/smoke_retry.py— retry-layer integration check; includes a recovery-probe mode that requires a live DT instance you can restart.
License
MIT — see LICENSE.
Available Tools
14 toolsbroadcast_triageA
⚠ WRITE (when mode="exact"). Fan out triage decisions to all versions.
A specialised form of carry_over_triage for the case where a new
CVE is found simultaneously in multiple versions of the same product.
Instead of running carry_over N times, triage the finding once in any
version, then call this tool to propagate the decision in all
directions (newer AND older versions).
Steps:
Fetches every version of
project_namefrom DT.Excludes the reference version.
Calls
carry_over_triage(reference → target)for each.Returns per-target results plus an aggregate summary.
ALWAYS run mode="dry_run" first to review the plan.
Args: reference_project_uuid: UUID of the version that already has the triage decision to broadcast. project_name: Exact project name (used to find all other versions). mode: "dry_run" (no writes) or "exact" (performs writes). include_updated_components: Also transfer updated_component matches. Default False — conservative. overwrite_not_set: Transfer over targets in state NOT_SET. Default True. overwrite_any: Transfer over targets in any state. Default False. comment_prefix: Prepended to every carry-over comment. max_operations: Per-target cap. Raise if a single target needs more. active_only: Skip inactive/archived versions. Default True.
Inputs: reference_project_uuid (required), project_name (required), mode, include_updated_components, +5 more. Returns: object
carry_over_triageA
⚠ WRITE (when mode="exact"). Transfer triage decisions v1 → v2.
ALWAYS run with mode="dry_run" first. Only switch to
mode="exact" after a human has reviewed the plan. In exact mode
each transfer issues PUT /api/v1/analysis and appends a comment
noting the source project and match reason. The full history of
source comments is preserved in the original project untouched.
Skip rules:
source has no actionable analysis (state NOT_SET) → skipped
target already triaged (state ≠ NOT_SET) and overwrite_any=False → skipped
target NOT_SET and overwrite_not_set=False → skipped
Safety caps:
max_operations(default 500) early-fails in exact mode when the plan is larger than the cap. Raise explicitly for huge transfers, or split into batches.DTRACK_WRITE_DELAY_MSenv var adds a per-write sleep for rate-limit-sensitive instances.
Args: source_project_uuid: Old version UUID with existing triage. target_project_uuid: New version UUID to populate. mode: "dry_run" (no writes, returns plan) or "exact" (performs writes). include_updated_components: Also transfer updated_component matches (same CVE, different component version). Default False — conservative. overwrite_not_set: Transfer over target entries in state NOT_SET. Default True. overwrite_any: Transfer over target entries in any state. Default False. comment_prefix: Prepended to every carry-over comment. max_operations: Sanity cap against hallucination-driven bulk writes in exact mode. Raise if you genuinely need to transfer more.
Inputs: source_project_uuid (required), target_project_uuid (required), mode, include_updated_components, +4 more. Returns: object
diff_findingsA
Compute carried / updated_component / new / gone between two versions.
Typical use: upgrading a product v1 → v2. source is v1 (where
triage decisions already exist), target is v2 (new SBOM just
uploaded). Returns four lists:
carried— same component + same vulnerability, safe to transfer analyses 1:1.updated_component— same vulnerability, component version changed (patch or major). Decision may or may not still apply.new— appeared in target only.gone— were in source only; reason isvuln_fixed(component still there) orcomponent_removed.
Component matching uses (purl_type, purl_namespace, purl_name) —
deliberately drops qualifiers so DT 4.13→4.14 upgrades that add
distro=... don't invalidate every match. Ambiguous matches
(multi-arch SBOMs with the same component at different qualifiers)
emit an entry in warnings. Read-only.
Args: source_project_uuid: Old version UUID (usually with existing triage). target_project_uuid: New version UUID. include_analysis: Load current analysis for each source finding (needed for carry_over; adds one HTTP call per finding).
Inputs: source_project_uuid (required), target_project_uuid (required), include_analysis. Returns: object
find_duplicate_analysesA
Find analyses of duplicates of a finding across DT.
Given one finding, returns three parallel lists of duplicates with their current analysis (state + comment history), intended for a triage loop that wants to reuse prior decisions:
aliases_in_project— other findings in the same project in the same alias cluster (CVE ↔ GHSA ↔ OSV of the same issue).same_vuln_other_components— same vulnerability uuid on other components/versions in the same project.other_projects— findings in other DT projects that share any id in the target's alias cluster; each entry carries its project uuid/name/version.
Each entry bundles {component, vulnerability, analysis}; entries
in other_projects also carry project. Read-only.
Filters (v0.4):
states— whitelist of analysis states (e.g.["NOT_AFFECTED","EXPLOITABLE"]) applied to all three output buckets.targetis never filtered.only_analyzed— shorthand for every state except NOT_SET. Ignored whenstatesis non-empty (stateswins).active_only(default True) — skip archived/inactive DT projects inother_projects. v0.4 default flip — existing callers that don't pass the flag stop seeing archived hits.project_tag— inother_projectsonly, keep projects carrying this tag (case-insensitive name equality).compact— strip bulky fields (description, CVSS vectors, analysis details, long comment bodies truncated to 200 chars). See SPEC §13.4.1 for the exact field list.
Args:
project_uuid: DT project UUID of the target finding.
component_uuid: DT component UUID of the target finding.
vulnerability_uuid: DT vulnerability UUID of the target finding.
states: Whitelist of analysis state strings, e.g.
["NOT_AFFECTED","EXPLOITABLE"].
only_analyzed: If true, keep only entries with a non-NOT_SET
analysis. Ignored when states is non-empty.
active_only: If true (default), skip archived projects in
other_projects.
project_tag: Optional DT tag name; restricts other_projects
to projects carrying this tag (case-insensitive).
compact: If true, strip bulky fields from the payload.
Inputs: project_uuid (required), component_uuid (required), vulnerability_uuid (required), states, +4 more. Returns: object
find_vulnerabilityA
Fetch the full detail record of a vulnerability.
When source is given (e.g. "NVD", "GITHUB"), fetches directly.
When omitted, probes candidate sources based on the id prefix
(CVE-* → NVD, GHSA-* → GITHUB, etc.) and returns the first hit,
or null if nothing matches. Read-only.
Returns title, description, CVSS v2/v3/v4 scores and vectors, CWEs, EPSS score and percentile, KEV flag, references, and alias list.
Args: vuln_id: Vulnerability id, e.g. "CVE-2024-1234", "GHSA-xxxx-yyyy-zzzz". source: Optional DT source namespace — "NVD", "GITHUB", "OSV", "SNYK", "SONATYPE", "VULNDB", "INTERNAL", etc. When omitted the source is inferred from the id prefix.
Inputs: vuln_id (required), source. Returns: result
get_analysisA
Fetch the analysis record for one finding.
Returns the analysis state, justification, response, details,
suppressed flag, and the full comment history. If DT has no analysis
row yet, returns an empty-analysis default (state NOT_SET, no
comments) — callers never get null. Read-only.
Args: project_uuid: DT project UUID. component_uuid: DT component UUID inside that project. vulnerability_uuid: DT vulnerability UUID.
Inputs: project_uuid (required), component_uuid (required), vulnerability_uuid (required). Returns: object
get_project_versionsA
List all versions of a project by exact name.
Returns {name, total, versions} where versions are sorted newest
first (semver-aware, lexicographic fallback). Used to pick source /
target UUIDs for diff_findings and carry_over_triage.
Read-only.
Args: name: Exact project name. active_only: Exclude projects marked inactive in DT.
Inputs: name (required), active_only. Returns: object
group_findings_by_aliasA
Group findings by alias (transitive closure) — dedup CVE/GHSA/OSV.
Vulnerabilities reported under different ids (e.g. CVE-2024-X and
GHSA-Y-Z) often refer to the same issue and are linked via DT's
aliases. This tool runs union-find over that alias graph and returns
one cluster per real issue. Each cluster carries a canonical id
(CVE first, then GHSA, then OSV, then SNYK, then INTERNAL, then
alphabetical), the full alias list, a merge_reason trace of the
edges that joined the cluster, and every finding in the project
belonging to it.
Same filters as list_findings. Pagination applies to groups, not to the findings inside them — a group always ships with all its findings intact. Sorted by highest CVSS score (v3 or v4) descending. Read-only.
include_details=True (v0.3) embeds title/description/references
in every finding's vulnerability summary. The same description text
repeats on each finding inside a group — acceptable tradeoff for a
single-call triage flow.
Args: project_uuid: DT project UUID. suppressed: Include suppressed findings. analysis_states: Whitelist of analysis state strings. severities: Whitelist of severity strings. page: 1-based page of groups (not findings). page_size: Groups per page (max 500). include_details: If true, embed title/description/references in each finding's vulnerability summary (v0.3). Default false.
Inputs: project_uuid (required), suppressed, analysis_states, severities, +3 more. Returns: object
list_findingsA
List vulnerability findings for a project with optional filters.
Returns normalized findings — each one bundles the vulnerability
(severity, CVSS v3/v4, CWE, EPSS, aliases), the affected component
(name, version, purl, latest known version), and the analysis state.
All filters are applied client-side before pagination, so total
reflects the post-filter count. Read-only.
When include_details=True, every finding's vulnerability summary
also carries title, description, and references so an
LLM can draft a verdict without a separate get_vulnerability
call. Off by default because descriptions can be 2–4 KB each — set
it to true only for focused batches (20–30 findings), not
project-wide scans.
Args: project_uuid: DT project UUID (get it from list_projects or lookup_project). suppressed: Include findings suppressed by an analyst. analysis_states: Whitelist, e.g. ["NOT_SET", "IN_TRIAGE", "EXPLOITABLE", "FALSE_POSITIVE", "NOT_AFFECTED", "RESOLVED"]. severities: Whitelist, e.g. ["CRITICAL", "HIGH", "MEDIUM", "LOW", "UNASSIGNED"]. page: 1-based page number (applied after filtering). page_size: Items per page (max 500). include_details: If true, embed title/description/references in each finding's vulnerability summary (v0.3). Default false.
Inputs: project_uuid (required), suppressed, analysis_states, severities, +3 more. Returns: object
list_projectsA
List Dependency-Track projects.
List projects in the Dependency-Track instance, optionally filtered by a substring of the project name. Returns normalized projects with per-severity vulnerability counts. Read-only.
Args: name_filter: Case-insensitive substring on project name. active_only: If true, exclude projects marked inactive in DT. page: 1-based page number. page_size: Items per page (max 500).
Inputs: name_filter, active_only, page, page_size. Returns: object
resolve_projectA
Resolve a project by UUID or by exact (name, version).
Two lookup paths — use whichever you have:
project_uuid— direct UUID lookup (e.g. copied from the DT UI URL or returned by another tool).name+version— exact-match lookup by project name and version string.
When project_uuid is provided it takes precedence; name and
version are ignored. Returns a normalized project, or null if
nothing matches. Read-only.
Args:
project_uuid: DT project UUID. Takes precedence when provided.
name: Exact project name (requires version).
version: Exact project version (requires name).
Inputs: project_uuid, name, version. Returns: result
search_vulnerabilityA
Search which projects are affected by a vulnerability.
Given a vulnerability id (e.g. "CVE-2024-1234"), resolves it, then finds every DT project that contains a finding for this vulnerability (or any of its aliases). For each project returns the analysis state per affected component.
Use this to answer "which products are affected by CVE-X and what's been decided?" without manually iterating over projects. Read-only.
Args: vuln_id: Vulnerability id (e.g. "CVE-2024-1234", "GHSA-xxxx"). active_only: Skip inactive/archived projects. Default True. only_analyzed: Only include projects/findings with a non-NOT_SET analysis state. Default False.
Inputs: vuln_id (required), active_only, only_analyzed. Returns: result
set_analysisA
⚠ WRITE. Update the analysis record for one finding.
Two ways to identify the finding:
Pass
component_uuidandvulnerability_uuiddirectly.Pass
finding— a NormalizedFinding dict as returned bylist_findings,group_findings_by_alias, or entries insidefind_duplicate_analyses. The UUIDs are extracted automatically, avoiding copy-paste errors in the triage loop.
When finding is provided, its UUIDs take precedence.
project_uuid is always required because findings from
find_duplicate_analyses → other_projects may belong to a
different project.
Issues PUT /api/v1/analysis; the connection-layer guard refuses
any other write path. Fields left as None are omitted from the
body, so DT keeps its current value. comment appends to the
history, it does not replace existing comments. Returns the full
normalized analysis after the write.
Args:
project_uuid: DT project UUID.
state: One of NOT_SET, IN_TRIAGE, EXPLOITABLE, FALSE_POSITIVE,
NOT_AFFECTED, RESOLVED.
component_uuid: DT component UUID (required unless finding
is provided).
vulnerability_uuid: DT vulnerability UUID (required unless
finding is provided).
finding: A NormalizedFinding dict. When provided, component_uuid
and vulnerability_uuid are extracted from it.
justification: Optional CycloneDX justification enum
(e.g. CODE_NOT_REACHABLE, REQUIRES_CONFIGURATION).
response: Optional response enum (e.g. CAN_NOT_FIX, WILL_NOT_FIX,
UPDATE, ROLLBACK, WORKAROUND_AVAILABLE).
details: Optional free-text analysis details.
comment: Optional free-text comment appended to the history.
suppressed: Optional bool to suppress/unsuppress the finding.
Inputs: project_uuid (required), state (required), component_uuid, vulnerability_uuid, +6 more. Returns: object
upload_bomA
⚠ WRITE. Upload a CycloneDX/SPDX SBOM to Dependency-Track.
Issues POST /api/v1/bom with the SBOM as a base64-encoded
string. Returns an upload token — the caller should poll
GET /api/v1/bom/token/{token} (not an MCP tool in v0.2) to
detect when processing finishes and findings become visible.
When auto_create=True, the project is created if missing; this
requires the PROJECT_CREATION_UPLOAD permission in DT.
Args: project_name: Target project name (must exist unless auto_create=True). project_version: Target project version. bom: Base64-encoded SBOM document (CycloneDX or SPDX). auto_create: Create project/version if missing. Requires extra permission. parent_name: Optional parent project name for hierarchy. parent_version: Optional parent project version.
Inputs: project_name (required), project_version (required), bom (required), auto_create, +2 more. Returns: object
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 14 tools
Each tool has a distinct purpose in the triage workflow. For example, broadcast_triage and carry_over_triage are clearly differentiated, and finding deduplication tools like find_duplicate_analyses and group_findings_by_alias serve different scopes.
All tools use consistent snake_case naming with a verb_noun pattern (e.g., list_findings, upload_bom, set_analysis), making them predictable and easy to navigate.
With 14 tools, the server is well-scoped for a Dependency-Track integration, covering project management, finding analysis, vulnerability search, and SBOM upload without excess.
The tool set covers the core triage and analysis lifecycle comprehensively. Minor gaps exist, such as explicit project creation or deletion tools, but these are partially addressed by upload_bom with auto_create.
Maintenance
Related MCP Connectors
An MCP server that provides an API to LLMs to manage their JumpCloud resources.
MCP server for Pentest-Tools.com: run scans, manage findings and reports via your preffered LLM.
MCP server unifying ERPs, CRMs, APIs and knowledge base for Claude, ChatGPT and Gemini.
MCP server for the Vulners.com API
Related MCP Servers
- AlicenseBqualityFmaintenanceAn MCP server for the OnSecurity API that allows Claude to query Rounds, Findings, Prerequisites, Blocks and Notifications.5MIT
- AlicenseBqualityDmaintenanceThis MCP server transforms Claude into a comprehensive security analyst by providing access to 27 security tools across 21 APIs for vulnerability intelligence. It enables users to query multiple sources like NVD, EPSS, CISA KEV, and threat intelligence platforms in parallel to get correlated security insights and risk assessments for CVEs.281,766Apache 2.0

@repomend/mcpofficial
AlicenseNot gradedqualityDmaintenanceSecurity scanning MCP server that connects Claude to RepoMend findings, enabling vulnerability management and automated fix drafting.MIT- FlicenseNot gradedqualityDmaintenanceThis MCP server connects Claude Desktop to OpenCTI for AI-augmented threat intelligence analysis, enabling natural language queries and instant, contextualized answers from your threat intelligence database.29-