Skip to main content
Glama
Neeraj829784

OWASP ZAP MCP Server

by Neeraj829784

๐Ÿ•ท๏ธ OWASP ZAP MCP Server

Point Claude, Cursor, or any MCP client at OWASP ZAP and run real crawls, authenticated scans, and vulnerability triage โ€” through 67 curated, safety-gated tools built straight from the official ZAP API.

CI License: MIT Python 3.10+ OWASP ZAP 2.17 MCP Tools Docker

Quick start ยท See it in action ยท The 67 tools ยท Authenticated scanning ยท Security ยท Responsible use

WARNING

Authorized use only. Active scanning sends real attack payloads. Run it only against systems you have explicit, written permission to test. This server refuses cloud-metadata targets and can be pinned to an engagement scope โ€” but the responsibility is yours.


โšก See it in action

Once it's connected, you drive ZAP in plain language. The model picks the right tools and chains them โ€” no REST calls, no ZAP UI:

๐Ÿ’ฌ You: Crawl https://juice-shop.herokuapp.com, run an active scan, and summarise what you find.

Behind that one sentence, the assistant orchestrates the toolchain:

โ‘   zap_access_url            seed the target into ZAP's site tree
โ‘ก  zap_spider_scan       ๐ŸŽฏ  crawl for pages, forms, and parameters
โ‘ข  zap_active_scan       ๐ŸŽฏ  attack the discovered surface (SQLi, XSS, โ€ฆ)
โ‘ฃ  zap_get_alerts_summary    triage findings by severity
โ‘ค  zap_generate_report       export a shareable HTML report

๐Ÿค– Assistant: Crawled 41 URLs and completed the active scan. Findings by severity โ€” High: SQL injection on /rest/products/search; Medium: missing CSP and reflected XSS; plus several low-severity header issues. Full report written to /zap/wrk/report.html.

Every attack-capable step (๐ŸŽฏ) is checked against the target-authorization policy first, so the model physically cannot fire a scan at an out-of-scope or cloud-metadata host โ€” regardless of what it's asked to do.


Related MCP server: VibeDefender MCP Server

โœจ Why this exists

Talking to ZAP's raw REST API from an LLM is clumsy and risky: hundreds of endpoints, no guardrails, and it's easy to point an attack at the wrong host. This project gives your AI assistant a small, opinionated, safe surface:

๐Ÿ” Safe by design

  • Cloud metadata endpoints (169.254.169.254) are always refused

  • Optional scope allowlist + private-range blocking

  • Control port bound to localhost by default

๐Ÿ”‘ Real authenticated scanning

  • Full workflow: context โ†’ auth method โ†’ indicators โ†’ user โ†’ forced-user

  • scan_as_user for spider, AJAX spider, and active scan

  • The thing most ZAP wrappers skip entirely

๐Ÿงฑ Reliable under load

  • One pooled async client, bounded retries with backoff

  • Typed errors + a uniform result envelope

  • A single bad call can never crash the server

๐Ÿšข Production posture

  • Fail-closed config, secrets never logged

  • Pinned, non-root, health-gated containers

  • Green CI on every push


๐Ÿ—๏ธ Architecture

flowchart LR
    A["๐Ÿค– LLM client<br/>(Claude ยท Cursor)"] -->|MCP / Streamable HTTP<br/>127.0.0.1:8000| B
    B["๐Ÿ•ท๏ธ zap-mcp-server<br/>67 tools ยท target policy<br/>pooled async client"] -->|internal docker net<br/>http://zap:8080| C
    C["๐Ÿ›ก๏ธ zap-daemon<br/>OWASP ZAP 2.17.0<br/>API restricted to private ranges"]
    B <-->|shared volume<br/>/zap/wrk| D["๐Ÿ“ zap-wrk<br/>reports ยท imports"]
    C <-->|shared volume<br/>/zap/wrk| D
    B -.->|refuses metadata / out-of-scope| X["โ›” blocked targets"]
  • The MCP server reaches ZAP over the internal Docker network.

  • Both ports are published on 127.0.0.1 only โ€” nothing is world-exposed.

  • Every state-changing ZAP action requires the API key.

  • Shared /zap/wrk volume. ZAP resolves every file path in its API against its own filesystem, so both containers mount the same volume at the same path. This is what makes file-based tools work end to end: the agent stages an input (HAR, OpenAPI spec, URL list, automation plan) for ZAP to read, and reads back reports ZAP writes. A one-shot wrk-init service prepares the directory as 1000:1000 mode 2775 (setgid) before ZAP starts; the MCP server joins gid 1000 via group_add, so both unprivileged users can read and write there and new files inherit the shared group automatically.

The optional all-in-one image collapses this into one container: same components and the same MCP endpoint, but ZAP is reached over loopback and /zap/wrk is just a local directory, so no volume sharing is needed. See Quick start.


๐Ÿš€ Quick start

Fastest path โ€” one image, zero config:

