OpenVAS-MCP
A self-hosted MCP server that gives AI agents structured access to OpenVAS / Greenbone vulnerability scanning — without sending your data anywhere.
OpenVAS has no native interface for AI agents. Most integrations require cloud connectivity or expose GVM credentials to every client. OpenVAS-MCP solves this:
- Local-first. Talks only to your GVM instance. No telemetry, no external calls — verified by CI.
- Credential isolation. AI agents authenticate to the MCP server; the server holds the single GVM service account.
- Thin bridge. Returns structured scan data as-is. Analysis and reporting logic belong in the agent or a platform built on top.
See docs/architecture.md for a full architecture diagram and design details.
Quick start
0. Vibeinstall (optional, if you trust claude more than yourself)
Run in your terminal:
claude "install this, make no mistake."
If you prefer to stay in control, follow the manual setup below.
1. Get a GVM instance
Don't have one? Spin up the bundled Greenbone Community Edition stack:
docker compose -f docker/openvas/compose.yaml up -d
2. Connect an MCP client
stdio (Claude Desktop, Cursor, Windsurf, Cline, …)
Requirements: Python 3.10+
git clone https://github.com/CyberSecAuto-Labs/OpenVAS-MCP
cd OpenVAS-MCP
python3.11 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
Add to mcpServers in your client config file:
{
"mcpServers": {
"openvas": {
"command": "/path/to/.venv/bin/python", // ← edit this to your venv path
"args": ["-m", "openvas_mcp"],
"env": { "GVM_PASSWORD": "secret" } // ← edit this to your GVM password
}
}
}
Config file locations:
| Client | Path |
|---|---|
| Claude Desktop (macOS) | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Claude Desktop (Windows) | %APPDATA%\Claude\claude_desktop_config.json |
| Cursor | ~/.cursor/mcp.json |
| Windsurf | ~/.codeium/windsurf/mcp_config.json |
| Cline / Roo Code | via the MCP panel in the VS Code extension |
HTTP/SSE (networked agents)
Requirements: Docker
Download the compose files from the latest release and run:
# GVM running locally via Unix socket
MCP_API_KEYS="supersecrettoken:my-agent" GVM_PASSWORD=secret docker compose up
# GVM on a remote host via TCP
MCP_API_KEYS="supersecrettoken:my-agent" GVM_HOST=192.168.1.10 GVM_PASSWORD=secret docker compose up
[!NOTE]
MCP_API_KEYSis a comma-separated list oftoken:namepairs sent as a Bearer token by the MCP client. Multiple clients:"tok1:agent1,tok2:agent2". PassMCP_ALLOW_UNAUTHENTICATED=1instead to skip auth on a trusted network.
Point your MCP client at the server:
{
"mcpServers": {
"openvas": {
"url": "http://your-server:8000/sse", // ← edit this to your server address
"headers": {
"Authorization": "Bearer supersecrettoken" // ← your MCP_API_KEYS token
}
}
}
}
[!WARNING] Plain TCP connections (
GVM_HOSTset,GVM_TLSunset) send GVM credentials unencrypted. UseGVM_TLS=1or a Unix socket for anything beyond local dev.
All-in-one dev setup
Greenbone Community Edition + MCP server from source in one go:
# Start the Greenbone stack
docker compose -f docker/openvas/compose.yaml up -d
# Start the MCP server, connected via gvmd socket
GVM_PASSWORD=secret docker compose -f compose.yaml -f compose.override.yaml up --build
[!TIP] See
compose.override.yamlfor how the socket volume is mounted.
Configuration
| Variable | Default | Description |
|---|---|---|
GVM_PASSWORD |
— | GVM password (required) |
GVM_SOCKET_PATH |
/run/gvmd/gvmd.sock |
Unix socket path (default connection) |
GVM_HOST |
— | Connect via TCP instead of socket (IPv4 and IPv6) |
MCP_TRANSPORT |
stdio |
stdio, sse, or streamable-http |
MCP_API_KEYS |
— | Bearer API keys for HTTP transport auth (token:name,...) |
See docs/configuration.md for the full reference, including TLS options, policy file, scan limits, and logging.
Available tools
| Tool | Description |
|---|---|
list_targets |
Return all scan targets |
create_target |
Create a target with specified hosts/CIDRs |
list_tasks |
Return all scan tasks |
start_scan |
Create and start a scan against a target |
get_scan_status |
Poll status and progress of a running scan |
fetch_scan_results |
Retrieve findings, optionally filtered by minimum severity |
Example: "Scan 192.168.1.0/24 and show me anything above severity 7" — the agent calls create_target → start_scan → get_scan_status → fetch_scan_results(min_severity=7.0).
Release integrity
Every release image is:
- Signed with cosign keyless OIDC signing — no long-lived key to compromise.
- SBOM attached — a CycloneDX JSON bill of materials is generated with syft and attached to each GitHub Release.
- Vulnerability-scanned — grype scans the SBOM on every PR (
vuln-scan.yml) and at release time, failing on fixablehighseverity findings (--only-fixed). - Egress-audited — the
startup-egressworkflow tracesconnect()syscalls viastraceon every push and PR, asserting no unexpected outbound connections at startup. Integration tests extend this to live GMP code paths.
Verify the image signature before running:
cosign verify \
--certificate-identity-regexp "https://github.com/CyberSecAuto-Labs/OpenVAS-MCP/.github/workflows/release.yml@refs/tags/.*" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
ghcr.io/cybersecauto-labs/openvas-mcp:<version>
Docs
- docs/architecture.md — architecture diagram, component overview, and transport details
- docs/configuration.md — full environment variable reference, TLS, policy file, scan limits, logging
- docs/design.md — design decisions and known limitations
- docs/ci.md — CI workflows, guarantees, and tradeoffs
No comments yet
Be the first to share your take.