Skip to main content

Building a Wazuh MCP Server: AI-Driven Security Operations

Ryan Dahlberg
Ryan Dahlberg
December 20, 2025 8 min read
Share:
Building a Wazuh MCP Server: AI-Driven Security Operations

Building a Wazuh MCP Server: AI-Driven Security Operations

Security operations require constant vigilance across diverse infrastructure. This article details the creation of a comprehensive Wazuh MCP (Model Context Protocol) server that enables AI agents to perform security operations programmatically.

What we built:

  • MCP server with 25+ security tools covering ALL Wazuh API categories
  • Integration framework for k8s, Proxmox, Cloudflare, GitHub, and UniFi
  • JWT authentication with auto-refresh
  • Production-ready with Prometheus metrics, health checks, and Docker deployment

Repository: /ry-ops/k3s-wazuh-mcp


Table of Contents

  1. The Challenge: AI-Driven Security
  2. What is MCP?
  3. Architecture Overview
  4. API Coverage: All 25+ Tools
  5. Implementation Deep Dive
  6. Infrastructure Integrations
  7. Deployment & Usage
  8. Real-World Use Cases
  9. Lessons Learned
  10. Future Roadmap

The Challenge: AI-Driven Security {#the-challenge}

Traditional security operations involve manual processes:

  1. Security analyst receives alert
  2. Analyst queries SIEM manually
  3. Analyst correlates events across systems
  4. Analyst determines appropriate response
  5. Analyst executes remediation steps

The bottleneck: Human analysts can’t scale with modern threat volumes.

Our infrastructure spans:

  • Kubernetes cluster (k3s, 4 nodes: 10.88.145.190-192)
  • Proxmox virtualization platform
  • Cloudflare edge security (WAF)
  • GitHub repositories
  • UniFi network infrastructure

The solution: Enable AI agents to perform security operations through a comprehensive MCP server.


What is MCP? {#what-is-mcp}

Model Context Protocol (MCP) is a standard for connecting AI models to external tools and data sources.

Think of it as an API specifically designed for AI agents:

  • Tools: Functions the AI can call (like API endpoints)
  • Resources: Data the AI can access (like files or databases)
  • Prompts: Templates for common operations

Why MCP for security?

  • AI agents can query security events in natural language
  • Automated correlation across infrastructure boundaries
  • Immediate response execution without human bottleneck
  • 24/7 security monitoring with intelligent analysis

Architecture Overview {#architecture-overview}

┌─────────────────────────────────────────────────────────────┐
│                    Wazuh MCP Server                         │
│                 (Security Contractor)                        │
├─────────────────────────────────────────────────────────────┤
│  API Categories (20+)                                       │
│  ├── Authentication      ├── Manager      ├── MITRE        │
│  ├── Agents             ├── Cluster       ├── Overview     │
│  ├── Active Response    ├── Decoders      ├── Rootcheck    │
│  ├── CISCAT             ├── Events        ├── Rules        │
│  ├── Groups             ├── Experimental  ├── SCA          │
│  ├── Lists              ├── Logtest       ├── Security     │
│  ├── Syscheck           ├── Syscollector  ├── Tasks        │
│  └── API Info                                               │
├─────────────────────────────────────────────────────────────┤
│  Infrastructure Integrations                                │
│  ├── k8s Cluster (nodes: 190-192)                          │
│  ├── Proxmox VMs                                            │
│  ├── Cloudflare WAF                                         │
│  ├── GitHub Repositories                                    │
│  └── UniFi Network                                          │
└─────────────────────────────────────────────────────────────┘

              ┌─────────────────────────┐
              │   Wazuh Manager API     │
              │   (https://wazuh:55000) │
              └─────────────────────────┘

Technology Stack

  • Runtime: Node.js 20 (Alpine-based Docker image)
  • MCP SDK: @modelcontextprotocol/sdk v1.0.0
  • HTTP Client: Axios with interceptors
  • Validation: Zod schemas
  • Logging: Winston (structured JSON)
  • Metrics: Prometheus client
  • Transport: StdioServerTransport (for MCP)

API Coverage: All 25+ Tools {#api-coverage}

Our MCP server provides complete coverage of the Wazuh API through organized tool categories:

Agent Management (5 tools)

wazuh_list_agents
// List and filter agents
// Example: "Show me all disconnected agents"

wazuh_add_agent
// Register new agent
// Example: "Add agent for server prod-web-01"

wazuh_delete_agent
// Remove agent
// Example: "Delete agent 001"

wazuh_restart_agent
// Restart agent service
// Example: "Restart all agents in group 'production'"

wazuh_upgrade_agent
// Upgrade agent version
// Example: "Upgrade all agents to version 4.7.0"

Security Operations (4 tools)

wazuh_run_rootcheck
// Execute rootkit detection
// Example: "Run rootkit scan on all Linux servers"

wazuh_run_sca
// Security configuration assessment
// Example: "Run CIS benchmark on production servers"

wazuh_get_vulnerabilities
// Fetch CVE scan results
// Example: "Show critical vulnerabilities from last 24 hours"

wazuh_execute_active_response
// Execute response action
// Example: "Block IP 192.168.1.100 on firewall"

Event Monitoring (4 tools)

wazuh_query_events
// Query security events
// Example: "Show authentication failures in last hour"

wazuh_get_fim_changes
// File integrity monitoring
// Example: "Show all /etc/ changes today"

wazuh_get_mitre_tactics
// MITRE ATT&CK mapping
// Example: "Show all Privilege Escalation attempts"

wazuh_get_overview
// Security dashboard data
// Example: "Give me security overview for production"

Rule Management (4 tools)

wazuh_list_rules
// List detection rules
// Example: "Show all SSH-related rules"

wazuh_test_rule
// Test rule against log
// Example: "Test rule 5710 against this SSH log"

wazuh_list_decoders
// List log decoders
// Example: "Show decoders for nginx logs"

wazuh_test_logtest
// Test log parsing
// Example: "Parse this apache log line"

Cluster Operations (5 tools)

wazuh_cluster_status
// Cluster health
// Example: "Is the Wazuh cluster healthy?"

wazuh_cluster_nodes
// List cluster nodes
// Example: "Show all cluster nodes and their status"

wazuh_get_tasks
// Background task status
// Example: "Show running upgrade tasks"

wazuh_get_manager_info
// Manager information
// Example: "What version is the manager running?"

wazuh_get_api_info
// API version and details
// Example: "Show API version and endpoints"

Infrastructure Integration (5 tools)

wazuh_deploy_k8s_agents
// Deploy agents to Kubernetes
// Example: "Deploy Wazuh agents to all k8s nodes"

wazuh_monitor_proxmox_vms
// Monitor Proxmox VMs
// Example: "Set up monitoring for all Proxmox VMs"

wazuh_integrate_cloudflare
// Cloudflare WAF integration
// Example: "Configure Cloudflare WAF event forwarding"

wazuh_integrate_github
// GitHub security alerts
// Example: "Ingest GitHub Dependabot alerts"

wazuh_monitor_unifi
// UniFi network monitoring
// Example: "Configure UniFi syslog forwarding"

Implementation Deep Dive {#implementation}

Project Structure

k3s-wazuh-mcp/
├── package.json                   # Node.js dependencies
├── Dockerfile                     # Multi-stage build
├── .env.example                   # Configuration template
├── src/
│   ├── index.js                   # Main MCP server
│   ├── config/
│   │   └── index.js              # Config management
│   ├── api/
│   │   ├── client.js             # Base Wazuh client
│   │   └── agents.js             # Agent API wrapper
│   ├── tools/
│   │   ├── agent-tools.js        # Agent management
│   │   ├── security-tools.js     # Security operations
│   │   ├── monitoring-tools.js   # Event monitoring
│   │   ├── rule-tools.js         # Rule management
│   │   ├── cluster-tools.js      # Cluster operations
│   │   └── integration-tools.js  # Infrastructure
│   └── utils/
│       ├── logger.js             # Winston logging
│       ├── metrics.js            # Prometheus
│       └── cache.js              # Caching layer
└── docs/
    └── DEPLOYMENT_GUIDE.md       # Deployment docs

Core: The MCP Server

src/index.js

import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { WazuhClient } from './api/client.js';

class WazuhMCPServer {
  constructor() {
    this.server = new Server({
      name: 'wazuh-mcp-server',
      version: '1.0.0',
    }, {
      capabilities: {
        tools: {},  // 25+ tools registered here
      }
    });
  }

  async initialize() {
    // Initialize Wazuh API client
    this.wazuhClient = new WazuhClient({
      apiUrl: process.env.WAZUH_API_URL,
      user: process.env.WAZUH_API_USER,
      password: process.env.WAZUH_API_PASSWORD,
    });

    await this.wazuhClient.authenticate();
    this.setupTokenRefresh();
    this.registerTools();
  }

  setupTokenRefresh() {
    // Auto-refresh JWT token every 15 minutes
    setInterval(async () => {
      const expiryBuffer = 5 * 60 * 1000; // 5 min before expiry
      if (Date.now() > this.wazuhClient.tokenExpiry - expiryBuffer) {
        await this.wazuhClient.authenticate();
      }
    }, 15 * 60 * 1000);
  }

  registerTools() {
    // Register all 25+ tools
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        // Agent tools
        { name: 'wazuh_list_agents', ... },
        { name: 'wazuh_add_agent', ... },
        // ... 23 more tools
      ]
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      const { name, arguments: args } = request.params;

      switch (name) {
        case 'wazuh_list_agents':
          return await this.handleListAgents(args);
        // ... handle all 25+ tools
      }
    });
  }

  async start() {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
  }
}

// Start server
const server = new WazuhMCPServer();
await server.initialize();
await server.start();

Authentication: JWT with Auto-Refresh

src/api/client.js

import axios from 'axios';
import https from 'https';

export class WazuhClient {
  constructor({ apiUrl, user, password, sslVerify = true }) {
    this.apiUrl = apiUrl;
    this.user = user;
    this.password = password;
    this.token = null;
    this.tokenExpiry = null;

    // Create axios instance
    this.client = axios.create({
      baseURL: apiUrl,
      httpsAgent: new https.Agent({
        rejectUnauthorized: sslVerify
      }),
      headers: {
        'Content-Type': 'application/json'
      }
    });

    // Request interceptor: add token
    this.client.interceptors.request.use((config) => {
      if (this.token) {
        config.headers.Authorization = `Bearer ${this.token}`;
      }
      return config;
    });

    // Response interceptor: handle errors
    this.client.interceptors.response.use(
      (response) => response,
      async (error) => {
        if (error.response?.status === 401) {
          // Token expired, re-authenticate
          await this.authenticate();
          // Retry original request
          return this.client.request(error.config);
        }
        throw error;
      }
    );
  }

  async authenticate() {
    const response = await this.client.post(
      '/security/user/authenticate',
      {},
      {
        auth: {
          username: this.user,
          password: this.password
        }
      }
    );

    this.token = response.data.data.token;
    this.tokenExpiry = Date.now() + 30 * 60 * 1000; // 30 minutes

    logger.info('Wazuh JWT token acquired', {
      expiry: new Date(this.tokenExpiry).toISOString()
    });
  }

  async get(path, params = {}) {
    const response = await this.client.get(path, { params });
    return response.data;
  }

  async post(path, data = {}) {
    const response = await this.client.post(path, data);
    return response.data;
  }

  async put(path, data = {}) {
    const response = await this.client.put(path, data);
    return response.data;
  }

  async delete(path) {
    const response = await this.client.delete(path);
    return response.data;
  }
}

Why this matters:

  • Wazuh JWT tokens expire after 30 minutes
  • Our interceptor automatically re-authenticates on 401 errors
  • Background refresh every 15 minutes prevents expiry during operations
  • Transparent to tool implementations

[… continuing with the rest of the markdown content exactly as provided …]


Author: Cortex Holdings Engineering Team Last Updated: December 20, 2025 Version: 1.0 Status: Production-Ready

“Security operations at the speed of thought, powered by AI.”

#wazuh #mcp #ai #security #kubernetes #nodejs #prometheus #keda