docker build -f Dockerfile.allinone -t zap-mcp-server:all-in-one .
docker run -d --name zap-mcp -p 127.0.0.1:8000:8000 zap-mcp-server:all-in-one

No .env, no API key to invent โ€” ZAP's API is bound to loopback inside the container and its key is generated at startup. First boot takes ~60โ€“90s while ZAP's JVM warms up; wait for healthy, then you're live at http://localhost:8000/mcp:

docker inspect --format '{{.State.Health.Status}}' zap-mcp   # -> healthy

That green status is meaningful: the container reports healthy only when both ZAP's API and the MCP endpoint answer.

For real engagements, the two-container compose setup is recommended โ€” independently upgradable ZAP, and restarting the server won't discard a live scan session.

Which setup should I use?

๐Ÿ… &nbsp;All-in-one

๐Ÿ…‘ &nbsp;Compose (recommended)

Launch

docker build once, then docker run

docker compose up -d

Config before first run

none

create .env, choose an API key

Containers

1

2

ZAP API key

auto-generated per container

you supply it

ZAP proxy/API reachable

no โ€” loopback-only in the container

yes, on 127.0.0.1

Restart MCP without losing scan state

โœ—

โœ“

Upgrade ZAP independently

โœ— rebuild

โœ“ change the image tag

Best for

first look, demos, CI throwaways

real engagements, long-running work

Running two processes in one container is normally an anti-pattern, so the parts that usually break are handled explicitly:

  • s6-overlay is PID 1 โ€” reaps orphaned processes and forwards signals, rather than backgrounding with & and leaking zombies.

  • Ordered startup โ€” services run apikey โ†’ zap โ†’ zap-ready โ†’ mcp-server. The zap-ready gate polls ZAP's API and only then starts the MCP server, which is the single-container equivalent of compose's depends_on: service_healthy.

  • No degraded state โ€” if ZAP never becomes ready the container exits non-zero instead of serving an MCP endpoint with a dead scanner behind it.

  • Self-healing โ€” if either process dies, s6 restarts it.

  • Clean shutdown โ€” docker stop exits 0 in ~3s; s6's grace periods are kept under Docker's 10s SIGKILL deadline.

  • Unprivileged โ€” s6 sets up as root, then both ZAP and the MCP server drop to uid 1000. Neither runs as root.

  • Pinned supervisor โ€” s6-overlay is fetched by version and checksum-verified at build time.

Overridable via -e: ZAP_API_KEY (use a fixed key), ZAP_STARTUP_TIMEOUT (readiness budget, default 300s), plus the usual ZAP_TARGET_ALLOWLIST / ZAP_BLOCK_PRIVATE_TARGETS scope controls.

NOTE

The all-in-one image doesnot expose ZAP's HTTP proxy or API, so you cannot point a browser through it or open ZAP's own tooling. Use compose if you need that. Trade-offs worth knowing before you rely on it: a ZAP upgrade means rebuilding the image, restarting the MCP server also restarts ZAP and discards live scan state, and one health signal covers both processes so it is less obvious which half failed.

# 1. Set your secret (never committed)
cp .env.example .env
#    edit .env -> ZAP_API_KEY=<long-random-value>

# 2. Launch (ZAP starts, becomes healthy, then the MCP server starts)
docker compose up -d --build

# 3. Confirm
docker compose ps                    # both services: healthy
docker compose logs -f mcp-server    # "Registered 67 MCP tools"

Connect your client

Either option serves the same endpoint, so the client config is identical:

{
  "mcpServers": {
    "owasp-zap": { "url": "http://localhost:8000/mcp" }
  }
}

๐Ÿ”‘ Authenticated scanning in 6 steps

The capability most ZAP wrappers skip โ€” scan behind a login:

1. create_context โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ scope it (include app, exclude /logout)
2. set_authentication_method โ”€โ”€โ–บ e.g. formBasedAuthentication
3. set_logged_in / out_indicator
4. new_user โ†’ set_user_credentials โ†’ set_user_enabled
5. set_forced_user (+ mode)  โ”€โ”€โ–บ keeps the session alive during scans
6. spider_scan_as_user โ†’ active_scan_as_user

Prefer repeatable runs? Drive the whole pipeline with a ZAP Automation Framework plan via zap_run_automation_plan.


๐Ÿงฐ The 67 tools

Tools marked ๐ŸŽฏ are gated by the target-authorization policy. Every tool returns a uniform envelope: {"status":"success",...} or {"status":"error","code":...,"retryable":...}.

Group

Tools

Core & health

get_version, access_url ๐ŸŽฏ, get_sites, get_urls, new_session

Spider

spider_scan ๐ŸŽฏ, spider_scan_as_user ๐ŸŽฏ, spider_status, spider_results, spider_stop

AJAX spider

ajax_spider_scan ๐ŸŽฏ, ajax_spider_scan_as_user ๐ŸŽฏ, ajax_spider_status, ajax_spider_results, ajax_spider_stop

