npm package: @modular-intelligence/siem-query
Description: MCP server for multi-SIEM query interface (Splunk, Elasticsearch, Wazuh)
Latest version: 1.0.2

# SIEM Query MCP Server

A comprehensive multi-SIEM query interface that provides unified access to multiple security information and event management (SIEM) platforms. This MCP (Model Context Protocol) server enables Claude to execute queries across Splunk, Elasticsearch, and Wazuh, with additional tools for query translation, validation, and explanation.

## Overview

This server provides access to three major SIEM platforms through a unified interface, plus utility tools for working with SIEM queries:

- **Splunk** - Execute SPL (Search Processing Language) queries for log analysis and security investigations
- **Elasticsearch** - Execute KQL/Lucene queries for distributed search and analytics
- **Wazuh** - Query Wazuh alerts, events, and security monitoring data

Query utilities include Sigma rule translation, syntax validation, and natural language query explanations.

Perfect for security investigations, threat hunting, alert correlation, query optimization, and SIEM-agnostic threat detection.

## Tools

| Tool | SIEM | Description |
|------|------|-------------|
| `splunk-search` | Splunk | Execute SPL queries with flexible time ranges and result limits |
| `elastic-search` | Elasticsearch | Execute KQL/Lucene queries for document search and analysis |
| `wazuh-search` | Wazuh | Query Wazuh alerts and security events with agent filtering |
| `query-translate` | All | Translate Sigma detection rules to SIEM-specific query syntax |
| `query-validate` | All | Validate query syntax and detect potential errors or issues |
| `query-explain` | All | Generate plain English explanations of SIEM queries |

### Splunk Search

Execute SPL queries against a Splunk instance to search logs and events.

**Input Parameters:**
```typescript
{
  query: string          // SPL search query (e.g., "error OR failed")
  index: string          // Splunk index to search (optional, default: main)
  time_range: string     // Time range in Splunk format (e.g., "-24h")
  max_results: number    // Maximum number of results to return (1-10000)
}
```

**Example Request:**
```json
{
  "query": "EventCode=4625 | stats count by src_ip",
  "index": "security",
  "time_range": "-24h",
  "max_results": 100
}
```

**Example Output:**
```json
{
  "platform": "splunk",
  "query": "search index=security EventCode=4625 | stats count by src_ip earliest=-24h | head 100",
  "results": [
    {
      "src_ip": "192.168.1.100",
      "count": 42
    },
    {
      "src_ip": "10.0.0.50",
      "count": 18
    },
    {
      "src_ip": "172.16.0.20",
      "count": 5
    }
  ],
  "count": 3,
  "time_range": "-24h"
}
```

### Elasticsearch Search

Execute KQL or Lucene queries against an Elasticsearch cluster.

**Input Parameters:**
```typescript
{
  query: string          // KQL or Lucene query string
  index: string          // Index pattern to search (e.g., "logs-*")
  time_range: string     // Time range in Splunk format (e.g., "-1h")
  max_results: number    // Maximum number of results to return (1-10000)
}
```

**Example Request:**
```json
{
  "query": "status:500 AND method:POST",
  "index": "logs-web-*",
  "time_range": "-1h",
  "max_results": 50
}
```

**Example Output:**
```json
{
  "platform": "elasticsearch",
  "query": "status:500 AND method:POST",
  "index": "logs-web-*",
  "results": [
    {
      "@timestamp": "2024-01-15T10:30:45.123Z",
      "status": 500,
      "method": "POST",
      "path": "/api/users",
      "response_time": 2500,
      "error": "Database connection timeout"
    },
    {
      "@timestamp": "2024-01-15T10:31:20.456Z",
      "status": 500,
      "method": "POST",
      "path": "/api/events",
      "response_time": 1800,
      "error": "Memory limit exceeded"
    }
  ],
  "count": 2,
  "time_range": "-1h"
}
```

### Wazuh Search

