# Wazuh MCP Server **A Model Context Protocol (MCP) server for the Wazuh SIEM.** Lets an MCP client — Claude, Open WebUI backed by a local model, or any client that speaks Streamable HTTP — query alerts, agents, vulnerabilities and compliance data, and dispatch active responses, with scope-based access control and audit logging. [![CI](https://github.com/gensecaihq/Wazuh-MCP-Server/actions/workflows/ci.yml/badge.svg)](https://github.com/gensecaihq/Wazuh-MCP-Server/actions/workflows/ci.yml) [![Security Audit](https://github.com/gensecaihq/Wazuh-MCP-Server/actions/workflows/security.yml/badge.svg)](https://github.com/gensecaihq/Wazuh-MCP-Server/actions/workflows/security.yml) [![Release](https://img.shields.io/github/v/release/gensecaihq/Wazuh-MCP-Server?color=2ea44f&label=release)](https://github.com/gensecaihq/Wazuh-MCP-Server/releases/latest) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![Python 3.11+](https://img.shields.io/badge/Python-3.11%2B-3776AB?logo=python&logoColor=white)](https://www.python.org/downloads/) [![MCP 2026-07-28](https://img.shields.io/badge/MCP-2026--07--28-6E56CF)](https://modelcontextprotocol.io/specification/2026-07-28) [![Wazuh 4.8.0–4.14.7](https://img.shields.io/badge/Wazuh-4.8.0–4.14.7-005792?logo=wazuh&logoColor=white)](WAZUH_COMPATIBILITY.md) [![GHCR image](https://img.shields.io/badge/ghcr.io-image-2496ED?logo=docker&logoColor=white)](https://github.com/gensecaihq/Wazuh-MCP-Server/pkgs/container/wazuh-mcp-server) [Quick Start](#quick-start) · [Clients](#connecting-clients) · [Tools](#tools) · [Security](#security-model) · [Configuration](#configuration) · [Docs](#documentation) · [Changelog](CHANGELOG.md) · [Upgrading](UPGRADING.md)
--- ## Overview - **55 tools** in 8 toolsets: alerts, agents, vulnerabilities, threat analysis, compliance (PCI-DSS, HIPAA, SOX, GDPR, NIST, ISO 27001:2022), manager/cluster health, and active response with verification and rollback. Also 5 guided prompts, 6 resources and 3 resource templates. - **Read-only by default.** The 14 state-changing tools require the `wazuh:write` scope, which is never granted implicitly. - **MCP transport:** Streamable HTTP at `/mcp`. Serves protocol revision 2026-07-28 (stateless requests) and the `initialize` handshake for 2025-11-25, 2025-06-18, 2025-03-26 and 2024-11-05. The legacy HTTP+SSE endpoint `/sse` returns `410 Gone`. - **Authentication:** bearer tokens minted from an API key; OAuth 2.0 (authorization code + PKCE) with sign-in by API key or at an OpenID Connect provider (Entra ID, Google Workspace, Okta, Keycloak); or no auth for local development. - **Deployment:** Docker Compose or a published multi-arch image; optional Redis for multi-instance sessions; optional multi-cluster routing. - **Local models:** a vLLM + Open WebUI stack (`compose.local-llm.yml`) and toolset filtering for small models. The only tool that calls a service outside your Wazuh deployment is the optional `search_external_context` (You.com), which can be disabled on its own. Supported Wazuh versions: 4.8.0 through 4.14.7. Alert, vulnerability and alert-backed compliance tools need the Wazuh Indexer. See [WAZUH_COMPATIBILITY.md](WAZUH_COMPATIBILITY.md). --- ## Quick Start Requires Docker with Compose v2 and a Wazuh Manager API user. ```bash git clone https://github.com/gensecaihq/Wazuh-MCP-Server.git cd Wazuh-MCP-Server cp .env.example .env ``` Set the Wazuh connection in `.env`: ```env WAZUH_HOST=your-wazuh-manager WAZUH_USER=your-api-user WAZUH_PASS=your-api-password # Needed for alert, vulnerability and alert-backed compliance tools WAZUH_INDEXER_HOST=your-wazuh-indexer WAZUH_INDEXER_USER=your-indexer-user WAZUH_INDEXER_PASS=your-indexer-password ``` The Manager's TLS certificate is verified. A stock Wazuh install uses a self-signed API certificate that cannot pass verification, so either reissue it for your host and set `WAZUH_CA_BUNDLE`, or, for a first test, add `WAZUH_ALLOW_SELF_SIGNED=true` (connects without verification; logged at startup). Details: [Manager TLS](docs/configuration.md#manager-tls). Generate the signing secret and an API key. `compose.yml` runs the server with `ENVIRONMENT=production`, which refuses to start without `AUTH_SECRET_KEY`: ```bash echo "AUTH_SECRET_KEY=$(openssl rand -hex 32)" >> .env echo "MCP_API_KEY=wazuh_$(openssl rand -base64 32 | tr '+/' '-_' | tr -d '=')" >> .env ``` Start the server and check it: ```bash docker compose up -d curl http://localhost:3000/health # liveness curl http://localhost:3000/ready # checks Manager/Indexer reachability ``` Exchange the API key for a bearer token (valid for `TOKEN_LIFETIME_HOURS`, default 24): ```bash curl -s -X POST http://localhost:3000/auth/token -H 'Content-Type: application/json' \ -d "{\"api_key\": \"$(grep ^MCP_API_KEY= .env | cut -d= -f2)\"}" ``` The key is read-only. To allow active-response tools, add `MCP_API_KEY_SCOPES="wazuh:read wazuh:write"` to `.env`, recreate the container with `docker compose up -d` (`restart` keeps the old environment), and mint a new token. Compose publishes the port on `127.0.0.1` only. The server speaks plain HTTP; put a TLS-terminating reverse proxy in front before exposing it (set `MCP_BIND` to change the host bind address). `python3 deploy.py` (or `deploy.bat` on Windows) performs the same steps, generating `AUTH_SECRET_KEY` and `MCP_API_KEY` if they are missing. ### Pre-built image Multi-arch images (amd64, arm64) are published to GitHub Container Registry and can be pulled without logging in: ```bash docker pull ghcr.io/gensecaihq/wazuh-mcp-server:latest # tracks main docker pull ghcr.io/gensecaihq/wazuh-mcp-server:5.0.0 # latest tagged release ``` `latest` is built from `main` and may include changes listed under *Unreleased* in the [changelog](CHANGELOG.md). Release images are tagged `5.0.0`, `5.0` and `v5.0.0` (4.3.0 and earlier have no `v`-prefixed tag). Upgrading from 4.x: read [UPGRADING.md](UPGRADING.md) first. ```bash docker run -d --name wazuh-mcp-server --env-file .env -e MCP_HOST=0.0.0.0 -e ENVIRONMENT=production \ -p 127.0.0.1:3000:3000 ghcr.io/gensecaihq/wazuh-mcp-server:latest ``` `MCP_HOST=0.0.0.0` is required inside a container because `.env.example` sets `MCP_HOST=127.0.0.1` for bare-metal installs. `-e` wins over `--env-file`, so `ENVIRONMENT=production` holds even if your `.env` sets another value. --- ## Connecting Clients All clients use the Streamable HTTP endpoint `https:///mcp`. | Client | Auth mode | How it authenticates | |--------|-----------|----------------------| | Claude custom connectors (claude.ai, Claude Desktop) | `oauth` | OAuth authorization code with PKCE. The server pre-registers a public client, `claude-desktop`, for Claude's callback URLs. Users sign in on the server's `/oauth/authorize` page with a `wazuh_` API key (the grant is capped at that key's scopes), or at your OpenID Connect provider when `OAUTH_IDP_ISSUER` is set. | | Open WebUI, LibreChat, scripts and other MCP clients | `bearer` (default) | `Authorization: Bearer ` using a token from `POST /auth/token`. | In OAuth mode, set `OAUTH_ISSUER_URL` to the server's public HTTPS URL (otherwise it is derived from each request, which behind a proxy may not be the public URL). Dynamic Client Registration (`/oauth/register`) is off unless `OAUTH_ENABLE_DCR=true`, and cannot be combined with `OAUTH_IDP_ISSUER`. Guides: [Claude Integration](docs/CLAUDE_INTEGRATION.md) · [Local LLMs](docs/LOCAL_LLM.md) --- ## Local LLMs The server does not call a model; it only executes tools. To keep SIEM data on-premises, pair it with a local model: ```bash cat >> .env <