wazuh-mcp 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,76 +1,107 @@
1
- # wazuh-mcp
1
+ <p align="center">
2
+ <img src="docs/assets/wazuh-mcp-social-preview.jpg" alt="wazuh-mcp banner" width="900">
3
+ </p>
2
4
 
3
- [![TypeScript](https://img.shields.io/badge/TypeScript-5.7-blue.svg)](https://www.typescriptlang.org/)
4
- [![Node.js](https://img.shields.io/badge/Node.js-20%2B-green.svg)](https://nodejs.org/)
5
- [![MCP](https://img.shields.io/badge/MCP-1.12-purple.svg)](https://modelcontextprotocol.io/)
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ <p align="center">
6
+ <a href="https://lidless.dev"><img src="docs/assets/marks/wazuh-mcp-circle.png" width="48" alt="Lidless Labs"></a>
7
+ </p>
7
8
 
8
- A [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) server for the [Wazuh](https://wazuh.com/) SIEM/XDR platform. Query agents, security alerts, detection rules, and decoders directly from Claude or any MCP-compatible client.
9
+ <h1 align="center">wazuh-mcp</h1>
9
10
 
10
- ## Features
11
+ <p align="center">
12
+ <strong>A read-only Wazuh SIEM/XDR control CLI and MCP adapter for alerts, agents, vulnerabilities, rules, and more.</strong>
13
+ </p>
11
14
 
12
- - **25 MCP Tools** - Agents, alerts, rules, decoders, SCA, syscollector, FIM, rootcheck, groups, and manager
13
- - **3 MCP Resources** - Pre-built views for agents, recent alerts, and rule summaries
14
- - **3 MCP Prompts** - Alert investigation, agent health checks, and security overviews
15
- - **JWT Authentication** - Automatic token management with refresh on expiry
16
- - **Full Compliance Mapping** - PCI-DSS, GDPR, HIPAA, NIST 800-53, MITRE ATT&CK
17
- - **Pagination** - All list endpoints support limit/offset pagination
18
- - **Type-Safe** - Full TypeScript with strict mode and Zod schema validation
15
+ <p align="center">
16
+ <strong>Website:</strong> <a href="https://lidless.dev/wazuh-mcp">lidless.dev/wazuh-mcp</a>
17
+ </p>
19
18
 
20
- ## Prerequisites
19
+ <p align="center">
20
+ <img src="https://shieldcn.dev/github/ci/lidless-labs/wazuh-mcp.svg?branch=main&workflow=ci.yml" alt="CI status">
21
+ <img src="https://shieldcn.dev/npm/wazuh-mcp.svg" alt="npm version">
22
+ <img src="https://shieldcn.dev/badge/MCP-server-8A2BE2.svg" alt="MCP server">
23
+ <img src="https://shieldcn.dev/badge/license-MIT-green.svg" alt="MIT License">
24
+ <img src="https://shieldcn.dev/badge/Wazuh-SIEM%2FXDR-3385ff.svg" alt="Wazuh SIEM/XDR">
25
+ <img src="https://shieldcn.dev/badge/MITRE_ATT%26CK-mapped-0f766e.svg" alt="MITRE ATT&CK mapped">
26
+ </p>
21
27
 
22
- - Node.js 20+
23
- - A running Wazuh manager with API access (default port 55000)
24
- - Wazuh API credentials (username/password)
25
- - (Optional) Wazuh Indexer (OpenSearch) access for alert queries
28
+ wazuhctrl is a read-only control CLI for the [Wazuh](https://wazuh.com/) SIEM/XDR platform. The same package ships `wazuh-mcp`, a [Model Context Protocol](https://modelcontextprotocol.io/) (MCP) adapter that exposes your Wazuh manager and Wazuh Indexer as MCP tools so Claude, Claude Code, or any MCP-compatible client can investigate alerts, triage agents, and pull vulnerability inventory in plain language. It is read-only by design and security-first: TLS verification is on by default, sensitive fields (agent IPs, full logs, file hashes, command lines) are hidden unless you opt in per call, and attacker-controlled SIEM text is wrapped in untrusted-data markers to blunt prompt injection against the calling model.
29
+
30
+ ## What it does
31
+
32
+ wazuhctrl and wazuh-mcp turn a Wazuh SIEM/XDR deployment into typed operator surfaces. Point your MCP client at the server, give it your Wazuh manager and (optionally) Wazuh Indexer credentials, and the model can list active and disconnected agents, retrieve and full-text search security alerts, pull vulnerability inventory by CVE or severity, inspect detection rules and decoders, review SCA (Security Configuration Assessment) results, walk system inventory (OS, packages, processes, ports, network, hotfixes), read File Integrity Monitoring and rootcheck findings, fetch manager logs and configuration, and run a connection diagnostic. The CLI starts with status, agent inventory, and diagnostics for shells, cron, and CI. The package ships 28 MCP tools, 3 resources, and 3 guided prompts over stdio. Both surfaces only read from Wazuh: the sole writes they perform are JWT authentication against the manager and `_search` queries against the indexer.
26
33
 
27
34
  ## Installation
28
35
 
36
+ The quickstart below runs the published npm package with `npx`, which is the recommended path. To work from source instead:
37
+
29
38
  ```bash
30
- git clone https://github.com/solomonneas/wazuh-mcp.git
39
+ git clone https://github.com/lidless-labs/wazuh-mcp.git
31
40
  cd wazuh-mcp
32
41
  npm install
33
42
  npm run build
34
43
  ```
35
44
 
36
- ## Configuration
45
+ ## Quickstart
37
46
 
38
- Set the following environment variables:
47
+ Run it straight from npm with `npx`, no clone or build required:
39
48
 
40
- | Variable | Required | Default | Description |
41
- |----------|----------|---------|-------------|
42
- | `WAZUH_URL` | Yes | - | Wazuh API URL (e.g., `https://10.0.0.2:55000`) |
43
- | `WAZUH_USERNAME` | Yes | - | API username |
44
- | `WAZUH_PASSWORD` | Yes | - | API password |
45
- | `WAZUH_VERIFY_SSL` | No | `false` | Set to `true` to verify SSL certificates |
49
+ ```json
50
+ {
51
+ "mcpServers": {
52
+ "wazuh": {
53
+ "command": "npx",
54
+ "args": ["-y", "wazuh-mcp"],
55
+ "env": {
56
+ "WAZUH_URL": "https://your-wazuh-manager:55000",
57
+ "WAZUH_USERNAME": "wazuh-wui",
58
+ "WAZUH_PASSWORD": "your-password",
59
+ "WAZUH_INDEXER_URL": "https://your-wazuh-indexer:9200",
60
+ "WAZUH_INDEXER_USERNAME": "admin",
61
+ "WAZUH_INDEXER_PASSWORD": "your-indexer-password"
62
+ }
63
+ }
64
+ }
65
+ }
66
+ ```
46
67
 
47
- Alternative variable names `WAZUH_BASE_URL` and `WAZUH_USER` are also supported.
68
+ Drop that into your MCP client's server config (see [Usage](#usage) for the exact file per client), restart the client, and ask it something like *"list the active Wazuh agents"* or *"search alerts for brute force in the last 24 hours."* The indexer settings are optional: without them the agent, rule, decoder, and version tools still work, and the alert and vulnerability tools return a configuration message instead of failing.
48
69
 
49
- ### Wazuh Indexer (OpenSearch) - Required for Alerts
70
+ Prefer a global install?
50
71
 
51
- Wazuh 4.x stores alerts in the Wazuh Indexer (OpenSearch), not the REST API. To enable alert tools (`get_alerts`, `get_alert`, `search_alerts`) and the `wazuh://alerts/recent` resource, configure the indexer connection:
72
+ ```bash
73
+ npm install -g wazuh-mcp
74
+ # then use "command": "wazuh-mcp" instead of the npx invocation above
75
+ ```
52
76
 
53
- | Variable | Required | Default | Description |
54
- |----------|----------|---------|-------------|
55
- | `WAZUH_INDEXER_URL` | No | - | Wazuh Indexer URL (e.g., `https://10.0.0.2:9200`) |
56
- | `WAZUH_INDEXER_USERNAME` | No | `admin` | Indexer username |
57
- | `WAZUH_INDEXER_PASSWORD` | No | - | Indexer password |
58
- | `WAZUH_INDEXER_VERIFY_SSL` | No | `false` | Set to `true` to verify SSL certificates |
77
+ ## CLI
59
78
 
60
- If `WAZUH_INDEXER_URL` is not set, alert tools will return a helpful configuration message. All other tools (agents, rules, decoders, version) work without the indexer.
79
+ The package ships `wazuhctrl` for shells, cron, and CI. Compatibility alias `wazuhctl` points at the same binary, and `wazuh-mcp` remains the MCP stdio adapter.
80
+
81
+ ```bash
82
+ wazuhctrl status --json
83
+ wazuhctrl agents list --limit 20
84
+ wazuhctrl diagnostics
85
+ wazuhctrl diagnostics --no-connectivity
86
+ wazuhctrl mcp
87
+ ```
88
+
89
+ `wazuhctrl` reads the same environment as the MCP adapter: `WAZUH_URL`, `WAZUH_USERNAME`, `WAZUH_PASSWORD`, optional `WAZUH_INDEXER_URL`, and optional indexer credentials. Agent IP addresses stay hidden unless a command explicitly requests them.
61
90
 
62
91
  ## Usage
63
92
 
93
+ The quickstart `mcpServers` block at the top works for most clients. The per-client recipes below give you the exact file location or CLI command for each.
94
+
64
95
  ### Claude Desktop
65
96
 
66
- Add to your Claude Desktop configuration (`claude_desktop_config.json`):
97
+ Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
67
98
 
68
99
  ```json
69
100
  {
70
101
  "mcpServers": {
71
102
  "wazuh": {
72
- "command": "node",
73
- "args": ["/path/to/wazuh-mcp/dist/index.js"],
103
+ "command": "npx",
104
+ "args": ["-y", "wazuh-mcp"],
74
105
  "env": {
75
106
  "WAZUH_URL": "https://your-wazuh-manager:55000",
76
107
  "WAZUH_USERNAME": "wazuh-wui",
@@ -84,39 +115,108 @@ Add to your Claude Desktop configuration (`claude_desktop_config.json`):
84
115
  }
85
116
  ```
86
117
 
118
+ ### Claude Code
119
+
120
+ ```bash
121
+ claude mcp add wazuh \
122
+ --env WAZUH_URL=https://your-wazuh-manager:55000 \
123
+ --env WAZUH_USERNAME=wazuh-wui \
124
+ --env WAZUH_PASSWORD=your-password \
125
+ --env WAZUH_INDEXER_URL=https://your-wazuh-indexer:9200 \
126
+ --env WAZUH_INDEXER_USERNAME=admin \
127
+ --env WAZUH_INDEXER_PASSWORD=your-indexer-password \
128
+ -- npx -y wazuh-mcp
129
+ ```
130
+
131
+ Add `--scope user` to make it available from any directory instead of only the current project.
132
+
133
+ ### Codex CLI
134
+
135
+ [Codex CLI](https://github.com/openai/codex) registers MCP servers via `codex mcp add`:
136
+
137
+ ```bash
138
+ codex mcp add wazuh \
139
+ --env WAZUH_URL=https://your-wazuh-manager:55000 \
140
+ --env WAZUH_USERNAME=wazuh-wui \
141
+ --env WAZUH_PASSWORD=your-password \
142
+ --env WAZUH_INDEXER_URL=https://your-wazuh-indexer:9200 \
143
+ --env WAZUH_INDEXER_USERNAME=admin \
144
+ --env WAZUH_INDEXER_PASSWORD=your-indexer-password \
145
+ -- npx -y wazuh-mcp
146
+ ```
147
+
148
+ Codex writes the entry to `~/.codex/config.toml` under `[mcp_servers.wazuh]`. Verify with `codex mcp list`.
149
+
87
150
  ### OpenClaw
88
151
 
89
- Add to your `openclaw.json`:
152
+ With the npm package:
90
153
 
91
- ```json
92
- {
93
- "mcp": {
94
- "servers": {
95
- "wazuh": {
96
- "type": "stdio",
97
- "command": "node",
98
- "args": ["/path/to/wazuh-mcp/dist/index.js"],
99
- "env": {
100
- "WAZUH_URL": "https://your-wazuh-manager:55000",
101
- "WAZUH_USERNAME": "wazuh-wui",
102
- "WAZUH_PASSWORD": "your-password",
103
- "WAZUH_INDEXER_URL": "https://your-wazuh-indexer:9200",
104
- "WAZUH_INDEXER_USERNAME": "admin",
105
- "WAZUH_INDEXER_PASSWORD": "your-indexer-password"
106
- }
107
- }
108
- }
154
+ ```bash
155
+ openclaw mcp set wazuh '{
156
+ "command": "npx",
157
+ "args": ["-y", "wazuh-mcp"],
158
+ "env": {
159
+ "WAZUH_URL": "https://your-wazuh-manager:55000",
160
+ "WAZUH_USERNAME": "wazuh-wui",
161
+ "WAZUH_PASSWORD": "your-password",
162
+ "WAZUH_INDEXER_URL": "https://your-wazuh-indexer:9200",
163
+ "WAZUH_INDEXER_USERNAME": "admin",
164
+ "WAZUH_INDEXER_PASSWORD": "your-indexer-password"
109
165
  }
110
- }
166
+ }'
167
+ ```
168
+
169
+ Or, when running from a source checkout, point `command`/`args` at the built `dist/mcp-bin.js`:
170
+
171
+ ```bash
172
+ openclaw mcp set wazuh '{
173
+ "command": "node",
174
+ "args": ["/absolute/path/to/wazuh-mcp/dist/mcp-bin.js"],
175
+ "env": {
176
+ "WAZUH_URL": "https://your-wazuh-manager:55000",
177
+ "WAZUH_USERNAME": "wazuh-wui",
178
+ "WAZUH_PASSWORD": "your-password",
179
+ "WAZUH_INDEXER_URL": "https://your-wazuh-indexer:9200",
180
+ "WAZUH_INDEXER_USERNAME": "admin",
181
+ "WAZUH_INDEXER_PASSWORD": "your-indexer-password"
182
+ }
183
+ }'
184
+ ```
185
+
186
+ Then restart the gateway so the new server is picked up:
187
+
188
+ ```bash
189
+ systemctl --user restart openclaw-gateway
190
+ openclaw mcp list # confirm "wazuh" is registered
111
191
  ```
112
192
 
193
+ ### Hermes Agent
194
+
195
+ [Hermes Agent](https://github.com/NousResearch/hermes-agent) reads MCP config from `~/.hermes/config.yaml` under the `mcp_servers` key. Add an entry:
196
+
197
+ ```yaml
198
+ mcp_servers:
199
+ wazuh:
200
+ command: "npx"
201
+ args: ["-y", "wazuh-mcp"]
202
+ env:
203
+ WAZUH_URL: "https://your-wazuh-manager:55000"
204
+ WAZUH_USERNAME: "wazuh-wui"
205
+ WAZUH_PASSWORD: "your-password"
206
+ WAZUH_INDEXER_URL: "https://your-wazuh-indexer:9200"
207
+ WAZUH_INDEXER_USERNAME: "admin"
208
+ WAZUH_INDEXER_PASSWORD: "your-indexer-password"
209
+ ```
210
+
211
+ Then reload MCP from inside a Hermes session with `/reload-mcp`.
212
+
113
213
  ### Standalone
114
214
 
115
215
  ```bash
116
216
  export WAZUH_URL=https://your-wazuh-manager:55000
117
217
  export WAZUH_USERNAME=wazuh-wui
118
218
  export WAZUH_PASSWORD=your-password
119
- npm start
219
+ npx -y wazuh-mcp
120
220
  ```
121
221
 
122
222
  ### Development
@@ -129,6 +229,8 @@ npm test # Run tests
129
229
 
130
230
  ## MCP Tools
131
231
 
232
+ All 28 tools are read-only.
233
+
132
234
  ### Agent Tools
133
235
 
134
236
  | Tool | Description |
@@ -141,9 +243,16 @@ npm test # Run tests
141
243
 
142
244
  | Tool | Description |
143
245
  |------|-------------|
144
- | `get_alerts` | Retrieve recent alerts with filtering by level, agent, rule, and text search |
246
+ | `get_alerts` | Retrieve recent alerts with filtering by time range, level, agent, rule, and text search |
145
247
  | `get_alert` | Retrieve a single alert by ID |
146
- | `search_alerts` | Full-text search across all alerts |
248
+ | `search_alerts` | Full-text search across alerts with optional time range filtering |
249
+
250
+ ### Vulnerability Tools
251
+
252
+ | Tool | Description |
253
+ |------|-------------|
254
+ | `list_vulnerabilities` | List vulnerability inventory with optional CVE, agent, severity, and package filters |
255
+ | `search_vulnerabilities` | Search vulnerability inventory by CVE, package, agent, or description |
147
256
 
148
257
  ### Rule Tools
149
258
 
@@ -183,7 +292,7 @@ npm test # Run tests
183
292
  | Tool | Description |
184
293
  |------|-------------|
185
294
  | `get_manager_logs` | Get Wazuh manager logs filtered by level and module |
186
- | `get_manager_config` | Get active manager configuration by section |
295
+ | `get_manager_config` | Get active manager configuration by section with secret-like values redacted by default |
187
296
 
188
297
  ### Group Tools
189
298
 
@@ -198,6 +307,94 @@ npm test # Run tests
198
307
  |------|-------------|
199
308
  | `list_decoders` | List log decoders with optional name filtering |
200
309
  | `get_wazuh_version` | Get Wazuh manager version and API info |
310
+ | `diagnose_wazuh_connection` | Check sanitized configuration, URL/TLS settings, manager auth/version, and indexer readiness |
311
+
312
+ ## Configuration
313
+
314
+ Set the following environment variables:
315
+
316
+ | Variable | Required | Default | Description |
317
+ |----------|----------|---------|-------------|
318
+ | `WAZUH_URL` | Yes | - | Wazuh API URL (e.g., `https://192.0.2.2:55000`). Must be `https://` (or `http://` with `WAZUH_ALLOW_INSECURE_HTTP=true`), and must not contain embedded credentials, a query string, or a fragment. A path prefix for a reverse proxy is allowed. |
319
+ | `WAZUH_USERNAME` | Yes | - | API username |
320
+ | `WAZUH_PASSWORD` | Yes | - | API password |
321
+ | `WAZUH_VERIFY_SSL` | No | `true` | Verifies SSL certificates by default. Set to `false` (also accepts `0`/`no`/`off`) to disable verification for trusted self-signed lab environments only. |
322
+ | `WAZUH_CA_FILE` | No | - | Path to a PEM CA bundle used to verify the manager's TLS certificate (private CA or self-signed). Read at startup; the server exits with an error if the file cannot be read. Prefer this over `WAZUH_VERIFY_SSL=false`. |
323
+ | `WAZUH_ALLOW_INSECURE_HTTP` | No | `false` | Allow plain `http://` for `WAZUH_URL` and `WAZUH_INDEXER_URL`. When unset, `http://` URLs are rejected at startup. When enabled and in use, the server prints a startup warning to stderr. Trusted lab networks only. |
324
+ | `WAZUH_TIMEOUT` | No | `30` | Request timeout in seconds. Must be a positive integer. |
325
+ | `WAZUH_ALLOW_SENSITIVE_CONFIG` | No | `false` | Server-side gate for `get_manager_config`. When unset/`false`, sensitive configuration values are always redacted even if the tool's `include_sensitive_config` argument is `true`. Set to `true` (also accepts `1`/`yes`/`on`) to allow unredacted output when explicitly requested. |
326
+ | `WAZUH_MCP_MAX_RESPONSE_BYTES` | No | `250000` | Maximum MCP tool response size before returning a truncated preview with metadata. Values below `1024` are raised to `1024`. The truncated envelope itself always fits within the cap. |
327
+ | `WAZUH_MCP_MAX_STDIO_BUFFER_BYTES` | No | `8388608` (8 MiB) | Maximum stdio read-buffer size in bytes. Must be a positive integer. A single client message exceeding it makes the transport error and close. |
328
+
329
+ Alternative variable names `WAZUH_BASE_URL` and `WAZUH_USER` are also supported.
330
+
331
+ ### Wazuh Indexer (OpenSearch) - Required for Alerts and Vulnerabilities
332
+
333
+ Wazuh 4.x stores alerts and vulnerability inventory in the Wazuh Indexer (OpenSearch), not the REST API. To enable alert tools (`get_alerts`, `get_alert`, `search_alerts`), vulnerability tools (`list_vulnerabilities`, `search_vulnerabilities`), and the `wazuh://alerts/recent` resource, configure the indexer connection:
334
+
335
+ | Variable | Required | Default | Description |
336
+ |----------|----------|---------|-------------|
337
+ | `WAZUH_INDEXER_URL` | No | - | Wazuh Indexer URL (e.g., `https://192.0.2.2:9200`). Same URL rules as `WAZUH_URL`. |
338
+ | `WAZUH_INDEXER_USERNAME` | No | `admin` | Indexer username |
339
+ | `WAZUH_INDEXER_PASSWORD` | Yes, when `WAZUH_INDEXER_URL` is set | - | Indexer password. The server fails fast at startup if `WAZUH_INDEXER_URL` is set without it. |
340
+ | `WAZUH_INDEXER_VERIFY_SSL` | No | `true` | Verifies SSL certificates by default. Set to `false` (also accepts `0`/`no`/`off`) to disable verification for trusted self-signed lab environments only. |
341
+ | `WAZUH_INDEXER_CA_FILE` | No | - | Path to a PEM CA bundle used to verify the indexer's TLS certificate. Read at startup; the server exits with an error if the file cannot be read. |
342
+ | `WAZUH_INDEXER_TIMEOUT` | No | `30` | Indexer request timeout in seconds. Must be a positive integer. |
343
+
344
+ If `WAZUH_INDEXER_URL` is not set, alert and vulnerability tools will return a helpful configuration message. All other tools (agents, rules, decoders, version) work without the indexer.
345
+
346
+ Indexer searches count at most 10000 matching hits and carry a 30-second server-side `timeout`. When the real match count is higher, `pagination.total_is_lower_bound` is `true` and `total` is 10000. Alert and vulnerability tools accept `offset` up to 9999 and reject requests where `offset + limit` exceeds 10000 (OpenSearch's default `max_result_window`); narrow the query with filters or a time range instead of paging deeper.
347
+
348
+ SSL certificate verification is enabled by default (secure by default). When either SSL verification setting is explicitly set to `false`, the server prints a startup warning to stderr. TLS verification is disabled only for that configured Wazuh client.
349
+
350
+ ### Sensitive Output Defaults
351
+
352
+ Several tools return minimized output by default to avoid exposing raw logs, IPs, command lines, hashes, or raw event payloads unless requested:
353
+
354
+ | Tool | Hidden by default | Opt-in field |
355
+ |------|-------------------|--------------|
356
+ | `list_agents`, `get_agent`, `get_group_agents` | Agent IP details | `include_ip: true` |
357
+ | `get_alerts`, `search_alerts` | `full_log` | `include_full_log: true` |
358
+ | `get_alert` | `full_log`, raw `data` | `include_full_log: true`, `include_raw_data: true` |
359
+ | `list_vulnerabilities`, `search_vulnerabilities` | Vulnerability descriptions | `include_description: true` |
360
+ | `get_agent_processes` | Process command lines and arguments | `include_command: true` |
361
+ | `get_fim_files` | MD5 and SHA-256 hashes | `include_hashes: true` |
362
+ | `get_manager_logs` | Full log descriptions | `include_description: true` |
363
+ | `get_manager_config` | Secret-like config values | `include_sensitive_config: true` (only honored when the server-side `WAZUH_ALLOW_SENSITIVE_CONFIG` flag is enabled; otherwise always redacted) |
364
+
365
+ ### Untrusted SIEM Content
366
+
367
+ Alert, log, and inventory fields originate on monitored endpoints: anyone who can write a log line to a monitored host (a failed SSH login with a crafted username, a web request path, a syslog message) or control a process, package, file, or hostname on it controls the text that lands in `full_log`, alert `rule_description`, `agent_name`, `location`, and `decoder`, raw event `data`, manager log descriptions, agent names and OS fields, syscollector process/package/port/interface/hotfix fields, FIM paths and owners, rootcheck events, SCA policy and check text, and vulnerability package and description fields. To blunt prompt injection against the calling agent, the server wraps those values in `<untrusted_siem_data>...</untrusted_siem_data>` markers, includes an `output.untrusted_data_note` warning in affected responses, and states in the tool descriptions that the content is attacker-influenced data, never instructions to follow.
368
+
369
+ ### Input Validation
370
+
371
+ Tool inputs are validated before requests are sent to Wazuh. Pagination is bounded, search text is length-limited, sort fields are enumerated per tool, and path-oriented identifiers such as agent IDs, alert IDs, group IDs, and SCA policy IDs reject unsupported characters.
372
+
373
+ Paginated tool responses include a `pagination` object with `total`, `limit`, `offset`, and `has_more` fields while preserving the existing top-level `total`, `limit`, and `offset` fields.
374
+
375
+ Tool responses are capped by `WAZUH_MCP_MAX_RESPONSE_BYTES`. Oversized responses return valid JSON with `output.response_truncated`, byte counts, and a preview instead of flooding the MCP client.
376
+
377
+ Transient manager `GET` requests and indexer search/readiness requests retry briefly on `429`, `502`, `503`, `504`, and common transient network reset or timeout errors.
378
+
379
+ ## Features
380
+
381
+ - **28 MCP Tools** - Agents, alerts, vulnerabilities, rules, decoders, SCA, syscollector, FIM, rootcheck, groups, manager, and diagnostics
382
+ - **3 MCP Resources** - Pre-built views for agents, recent alerts, and rule summaries
383
+ - **3 MCP Prompts** - Alert investigation, agent health checks, and security overviews
384
+ - **Read-only by design** - The only writes are JWT auth and indexer `_search`; no tool changes Wazuh state
385
+ - **Secure by default** - TLS verification on, sensitive fields redacted unless opted in, untrusted SIEM content delimited, every error sanitized before it reaches the client
386
+ - **JWT Authentication** - Automatic token management with refresh on expiry
387
+ - **Full Compliance Mapping** - PCI-DSS, GDPR, HIPAA, NIST 800-53, MITRE ATT&CK
388
+ - **Pagination** - All list endpoints support limit/offset pagination
389
+ - **Type-Safe** - Full TypeScript with strict mode and Zod schema validation
390
+
391
+ ## Prerequisites
392
+
393
+ - Node.js 22+
394
+ <!-- content-guard: allow port-reference -->
395
+ - A running Wazuh manager with API access (default port 55000)
396
+ - Wazuh API credentials (username/password)
397
+ - (Optional) Wazuh Indexer (OpenSearch) access for alert queries
201
398
 
202
399
  ## MCP Resources
203
400
 
@@ -244,11 +441,38 @@ List all rules with level 12 or higher to see critical detection rules
244
441
  and their compliance framework mappings.
245
442
  ```
246
443
 
444
+ ## Why not the Wazuh dashboard or the raw API?
445
+
446
+ - **The Wazuh dashboard** is built for humans clicking through Kibana-style views. It is great for a SOC analyst at a screen, but an AI agent cannot drive it, and it does not turn natural-language questions into the right manager and indexer queries. wazuh-mcp gives the model typed tools instead.
447
+ - **The raw Wazuh REST API + indexer `_search`** can be called directly, but then every agent has to learn JWT auth, the manager-versus-indexer split (alerts and vulnerabilities live in the indexer in Wazuh 4.x), pagination shapes, and which fields are sensitive. wazuh-mcp wraps all of that, validates inputs, caps response size, and sanitizes errors so credentials never leak back to the model.
448
+ - **A general "run any HTTP request" tool** would technically reach Wazuh, but it hands the model your credentials, no input validation, no read-only guarantee, and no redaction of IPs, hashes, or full logs. This server is deliberately read-only and minimizes sensitive output by default.
449
+ - **Writing your own Wazuh MCP shim** is reasonable, and the source here is MIT-licensed if you want to fork it. This one already handles auth refresh, the indexer fallback message, untrusted-content delimiting, transient-error retries, and 28 vetted tools.
450
+
451
+ ## What wazuh-mcp is not
452
+
453
+ - **Not a write path.** No tool modifies Wazuh state. It cannot restart agents, edit rules, acknowledge alerts, or change configuration. The only writes are JWT authentication and indexer `_search` queries.
454
+ - **Not a replacement for the Wazuh dashboard or SIEM.** It is a query surface for AI clients, not an analyst UI, a data store, or an alerting engine.
455
+ - **Not a hosted service.** It runs locally as a stdio MCP server next to your client. Your Wazuh credentials stay on your machine and in your client's config.
456
+ - **Not a guarantee against prompt injection.** It delimits attacker-influenced SIEM content and warns the model, which reduces risk but does not eliminate it. Treat tool output as data, not instructions.
457
+ - **Not a way to bypass Wazuh access control.** It uses the credentials you give it and can see only what that account can see.
458
+
459
+ ## Documentation and links
460
+
461
+ - **Website:** [lidless.dev/wazuh-mcp](https://lidless.dev/wazuh-mcp)
462
+ - **npm:** [`wazuh-mcp`](https://www.npmjs.com/package/wazuh-mcp)
463
+ - **Issues:** [github.com/lidless-labs/wazuh-mcp/issues](https://github.com/lidless-labs/wazuh-mcp/issues)
464
+ - **Changelog:** [CHANGELOG.md](CHANGELOG.md)
465
+ - **Security policy:** [SECURITY.md](SECURITY.md)
466
+ - **Contributing:** [CONTRIBUTING.md](CONTRIBUTING.md)
467
+
247
468
  ## Testing
248
469
 
249
470
  ```bash
250
- npm test # Run all tests
251
- npm run test:watch # Watch mode
471
+ npm test # Run all tests
472
+ npm run typecheck # Type-check TypeScript
473
+ npm audit --omit=dev # Audit production dependencies
474
+ npm run pack:check # Verify package contents
475
+ npm run test:watch # Watch mode
252
476
  ```
253
477
 
254
478
  Tests use mocked Wazuh API responses - no live Wazuh instance needed.
@@ -258,7 +482,9 @@ Tests use mocked Wazuh API responses - no live Wazuh instance needed.
258
482
  ```
259
483
  wazuh-mcp/
260
484
  ├── src/
261
- │ ├── index.ts # MCP server entry point
485
+ │ ├── mcp-bin.ts # MCP server entry point
486
+ │ ├── cli.ts # wazuhctrl command entry point
487
+ │ ├── mcp-server.ts # shared MCP server factory
262
488
  │ ├── config.ts # Environment configuration
263
489
  │ ├── client.ts # Wazuh REST API client (JWT auth)
264
490
  │ ├── indexer-client.ts # Wazuh Indexer (OpenSearch) client
@@ -288,4 +514,12 @@ wazuh-mcp/
288
514
 
289
515
  ## License
290
516
 
291
- MIT
517
+ MIT. See [LICENSE](LICENSE).
518
+
519
+ ---
520
+
521
+ <p align="center"><a href="https://lidless.dev">Part of <strong>Lidless Labs</strong></a> &middot; the eye does not close</p>
522
+
523
+ <p align="center"><sub><strong>Security / SOC:</strong> <a href="https://github.com/lidless-labs/soc-stack">soc-stack</a> &middot; <a href="https://github.com/lidless-labs/misp-mcp">misp-mcp</a> &middot; <a href="https://github.com/lidless-labs/suricata-mcp">suricata-mcp</a> &middot; <a href="https://github.com/lidless-labs/thehive-mcp">thehive-mcp</a> &middot; <a href="https://github.com/lidless-labs/cortex-mcp">cortex-mcp</a> &middot; <a href="https://github.com/lidless-labs/mitre-mcp">mitre-mcp</a> &middot; <a href="https://github.com/lidless-labs/zeek-mcp">zeek-mcp</a> &middot; <a href="https://github.com/lidless-labs/hotwash">hotwash</a></sub></p>
524
+
525
+ <p align="center"><sub><a href="https://lidless.dev">All tools</a> &middot; <a href="https://github.com/lidless-labs">Lidless Labs on GitHub</a></sub></p>