Query Wazuh alerts and security events with optional agent filtering.

**Input Parameters:**
```typescript
{
  query: string          // Wazuh query in key=value format (e.g., "rule.level=10")
  agent_id: string       // Wazuh agent ID to filter by (optional)
  time_range: string     // Time range in Splunk format (e.g., "-7d")
  max_results: number    // Maximum number of results to return (1-10000)
}
```

**Example Request:**
```json
{
  "query": "rule.level=10;rule.groups=web_application_attack",
  "agent_id": "001",
  "time_range": "-7d",
  "max_results": 200
}
```

**Example Output:**
```json
{
  "platform": "wazuh",
  "query": "rule.level=10;rule.groups=web_application_attack",
  "agent_id": "001",
  "results": [
    {
      "timestamp": "2024-01-14T15:30:00.000Z",
      "rule": {
        "id": 31105,
        "level": 10,
        "description": "SQL injection attempt detected",
        "groups": ["web_application_attack", "attack_attempt"]
      },
      "agent": {
        "id": "001",
        "name": "web-server-01"
      },
      "data": {
        "srcip": "203.0.113.45",
        "dstip": "192.0.2.1",
        "http_method": "POST",
        "uri": "/api/search?q=1' OR '1'='1"
      }
    },
    {
      "timestamp": "2024-01-14T16:45:30.000Z",
      "rule": {
        "id": 31108,
        "level": 10,
        "description": "XSS attempt detected",
        "groups": ["web_application_attack"]
      },
      "agent": {
        "id": "001",
        "name": "web-server-01"
      },
      "data": {
        "srcip": "203.0.113.46",
        "dstip": "192.0.2.1",
        "http_method": "GET",
        "uri": "/search?q=<script>alert('xss')</script>"
      }
    }
  ],
  "count": 2,
  "time_range": "-7d"
}
```

### Query Translate

Translate Sigma detection rules to SIEM-specific query syntax.

**Input Parameters:**
```typescript
{
  sigma_rule: string     // Sigma rule in YAML format
  target_siem: string    // Target SIEM: "splunk", "elastic", or "wazuh"
}
```

**Example Request:**
```json
{
  "sigma_rule": "title: Suspicious PowerShell Download\ndescription: Detects PowerShell used to download files\ndetection:\n  selection:\n    EventID: 4688\n    CommandLine: '*DownloadFile*'\n  condition: selection\nlogsource:\n  product: windows\n  service: security",
  "target_siem": "splunk"
}
```

**Example Output:**
```json
{
  "source": "sigma",
  "target_siem": "splunk",
  "original_title": "Suspicious PowerShell Download",
  "translated_query": "sourcetype=\"windows\" EventID=\"4688\" CommandLine=\"*DownloadFile*\"",
  "logsource": {
    "product": "windows",
    "service": "security"
  }
}
```

### Query Validate

Validate query syntax for target SIEM platforms and detect potential errors.

**Input Parameters:**
```typescript
{
  query: string          // Query string to validate
  target_siem: string    // Target SIEM: "splunk", "elastic", or "wazuh"
}
```

**Example Request:**
```json
{
  "query": "index=main source=\"*.log\" | stats count by host",
  "target_siem": "splunk"
}
```

**Example Output:**
```json
{
  "valid": true,
  "errors": [],
  "warnings": [],
  "query": "index=main source=\"*.log\" | stats count by host",
  "target_siem": "splunk"
}
```

### Query Explain

Generate plain English explanations of SIEM queries to understand what they do.

**Input Parameters:**
```typescript
{
  query: string          // Query string to explain
  target_siem: string    // Target SIEM: "splunk", "elastic", or "wazuh"
}
```

**Example Request:**
```json
{
  "query": "index=security EventCode=4625 | stats count by src_ip | sort -count",
  "target_siem": "splunk"
}
```

