Connecting Claude.AI to Wazuh for Intuitive Management


This article will guide you through setting up a Model Context Protocol (MCP) server to bridge communication between Anthropic Claude Desktop and the Wazuh API. This allows you to manage your Wazuh infrastructure using natural language commands directly from Claude.
Understanding MCP
The Model Context Protocol is an open standard developed by Anthropic. Its purpose is to facilitate seamless integration between AI models and external tools or data sources. Essentially, it enables AI assistants like Claude to interact with various systems through standardized interfaces.
In this guide, we'll architect and implement a minimal MCP server (which we'll refer to as a Master Control Program or MCP server). This server will act as a bridge between Claude Desktop (our AI interface) and the Wazuh API. The goal is to allow natural-language command interpretation and execution to manage Wazuh’s infrastructure.
Here's a high-level overview of what our Python Flask-based MCP server will do:
Receive a command (i.e.: "List all Wazuh agents") from Claude Desktop.
Interpret the command (using basic NLP).
Send the command to the Wazuh API.
Return the response to Claude Desktop.
Project Setup
Let's get your project organized. Here's the directory structure we'll use:
wazuh-mcp/
│
├── .env
├── wazuh_client.py # Wazuh API logic
├── wazuh_mcp.py # MCP Server logic
├── pyproject.toml
1. Install Poetry
First, you'll need to install Poetry. Poetry is a Python tool for managing dependencies and packaging. It helps you declare the libraries your project needs, installs and updates them, and uses a lockfile to ensure reproducible installations. It can also build your project for distribution.
You can find installation instructions on the official Poetry website.
2. Manage Dependencies
If you've previously experimented with other MCP frameworks (like FastMCP) and encountered compatibility issues with Claude Desktop, it's best to clean up those old dependencies first.
Remove old dependencies (if applicable):
poetry run pip uninstall fastmcp
Now, let's add the correct dependencies for our MCP server:
poetry add mcp requests python-decouple pydantic
3. Configure Wazuh API Access
Create a .env file in your wazuh-mcp/ directory to securely store your Wazuh API credentials. Replace the placeholder values with your actual Wazuh server details.
WAZUH_API=https://<WAZUH_SERVER>:55000
WAZUH_USER=wazuh-wui
WAZUH_PASSWORD=your_password
VERIFY_SSL=False
4. Configure pyproject.toml
The pyproject.toml
file is where Poetry defines your project's metadata and dependencies. Create or update this file in your wazuh-mcp/
directory with the following content.
[tool.poetry]
name = "wazuh-mcp"
version = "0.1.0"
description = "Wazuh MCP Server"
authors = ["WhatDoesKmean"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.10"
mcp = "^1.0.0"
requests = "^2.31.0"
python-decouple = "^3.8"
pydantic = "^2.0.0"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
5. Implement Wazuh API Client
Next, we'll create a Python module, wazuh_client.py
, to encapsulate the logic for interacting with the Wazuh API. This module will handle authentication and making API calls.
import requests
from decouple import config
WAZUH_API = config("WAZUH_HOST")
USERNAME = config("WAZUH_USER")
PASSWORD = config("WAZUH_PASSWORD")
VERIFY_SSL = config("VERIFY_SSL", default="true").lower() == "true"
def get_token():
r = requests.post(f"{WAZUH_API}/security/user/authenticate",
auth=(USERNAME, PASSWORD),
verify=VERIFY_SSL)
r.raise_for_status()
return r.json()["data"]["token"]
def list_agents(token):
r = requests.get(f"{WAZUH_API}/agents",
headers={"Authorization": f"Bearer {token}"},
verify=VERIFY_SSL)
r.raise_for_status()
return r.json()
def add_agent(token, name, group=None):
payload = {"name": name}
if group:
payload["group"] = group
r = requests.post(f"{WAZUH_API}/agents",
headers={"Authorization": f"Bearer {token}"},
json=payload,
verify=VERIFY_SSL)
r.raise_for_status()
return r.json()
6. Set Up the MCP Server
Now, let's create the main application file, wazuh_mcp.py
. This file will initialize the MCP server and define the mappings between natural language commands from Claude and the corresponding Wazuh API calls.
#!/usr/bin/env python3
import asyncio
import json
import sys
from typing import Any, Dict
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
from pydantic import BaseModel
from wazuh_client import get_token, list_agents, add_agent
# Create server instance
server = Server("wazuh")
class AddAgentArgs(BaseModel):
name: str
group: str = None
@server.list_tools()
async def list_tools() -> list[Tool]:
# List available tools
return [
Tool(
name="list_agents",
description="List all Wazuh agents",
inputSchema={
"type": "object",
"properties": {},
"required": []
}
),
Tool(
name="add_agent",
description="Add a new Wazuh agent",
inputSchema={
"type": "object",
"properties": {
"name": {"type": "string", "description": "Agent name"},
"group": {"type": "string", "description": "Agent group (optional)"}
},
"required": ["name"]
}
)
]
@server.call_tool()
async def call_tool(name: str, arguments: Dict[str, Any]) -> list[TextContent]:
# Handle tool calls
try:
if name == "list_agents":
token = get_token()
result = list_agents(token)
return [TextContent(type="text", text=json.dumps(result, indent=2))]
elif name == "add_agent":
args = AddAgentArgs(**arguments)
token = get_token()
result = add_agent(token, args.name, args.group)
return [TextContent(type="text", text=json.dumps(result, indent=2))]
else:
raise ValueError(f"Unknown tool: {name}")
except Exception as e:
return [TextContent(type="text", text=f"Error: {str(e)}")]
async def main():
# Main entry point
async with stdio_server() as (read_stream, write_stream):
await server.run(
read_stream,
write_stream,
server.create_initialization_options()
)
if __name__ == "__main__":
asyncio.run(main())
Running and Testing the MCP Server
To start your MCP server, navigate to your wazuh-mcp
directory in your terminal and execute the following command:
poetry run python wazuh_mcp.py
NOTE: When you run this command directly, you should expect no visible output, and the program will appear to "hang" or wait indefinitely. This is normal behavior for an MCP server because:
MCP servers communicate via stdio (standard input/output streams), not directly through the terminal.
The server is waiting for JSON-RPC messages from a client (like Claude Desktop).
No client is connected when you run it directly, so it simply waits silently.
You will see something similar to this:
C:\Users\WDKM\wazuh-mcp> poetry run python wazuh_mcp.py
[cursor waits here indefinitely with no output]
To test if it's working:
Press Ctrl+C to stop the server (you should see it exit cleanly).
Check for any error messages during startup (if there are import errors, authentication issues, etc, they would appear immediately)
Test the MCP Server step by step
Let's perform some basic tests to ensure everything is set up correctly.
Navigate to your project directory:
cd C:\Users\WDKM\wazuh-mcp
Test the MCP installation:
poetry run python -c "import mcp; print('Hello World from MCP!')
Test your wazuh_client:
poetry run python -c "from wazuh_client import get_token; print('Wazuh client works fine…')"
Integrate with Claude Desktop
The MCP server is designed to be launched and managed by Claude Desktop using your claude_desktop_config.json
configuration. When Claude Desktop starts the server, it handles initialization messages and JSON-RPC communication, calling your defined tools when requested.
Once your MCP server is running and configured with Claude Desktop, you can issue natural language commands like "List all Wazuh agents" which your MCP server will translate into appropriate API calls to Wazuh.
Add the following configuration to your claude_desktop_config.json
file. Remember to replace the placeholder values with your specific Wazuh server details and the correct path to your wazuh-mcp
directory.
{
"mcpServers": {
"wazuh": {
"command": "poetry",
"env": {
"WAZUH_HOST": "<YOUR-WAZUH-SERVER>:55000",
"WAZUH_USER": "<USERNAME>",
"WAZUH_PASSWORD": "<API_PASSWORD>",
"VERIFY_SSL": "false"
},
"args": [
"--directory",
"C:/Users/WDKM/wazuh-mcp",
"run",
"python",
"wazuh_mcp.py",
"stdio"
]
}
}
}
Usage with Claude Desktop
Once configured, you can start using natural language to interact with Wazuh through Claude.
Examples:
- List available agents:
Could you list all active agents in my Wazuh system?
- Create an authorization key for a new agent:
Could you generate an auth key to add a new Wazuh agent?
This setup provides a powerful way to manage your Wazuh environment, leveraging the natural language capabilities of Claude Desktop combined with the automation provided by your MCP server.
The foundation is set…. It's now up to you to implement and expose additional Wazuh API calls via your MCP server. 🔨
Now you Know! 😊💡
Subscribe to my newsletter
Read articles from WhatDoesKmean? directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

WhatDoesKmean?
WhatDoesKmean?
CyberSecurity 👽 | Splunk Ninja 🦸 | DataDog Tamer 🐾 | Sumo Logic Fighter 🧌 | Wazuh Explorer 🧙♂️ | EkoParty 2021 🎉 & SANS DFIR 2022 🔑 Speaker