# 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.
[](https://github.com/gensecaihq/Wazuh-MCP-Server/actions/workflows/ci.yml)
[](https://github.com/gensecaihq/Wazuh-MCP-Server/actions/workflows/security.yml)
[](https://github.com/gensecaihq/Wazuh-MCP-Server/releases/latest)
[](LICENSE)
[](https://www.python.org/downloads/)
[](https://modelcontextprotocol.io/specification/2026-07-28)
[](WAZUH_COMPATIBILITY.md)
[](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 < [@andrzej-piotrowski-pl](https://github.com/andrzej-piotrowski-pl) — ISO 27001:2022 compliance tools: Annex A control mapping, domain scoring, gap analysis (#74)
-
[@blackwell-systems](https://github.com/blackwell-systems) — opt-in GCF response encoding for record tools (#102, #104)
-
[@lucascruzb](https://github.com/lucascruzb) — period-wide alert aggregation via scroll, the basis of `get_alerts_aggregated` (#79)
-
[@kanylbullen](https://github.com/kanylbullen) — compact output mode for token-efficient responses (#65)
-
[@mouse-value-add](https://github.com/mouse-value-add) — optional You.com web-search context (#85)
-
[@DrRSatzteil](https://github.com/DrRSatzteil) — `tools/list` pagination fix (#70)
-
[@SiM22](https://github.com/SiM22) — MCP 2025-06-18 support for Windsurf compatibility (#66)
-
[@aiunmukto](https://github.com/aiunmukto) — `.env.example` (#12), an early CI workflow and the Glama registry listing
-
[@Karibusan](https://github.com/Karibusan) — dependency fixes (#38)
-
[@lwsinclair](https://github.com/lwsinclair) — MseeP.ai listing (#9)
-
[@markeclaudio](https://github.com/markeclaudio) — OpenID Connect sign-in (#123), active-response guard-rails (#124, #125), session-store bounds (#126), Manager TLS verification by default (#127)
-
[@MilkyWay88](https://github.com/MilkyWay88) and
[@taylorwalton](https://github.com/taylorwalton) — early pull requests on configuration, logging and packaging
**Bug reports and discussions**
[@cbassonbgroup](https://github.com/cbassonbgroup),
[@cybersentinel-06](https://github.com/cybersentinel-06),
[@daod-arshad](https://github.com/daod-arshad),
[@mamema](https://github.com/mamema),
[@marcolinux46](https://github.com/marcolinux46),
[@matveevandrey](https://github.com/matveevandrey),
[@punkpeye](https://github.com/punkpeye),
[@tonyliu9189](https://github.com/tonyliu9189),
[@Uberkarhu](https://github.com/Uberkarhu),
[@bl4ck5w4n07](https://github.com/bl4ck5w4n07),
[@gnix45](https://github.com/gnix45),
[@hackdefendr](https://github.com/hackdefendr),
[@melmasry1987](https://github.com/melmasry1987),
[@Vasanth120v](https://github.com/Vasanth120v),
[@wqfh](https://github.com/wqfh)
**Built on and works with**
- [Wazuh](https://wazuh.com/) — open source security platform
- [Model Context Protocol](https://modelcontextprotocol.io/) — the protocol this server implements
- [vLLM](https://github.com/vllm-project/vllm), [Ollama](https://ollama.com/) and [Open WebUI](https://github.com/open-webui/open-webui) — local model serving and chat, used in the local LLM stack
---
Contributors
### Contributors
| Avatar | Username | Contributions |
|--------|----------|---------------|
|
| [@alokemajumder](https://github.com/alokemajumder) | 💻 Code, 🐛 Issues, 🔀 PRs, 💬 Discussions |
|
| [@Karibusan](https://github.com/Karibusan) | 💻 Code, 🐛 Issues, 🔀 PRs |
|
| [@gensecai-dev](https://github.com/gensecai-dev) | 💻 Code, 🔀 PRs, 💬 Discussions |
|
| [@aiunmukto](https://github.com/aiunmukto) | 💻 Code, 🔀 PRs |
|
| [@andrzej-piotrowski-pl](https://github.com/andrzej-piotrowski-pl) | 💻 Code, 🔀 PRs |
|
| [@blackwell-systems](https://github.com/blackwell-systems) | 💻 Code, 🔀 PRs |
|
| [@kanylbullen](https://github.com/kanylbullen) | 💻 Code, 🔀 PRs |
|
| [@lucascruzb](https://github.com/lucascruzb) | 💻 Code, 🔀 PRs |
|
| [@lwsinclair](https://github.com/lwsinclair) | 💻 Code, 🔀 PRs |
|
| [@mouse-value-add](https://github.com/mouse-value-add) | 💻 Code, 🔀 PRs |
|
| [@SiM22](https://github.com/SiM22) | 💻 Code, 🔀 PRs |
|
| [@DrRSatzteil](https://github.com/DrRSatzteil) | 🔀 PRs |
|
| [@MilkyWay88](https://github.com/MilkyWay88) | 🔀 PRs |
|
| [@taylorwalton](https://github.com/taylorwalton) | 🔀 PRs |
|
| [@cbassonbgroup](https://github.com/cbassonbgroup) | 🐛 Issues |
|
| [@cybersentinel-06](https://github.com/cybersentinel-06) | 🐛 Issues |
|
| [@daod-arshad](https://github.com/daod-arshad) | 🐛 Issues |
|
| [@mamema](https://github.com/mamema) | 🐛 Issues |
|
| [@marcolinux46](https://github.com/marcolinux46) | 🐛 Issues |
|
| [@matveevandrey](https://github.com/matveevandrey) | 🐛 Issues |
|
| [@punkpeye](https://github.com/punkpeye) | 🐛 Issues |
|
| [@tonyliu9189](https://github.com/tonyliu9189) | 🐛 Issues |
|
| [@Uberkarhu](https://github.com/Uberkarhu) | 🐛 Issues |
|
| [@bl4ck5w4n07](https://github.com/bl4ck5w4n07) | 💬 Discussions |
|
| [@gnix45](https://github.com/gnix45) | 💬 Discussions |
|
| [@hackdefendr](https://github.com/hackdefendr) | 💬 Discussions |
|
| [@melmasry1987](https://github.com/melmasry1987) | 💬 Discussions |
|
| [@Vasanth120v](https://github.com/Vasanth120v) | 💬 Discussions |
|
| [@wqfh](https://github.com/wqfh) | 💬 Discussions |
**Legend:** 💻 Code · 🐛 Issues · 🔀 Pull Requests · 💬 Discussions
> Auto-updated by [GitHub Actions](.github/workflows/update-contributors.yml)