**Example Output:**
```json
{
  "summary": "Search and process events through 2 transformation step(s)",
  "breakdown": [
    "Search for events matching: EventCode=4625",
    "Calculate statistics: count by src_ip",
    "Sort results by: -count"
  ],
  "query": "index=security EventCode=4625 | stats count by src_ip | sort -count",
  "target_siem": "splunk"
}
```

## Configuration

### Environment Variables

This server requires connection details for one or more SIEM platforms. Set these environment variables before running:

```bash
export SPLUNK_URL="https://your-splunk-instance:8089"
export SPLUNK_TOKEN="your-splunk-api-token"
export ELASTIC_URL="https://your-elasticsearch-instance:9200"
export ELASTIC_API_KEY="your-elasticsearch-api-key"
export WAZUH_URL="https://your-wazuh-instance:55000"
export WAZUH_USER="your-wazuh-username"
export WAZUH_PASSWORD="your-wazuh-password"
```

### Getting Credentials

**Splunk**
- Access your Splunk instance at https://your-splunk-instance:8089
- Navigate to Settings -> Tokens to create or retrieve an API token
- Use the token for SPLUNK_TOKEN
- URL format: https://splunk-host:8089 (default port is 8089)

**Elasticsearch**
- Access your Elasticsearch cluster at https://your-elastic-instance:9200
- Generate an API key via Management -> Dev Tools -> Elasticsearch API
- Use the encoded API key for ELASTIC_API_KEY
- URL format: https://elastic-host:9200

**Wazuh**
- Access your Wazuh manager API at https://your-wazuh-instance:55000
- Default credentials are admin/admin (change immediately in production)
- Uses Basic Auth with username and password
- URL format: https://wazuh-host:55000

## Installation

### Prerequisites

- Bun runtime (version 1.x or later)
- Node.js 18+ (alternative runtime)
- Network access to at least one configured SIEM platform
- Valid credentials or API keys for your SIEM instances

### Steps

1. Clone or download this repository:
```bash
git clone <repo-url>
cd siem-query
```

2. Install dependencies:
```bash
bun install
```

3. Build the project:
```bash
bun run build
```

4. Set environment variables:
```bash
export SPLUNK_URL="https://your-splunk:8089"
export SPLUNK_TOKEN="your-token"
export ELASTIC_URL="https://your-elastic:9200"
export ELASTIC_API_KEY="your-key"
export WAZUH_URL="https://your-wazuh:55000"
export WAZUH_USER="username"
export WAZUH_PASSWORD="password"
```

5. Run the server:
```bash
bun run start
```

The server will start listening on stdio transport.

## Usage

### Running the Server

Start the server with Bun:

```bash
bun run src/index.ts
```

The server implements the Model Context Protocol (MCP) and communicates via stdio transport. It can be integrated with Claude or other MCP clients.

### Claude Desktop Configuration

Add the server to your Claude Desktop configuration at `~/Library/Application Support/Claude/claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "siem-query": {
      "command": "bun",
      "args": [
        "run",
        "/path/to/siem-query/src/index.ts"
      ],
      "env": {
        "SPLUNK_URL": "https://your-splunk:8089",
        "SPLUNK_TOKEN": "your-splunk-token",
        "ELASTIC_URL": "https://your-elasticsearch:9200",
        "ELASTIC_API_KEY": "your-elasticsearch-key",
        "WAZUH_URL": "https://your-wazuh:55000",
        "WAZUH_USER": "your-wazuh-username",
        "WAZUH_PASSWORD": "your-wazuh-password"
      }
    }
  }
}
```

### Claude Code MCP Settings

Configure the server in Claude Code's MCP settings (typically in `.mcp.json` or via settings UI):

