Elastic MCP Server
A read-only MCP server that gives AI assistants natural language access to Elasticsearch/Kibana logs for querying and analysis.
README
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.
<p align="center"> <img src="https://img.shields.io/badge/MCP-compatible-blue" alt="MCP Compatible" /> <img src="https://img.shields.io/badge/node-%3E%3D18-green" alt="Node >= 18" /> <img src="https://img.shields.io/badge/license-MIT-brightgreen" alt="MIT License" /> </p>
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.
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.jswhere 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-serverwith 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
-iand no-t? MCP communicates over stdin/stdout using structured JSON. The-iflag keeps stdin open for the MCP protocol. The-tflag (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
indexparameter 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_viewstool 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:
- Read from
~/.cloudflared/cache —cloudflaredstores tokens as files named<hostname>-<audience>-tokenafter a successful login - Auto-refresh via
cloudflared access login— if the cached token is expired or missing, the server runscloudflared access login <url>which opens a browser for authentication (Node.js only, not in Docker) - Fall back to
CF_ACCESS_TOKENenv var — if the above steps fail, the server uses the token from the environment variable - 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
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.