Skip to main content
Glama
vanovarderesyan

Elastic MCP Server

Elastic MCP Server

A Model Context Protocol (MCP) server that gives AI assistants read-only access to your Elasticsearch/Kibana logs. Ask questions in natural language and get answers backed by real log data.

Works with Claude Code, Claude Desktop, and any MCP-compatible client. Your project can be in any language — Python, Java, Go, Node.js, etc. This server runs independently.

How It Works

Your project (any language)     elastic-mcp-server           Kibana / Elasticsearch
┌───────────────────┐          ┌──────────────────┐          ┌──────────────────┐
│  Claude Code or   │  stdio   │                  │  HTTPS   │                  │
│  Claude Desktop   │────MCP──>│  Translates to   │────────->│  Executes query  │
│                   │<─────────│  Kibana queries  │<─────────│  Returns results │
└───────────────────┘          └──────────────────┘          └──────────────────┘

The server runs as a standalone Node.js process alongside your project. Claude spawns it, communicates via stdio, and uses it to search your logs. It doesn't depend on your project's language, framework, or build system. All requests are read-only.

Related MCP server: log-mcp

Requirements

  • Node.js >= 18 or Docker (only for running this MCP server — your project can use any language)

Quick Start

Option A: Node.js

1. Clone and build

git clone https://github.com/vanovarderesyan/elastic-mcp-server.git
cd elastic-mcp-server
npm install
npm run build

This creates the compiled server at dist/main.js.

2. Configure

Add the MCP server to your AI assistant. You need:

  • The absolute path to dist/main.js where you cloned this repo

  • Your Kibana URL and credentials

Claude Code — create or edit .mcp.json in any project directory:

{
  "mcpServers": {
    "elasticsearch": {
      "command": "node",
      "args": ["/home/john/elastic-mcp-server/dist/main.js"],
      "env": {
        "ELASTIC_NODES": "https://kibana.example.com",
        "ELASTIC_USERNAME": "your-username",
        "ELASTIC_PASSWORD": "your-password"
      }
    }
  }
}

Claude Desktop — edit the config file:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

  • Windows: %APPDATA%\Claude\claude_desktop_config.json

{
  "mcpServers": {
    "elasticsearch": {
      "command": "node",
      "args": ["/home/john/elastic-mcp-server/dist/main.js"],
      "env": {
        "ELASTIC_NODES": "https://kibana.example.com",
        "ELASTIC_USERNAME": "your-username",
        "ELASTIC_PASSWORD": "your-password"
      }
    }
  }
}

Note: Replace /home/john/elastic-mcp-server with the actual path where you cloned this repo.

Option B: Docker

No Node.js required — just Docker.

1. Build the image

git clone https://github.com/vanovarderesyan/elastic-mcp-server.git
cd elastic-mcp-server
docker build -t elastic-mcp-server .

2. Configure

Claude Code — create or edit .mcp.json:

{
  "mcpServers": {
    "elasticsearch": {
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "-e", "ELASTIC_NODES=https://kibana.example.com",
        "-e", "ELASTIC_USERNAME=your-username",
        "-e", "ELASTIC_PASSWORD=your-password",
        "elastic-mcp-server"
      ]
    }
  }
}

Claude Desktop — same structure in your claude_desktop_config.json.

Why -i and no -t? MCP communicates over stdin/stdout using structured JSON. The -i flag keeps stdin open for the MCP protocol. The -t flag (TTY) is not needed and would interfere with the JSON stream.

Cloudflare Zero Trust with Docker

If your Kibana is behind Cloudflare Access, the container needs access to your host's Cloudflare tokens. Mount the ~/.cloudflared directory as a read-only volume:

{
  "mcpServers": {
    "elasticsearch": {
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "-v", "/home/john/.cloudflared:/root/.cloudflared:ro",
        "-e", "ELASTIC_NODES=https://kibana.example.com",
        "-e", "ELASTIC_USERNAME=your-username",
        "-e", "ELASTIC_PASSWORD=your-password",
        "elastic-mcp-server"
      ]
    }
  }
}