```json
{
  "servers": {
    "siem-query": {
      "transport": "stdio",
      "command": "bun",
      "args": ["run", "/path/to/siem-query/src/index.ts"],
      "env": {
        "SPLUNK_URL": "https://your-splunk:8089",
        "SPLUNK_TOKEN": "your-splunk-token",
        "ELASTIC_URL": "https://your-elasticsearch:9200",
        "ELASTIC_API_KEY": "your-elasticsearch-key",
        "WAZUH_URL": "https://your-wazuh:55000",
        "WAZUH_USER": "your-wazuh-username",
        "WAZUH_PASSWORD": "your-wazuh-password"
      }
    }
  }
}
```

### Example Usage in Claude

Once configured, you can use the tools directly in conversations with Claude:

**Request:**
"Search Splunk for failed login attempts in the last 24 hours and show me the top 10 source IPs"

**Claude will call:**
```json
{
  "tool": "splunk-search",
  "input": {
    "query": "EventCode=4625 | stats count by src_ip | sort -count",
    "index": "security",
    "time_range": "-24h",
    "max_results": 10
  }
}
```

**Request:**
"Check Elasticsearch for HTTP 500 errors in the last hour"

**Claude will call:**
```json
{
  "tool": "elastic-search",
  "input": {
    "query": "status:500",
    "index": "logs-*",
    "time_range": "-1h",
    "max_results": 100
  }
}
```

**Request:**
"Translate this Sigma rule to Splunk format: [Sigma YAML rule]"

**Claude will call:**
```json
{
  "tool": "query-translate",
  "input": {
    "sigma_rule": "title: Suspicious Activity\ndetection:\n  selection:\n    EventID: 4688\n  condition: selection",
    "target_siem": "splunk"
  }
}
```

**Request:**
"Validate this Splunk query and check for syntax errors: index=main | stats count"

**Claude will call:**
```json
{
  "tool": "query-validate",
  "input": {
    "query": "index=main | stats count",
    "target_siem": "splunk"
  }
}
```

**Request:**
"Explain what this Wazuh query does: rule.level=10;agent.id=001"

**Claude will call:**
```json
{
  "tool": "query-explain",
  "input": {
    "query": "rule.level=10;agent.id=001",
    "target_siem": "wazuh"
  }
}
```

## Security

This server implements comprehensive security controls to prevent injection attacks, enforce read-only access, and protect sensitive credentials:

### Query Restrictions

**Splunk SPL Blocking**
- Blocked destructive commands: delete, sendalert, outputlookup, outputcsv, collect, mcollect
- No shell command injection characters allowed: semicolons, ampersands, backticks, dollar signs
- Maximum query length: 5,000 characters
- All queries are read-only searches only

**Elasticsearch Protection**
- No destructive HTTP methods allowed (DELETE, PUT)
- Write operations blocked at query validation layer
- Maximum query length: 5,000 characters
- Only read operations permitted (GET, POST searches)

**Wazuh Query Restrictions**
- Query format strictly enforced as key=value pairs separated by semicolons
- No shell metacharacters allowed: semicolons, ampersands, backticks, dollar signs
- Maximum query length: 5,000 characters
- Alert queries only, no configuration modifications

### Credential Protection

- All credentials passed via environment variables only
- No credentials logged or returned in responses
- Credentials never sent to Claude or logged in query results
- API keys and passwords handled securely in memory

### Input Validation

- Query syntax validation before execution
- Malicious command detection (regex-based blocking)
- Parenthesis and quote matching verification
- Special character validation for each SIEM type
- Query length limits enforced pre-execution

### What Gets Blocked

The server rejects:
- Queries containing blocked destructive SPL commands
- Elasticsearch DELETE/PUT operations
- Queries exceeding 5,000 character limit
- Queries with unmatched quotes or parentheses
- Queries containing shell injection metacharacters
- Missing or invalid SIEM configuration variables
- Elasticsearch write operations

### Error Handling

- Invalid queries return descriptive error messages
- API authentication failures reported with guidance
- Configuration errors provide setup instructions
- Network timeouts handled gracefully
- Invalid input caught before SIEM queries execute

## License

ISC License - see LICENSE file for details