{"id":"508125a0-4f8a-45ad-9a5b-23d26e51aee5","slug":"defectdojo-mcp-server","kind":"mcp","name":"DefectDojo-MCP-Server","summary":"MCP server for DefectDojo OSS with capability parity to DefectDojo Pro's built-in MCP, plus a deterministic analytics superset. Uses the caller's own API token, so DefectDojo's permissions decide what","owner":"ZeeshanSultan","repoUrl":"https://github.com/ZeeshanSultan/DefectDojo-MCP-Server","license":"AGPL-3.0","licenseTier":"strong-copyleft","trust":"community","stars":2,"match":null,"fewStars":true,"format":"mcp","createdAt":"2026-08-25T11:55:52.000Z","addedAt":"2026-10-21T05:01:03.636Z","logoUrl":"https://avatars.githubusercontent.com/u/8667667?u=3c54eba78e3568fb85e24e1a9da0a53d363dd89c&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["cloud-infra","data-analytics"],"tags":["appsec","claude","defectdojo","vulnerability-management"],"runtime":"python","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-21T05:02:22.708Z","sample":false,"url":"https://conduits.dev/tools/defectdojo-mcp-server","language":"Python","transports":["stdio"],"install":{"pypi":"defectdojo-mcp"},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# DefectDojo Intelligence MCP Server\n\nA permission-preserving [Model Context Protocol](https://modelcontextprotocol.io) server over\n**DefectDojo OSS v2.58.4** REST API v2. It reaches **1:1 capability parity** with the official\nDefectDojo **Pro** built-in MCP Server (12 read tools + a Security Landscape prompt) — and adds\na deterministic analytics/reporting **superset** — implemented as an **external adapter**,\nbecause the built-in Pro MCP requires Pro edition + instance v2.51.2+ and is unavailable on an\nOSS 2.58.4 host.\n\nEvery call uses the **caller's own DefectDojo API token**, so DefectDojo decides visibility.\nThe token is held in memory only, never logged, never sent to the LLM.\n\n- **Status:** Phases 0–4 complete, hardened, and dockerized. **140 tests, ruff-clean.**\n- **Deploy:** see [deploy/DEPLOY.md](deploy/DEPLOY.md) for the dockerized\n  `https://dojo.example.com/mcp/` setup. Note: that deployment **enables the write and\n  DB-history tool groups** (see [Security model](#security-model) and DEPLOY.md). Spec: [PRD.md](PRD.md).\n\n## Tools\n\n**26 tools** — 20 on by default (14 read + 6 analytics/report), 6 gated off by default. Plus 1 MCP prompt primitive.\n\n| Group | Tools | Default |\n|---|---|---|\n| **Core read** (Pro parity + source resolver) | `dd_get_findings`, `dd_get_finding_by_id`, `dd_get_products`, `dd_get_product_by_id`, `dd_get_product_types`, `dd_get_engagements`, `dd_get_tests`, `dd_resolve_finding_source`, `dd_get_users`, `dd_get_user_by_id`, `dd_get_groups`, `dd_get_group_by_id`, `dd_get_dojo_group_members`, `dd_get_roles` | ✅ on |\n| **Analytics / reports** (superset) | `dd_get_sla_breaches`, `dd_get_top_cwes`, `dd_prioritize_findings`, `dd_generate_product_security_summary`, `dd_generate_executive_report`, `dd_generate_engineering_report` | ✅ on |\n| **DB history** (read-only, API-gated) | `dd_get_finding_history`, `dd_get_reopened_findings`, `dd_get_product_risk_trend` | ⛔ off (`enable_db_tools` + reporting DB) |\n| **Writes** (confirmation-gated) | `dd_add_finding_note`, `dd_mark_finding_false_positive`, `dd_close_finding` | ⛔ off (`enable_write_tools`) |\n| **Prompt** | `security_landscape_report` | ✅ on |\n\nAnalytics tools compute **deterministically server-side** (no LLM, no invented data — every\nfigure traces to a findings-API query) and return typed, structured output. SLA uses\nDefectDojo's own `outside_of_sla` filter; prioritization uses real KEV/EPSS/SLA signals.\n\n## Capability parity with Pro\n\n| Pro tool | This server |\n|---|---|\n| `get_findings`, `get_finding_by_id` | `dd_get_findings`, `dd_get_finding_by_id` |\n| `get_products`, `get_product_types` | `dd_get_products`, `dd_get_product_types` (+ `dd_get_product_by_id`) |\n| `get_engagements`, `get_tests` | `dd_get_engagements`, `dd_get_tests` |\n| `get_users`, `get_user_by_id` | `dd_get_users`, `dd_get_user_by_id` |\n| `get_groups`, `get_group_by_id`, `get_dojo_group_members` | `dd_get_groups`, `dd_get_group_by_id`, `dd_get_dojo_group_members` |\n| `get_roles` | `dd_get_roles` |\n| _(no Pro equivalent)_ | `dd_resolve_finding_source` — finding → repo/branch/commit to clone (superset) |\n| 📊 Security Landscape Report (prompt) | `security_landscape_report` (MCP prompt primitive) |\n| 🛡️ SAST Review Report (prompt) | intentionally dropped — low value for DAST/pentest data |\n\n## Security model\n\n- **Token:** `Authorization: Token <token>` (not Bearer). HTTP reads it **per-request** from the\n  incoming header (the caller's own token); stdio reads `DD_API_TOKEN`. On streamable-http the\n  env fallback is **disabled** — a request with no/garbled Authorization header is rejected, never\n  silently run as the server's env token.\n- **Token isolation under pooling:** the shared connection pool uses a no-store cookie jar, so an\n  upstream `Set-Cookie` is never replayed across different callers' tokens. Auth is per-request only.\n- **Validation:** the token is validated once per TTL (cached by 128-bit fingerprint, never the\n  token; cache is bounded) before the first tool call.\n- **Redaction:** applied **centrally to every tool's output** when\n  `redact_secrets`/`treat_finding_text_as_untrusted` is set — secrets scrubbed (incl. URL-embedded\n  credentials), PII stripped when `omit_user_pii`, free-text truncated to `max_evidence_chars`.\n- **Errors:** failures (401/403/404/timeout/…) surface as MCP tool errors (`isError`), not as\n  success payloads, so a client can't mistake a denial for data.\n- **Writes:** off by default; when enabled, every write requires a non-empty reason + explicit\n  `confirm=true` (otherwise a no-op preview), with before/after state-hash audit. REST API only —\n  never writes to the DB.\n- **DB history:** off by default; each tool authorizes the object via REST **first**, then reads an\n  **allowlisted read-only view** (never arbitrary SQL).\n- **Transport:** streamable-http gets explicit `TransportSecuritySettings` (DNS-rebinding/Origin\n  protection) from `mcp.allowed_hosts`/`allowed_origins`; loopback is always allowed for healthchecks.\n- **Deprecated endpoints** (`credentials`, `credential_mappings`, `stub_findings`) are blocked; a\n  disallowed-tool allowlist is asserted at registration.\n- **Audit:** one structured JSON event per tool call, to **stderr** (stdout is the MCP stream).\n\n## Hardening\n\nBounded retry/backoff for transient GET failures (429/502/503/504/timeout; honors Retry-After\nincl. HTTP-date form; never retries writes or 4xx) · process-wide concurrency `Semaphore` ·\nshared pooled httpx client (per-request auth, closed on lifespan shutdown) · per-token roles TTL\ncache. See `tests/test_hardening.py`.\n\n## Production deployment (dockerized, `/mcp/`)\n\nFull guide: **[deploy/DEPLOY.md](deploy/DEPLOY.md)**. In short:\n\n```bash\ncd deploy\n# Optional: set DOJO_NETWORK / DD_URL / DD_MCP_REPORTING_DB_DSN in a deploy/.env\n# (docker compose reads ${...} env defaults; there is no committed .env template —\n#  config lives in deploy/config.prod.yaml, bind-mounted at /app/config.yaml).\ndocker compose up -d --build\ncurl -s http://127.0.0.1:9900/healthz   # -> {\"status\":\"ok\",\"transport\":\"streamable-http\"}\n```\n\nThen add the `location ~ ^/(mcp|sse|messages)` block from\n[deploy/nginx-mcp.conf](deploy/nginx-mcp.conf) to the host nginx and reload. Clients connect to\n`https://dojo.example.com/mcp/` with their own `Authorization: Token <token>` (via `mcp-remote`).\nThe container binds `0.0.0.0:9000` inside the container, published on the host as\n`127.0.0.1:9900` only, and reaches DefectDojo internally at `http://nginx:8080`. The prod config\n**enables the write and DB-history tool groups** (`enable_write_tools` / `enable_db_tools`);\nwrites remain confirmation-gated and DB tools API-authorize first — see DEPLOY.md.\n\n## Local development\n\n### Phase 0 — verify the live schema first\n\n```bash\nDD_URL=https://dojo.internal DD_API_TOKEN=xxxx ./scripts/check_schema.sh\n```\nConfirms the required collection endpoints exist and flags deprecated ones. The DRF filter\nlookups in `src/defectdojo_mcp/tools/*` are verified against DefectDojo 2.58.4 source — re-confirm\nagainst your live schema here.\n\n### Install & run\n\n```bash\npip install -e \".[dev]\"\ncp config.example.yaml config.yaml          # edit base_url\n# stdio reads the token from the DD_API_TOKEN env var (and DD_URL) directly — no .env file.\n\n# stdio (single user / desktop client)\nDD_URL=https://dojo.internal DD_API_TOKEN=xxxx DD_MCP_CONFIG=config.yaml defectdojo-mcp\n\n# streamable-http (multi-user; token per-request). Set mcp.allowed_hosts for a 0.0.0.0 bind.\nDD_MCP_TRANSPORT=streamable-http DD_MCP_HOST=0.0.0.0 DD_MCP_PORT=9000 defectdojo-mcp\n# endpoint: http://<host>:9000/mcp   health: http://<host>:9000/healthz\n```\n\n### Claude Desktop (stdio)\n\n```json\n{\n  \"mcpServers\": {\n    \"defectdojo\": {\n      \"command\": \"defectdojo-mcp\",\n      \"env\": {\n        \"DD_URL\": \"https://dojo.internal\",\n        \"DD_API_TOKEN\": \"your-token\",\n        \"DD_MCP_CONFIG\": \"/path/to/config.yaml\"\n      }\n    }\n  }\n}\n```\n\n### Test\n\n```bash\npytest          # 140 tests: redaction/sanitize, params, paging, auth+validator, context,\n                # server gating, intelligence scoring, source resolution, writes, history,\n                # hardening (retry/pool/semaphore/cache)\nruff check .\n```\n\n## Configuration\n\nKey `config.yaml` knobs (see `config.example.yaml` / `deploy/config.prod.yaml`):\n\n- `mcp.transport` (`stdio` | `streamable-http`), `mcp.host`/`port`\n- `mcp.enable_write_tools`, `mcp.enable_db_tools` — gate the off-by-default groups\n- `mcp.enable_dns_rebinding_protection`, `mcp.allowed_hosts`, `mcp.allowed_origins`\n- `security.redact_secrets`, `omit_user_pii`, `require_confirmation_for_writes`\n- `limits.max_concurrent_api_calls`, `max_retries`, `max_evidence_chars`, `roles_cache_ttl_seconds`\n- `database.enabled`, `dsn_env`, `allowed_views` (Phase 3 reporting DB)\n\n## Discoverability & extensibility\n\nEvery tool carries `ToolAnnotations` (`readOnlyHint`/`idempotentHint`/`title`; writes are\nnon-read-only); enum params publish enums; list tools declare a typed `Page` output schema; the\nserver advertises `instructions`; the prompt has a title + documented args. Tools register through\na `(module, predicate)` registry in `server.py` (single default-off gate per optional group);\n`ServerContext.execute()` / `execute_db_gated()` / `execute_write()` are the chokepoints for token\nvalidation, the central egress transform, audit, and error mapping.\n\n## License\n\nGNU Affero General Public License v3.0 only (AGPL-3.0-only). Full text in\n[LICENSE](LICENSE).\n\nCopyright (C) 2026 Zeeshan Sultan.\n\n## Security\n\nPlease report vulnerabilities privately — see [SECURITY.md](SECURITY.md). Do not\nopen a public issue for a security problem.\n\n## Related\n\nBuilt alongside [ShadowDSO](https://github.com/ZeeshanSultan/ShadowDSO), an\nout-of-band security scanning platform that uses DefectDojo as its system of\nrecord. This server stands alone and does not depend on it.\n"}