Replace /home/john/.cloudflared with your actual home directory path (e.g., /Users/jane/.cloudflared on macOS).

You must authenticate on your host machine first:

cloudflared access login https://kibana.example.com

The container reads the cached token from the mounted volume. When the token expires, re-run cloudflared access login on your host — the container picks up the new token automatically.

Alternatively, pass a token directly via environment variable:

"-e", "CF_ACCESS_TOKEN=eyJhbGciOi..."

3. Discover your indices

Every Elasticsearch setup is different. Your indices might be named filebeat-*, logstash-*, production-api-*, staging-*, or something else entirely. The first thing to do after setup is ask Claude to list what's available:

List all data views

This shows you the actual index patterns in your Kibana. Use those names when searching.

Important: By default, the index parameter is *, which searches all environments (production, staging, dev, etc.). Always specify the index when searching to avoid mixing logs from different environments:

Search for errors in production-api-* from the last hour
Show me logs from staging-auth-service-* today
Find "timeout" errors in filebeat-production-*

4. Start asking questions

Show me errors in production-* from the last hour
What are the top 10 services by log volume today in production-*?
Find logs containing "connection refused" in staging-*
Show error trends over the past 24 hours in filebeat-*
What fields are available in the logs-nginx-* index?

Tip: If you're not sure which index to use, just ask Claude — it will use the list_data_views tool to find the right one.

Tools

The server exposes 7 read-only tools:

search_logs

Search logs with filters, time ranges, and free-text queries.

Parameter

Type

Default

Description

index

string

*

Index pattern (e.g., logs-*, filebeat-*)

query

string

Free-text query (Lucene syntax)

service

string

Filter by kubernetes.container.name

from

string

Start time (now-1h, 2024-01-01T00:00:00Z)

to

string

End time (now)

filters

object

Key-value field filters (e.g., {"stream": "stderr"})

messageLevel

string

Log level inside JSON message (error, warn, info)

messageQuery

string

Phrase search within message field

excludeHealthChecks

boolean

false

Exclude health/readiness probe logs

size

number

50

Max results to return

sort

asc/desc

desc

Sort order by timestamp

aggregate_logs

Run aggregations for analytics and trend analysis.

Parameter

Type

Default

Description

index

string

*

Index pattern

aggType

enum

terms, date_histogram, stats, or count

field

string

Field to aggregate on

service

string

Filter by service name

from / to

string

Time range

interval

string

1h

Interval for date_histogram

query

string

Free-text filter

messageQuery

string

Phrase search in message

filters

object

Key-value field filters

list_indices

List available data views filtered by pattern.

Parameter

Type

Default

Description

pattern

string

*

Glob-style filter (e.g., logs-*)

list_data_views

List all Kibana data views (index patterns). No parameters.

get_mapping

Get field names, types, and whether they are aggregatable for an index.

Parameter

Type

Description

index

string

Index pattern (e.g., filebeat-*)

get_document

Fetch a single document by its Elasticsearch ID.

Parameter

Type

Description

index

string

Index name or pattern

id

string

Document ID

cluster_health

Check Kibana connectivity and list sample data views. No parameters.

Configuration

All configuration is via environment variables in the env block of your MCP config.

Variable

Required

Default

Description

ELASTIC_NODES

Yes