Group

Tools

Active scan

active_scan ๐ŸŽฏ, active_scan_as_user ๐ŸŽฏ, active_scan_status, active_scan_progress, active_scan_stop, active_scan_pause, active_scan_resume, list_scan_policies

Passive scan

passive_scan_status, passive_scan_set_enabled, passive_scan_clear_queue

Findings & triage

get_alerts, get_alerts_summary, get_alert_details, get_number_of_alerts, delete_all_alerts, add_alert_filter, list_alert_filters, apply_alert_filters, retest_alerts

Group

Tools

Context & scope

create_context, include_in_context, exclude_from_context, list_contexts, get_context, export_context, import_context

Authentication

get_auth_methods, get_auth_method_config_params, set_authentication_method, get_authentication_method, set_logged_in_indicator, set_logged_out_indicator

Users

new_user, set_user_credentials, set_user_enabled, list_users, get_user

Forced user

set_forced_user, set_forced_user_mode, get_forced_user, is_forced_user_mode_enabled

Imports & automation

import_openapi_url, import_openapi_file, import_graphql_url, import_har, import_urls, run_automation_plan, automation_plan_progress

Reports

list_report_templates, report_template_details, generate_report

All tool names are prefixed with zap_ (e.g. zap_active_scan).


๐Ÿ›ก๏ธ Security model

Control

Behavior

API key

Required. Server won't start without ZAP_API_KEY (dev override: ZAP_ALLOW_INSECURE=true). Never logged by this server: httpx request logging is suppressed because the ZAP API takes the key as a ?apikey= query parameter. ZAP itself still echoes it once in its own startup line.

Metadata block

169.254.169.254, metadata.google.internal, etc. are always refused. Not configurable.

Scope allowlist

ZAP_TARGET_ALLOWLIST pins attackable hosts to your engagement.

Private-range block

ZAP_BLOCK_PRIVATE_TARGETS=true refuses internal targets.

Localhost binding

MCP :8000 and ZAP :8080 publish on 127.0.0.1 only.

Response caps

Large lists are bounded (max_response_items) with truncated metadata.

IMPORTANT

The MCP endpoint hasno built-in auth and can launch attacks. To expose it beyond localhost, set MCP_BIND=0.0.0.0 and front it with an authenticating reverse proxy.

Variable

Default

Description

ZAP_API_KEY

(required)

Must match the ZAP daemon's key.

ZAP_BASE_URL

http://zap:8080

ZAP API base URL.

ZAP_TARGET_ALLOWLIST

(empty)

Comma-separated allowed host suffixes.

ZAP_BLOCK_PRIVATE_TARGETS

false

Refuse private/loopback targets.

MCP_BIND

127.0.0.1

Host interface the MCP port binds to.

REQUEST_TIMEOUT / CONNECT_TIMEOUT

60 / 10

HTTP timeouts (s).

ZAP_MAX_RETRIES / ZAP_RETRY_BACKOFF

2 / 0.5

Retry policy.

ZAP_MAX_RESPONSE_ITEMS

500

Cap on returned list items.

ZAP_REPORT_DIR

/zap/wrk

Report output dir. Must be on the volume shared with ZAP.

ZAP_STARTUP_TIMEOUT

300

All-in-one only: seconds to wait for ZAP's API before failing the container.


๐Ÿงช Development

python -m venv .venv && source .venv/bin/activate
pip install -r requirements-dev.txt
pytest -q          # policy, config, error envelope, tool behavior

CI runs py_compile + pytest on every push to main.


โš–๏ธ Responsible use

Active scanning is an attack. In most jurisdictions, testing systems without permission is illegal. Before you scan:

  • โœ… Confirm the target is in scope for an engagement you're authorized to run

  • โœ… Pin scope with ZAP_TARGET_ALLOWLIST; consider ZAP_BLOCK_PRIVATE_TARGETS=true

  • โœ… Use a long random ZAP_API_KEY; never commit .env

Cloud metadata endpoints are always refused and this cannot be overridden.


๐Ÿ“„ License

MIT ยฉ Neeraj829784 โ€” swap the LICENSE file for Apache-2.0 if you want an explicit patent grant.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables AI assistants to perform authorized security testing and penetration testing operations including SSL/TLS analysis, port scanning, vulnerability scanning, and HTTP security header audits through natural language interactions.
    1
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables AI assistants to perform penetration testing and security assessments by exposing 60+ Kali Linux security tools including network scanning, web security testing, password cracking, exploitation frameworks, and OSINT capabilities through an AI-friendly interface.
    2
    MIT
  • F
    license
    A
    quality
    D
    maintenance
    Enables AI assistants to perform authorized penetration testing and security assessments by exposing 20+ Kali Linux security tools (nmap, sqlmap, gobuster, hydra, etc.) through a safe, validated interface with command allowlists, rate limiting, and input sanitization.
    19
    1
    -