Kibana base URL (e.g., https://kibana.example.com)

ELASTIC_USERNAME

No

Basic auth username

ELASTIC_PASSWORD

No

Basic auth password

CF_ACCESS_TOKEN

No

auto

Cloudflare Access JWT (see below)

KIBANA_SPACE

No

default

Kibana space name

ELASTIC_TLS_REJECT_UNAUTHORIZED

No

true

Set false for self-signed certificates

ELASTIC_REQUEST_TIMEOUT

No

60000

Request timeout in milliseconds

ELASTIC_MAX_RESULTS

No

500

Maximum number of search results

ALLOWED_SERVICES

No

(all)

Comma-separated allowlist of kubernetes.container.name values

Index Patterns

The index parameter in search and aggregation tools accepts Elasticsearch index patterns. These depend on your setup. Common examples:

Setup

Typical Pattern

Example

Filebeat

filebeat-*

filebeat-2024.01.*

Logstash

logstash-*

logstash-production-*

Fluentd/Fluent Bit

fluentd-* or fluent-bit-*

fluent-bit-staging-*

Data streams

logs-*

logs-nginx-*

Custom per-env

production-*, staging-*

production-api-gateway*

Use the list_data_views tool to see what's available in your Kibana, or ask Claude: "What indices do we have?"

Service Allowlist

Restrict which Kubernetes services can be queried by setting ALLOWED_SERVICES:

"ALLOWED_SERVICES": "api-gateway,auth-service,payment-service"

When empty or unset, all services are accessible.

Authentication

Basic Auth

Set ELASTIC_USERNAME and ELASTIC_PASSWORD in the env block. These are sent as a standard HTTP Basic auth header on every request.

Cloudflare Zero Trust (optional)

If your Kibana is behind Cloudflare Zero Trust (formerly Cloudflare Access), the server handles token management automatically. No extra configuration is needed beyond the initial login.

How it works

When the server starts, it looks for a Cloudflare Access JWT to include as a cf-access-token header on every request to Kibana. The token resolution order is:

  1. Read from ~/.cloudflared/ cachecloudflared stores tokens as files named <hostname>-<audience>-token after a successful login

  2. Auto-refresh via cloudflared access login — if the cached token is expired or missing, the server runs cloudflared access login <url> which opens a browser for authentication (Node.js only, not in Docker)

  3. Fall back to CF_ACCESS_TOKEN env var — if the above steps fail, the server uses the token from the environment variable

  4. Retry on 401/403 — if a request fails with an auth error, the server re-reads the token from disk and retries once (handles cases where the token was refreshed externally)

Prerequisites

Install cloudflared:

# macOS
brew install cloudflared

# Linux (Debian/Ubuntu)
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflare-main $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/cloudflare-main.list
sudo apt update && sudo apt install cloudflared

First-time setup

cloudflared access login https://your-kibana-url.com

A browser window opens for authentication. After completing the login, a JWT is cached in ~/.cloudflared/. The server reads this automatically — no need to copy tokens manually.

Token lifecycle

Scenario

What happens

Token valid

Used automatically from ~/.cloudflared/

Token expired

Server runs cloudflared access login → browser opens → new token cached

Token expired (Docker)

Browser can't open — re-run cloudflared access login on your host machine

Manual token

Set CF_ACCESS_TOKEN env var to skip auto-detection entirely

Request gets 401/403

Server re-reads token from disk and retries once

Docker considerations

Docker containers can't open a browser for interactive Cloudflare login. Two options:

Option 1: Mount the host's token cache (recommended)

"-v", "/home/john/.cloudflared:/root/.cloudflared:ro"

The container reads your host's cached tokens. When they expire, re-run cloudflared access login on your host machine — the container sees the updated token immediately via the mounted volume.

Option 2: Pass token via environment variable

"-e", "CF_ACCESS_TOKEN=eyJhbGciOi..."

Obtain a token manually (cloudflared access login, then read the file from ~/.cloudflared/) and pass it directly. Note: this token will eventually expire and you'll need to update it.

Not using Cloudflare?

If your Kibana is not behind Cloudflare Zero Trust, you can ignore all of the above. The server will skip Cloudflare authentication entirely when no token is found and cloudflared is not installed.

Project Structure

src/
  main.ts                    # Entry point — registers tools and starts stdio transport
  kibana-client.ts           # Kibana HTTP client with auth and retry logic
  cf-token.ts                # Cloudflare Access token auto-management
  allowed-services.ts        # Service allowlist filtering
  utils/
    format-response.ts       # Response truncation and formatting
  tools/
    search-logs.tool.ts      # search_logs
    aggregate-logs.tool.ts   # aggregate_logs
    list-indices.tool.ts     # list_indices
    list-data-views.tool.ts  # list_data_views
    get-mapping.tool.ts      # get_mapping
    get-document.tool.ts     # get_document
    cluster-health.tool.ts   # cluster_health

License

MIT

A
license - permissive license
-
quality - not tested
D
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/vanovarderesyan/elastic-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server