Doramagic Project Pack · Human Manual
mcp-server-elasticsearch
The Elasticsearch MCP Server is a Model Context Protocol (MCP) server implementation that connects AI agents to Elasticsearch clusters. It enables natural language interactions with Elasti...
Project Introduction
Related topics: System Architecture, Available MCP Tools, Docker Deployment
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: System Architecture, Available MCP Tools, Docker Deployment
Project Introduction
Overview
The Elasticsearch MCP Server is a Model Context Protocol (MCP) server implementation that connects AI agents to Elasticsearch clusters. It enables natural language interactions with Elasticsearch indices, allowing agents to query, analyze, and retrieve data without custom APIs.
Source: README.md
Deprecation Notice
[!CAUTION]
This MCP server is deprecated and will only receive critical security updates going forward. It has been superseded by the Elastic Agent Builder MCP endpoint, which is available in Elastic 9.2.0+ and Elasticsearch Serverless projects.
Source: README.md
Project Metadata
The project is classified as a beta-stage library owned by the DevTools team at Elastic.
| Attribute | Value |
|---|---|
| Project Name | mcp-server-elasticsearch |
| Type | Library (MCP Server) |
| Lifecycle | Beta |
| Owner | group:devtools-team |
| CI/CD | Buildkite Pipeline |
| Copyright | Copyright 2025 Elasticsearch B.V. |
Source: catalog-info.yaml
Architecture
High-Level Architecture
graph TD
A[AI Agent / MCP Client] -->|MCP Protocol| B[MCP Server]
B --> C[Elasticsearch Cluster]
subgraph "MCP Server Components"
D[CLI Layer<br/>stdio/http] --> E[Protocol Handler]
E --> F[Tool Router]
F --> G[Elasticsearch Client]
end
subgraph "Tools"
H[list_indices]
I[get_mappings]
J[search]
K[esql]
L[get_shards]
M[Custom Tools]
end
G --> H
G --> I
G --> J
G --> K
G --> L
G --> MServer Modes
The MCP server supports two primary transport modes:
| Mode | Description | Use Case |
|---|---|---|
| Stdio | Standard input/output communication | Local MCP clients, Claude Desktop, Cursor |
| HTTP/SSE | HTTP with Server-Sent Events | Remote deployment, web-based clients, n8n integration |
Source: src/cli.rs:19-35
Configuration Structure
graph LR
A[Config File<br/>JSON5] --> B[Interpolator]
B --> C[Configuration]
subgraph "Elasticsearch Config"
D[url]
E[api_key]
F[username/password]
G[ssl_skip_verify]
end
subgraph "MCP Servers Config"
H[Stdio servers]
I[Streamable HTTP servers]
J[SSE servers]
endSource: src/lib.rs and src/cli.rs:63-80
Core Components
ElasticsearchMcpConfig
The main configuration struct for the Elasticsearch MCP server.
| Field | Type | Default | Description |
|---|---|---|---|
url | String | Required | Cluster URL |
api_key | Option<String> | None | API key authentication |
username | Option<String> | None | Username for basic auth |
password | Option<String> | None | Password for basic auth |
ssl_skip_verify | bool | false | Skip SSL certificate verification |
tools | Tools | Empty | Custom tools configuration |
prompts | Vec<String> | Empty | Prompts to expose |
Source: src/servers/elasticsearch/mod.rs:64-85
EsClientProvider
A wrapper around the Elasticsearch client that provides request-context-aware client instances.
pub struct EsClientProvider(Elasticsearch);
The provider supports authentication via:
- API Key:
Credentials::EncodedApiKey(api_key) - Basic Auth:
Credentials::Basic(username, password) - Per-request Auth: Extracts
Authorizationheader from incoming HTTP requests
Source: src/servers/elasticsearch/mod.rs:88-106
Available Tools
Base Tools
| Tool | Description | Read Only | |
|---|---|---|---|
list_indices | List all available Elasticsearch indices | Yes | |
get_mappings | Retrieve index mappings | Yes | |
search | Perform Elasticsearch query DSL search | Yes | |
esql | Execute ES | QL queries | Yes |
get_shards | Get shard information for indices | Yes |
Source: src/servers/elasticsearch/base_tools.rs
Custom Tools
The server supports extensible custom tools through the Tools struct:
| Field | Type | Description |
|---|---|---|
incl_excl | Option<IncludeExclude> | Include/exclude filter for tools |
custom | HashMap<String, CustomTool> | Map of custom tool definitions |
Supported custom tool types:
| Type | Description | |
|---|---|---|
Esql | ES | QL query tool with configurable result format |
SearchTemplate | Search template tool (by ID or inline) |
Source: src/servers/elasticsearch/mod.rs:1-65
ES|QL Result Formats
| Format | Description |
|---|---|
json (default) | Output as JSON array or single object |
value | If single object with single property, output only value |
Source: src/servers/elasticsearch/mod.rs:28-34
Data Models
Search Result
pub struct SearchResult {
pub hits: Hits,
#[serde(default)]
pub aggregations: IndexMap<String, Value>,
}
pub struct Hits {
pub total: Option<TotalHits>,
pub hits: Vec<Hit>,
}
pub struct TotalHits {
pub value: u64,
}
pub struct Hit {
#[serde(rename = "_source")]
pub source: Value,
}
Source: src/servers/elasticsearch/base_tools.rs:100-122
ES|QL Response
pub struct EsqlQueryResponse {
pub is_partial: Option<bool>,
pub columns: Vec<Column>,
pub values: Vec<Vec<Value>>,
}
pub struct Column {
pub name: String,
#[serde(rename = "type")]
pub type_: String,
}
Source: src/servers/elasticsearch/base_tools.rs:155-169
Transport Protocols
Stdio Transport
Used for local MCP client connections. Compatible with Claude Desktop, Cursor, and VS Code.
{
"mcpServers": {
"elasticsearch": {
"command": "npx",
"args": ["-y", "@elastic/mcp-server-elasticsearch"]
}
}
}
Source: src/cli.rs:42-55
HTTP Transport
HTTP server with optional SSE support for remote deployments.
| Endpoint | Method | Description |
|---|---|---|
/ | GET | Hello endpoint |
/ping | GET | Health check |
/ready | GET | Readiness probe |
/live | GET | Liveness probe |
/mcp/sse | GET | SSE endpoint |
/mcp | POST | MCP JSON-RPC handler |
Source: src/protocol/http.rs
Environment Variable Interpolation
The configuration file supports environment variable interpolation using ${VAR} or ${VAR:default} syntax.
fn interpolate(name: String, lookup: impl Fn(&str) -> Option<String>)
-> Result<String, InterpolationError>
| Syntax | Behavior |
|---|---|
${VAR} | Required - fails if not defined |
${VAR:default} | Optional - uses default if not defined |
Source: src/utils/interpolator.rs
CLI Commands
Stdio Command
mcp-server-elasticsearch stdio --config <path>
Source: src/cli.rs:36-41
HTTP Command
mcp-server-elasticsearch http [OPTIONS]
| Option | Environment Variable | Default |
|---|---|---|
--config <path> | - | None |
--address <ip:port> | HTTP_ADDRESS | 127.0.0.1:8080 |
--sse | - | Disabled |
Source: src/cli.rs:19-33
Dependency Management
The project uses Renovate for automated dependency updates.
| Setting | Value |
|---|---|
| Schema | https://docs.renovatebot.com/renovate-schema.json |
| Base Config | local>elastic/renovate-config |
| Schedule | after 1am on monday |
| Status | Rate-limited |
Source: renovate.json
Known Issues
Community-Reported Issues
| Issue | Description |
|---|---|
| #185 | get_mappings tool fails with "error decoding response body" when nested type is omitted in properties |
| #170 | Basic auth failed - 401 Unauthorized despite correct credentials |
| #191 | Missing Linux ARM64 binary releases |
| #17 | Request for streamable HTTP transport support |
Source: GitHub Issues
Quick Reference
Minimum Requirements
- Elasticsearch cluster (version 8.x or 9.x)
- Authentication credentials (API key or username/password)
- Docker (for container deployment)
- MCP-compatible client
Default Configuration
{
"elasticsearch": {
"url": "${ES_URL:}",
"api_key": "${ES_API_KEY:}",
"username": "${ES_USERNAME:}",
"password": "${ES_PASSWORD:}",
"ssl_skip_verify": "${ES_SSL_SKIP_VERIFY:false}"
}
}
Source: src/lib.rs:36-45
Version History
| Version | Key Changes |
|---|---|
| v0.4.6 | Deprecation notice added |
| v0.4.5 | ENV name fix for username |
| v0.4.4 | CA certificates added |
| v0.4.3 | Default port set to 8080 |
| v0.4.2 | Default port changed to 8000 |
| v0.3.1 | Hashbang fix for npx execution |
| v0.3.0 | OpenTelemetry support, aggregation results |
| v0.2.0 | get_shards tool added |
Source: GitHub Releases
Source: https://github.com/elastic/mcp-server-elasticsearch / Human Manual
Available MCP Tools
Related topics: System Architecture, Troubleshooting Guide
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: System Architecture, Troubleshooting Guide
Available MCP Tools
The Elasticsearch MCP Server exposes a set of tools that enable AI agents to interact with Elasticsearch clusters through natural language. These tools wrap the Elasticsearch REST API and provide a simplified interface for common operations like searching, exploring indices, and retrieving cluster metadata.
Overview
The MCP server implements the Model Context Protocol tool specification, using the rmcp framework with Rust macros for tool definition and routing. All tools are read-only by default, enabling safe interactions with production Elasticsearch clusters.
graph TD
A[MCP Client] -->|tool_request| B[EsBaseTools]
B --> C[Elasticsearch Cluster]
subgraph Tools
D[list_indices]
E[get_mappings]
F[search]
G[esql]
H[get_shards]
end
C -->|response| B
B -->|tool_result| ASource: src/servers/elasticsearch/base_tools.rs:30-36
Core Tools
Core tools are built-in MCP tools that provide fundamental Elasticsearch operations. They are implemented in EsBaseTools and registered via the #[tool_router] macro.
Source: src/servers/elasticsearch/base_tools.rs:1-40
list_indices
Lists all available Elasticsearch indices matching a specified pattern.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
index_pattern | String | Yes | Index pattern to filter indices (e.g., *, logs-*) |
Implementation Details:
The tool uses the Elasticsearch _cat/indices API with JSON response format, requesting the index, status, and docs.count fields.
#[tool(
description = "List all available Elasticsearch indices",
annotations(title = "List ES indices", read_only_hint = true)
)]
async fn list_indices(
&self,
req_ctx: RequestContext<RoleServer>,
Parameters(ListIndicesParams { index_pattern }): Parameters<ListIndicesParams>,
) -> Result<CallToolResult, rmcp::Error>
Source: src/servers/elasticsearch/base_tools.rs:70-92
get_mappings
Retrieves field mappings for a specific Elasticsearch index.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
index | String | Yes | Name of the Elasticsearch index |
Implementation Details:
The tool calls the _mapping endpoint for the specified index and returns the complete mapping definition.
#[tool(
description = "Get field mappings for a specific Elasticsearch index",
annotations(title = "Get ES index mappings", read_only_hint = true)
)]
async fn get_mappings(
&self,
req_ctx: RequestContext<RoleServer>,
Parameters(GetMappingsParams { index }): Parameters<GetMappingsParams>,
) -> Result<CallToolResult, rmcp::Error>
Known Issue: When a nested property is defined without explicitly specifying "type": "nested", the server may throw "error decoding response body" even though the mapping is valid according to the Elasticsearch specification. This is tracked in issue #185.
Source: src/servers/elasticsearch/base_tools.rs:95-115
search
Performs an Elasticsearch search using the Query DSL.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
index | String | Yes | Name of the Elasticsearch index to search |
fields | Vec<String> | No | Specific fields to return, augments _source |
query_body | Map<String, Value> | Yes | Complete Elasticsearch query DSL object |
Implementation Details:
The search tool accepts a full Elasticsearch query DSL object that can include query, size, from, sort, aggs, and other query parameters. If the fields parameter is provided, it augments the _source parameter to limit returned data.
The response includes both search hits and aggregations:
#[derive(Serialize, Deserialize)]
pub struct SearchResult {
pub hits: Hits,
#[serde(default)]
pub aggregations: IndexMap<String, Value>,
}
#[tool(
description = "Perform an Elasticsearch search with the provided query DSL.",
annotations(title = "Elasticsearch search DSL query", read_only_hint = true)
)]
async fn search(
&self,
req_ctx: RequestContext<RoleServer>,
Parameters(SearchParams {
index,
fields,
query_body,
}): Parameters<SearchParams>,
) -> Result<CallToolResult, rmcp::Error>
Source: src/servers/elasticsearch/base_tools.rs:118-165
esql
Executes an ES|QL query against Elasticsearch.
Parameters:
| Parameter | Type | Required | Description | |
|---|---|---|---|---|
query | String | Yes | Complete ES | QL query |
Response Formats:
The tool supports different output formats configured via the format parameter:
| Format | Description |
|---|---|
json (default) | Output as JSON array of objects or single object |
value | If single object with single property, output only its value |
Source: src/servers/elasticsearch/mod.rs:40-55
get_shards
Retrieves shard allocation information for all or specific indices.
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
index | Option<String> | No | Optional index name to filter shards |
Implementation Details:
The tool uses the Elasticsearch _cat/shards API with JSON format, requesting index, shard, prirep, state, docs, store, and node fields.
#[tool(
description = "Get shard information for all or specific indices.",
annotations(title = "Get ES shard information", read_only_hint = true)
)]
async fn get_shards(
&self,
req_ctx: RequestContext<RoleServer>,
Parameters(GetShardsParams { index }): Parameters<GetShardsParams>,
) -> Result<CallToolResult, rmcp::Error>
Source: src/servers/elasticsearch/base_tools.rs:168-200
Custom Tools
Custom tools extend the core functionality by allowing users to define pre-configured queries and templates. They are defined in the configuration file and registered at startup.
Source: src/servers/elasticsearch/mod.rs:15-30
Tool Definition Structure
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum CustomTool {
Esql(EsqlTool),
SearchTemplate(SearchTemplateTool),
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ToolBase {
pub description: String,
pub parameters: IndexMap<String, schemars::schema::SchemaObject>,
pub annotations: Option<ToolAnnotations>,
}
EsqlTool
Pre-configured ES|QL queries that can be exposed as tools. Includes a base definition with description, parameters schema, and the query to execute.
Source: src/servers/elasticsearch/mod.rs:32-55
SearchTemplateTool
Pre-configured search templates that accept parameters at runtime. Supports two template specification modes:
| Mode | Description |
|---|---|
template_id | Reference an existing saved search template by ID |
template | Inline template definition as JSON |
Source: src/servers/elasticsearch/mod.rs:57-72
Tool Configuration
Tools are configured in the server configuration file under the tools section:
{
"elasticsearch": {
"url": "${ES_URL}",
"api_key": "${ES_API_KEY}"
},
"tools": {
"incl_excl": {
"include": ["custom_tool_1"],
"exclude": ["custom_tool_2"]
},
"custom": {
"my_esql_query": {
"type": "esql",
"description": "Count documents by status",
"parameters": {},
"query": "FROM my-index | STATS count = COUNT(*)"
}
}
}
}
Source: src/lib.rs:45-65
Server Capabilities
Each tool set reports its capabilities through the get_info() method:
fn get_info(&self) -> ServerInfo {
ServerInfo {
protocol_version: ProtocolVersion::V_2025_03_26,
capabilities: ServerCapabilities::builder().enable_tools().build(),
server_info: Implementation::from_build_env(),
instructions: Some("Provides access to Elasticsearch".to_string()),
}
}
Source: src/servers/elasticsearch/base_tools.rs:27-36
Transport Support
Tools can be accessed through multiple transport protocols:
| Transport | Description | Use Case |
|---|---|---|
stdio | Direct process communication | Local clients, Claude Desktop |
streamable-http | HTTP-based with session support | Web integrations, remote access |
sse | Server-Sent Events (deprecated) | Legacy compatibility |
Source: src/cli.rs:20-35
Response Structure
All tools return results wrapped in CallToolResult:
Ok(CallToolResult::success(vec![
Content::text(format!("Found {} indices:", response.len())),
Content::json(response)?,
]))
The server returns:
- Text summary with counts or status
- JSON content with the full response data
Community Considerations
When using these tools, be aware of the following community-reported issues:
- Aggregation results in search: Some agents may not receive aggregation results properly. The enhancement request #45 tracks improvements to aggregation handling.
- get_mappings with nested types: Valid Elasticsearch mappings with implicit nested types may cause decoding errors. See issue #185.
- HTTP transport limitations: When accessing via HTTP/SSE, certain tool combinations may return errors. This is discussed in issue #173.
Source: https://github.com/elastic/mcp-server-elasticsearch / Human Manual
System Architecture
Related topics: MCP Protocol Configuration, Available MCP Tools
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: MCP Protocol Configuration, Available MCP Tools
System Architecture
Overview
The Elasticsearch MCP Server is a Model Context Protocol (MCP) server implementation written in Rust that connects AI agents to Elasticsearch clusters. It enables natural language interactions with Elasticsearch indices through a standardized tool interface, supporting both stdio and HTTP transport protocols.
Project Type: Library component (Backstage classification) Owner: devtools-team Lifecycle: Beta (deprecated as of v0.4.6) Source: catalog-info.yaml:1-56
High-Level Architecture
The server follows a layered architecture with distinct separation between transport handling, protocol implementation, and tool execution:
graph TD
subgraph Transport Layer
STDIOC[Stdio Transport]
HTTPC[HTTP Transport]
SSEC[SSE Transport]
end
subgraph Protocol Layer
MCP[Model Context Protocol]
end
subgraph Server Layer
ESMC[ElasticsearchMcp]
BASET[EsBaseTools]
end
subgraph Tools
SEARCH[search]
LISTIDX[list_indices]
GETMAP[get_mappings]
GETSHARDS[get_shards]
ESQL[esql]
end
subgraph Elasticsearch
ES[Elasticsearch Cluster]
end
STDIOC --> MCP
HTTPC --> MCP
SSEC --> MCP
MCP --> ESMC
ESMC --> BASET
BASET --> SEARCH
BASET --> LISTIDX
BASET --> GETMAP
BASET --> GETSHARDS
BASET --> ESQL
SEARCH --> ES
LISTIDX --> ES
GETMAP --> ES
GETSHARDS --> ES
ESQL --> ESTransport Layer
The server supports multiple transport mechanisms, configurable via command-line arguments:
Stdio Transport
The default transport mode for local MCP client integrations. Communicates via standard input/output streams using JSON-RPC messages.
pub enum Command {
Stdio(StdioCommand),
Http(HttpCommand),
}
pub struct StdioCommand {
/// Config file
#[clap(short, long)]
pub config: Option<PathBuf>,
}
Source: src/cli.rs:19-29
HTTP Transport
Supports remote deployments with optional Server-Sent Events (SSE) fallback:
pub struct HttpCommand {
/// Config file
#[clap(short, long)]
pub config: Option<PathBuf>,
/// Address to listen to [default: 127.0.0.1:8080]
#[clap(long, value_name = "IP_ADDRESS:PORT", env = "HTTP_ADDRESS")]
pub address: Option<std::net::SocketAddr>,
/// Also start an SSE server on '/sse'
#[clap(long)]
pub sse: bool,
}
| Parameter | Type | Default | Environment Variable |
|---|---|---|---|
| config | PathBuf | None | - |
| address | SocketAddr | 127.0.0.1:8080 | HTTP_ADDRESS |
| sse | bool | false | - |
Source: src/cli.rs:31-47
Runtime Entry Points
The CLI determines which transport to initialize based on the subcommand:
pub async fn run_stdio(cmd: StdioCommand, container_mode: bool) -> anyhow::Result<()> {
tracing::info!("Starting stdio server");
let handler = setup_services(&cmd.config, container_mode).await?;
let service = handler.serve(stdio()).await.inspect_err(|e| {
tracing::error!("serving error: {:?}", e);
})?;
// ...
}
pub async fn run_http(cmd: HttpCommand, container_mode: bool) -> anyhow::Result<()> {
let handler = setup_services(&cmd.config, container_mode).await?;
let server_provider = move || handler.clone();
// ...
}
Source: src/lib.rs:1-100
Configuration System
Configuration Structure
The server uses JSON5 format for configuration files, with support for comments and environment variable interpolation:
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Configuration {
pub elasticsearch: elasticsearch::ElasticsearchMcpConfig,
#[serde(default)]
pub mcp_servers: HashMap<String, McpServer>,
}
Source: src/cli.rs:104-111
Elasticsearch Configuration
pub struct ElasticsearchMcpConfig {
/// Cluster URL
pub url: String,
/// API key
#[serde(default, deserialize_with = "none_if_empty_string")]
pub api_key: Option<String>,
/// Username
#[serde(default, deserialize_with = "none_if_empty_string")]
pub username: Option<String>,
/// Password
#[serde(default, deserialize_with = "none_if_empty_string")]
pub password: Option<String>,
/// Should we skip SSL certificate verification?
#[serde(default, deserialize_with = "deserialize_bool_from_anything")]
pub ssl_skip_verify: bool,
/// Search templates to expose as tools or resources
#[serde(default)]
pub tools: Tools,
/// Prompts
#[serde(default)]
pub prompts: Vec<String>,
}
| Parameter | Required | Default | Environment Variable |
|---|---|---|---|
| url | Yes | - | ES_URL |
| api_key | No | - | ES_API_KEY |
| username | No | - | ES_USERNAME |
| password | No | - | ES_PASSWORD |
| ssl_skip_verify | No | false | ES_SSL_SKIP_VERIFY |
Source: src/servers/elasticsearch/mod.rs:1-100
Environment Variable Interpolation
The configuration parser supports ${VAR_NAME} and ${VAR_NAME:default_value} syntax:
fn expand(name: &str) -> Result<String, InterpolationError> {
let lookup = |s: &str| match s {
"foo" => Some("foo_value".to_string()),
"bar" => Some("bar_value".to_string()),
_ => None,
};
interpolate(name.to_string(), lookup)
}
Source: src/utils/interpolator.rs:1-50
Core Server Components
ElasticsearchMcp Server
The main server implementation wraps the Elasticsearch client and provides tool routing:
pub struct ElasticsearchMcp {}
impl ElasticsearchMcp {
pub fn new_with_config(
config: ElasticsearchMcpConfig,
container_mode: bool
) -> anyhow::Result<base_tools::EsBaseTools> {
let creds = if let Some(api_key) = config.api_key.clone() {
Some(Credentials::EncodedApiKey(api_key))
} else if let Some(username) = config.username.clone() {
let pwd = config.password.clone().ok_or(anyhow::Error::msg("missing password"))?;
Some(Credentials::Basic(username, pwd))
} else {
None
};
let url = config.url.as_str();
if url.is_empty() {
return Err(anyhow::Error::msg("Elasticsearch URL is empty"));
}
let mut url = Url::parse(url)?;
if container_mode {
rewrite_localhost(&mut url)?;
}
let pool = elasticsearch::http::transport::SingleNodeConnectionPool::new(url.clone());
let mut transport = elasticsearch::http::transport::TransportBuilder::new(pool);
if let Some(creds) = creds {
transport = transport.auth(creds);
}
// ...
}
}
Source: src/servers/elasticsearch/mod.rs:1-100
EsClientProvider
A wrapper around the Elasticsearch client that supports per-request authentication:
#[derive(Clone)]
pub struct EsClientProvider(Elasticsearch);
impl EsClientProvider {
pub fn new(client: Elasticsearch) -> Self {
EsClientProvider(client)
}
/// If the incoming request is a http request and has an `Authorization` header, use it
/// to authenticate to the remote ES instance.
pub fn get(&self, context: RequestContext<RoleServer>) -> Elasticsearch {
// Authentication context handling
}
}
Source: src/servers/elasticsearch/mod.rs:100-150
EsBaseTools
The core tool provider implementing the MCP tool protocol:
#[derive(Clone)]
pub struct EsBaseTools {
es_client: EsClientProvider,
tool_router: ToolRouter<EsBaseTools>,
}
impl EsBaseTools {
pub fn new(es_client: Elasticsearch) -> Self {
Self {
es_client: EsClientProvider::new(es_client),
tool_router: Self::tool_router(),
}
}
}
#[tool_handler]
impl ServerHandler for EsBaseTools {
fn get_info(&self) -> ServerInfo {
ServerInfo {
protocol_version: ProtocolVersion::V_2025_03_26,
capabilities: ServerCapabilities::builder().enable_tools().build(),
server_info: Implementation::from_build_env(),
instructions: Some("Provides access to Elasticsearch".to_string()),
}
}
}
Source: src/servers/elasticsearch/base_tools.rs:1-100
Tool System
Available Tools
| Tool | Description | Read Only | |
|---|---|---|---|
| list_indices | List all available Elasticsearch indices | Yes | |
| get_mappings | Retrieve index mappings | Yes | |
| get_shards | Get shard information for indices | Yes | |
| search | Perform Elasticsearch search with Query DSL | Yes | |
| esql | Execute ES\ | QL queries | Yes |
Source: src/servers/elasticsearch/base_tools.rs:100-200
Tool Parameter Definitions
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
struct ListIndicesParams {
/// Index pattern of Elasticsearch indices to list
pub index_pattern: String,
}
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
struct GetMappingsParams {
/// Name of the Elasticsearch index to get mappings for
index: String,
}
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
struct SearchParams {
/// Name of the Elasticsearch index to search
index: String,
/// Name of the fields that need to be returned (optional)
fields: Option<Vec<String>>,
/// Complete Elasticsearch query DSL object that can include query, size, from, sort, etc.
query_body: Map<String, Value>,
}
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
struct EsqlQueryParams {
/// Complete Elasticsearch ES|QL query
query: String,
}
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
struct GetShardsParams {
/// Optional index name to get shard information for
index: Option<String>,
}
Source: src/servers/elasticsearch/base_tools.rs:100-200
Custom Tools
The server supports extensible custom tools via configuration:
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum CustomTool {
Esql(EsqlTool),
SearchTemplate(SearchTemplateTool),
}
pub struct EsqlTool {
#[serde(flatten)]
base: ToolBase,
query: String,
#[serde(default)]
format: EsqlResultFormat,
}
#[derive(Debug, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum EsqlResultFormat {
#[default]
Json,
Value,
}
Source: src/servers/elasticsearch/mod.rs:20-50
Data Models
Search Response
#[derive(Serialize, Deserialize)]
pub struct SearchResult {
pub hits: Hits,
#[serde(default)]
pub aggregations: IndexMap<String, Value>,
}
#[derive(Serialize, Deserialize)]
pub struct Hits {
pub total: Option<TotalHits>,
pub hits: Vec<Hit>,
}
#[derive(Serialize, Deserialize)]
pub struct TotalHits {
pub value: u64,
}
Source: src/servers/elasticsearch/base_tools.rs:50-70
Index Mappings Response
pub type MappingResponse = HashMap<String, Mappings>;
#[derive(Serialize, Deserialize)]
pub struct Mappings {
pub mappings: Mapping,
}
#[derive(Serialize, Deserialize)]
pub struct Mapping {
#[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
pub meta: Option<JsonObject>,
properties: HashMap<String, MappingProperty>,
}
#[derive(Serialize, Deserialize)]
pub struct MappingProperty {
#[serde(rename = "type")]
pub type_: String,
#[serde(flatten)]
pub settings: HashMap<String, serde_json::Value>,
}
Source: src/servers/elasticsearch/base_tools.rs:100-130
ES|QL Response
#[derive(Serialize, Deserialize)]
pub struct EsqlQueryResponse {
pub is_partial: Option<bool>,
pub columns: Vec<Column>,
pub values: Vec<Vec<Value>>,
}
#[derive(Serialize, Deserialize)]
pub struct Column {
pub name: String,
#[serde(rename = "type")]
pub type_: String,
}
Source: src/servers/elasticsearch/base_tools.rs:130-150
Server Initialization Flow
sequenceDiagram
participant CLI as Command Line
participant Config as Config Loader
participant Interpolator as Env Interpolator
participant Server as ElasticsearchMcp
participant Transport as MCP Transport
CLI->>Config: Load config file
Config->>Interpolator: Expand ${VAR} placeholders
Interpolator->>Config: Expanded config
Config->>Server: Parse JSON5
Server->>Server: Create ES client with auth
CLI->>Transport: Start transport (stdio/http)
Transport->>Server: Serve MCP requestsSource: src/lib.rs:1-100
Known Limitations
Community-Reported Issues
- Nested Mapping Parsing (Issue #185): The
get_mappingstool fails with "error decoding response body" when a nested property is defined without explicitly specifying"type": "nested", even though the mapping is valid according to Elasticsearch spec.
- HTTP Transport Complexity (Issue #17): Support for streamable HTTP is a requested enhancement. Currently, the server supports stdio and HTTP/SSE transports only.
- Basic Auth Configuration (Issue #170): Users have reported confusion with authentication configuration when deploying via Docker, requiring careful setup of environment variables.
Architecture Summary
The Elasticsearch MCP Server implements a clean separation of concerns:
- Transport Layer: Handles protocol-agnostic communication (stdio/HTTP/SSE)
- Protocol Layer: Implements MCP specification for tool discovery and invocation
- Service Layer: Manages Elasticsearch connections and authentication
- Tool Layer: Provides domain-specific tools for Elasticsearch operations
This architecture enables the server to act as a bridge between AI agents and Elasticsearch clusters, translating natural language requests into structured queries while maintaining security through per-request authentication context propagation.
Source: https://github.com/elastic/mcp-server-elasticsearch / Human Manual
MCP Protocol Configuration
Related topics: Docker Deployment, System Architecture
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: Docker Deployment, System Architecture
MCP Protocol Configuration
Overview
The Elasticsearch MCP Server supports multiple transport protocols for communicating with MCP clients. The protocol configuration system allows operators to choose between stdio (for direct client connections) and streamable-HTTP (for web-based integrations, stateful sessions, and concurrent clients). Source: README.md
The server implements the Model Context Protocol specification and provides flexible configuration options through both CLI arguments and configuration files. The configuration system is designed to support various deployment scenarios, from local development to production cloud environments.
Source: https://github.com/elastic/mcp-server-elasticsearch / Human Manual
Docker Deployment
Related topics: MCP Protocol Configuration, Authentication and Security, Configuration Reference
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: MCP Protocol Configuration, Authentication and Security, Configuration Reference
Docker Deployment
The Elasticsearch MCP Server is distributed as a Docker container image, providing a self-contained deployment mechanism that encapsulates all dependencies and configurations required to run the MCP server. This approach ensures consistent behavior across different environments and simplifies the deployment process for users who need to integrate the MCP server with AI agents and tools.
Architecture Overview
The Docker deployment architecture supports two primary protocol modes: stdio and streamable-HTTP. The stdio mode provides direct communication between the MCP client and server, while the HTTP mode enables remote connections and supports multiple concurrent clients. Both modes share the same underlying Elasticsearch client configuration and authentication mechanisms.
graph TD
A[MCP Client] --> B{Docker Container}
B --> C[Elasticsearch MCP Server]
C --> D{Protocol Mode}
D -->|Stdio| E[Direct Stdio Communication]
D -->|HTTP| F[Streamable HTTP Endpoint]
F --> G[Port 8080 or 8000]
C --> H[Elasticsearch Cluster]
I[Environment Variables] --> C
J[Config File] --> CContainer Image
The official Docker image is published to the Elastic registry and uses a multi-stage build process to minimize the final image size. The image includes the compiled Rust binary and all necessary CA certificates for secure communication with Elasticsearch clusters.
| Property | Value |
|---|---|
| Image Registry | docker.elastic.co/mcp/elasticsearch |
| Default Port | 8080 |
| Alternative Port | 8000 |
| Base Image | Distroless/static (minimal attack surface) |
| User | Non-root (security hardened) |
Environment Variables
The Docker container accepts configuration through environment variables. These variables are interpolated into a JSON5 configuration file at runtime, allowing flexible deployment scenarios without requiring custom configuration files.
Authentication Variables
| Variable | Required | Description |
|---|---|---|
ES_URL | Yes | Elasticsearch cluster URL (e.g., https://your-cluster.es.amazonaws.com:9200) |
ES_API_KEY | Conditional | Elasticsearch API key for authentication (required if not using username/password) |
ES_USERNAME | Conditional | Username for basic authentication |
ES_PASSWORD | Conditional | Password for basic authentication |
ES_SSL_SKIP_VERIFY | No | Set to true to skip SSL/TLS certificate verification (not recommended for production) |
Source: src/lib.rs:setup_services()
Network Variables
| Variable | Description |
|---|---|
HTTP_ADDRESS | Socket address for HTTP server binding (format: IP:PORT) |
CLI_ARGS | Additional command-line arguments passed to the binary |
Running in Stdio Mode
The stdio protocol provides the most straightforward deployment pattern for MCP clients that run in the same environment as the container. The server reads from stdin and writes to stdout, enabling direct communication with the MCP client.
Basic Stdio Command
docker run -i --rm \
-e ES_URL="https://your-cluster.es.us-east-1.es.amazonaws.com:443" \
-e ES_API_KEY="your-api-key-here" \
docker.elastic.co/mcp/elasticsearch \
stdio
Claude Desktop Configuration
To integrate the MCP server with Claude Desktop, add the following configuration to your Claude Desktop settings file:
{
"mcpServers": {
"elasticsearch-mcp-server": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "ES_URL",
"-e", "ES_API_KEY",
"docker.elastic.co/mcp/elasticsearch",
"stdio"
],
"env": {
"ES_URL": "<elasticsearch-cluster-url>",
"ES_API_KEY": "<elasticsearch-api-key>"
}
}
}
}
Running in HTTP Mode
The streamable-HTTP protocol extends the MCP server's capabilities beyond local connections. This mode is recommended for web integrations, stateful sessions, and deployments that require supporting multiple concurrent clients.
Basic HTTP Command
docker run --rm \
-e ES_URL="https://your-cluster.es.us-east-1.es.amazonaws.com:443" \
-e ES_API_KEY="your-api-key-here" \
-p 8080:8080 \
docker.elastic.co/mcp/elasticsearch \
http
HTTP Endpoints
| Endpoint | Method | Description |
|---|---|---|
/mcp | POST/GET | Streamable-HTTP MCP endpoint |
/ping | GET | Health check endpoint (returns pong) |
Source: src/lib.rs:run_http()
HTTP-SSE Mode
For environments that require Server-Sent Events (SSE) for backwards compatibility, the HTTP server can be started with SSE support enabled:
docker run --rm \
-e ES_URL \
-e ES_API_KEY \
-p 8080:8080 \
docker.elastic.co/mcp/elasticsearch \
http --sse
Note that SSE is deprecated in favor of streamable-HTTP. The streamable-HTTP protocol provides better streaming support and should be preferred for new deployments.
HTTP Address Configuration
By default, the HTTP server binds to 127.0.0.1:8080, restricting access to local connections only. To allow external access, specify the binding address explicitly:
docker run --rm \
-e ES_URL \
-e ES_API_KEY \
-p 0.0.0.0:8080:8080 \
docker.elastic.co/mcp/elasticsearch \
http --address 0.0.0.0:8080
The server also respects the HTTP_ADDRESS environment variable for configuration.
Container Mode Host Resolution
The MCP server includes special handling for Docker container environments. When the Elasticsearch URL points to localhost, the server automatically attempts to resolve alternative hostnames that are commonly used for host access from within containers.
The following host aliases are checked in order:
| Alias | Platform |
|---|---|
host.docker.internal | Docker Desktop (Linux, macOS, Windows) |
docker.for.host.internal | Legacy Docker for macOS |
host.containers.internal | Podman and other container runtimes |
Source: src/servers/elasticsearch/mod.rs:container_mode()
If localhost is detected and no alias resolves successfully, the server logs a warning but continues operation:
tracing::warn!("Container mode: could not find a replacement for 'localhost'");
Configuration File
For more complex configurations, you can mount a configuration file into the container. The configuration supports JSON5 format, which allows comments and multi-line strings—useful for ES|QL queries.
Example Configuration File
{
"elasticsearch": {
"url": "${ES_URL}",
"api_key": "${ES_API_KEY:}",
"username": "${ES_USERNAME:}",
"password": "${ES_PASSWORD:}",
"ssl_skip_verify": "${ES_SSL_SKIP_VERIFY:false}"
}
}
Running with Configuration File
docker run --rm \
-v /path/to/config.json:/config.json:ro \
-p 8080:8080 \
docker.elastic.co/mcp/elasticsearch \
http --config /config.json
The configuration file supports environment variable interpolation using the ${VAR} and ${VAR:default} syntax.
Security Considerations
Credential Handling
The Elasticsearch MCP Server follows security best practices for credential management:
- API keys and passwords: Stored only in environment variables passed to the container. They are never persisted to disk or included in logs.
- Environment variables: Set when running the container. Use secret management services like AWS Secrets Manager or AWS Systems Manager Parameter Store in production environments.
- Non-root user: The container runs as a non-root user for security hardening.
Network Security
| Security Feature | Implementation |
|---|---|
| TLS/SSL | Enabled automatically when ES_URL uses https:// |
| Certificate Validation | Enabled by default; disabled with ES_SSL_SKIP_VERIFY=true |
| Localhost Binding | HTTP server binds to 127.0.0.1:8080 by default |
| Port Exposure | Only required ports should be exposed to the network |
Best Practices
- Rotate API keys regularly (every 30-90 days for production)
- Use API keys with minimal required permissions
- Never commit credentials to version control
- Use secret management services for credential storage
- Restrict network access to the container using firewall rules
Troubleshooting
Connection Issues
If the container fails to connect to Elasticsearch, verify the following:
- Check container logs: View logs using
docker logs <container-id>to identify authentication or connection errors.
``bash docker exec <container-id> curl -k -u <username>:<password> <ES_URL> ` Or with an API key: `bash docker exec <container-id> curl -k -H "Authorization: ApiKey <api-key>" <ES_URL> ``
- Test Elasticsearch connectivity: From within the container:
- Verify network access: Ensure the container can reach the Elasticsearch cluster. For cloud deployments, check security groups and network ACLs.
Authentication Failures
Common authentication issues and solutions:
| Issue | Solution |
|---|---|
| 401 Unauthorized | Verify ES_USERNAME and ES_PASSWORD are correct; check for typos in environment variable names |
| API key rejected | Ensure the API key has sufficient permissions for the operations being performed |
| Certificate errors | If using a self-signed certificate, set ES_SSL_SKIP_VERIFY=true (development only) |
Port Binding Issues
If port 8080 is already in use, either stop the conflicting service or use an alternative port:
docker run --rm \
-e ES_URL \
-e ES_API_KEY \
-p 8081:8080 \
docker.elastic.co/mcp/elasticsearch \
http --address 0.0.0.0:8080
Health Check Verification
Verify the server is running correctly by checking the health endpoint:
curl http://localhost:8080/ping
A successful response returns pong, indicating the server is operational.
Building the Docker Image
For development or custom deployments, you can build the Docker image locally using the provided Makefile:
make docker-build
The Makefile targets include:
| Target | Description |
|---|---|
docker-build | Build the default Docker image |
docker-build-8000 | Build the image with port 8000 as default |
docker-push | Push the built image to the registry |
Limitations and Known Issues
The following limitations are relevant to Docker deployment:
- Nested mapping decoding: The
get_mappingstool may fail with "error decoding response body" when nested properties are defined without explicitly specifying"type": "nested", even though the mapping is valid according to Elasticsearch specification. This is a known issue tracked at #185.
- Basic auth configuration: Users have reported authentication failures when using basic authentication via Docker environment variables. Ensure environment variable names are correctly specified (
ES_USERNAME,ES_PASSWORD) and that the credentials have appropriate permissions.
- ARM64 support: The project currently publishes Linux AMD64 binaries. ARM64 support is requested by the community (issue #191) but not yet available.
Source: https://github.com/elastic/mcp-server-elasticsearch / Human Manual
Authentication and Security
Related topics: Docker Deployment, Troubleshooting Guide
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: Docker Deployment, Troubleshooting Guide
Authentication and Security
The Elasticsearch MCP Server implements a multi-layered authentication system that supports both static server-side credentials and dynamic per-request authentication headers. This design enables secure connections to Elasticsearch clusters while maintaining flexibility for different deployment scenarios, including proxy-based authentication flows.
Overview
The MCP server handles authentication at two distinct layers:
| Layer | Purpose | Source |
|---|---|---|
| Server Configuration | Establishes baseline credentials for connecting to Elasticsearch | Environment variables or config file |
| Per-Request Auth | Allows MCP clients to inject authentication for specific requests | HTTP Authorization header |
This dual-layer approach allows administrators to configure default credentials while still supporting scenarios where clients need to authenticate with different credentials or where a reverse proxy handles authentication delegation.
Configuration Options
The ElasticsearchMcpConfig struct in src/servers/elasticsearch/mod.rs defines the supported authentication parameters:
pub struct ElasticsearchMcpConfig {
/// Cluster URL
pub url: String,
/// API key
#[serde(default, deserialize_with = "none_if_empty_string")]
pub api_key: Option<String>,
/// Username
#[serde(default, deserialize_with = "none_if_empty_string")]
pub username: Option<String>,
/// Password
#[serde(default, deserialize_with = "none_if_empty_string")]
pub password: Option<String>,
/// Should we skip SSL certificate verification?
#[serde(default, deserialize_with = "deserialize_bool_from_anything")]
pub ssl_skip_verify: bool,
}
Source: src/servers/elasticsearch/mod.rs:98-120
Supported Authentication Methods
| Method | Configuration | Priority |
|---|---|---|
| API Key | ES_API_KEY environment variable | Highest |
| Basic Authentication | ES_USERNAME + ES_PASSWORD environment variables | Fallback |
| No Authentication | Neither configured | When no credentials needed |
The credential resolution logic follows this precedence:
let creds = if let Some(api_key) = config.api_key.clone() {
Some(Credentials::EncodedApiKey(api_key))
} else if let Some(username) = config.username.clone() {
let pwd = config.password.clone().ok_or(anyhow::Error::msg("missing password"))?;
Some(Credentials::Basic(username, pwd))
} else {
None
};
Source: src/servers/elasticsearch/mod.rs:160-168
Environment Variable Mapping
| Environment Variable | Config Field | Description |
|---|---|---|
ES_URL | url | Elasticsearch cluster endpoint |
ES_API_KEY | api_key | Base64-encoded API key |
ES_USERNAME | username | Username for basic auth |
ES_PASSWORD | password | Password for basic auth |
ES_SSL_SKIP_VERIFY | ssl_skip_verify | Disable TLS verification (not recommended) |
The configuration template demonstrates environment variable interpolation:
{
"elasticsearch": {
"url": "${ES_URL}",
"api_key": "${ES_API_KEY:}",
"username": "${ES_USERNAME:}",
"password": "${ES_PASSWORD:}",
"ssl_skip_verify": "${ES_SSL_SKIP_VERIFY:false}"
}
}
Source: src/lib.rs:56-66
Per-Request Authentication
The EsClientProvider wraps the Elasticsearch client and implements dynamic credential injection for individual requests. This is particularly useful in HTTP/SSE deployment modes where clients may need to authenticate differently than the server's baseline configuration.
pub fn get(&self, context: RequestContext<RoleServer>) -> Cow<'_, Elasticsearch> {
let client = &self.0;
let Some(mut auth) = context
.extensions
.get::<Parts>()
.and_then(|p| p.headers.get(header::AUTHORIZATION))
.and_then(|h| h.to_str().ok())
else {
// No auth
return Cow::Borrowed(client);
};
// MCP inspector insists on sending a bearer token and prepends "Bearer" to the value provided
if auth.starts_with("Bearer ApiKey ") || auth.starts_with("Bearer Basic ") {
auth = auth.trim_start_matches("Bearer ");
}
let transport = client
.transport()
.clone_with_auth(Some(Credentials::AuthorizationHeader(auth.to_string())));
Cow::Owned(Elasticsearch::new(transport))
}
Source: src/servers/elasticsearch/mod.rs:126-151
Authorization Header Handling
The server intelligently processes the Authorization header by:
- Extracting the header value from the incoming HTTP request
- Stripping
Bearerprefixes that some MCP clients (like the MCP Inspector) automatically prepend - Creating a new Elasticsearch client transport with the extracted credentials
- Returning an owned
Cow<Elasticsearch>to ensure the per-request client is used
Supported Per-Request Formats
| Format | Example Header | Notes |
|---|---|---|
ApiKey <key> | Authorization: ApiKey VGhpcyBpcyBhbiBhcGkga2V5... | Direct API key |
Basic <credentials> | Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ= | Base64-encoded username:password |
Bearer ApiKey <key> | Authorization: Bearer ApiKey VGhpcyBpcyBhbiBhcGkga2V5... | MCP Inspector format for API key |
Bearer Basic <credentials> | Authorization: Bearer Basic dXNlcm5hbWU6cGFzc3dvcmQ= | MCP Inspector format for basic auth |
Deployment Modes and Authentication
The MCP server supports multiple transport modes that interact differently with authentication:
Stdio Mode
In stdio mode, the server reads credentials exclusively from environment variables or the configuration file. Per-request authentication via HTTP headers is not applicable in this mode.
#[derive(Debug, Subcommand)]
pub enum Command {
Stdio(StdioCommand),
Http(HttpCommand),
}
#[derive(Debug, Args)]
pub struct StdioCommand {
/// Config file
#[clap(short, long)]
pub config: Option<PathBuf>,
}
Source: src/cli.rs:42-52
HTTP/SSE Mode
In HTTP/SSE mode, the server can receive per-request authentication headers, enabling proxy-based authentication where the proxy handles credential validation and injects the appropriate Authorization header.
#[derive(Debug, Args)]
pub struct HttpCommand {
/// Config file
#[clap(short, long)]
pub config: Option<PathBuf>,
/// Address to listen to [default: 127.0.0.1:8080]
#[clap(long, value_name = "IP_ADDRESS:PORT", env = "HTTP_ADDRESS")]
pub address: Option<std::net::SocketAddr>,
/// Also start an SSE server on '/sse'
#[clap(long)]
pub sse: bool,
}
Source: src/cli.rs:30-41
Authentication Flow Diagram
sequenceDiagram
participant Client as MCP Client
participant MCP as MCP Server
participant ES as Elasticsearch
Note over MCP: Configuration Load
MCP->>MCP: Read ES_URL, ES_API_KEY/ES_USERNAME/ES_PASSWORD
MCP->>MCP: Create base Elasticsearch client
Note over Client,MCP: Per-Request Auth (HTTP mode only)
Client->>MCP: HTTP Request + Authorization header
MCP->>MCP: Extract Authorization header
alt Authorization header present
MCP->>MCP: Clone transport with new credentials
MCP->>ES: Request with per-request auth
else No Authorization header
MCP->>ES: Request with base client auth
end
ES-->>MCP: Response
MCP-->>Client: MCP ResponseSSL/TLS Configuration
Certificate Verification
By default, the server validates SSL/TLS certificates when connecting to Elasticsearch. To disable verification (not recommended for production):
ES_SSL_SKIP_VERIFY=true
This sets ssl_skip_verify: true in the configuration, which is passed to the Elasticsearch HTTP transport.
Container Mode URL Rewriting
When running inside a container, localhost connections may need URL rewriting to reach the host machine:
if container_mode {
rewrite_localhost(&mut url)?;
}
This ensures that http://localhost:9200 in a container context resolves to the actual host machine's Elasticsearch instance.
Common Issues and Troubleshooting
Issue: Basic Auth Failed - 401 Unauthorized
A frequently reported issue (#170) involves authentication failures even when credentials are correctly configured:
Symptoms:
- Server logs show
401 Unauthorized - Environment variables
ES_USERNAME,ES_PASSWORDare confirmed correct inside the container
Potential Causes:
| Cause | Solution |
|---|---|
| Conflicting env var names | Use ES_USERNAME and ES_PASSWORD (v0.4.5 updated env name) |
| Empty password string | Ensure ES_PASSWORD is not empty; use quotes if special characters present |
| API key takes precedence | If ES_API_KEY is set, it overrides username/password |
Diagnostic Steps:
- Verify all environment variables are correctly set
- Test connectivity directly to Elasticsearch with the same credentials
- For HTTP mode, check if a proxy is stripping or modifying the Authorization header
MCP Inspector Compatibility
The MCP Inspector prepends Bearer to authentication values. The server handles this by stripping the prefix:
if auth.starts_with("Bearer ApiKey ") || auth.starts_with("Bearer Basic ") {
auth = auth.trim_start_matches("Bearer ");
}
This ensures compatibility with tools that follow the OAuth2 bearer token convention.
Security Best Practices
| Practice | Recommendation |
|---|---|
| Credential Storage | Use secrets management systems (AWS Secrets Manager, HashiCorp Vault) rather than plain environment variables |
| SSL Verification | Keep ES_SSL_SKIP_VERIFY=false in production; only disable for local development |
| API Keys | Prefer API keys over username/password for machine-to-machine authentication |
| Network Exposure | Bind HTTP server to localhost (127.0.0.1) when possible; restrict network access |
| Proxy Authentication | When using a reverse proxy, ensure it properly forwards or handles the Authorization header |
Related Documentation
- Model Context Protocol Authentication
- Elasticsearch API Keys
- Elasticsearch User Management
- Docker Deployment Guide - For container-specific authentication configuration
Source: https://github.com/elastic/mcp-server-elasticsearch / Human Manual
Configuration Reference
Related topics: Docker Deployment, Authentication and Security
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: Docker Deployment, Authentication and Security
Configuration Reference
The Elasticsearch MCP Server supports flexible configuration through configuration files and environment variables. This reference documents all available configuration options, their purposes, and usage patterns.
Overview
The server configuration defines how the MCP server connects to Elasticsearch and which tools and features to expose. Configuration can be provided through:
- Configuration file (JSON or JSON5 format) specified via CLI
- Environment variables for containerized deployments
- Default values built into the binary
Source: src/lib.rs:66-85
Configuration File Structure
The configuration file uses JSON5 format, which extends JSON with support for comments and trailing commas. This allows for human-readable configuration with documentation.
{
"elasticsearch": {
"url": "${ES_URL}",
"api_key": "${ES_API_KEY:}",
"username": "${ES_USERNAME:}",
"password": "${ES_PASSWORD:}",
"ssl_skip_verify": "${ES_SSL_SKIP_VERIFY:false}",
"tools": {
"custom": {
// Custom tools configuration
}
},
"prompts": []
}
}
Source: src/lib.rs:66-79
Elasticsearch Configuration Options
The ElasticsearchMcpConfig struct defines all connection and behavior settings:
| Parameter | Type | Default | Description |
|---|---|---|---|
url | String | *(required)* | Elasticsearch cluster URL (e.g., https://localhost:9200) |
api_key | Option<String> | None | API key for authentication |
username | Option<String> | None | Username for basic authentication |
password | Option<String> | None | Password for basic authentication |
ssl_skip_verify | bool | false | Skip SSL certificate verification (not recommended for production) |
tools | Tools | {} | Custom tools configuration (ESQL, search templates) |
prompts | Vec<String> | [] | List of prompt identifiers to expose |
Source: src/servers/elasticsearch/mod.rs:101-129
Environment Variable Interpolation
The configuration system supports environment variable interpolation using ${VAR} or ${VAR:default} syntax. This enables containerized deployments and integration with orchestration systems.
Syntax
| Pattern | Behavior |
|---|---|
${VAR} | Required variable - server fails if not set |
${VAR:default} | Optional variable with default value |
Example Usage
# With all environment variables
ES_URL=https://elasticsearch:9200 \
ES_API_KEY=my-api-key \
./elastic-mcp stdio --config config.json5
# Using defaults (empty strings for optional vars)
ES_URL=https://elasticsearch:9200 \
./elastic-mcp stdio
The interpolator replaces ${VAR} patterns with actual environment variable values before JSON parsing occurs.
Source: src/utils/interpolator.rs:1-50
graph TD
A[Config File or Built-in Defaults] --> B[Environment Variable Interpolation]
B --> C[JSON5 Parsing]
C --> D[serde_json5 Deserialization]
D --> E[ElasticsearchMcpConfig]
E --> F[Server Initialization]
G[Environment Variables] -.->|${VAR} replacement| BAuthentication Configuration
The server supports multiple authentication methods. Only one should be configured per deployment.
API Key Authentication
{
"elasticsearch": {
"url": "https://elasticsearch:9200",
"api_key": "${ES_API_KEY}"
}
}
Username/Password Authentication
{
"elasticsearch": {
"url": "https://elasticsearch:9200",
"username": "${ES_USERNAME}",
"password": "${ES_PASSWORD}"
}
}
Authentication Priority
When multiple authentication methods are specified:
api_keytakes precedence overusername/password- Both
usernameandpasswordmust be present for basic auth
Source: src/servers/elasticsearch/mod.rs:70-81
CLI Configuration
Stdio Mode
Run the server in stdio mode for local integration with MCP clients:
elastic-mcp stdio [OPTIONS]
| Option | Description |
|---|---|
-c, --config <PATH> | Path to configuration file |
Source: src/cli.rs:38-45
HTTP Mode
Run the server as an HTTP server for remote access:
elastic-mcp http [OPTIONS]
| Option | Environment Variable | Default | Description |
|---|---|---|---|
-c, --config <PATH> | - | None | Path to configuration file |
--address <ADDR> | HTTP_ADDRESS | 127.0.0.1:8080 | Listen address |
--sse | - | false | Enable SSE endpoint at /mcp/sse |
Source: src/cli.rs:26-37
Address Binding Behavior
The server binds to different addresses based on execution mode:
| Mode | Default Address | Description |
|---|---|---|
| Stdio | N/A | No network binding required |
| HTTP | 127.0.0.1:8080 | Localhost only for security |
| HTTP (container) | 0.0.0.0:8080 | All interfaces for container networking |
Source: src/lib.rs:90-99
Container Mode
When running inside a Docker container, the server can automatically rewrite localhost URLs to the host machine's address. This is enabled by passing the --container flag or setting the CONTAINER_MODE environment variable.
Supported Host Aliases
Container mode rewrites localhost to one of these addresses (in order of priority):
| Platform | Host Alias |
|---|---|
| Docker Desktop | host.docker.internal |
| Podman | host.containers.internal |
| Kubernetes | host.containers.internal |
Source: src/servers/elasticsearch/mod.rs:158-175
graph TD
A[Container Mode Enabled] --> B{URL host = localhost?}
B -->|Yes| C[Resolve alias addresses]
B -->|No| E[Use URL as-is]
C --> D[Rewrite to resolved address]
D --> E
E --> F[Create ES Client]
F --> G[host.docker.internal]
F --> H[host.containers.internal]Custom Tools Configuration
The Tools struct allows exposing custom ES|QL queries and search templates as MCP tools:
{
"elasticsearch": {
"url": "https://elasticsearch:9200",
"tools": {
"custom": {
"esql_query": {
"type": "esql",
"description": "Execute ES|QL query",
"parameters": { /* JSON Schema */ },
"query": "FROM my-index | LIMIT 10"
},
"search_template": {
"type": "search_template",
"description": "Execute search template",
"template": {
"id": "my-template"
}
}
}
}
}
}
ES|QL Tool Configuration
| Field | Type | Description | |
|---|---|---|---|
type | String | Must be "esql" | |
description | String | Human-readable tool description | |
parameters | SchemaObject | JSON Schema for parameters | |
query | String | ES | QL query to execute |
format | String | Output format: json (default) or value |
Source: src/servers/elasticsearch/mod.rs:35-68
Complete Configuration Example
{
"elasticsearch": {
// Connection settings
"url": "${ES_URL}",
"api_key": "${ES_API_KEY:}",
"username": "${ES_USERNAME:}",
"password": "${ES_PASSWORD:}",
"ssl_skip_verify": "${ES_SSL_SKIP_VERIFY:false}",
// Custom tools
"tools": {
"custom": {
"log_analysis": {
"type": "esql",
"description": "Analyze recent error logs",
"query": "FROM logs-* | WHERE level == 'ERROR' | LIMIT 100"
}
}
},
// Prompts to expose
"prompts": []
}
}
Environment Variable Reference
| Variable | Required | Description |
|---|---|---|
ES_URL | Yes | Elasticsearch cluster URL |
ES_API_KEY | No* | API key for authentication |
ES_USERNAME | No* | Username for basic auth |
ES_PASSWORD | No* | Password for basic auth |
ES_SSL_SKIP_VERIFY | No | Set to true to skip SSL verification |
CLI_ARGS | No | Alternative to CLI arguments |
HTTP_ADDRESS | No | HTTP server listen address |
*Either ES_API_KEY or both ES_USERNAME and ES_PASSWORD should be set.
Source: .env-example
Known Configuration Issues
Basic Auth 401 Unauthorized
If using basic authentication fails with 401 errors, verify that:
- Both
ES_USERNAMEandES_PASSWORDare set - The Elasticsearch user has sufficient permissions
- The credentials are correctly passed to the container
Container Mode URL Resolution
Container mode requires the host alias to be resolvable. If you see warnings about failing to rewrite localhost, ensure:
- Docker Desktop is running (for
host.docker.internal) - Podman socket is active (for
host.containers.internal)
Source: src/servers/elasticsearch/mod.rs:169-170
Configuration Schema
The complete JSON Schema for validation:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["elasticsearch"],
"properties": {
"elasticsearch": {
"type": "object",
"required": ["url"],
"properties": {
"url": { "type": "string", "format": "uri" },
"api_key": { "type": ["string", "null"] },
"username": { "type": ["string", "null"] },
"password": { "type": ["string", "null"] },
"ssl_skip_verify": { "type": "boolean" },
"tools": { "$ref": "#/definitions/Tools" },
"prompts": { "type": "array", "items": { "type": "string" } }
}
}
}
}
Source: https://github.com/elastic/mcp-server-elasticsearch / Human Manual
Troubleshooting Guide
Related topics: Authentication and Security, Monitoring and Health Checks, Available MCP Tools
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: Authentication and Security, Monitoring and Health Checks, Available MCP Tools
Troubleshooting Guide
This guide covers common issues encountered when deploying and using the Elasticsearch MCP Server, with solutions based on the codebase and reported community issues.
Authentication Failures
401 Unauthorized with Basic Auth
Users deploying via Docker have reported authentication failures even when credentials appear correct.
Common Causes:
| Issue | Cause | Solution |
|---|---|---|
| Environment variable name | ES_lOGIN is a typo; use ES_USERNAME | Use ES_USERNAME and ES_PASSWORD correctly |
| Missing password | Username provided without password | Always provide ES_PASSWORD when ES_USERNAME is set |
| Container env setup | Variables not properly passed to container | Verify -e flags in docker run command |
Source: The server validates credentials in src/servers/elasticsearch/mod.rs:47-54:
let creds = if let Some(api_key) = config.api_key.clone() {
Some(Credentials::EncodedApiKey(api_key))
} else if let Some(username) = config.username.clone() {
let pwd = config.password.clone().ok_or(anyhow::Error::msg("missing password"))?;
Some(Credentials::Basic(username, pwd))
} else {
None
};
Verification Steps:
``bash docker exec -it <container_name> env | grep ES_ ``
- Enter the container and confirm environment variables:
- Verify the correct variable names are used:
ES_URL- Elasticsearch cluster URLES_USERNAME- Username (NOTES_lOGIN)ES_PASSWORD- PasswordES_API_KEY- API key (alternative to username/password)
- Test authentication directly against Elasticsearch using the same credentials.
API Key Authentication
When using API key authentication, ensure the key is properly formatted and has sufficient permissions.
Configuration:
{
"elasticsearch": {
"url": "${ES_URL}",
"api_key": "${ES_API_KEY:}"
}
}
The server will use api_key if provided, falling back to username/password authentication. Source: src/servers/elasticsearch/mod.rs:47
Configuration Issues
Empty Elasticsearch URL
The server fails to start with an empty ES_URL:
Error: "Elasticsearch URL is empty"
Source: src/servers/elasticsearch/mod.rs:53-55:
let url = config.url.as_str();
if url.is_empty() {
return Err(anyhow::Error::msg("Elasticsearch URL is empty"));
}
Solution: Ensure ES_URL is set to a valid Elasticsearch endpoint (e.g., https://localhost:9200).
SSL Certificate Verification Failures
For development or testing with self-signed certificates:
Configuration option:
{
"elasticsearch": {
"url": "${ES_URL}",
"ssl_skip_verify": "${ES_SSL_SKIP_VERIFY:false}"
}
}
Set ES_SSL_SKIP_VERIFY=true to skip SSL verification. Source: src/servers/elasticsearch/mod.rs:33-34
Environment Variable Interpolation
The server supports ${VAR_NAME:default_value} syntax in configuration files.
Source: src/utils/interpolator.rs:17-38:
if line.starts_with("${") {
if let Some(end) = line.find("$}") {
let expr = &line[2..end];
let value = if let Some((name, default)) = expr.split_once(':') {
lookup(name).unwrap_or(default.to_string())
} else {
lookup(expr).ok_or_else(|| err(...))?
};
result.push_str(&value);
}
}
Example:
{
"elasticsearch": {
"url": "${ES_URL:http://localhost:9200}",
"api_key": "${ES_API_KEY:}",
"username": "${ES_USERNAME:}",
"password": "${ES_PASSWORD:}",
"ssl_skip_verify": "${ES_SSL_SKIP_VERIFY:false}"
}
}
Config File Parsing Errors
Configuration files support JSON5 (JSON with comments and trailing commas).
Error format for parsing failures:
{
"elasticsearch": {
"url": "http://localhost:9200"
// Missing comma above causes parsing error
}
}
Source: src/lib.rs:58-63:
let config: Configuration = match serde_json5::from_str(&config) {
Ok(c) => c,
Err(serde_json5::Error::Message { msg, location }) if location.is_some() => {
let location = location.unwrap();
anyhow::bail!("Failed to parse config: {msg}, at line {line} column {column}");
}
Err(err) => return Err(err)?,
};
Tool-Specific Issues
get_mappings Decoding Error
Issue: get_mappings tool fails with "error decoding response body" when index mappings contain nested properties defined without explicit "type": "nested".
This is a known limitation when Elasticsearch returns mappings that are technically valid but contain implicit nested type declarations.
Source: The tool uses read_json to parse responses in src/servers/elasticsearch/base_tools.rs:140-150:
async fn get_mappings(
&self,
req_ctx: RequestContext<RoleServer>,
Parameters(GetMappingsParams { index }): Parameters<GetMappingsParams>,
) -> Result<CallToolResult, rmcp::Error> {
let es_client = self.es_client.get(req_ctx);
let response = es_client.indices().get_mapping(GetMappingParts::Index(&[&index])).send().await;
let mapping: GetMappingResponse = read_json(response).await?;
}
Workaround: Ensure your Elasticsearch index mappings explicitly define the type property for nested fields:
{
"mappings": {
"properties": {
"nested_field": {
"type": "nested"
}
}
}
}
search Tool Aggregation Results
The search tool returns aggregation results alongside hits:
Source: src/servers/elasticsearch/base_tools.rs:153-177:
let response: SearchResult = read_json(response).await?;
let mut results: Vec<Content> = Vec::new();
// Send result stats only if it's not pure aggregation results
if response.aggregations.is_empty() || !response.hits.hits.is_empty() {
let total = response.hits.total.map(|t| t.value).unwrap_or(0);
results.push(Content::text(format!("Found {} hits:", total)));
}
// Include aggregations if present
if !response.aggregations.is_empty() {
results.push(Content::text("Aggregations:"));
results.push(Content::json(response.aggregations)?);
}
ES|QL Query Response Format
ES|QL queries transform columnar results into object format:
Source: src/servers/elasticsearch/base_tools.rs:186-203:
let response: EsqlQueryResponse = read_json(response).await?;
// Transform response into an array of objects
let mut objects: Vec<Value> = Vec::new();
for row in response.values.into_iter() {
let mut obj = Map::new();
for (i, value) in row.into_iter().enumerate() {
obj.insert(response.columns[i].name.clone(), value);
}
objects.push(Value::Object(obj));
}
Deployment Issues
Docker Container Mode
When running in Docker, the server rewrites localhost references to enable container-to-container communication.
Source: src/servers/elasticsearch/mod.rs:57:
if container_mode {
rewrite_localhost(&mut url)?;
}
Default Ports:
| Mode | Default Address | Environment Variable |
|---|---|---|
| Docker (HTTP) | 0.0.0.0:8080 | HTTP_ADDRESS |
| Local (HTTP) | 127.0.0.1:8080 | HTTP_ADDRESS |
| Stdio | N/A | N/A |
Source: src/lib.rs:47-53:
let address: SocketAddr = if let Some(addr) = cmd.address {
addr
} else if container_mode {
SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 8080)
} else {
SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8080)
};
HTTP Transport Endpoints
When running in HTTP mode:
| Endpoint | Purpose |
|---|---|
/mcp | Streamable-HTTP MCP endpoint |
/ping | Health check endpoint |
Source: src/cli.rs:37-45:
pub struct HttpCommand {
/// Config file
#[clap(short, long)]
pub config: Option<PathBuf>,
/// Address to listen to [default: 127.0.0.1:8080]
#[clap(long, value_name = "IP_ADDRESS:PORT", env = "HTTP_ADDRESS")]
pub address: Option<std::net::SocketAddr>,
/// Also start an SSE server on '/sse'
#[clap(long)]
pub sse: bool,
}
Common Docker Deployment Errors
Container exits immediately:
- Check logs:
docker logs <container_name> - Verify environment variables are set
- Ensure Elasticsearch URL is accessible from container
Port already in use:
# Check what's using port 8080
lsof -i :8080
# Use different port
docker run -p 8081:8080 ...
Error Handling Reference
Server Error Responses
The server uses the rmcp::Error type for MCP protocol errors:
Source: src/servers/elasticsearch/mod.rs:66-74:
pub fn handle_error(result: Result<Response, elasticsearch::Error>) -> Result<Response, rmcp::Error> {
match result {
Ok(resp) => resp.error_for_status_code(),
Err(e) => {
tracing::error!("Error: {:?}", &e);
Err(e)
}
}
.map_err(internal_error)
}
Common Error Messages
| Error | Cause | Resolution |
|---|---|---|
error decoding response body | Invalid JSON in ES response | Check ES version compatibility; verify index mappings |
Request failed (remote) | ES cluster unreachable | Verify ES_URL; check network/firewall |
missing password | Username set without password | Provide ES_PASSWORD or use API key auth |
Elasticsearch URL is empty | ES_URL not set | Set ES_URL environment variable |
Diagnostic Checklist
When troubleshooting, verify:
- [ ]
ES_URLis set and points to a reachable Elasticsearch cluster - [ ] Authentication credentials (API key or username/password) are correct
- [ ] If using Basic auth, both
ES_USERNAMEandES_PASSWORDare set (notES_lOGIN) - [ ] SSL certificate issues: set
ES_SSL_SKIP_VERIFY=truefor self-signed certs - [ ] Docker networking allows container to reach Elasticsearch
- [ ] Index name is correct (use
list_indicestool to verify) - [ ] User has necessary Elasticsearch permissions for the requested operations
Getting Help
For additional support:
- Search existing GitHub issues
- Check the Dependency Dashboard for known dependency-related issues
- Enable debug logging by setting
RUST_LOG=debugwhen running the server
Source: https://github.com/elastic/mcp-server-elasticsearch / Human Manual
Monitoring and Health Checks
Related topics: Troubleshooting Guide, Docker Deployment
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: Troubleshooting Guide, Docker Deployment
Monitoring and Health Checks
The Elasticsearch MCP Server provides a comprehensive monitoring infrastructure to verify server operational status, validate Elasticsearch connectivity, and troubleshoot deployment issues. This page documents all health check endpoints, monitoring tools, and diagnostic procedures available to operators.
Health Check Endpoints
When running in HTTP mode, the MCP server exposes dedicated health check endpoints under the /_health path. These endpoints integrate with Kubernetes liveness/readiness probes, load balancers, and monitoring systems.
Available Endpoints
| Endpoint | Path | Method | Purpose |
|---|---|---|---|
| Readiness | /_health/ready | GET | Indicates server can accept requests |
| Liveness | /_health/live | GET | Indicates server process is alive |
| Ping | /ping | GET | Basic connectivity verification |
| Hello | / | GET | Server version and endpoint info |
Readiness Probe
The readiness endpoint signals when the server is fully initialized and ready to process MCP requests:
GET /_health/ready HTTP/1.1
Response:
HTTP/1.1 200 OK
Ready
The server becomes ready once the tool list is loaded and the Elasticsearch client is initialized. Source: src/protocol/http.rs:1-50
Liveness Probe
The liveness endpoint confirms the server process is running:
GET /_health/live HTTP/1.1
Response:
HTTP/1.1 200 OK
Alive
This endpoint returns immediately without checking Elasticsearch connectivity, making it suitable for detecting crashed processes. Source: src/protocol/http.rs:1-50
Ping Endpoint
The ping endpoint provides a quick health verification:
GET /ping HTTP/1.1
Response:
HTTP/1.1 200 OK
Ready
A successful pong response indicates the server is running and healthy. Source: README.md
Monitoring Tools
The MCP server exposes Elasticsearch monitoring tools that agents can invoke to gather cluster health information. These tools are available as MCP tool calls through the stdio or HTTP interfaces.
Tool Reference
| Tool | Purpose | Read-Only | |
|---|---|---|---|
list_indices | List available Elasticsearch indices | Yes | |
get_shards | Retrieve shard allocation information | Yes | |
get_mappings | Fetch index field mappings | Yes | |
search | Execute search queries | Yes | |
esql | Run ES | QL queries | Yes |
Shard Monitoring
The get_shards tool provides detailed shard distribution information across the cluster:
{
"name": "get_shards",
"arguments": {
"index": "optional-index-name"
}
}
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
index | String | No | Specific index name; omit for all indices |
Response Fields:
| Field | Description |
|---|---|
index | Index name |
shard | Shard number |
prirep | Primary (P) or replica (R) |
state | Shard state (STARTED, INITIALIZING, etc.) |
docs | Document count |
store | Store size |
node | Node name housing the shard |
This tool was introduced in v0.2.0 for cluster monitoring purposes. Source: src/servers/elasticsearch/base_tools.rs:1-50
Index Listing
The list_indices tool enumerates all indices accessible to the authenticated user:
{
"name": "list_indices",
"arguments": {
"index_pattern": "*"
}
}
Response Structure:
[
{
"index": "index-name",
"status": "open",
"doc_count": 12345
}
]
Mappings Retrieval
The get_mappings tool retrieves field mappings for index analysis:
{
"name": "get_mappings",
"arguments": {
"index": "my-index"
}
}
Note: There is a known issue where certain valid Elasticsearch mapping definitions with omitted nested types may cause "error decoding response body" errors. This occurs when a property is defined without explicitly specifying "type": "nested". Source: Community Issue #185
Architecture
Health Check Request Flow
graph TD
A[External Monitor] -->|HTTP GET| B[Main Router]
B -->|Route| C[Health Router]
C -->|/ready| D[Readiness Check]
C -->|/live| E[Liveness Check]
C -->|/ping| F[Basic Ping]
D -->|Initialize| G[Tool List Loaded]
G -->|ES Client Ready| H[200 OK]
E --> I[Process Alive]
I --> J[200 OK]
F --> K[Server Responding]
K --> L[pong]HTTP Server Structure
The HTTP server is built using the Axum framework with a layered router configuration:
graph TD
A[TCP Listener] --> B[Graceful Shutdown Handler]
B --> C[Main Router]
C -->|/| D[Hello Endpoint]
C -->|/ping| E[Ping Endpoint]
C -->|/mcp| F[MCP Handler]
C -->|/mcp/sse| G[SSE Handler]
C -->|/_health| H[Health Router]
H -->|/ready| I[Readiness Probe]
H -->|/live| J[Liveness Probe]Source: src/protocol/http.rs:1-50
Troubleshooting
Common Issues
#### Authentication Failures
Symptom: 401 Unauthorized errors when the server attempts to connect to Elasticsearch.
Diagnostic Steps:
``bash docker exec <container-id> env | grep ES_ ``
- Verify environment variables inside the container:
- Confirm credentials are correctly set:
ES_API_KEYfor API key authenticationES_USERNAMEandES_PASSWORDfor basic auth
```bash # Basic auth docker exec <container-id> curl -k -u <username>:<password> <ES_URL>
- Test connectivity directly from the container:
# API key docker exec <container-id> curl -k -H "Authorization: ApiKey <api-key>" <ES_URL> ```
Source: README.md
#### Container Logs Analysis
View logs to identify connection or authentication issues:
docker logs <container-id>
Look for these error patterns:
| Error Pattern | Likely Cause |
|---|---|
Connection refused | Elasticsearch URL incorrect or ES not running |
401 Unauthorized | Invalid credentials |
certificate verify failed | SSL verification issue |
#### Elasticsearch Connectivity Verification
From within the container, verify network connectivity:
docker exec <container-id> curl -k -u <username>:<password> <ES_URL>/_cluster/health
A successful response with cluster health JSON confirms the container can reach Elasticsearch.
Health Check Configuration
The default bind address is 127.0.0.1:8080, configurable via:
| Option | Environment Variable | CLI Flag |
|---|---|---|
| Bind address | HTTP_ADDRESS | --address |
Example configuration:
# Environment variable
HTTP_ADDRESS=0.0.0.0:8080
# CLI argument
--address 0.0.0.0:8080
Source: src/cli.rs
Best Practices
Kubernetes Probes
Configure Kubernetes probes to use the health check endpoints:
livenessProbe:
httpGet:
path: /_health/live
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /_health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
Monitoring Integration
- Use
/pingfor load balancer health checks - Use
/_health/readyfor orchestration readiness signals - Use
/_health/livefor process liveness detection - Leverage
get_shardstool for detailed cluster state monitoring via MCP
Security Considerations
- Health endpoints do not require authentication by default
- Restrict access using network policies in production
- The
/pingendpoint returns minimal information suitable for public exposure
Configuration Reference
Health Router Configuration
The health router is mounted at /_health within the main application router:
let health_router = Router::new()
.route("/ready", get(async || (StatusCode::OK, "Ready\n")))
.route("/live", get(async || "Alive\n"));
Source: src/protocol/http.rs:1-50
Server Initialization
Health endpoints become available after successful server initialization:
let handler = elasticsearch::ElasticsearchMcp::new_with_config(config.elasticsearch, container_mode)?;
The server is ready when the EsBaseTools handler is constructed with a valid Elasticsearch client. Source: src/lib.rs
Deprecation Notice
The Elasticsearch MCP Server is deprecated as of v0.4.6. For production monitoring workloads, consider migrating to the Elastic Agent Builder MCP endpoint available in Elastic 9.2.0+ and Elasticsearch Serverless projects. Source: README.md
Source: https://github.com/elastic/mcp-server-elasticsearch / Human Manual
Contributing Guide
Related topics: System Architecture
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: System Architecture
Contributing Guide
Welcome to the Elasticsearch MCP Server contributing guide. This document provides comprehensive instructions for developers who want to contribute to the project, covering development environment setup, coding standards, testing procedures, and submission workflows.
Overview
The Elasticsearch MCP Server is a Rust-based implementation of the Model Context Protocol (MCP) that enables AI agents to interact with Elasticsearch clusters. The project uses the rmcp framework for MCP protocol handling and the official Elasticsearch Rust client for cluster communication.
Contributions are welcome from the community. Before submitting changes, please review this guide thoroughly to ensure your contributions align with the project's standards and requirements.
Development Environment Setup
Prerequisites
| Requirement | Version | Purpose |
|---|---|---|
| Rust | 1.75+ | Primary language runtime |
| Cargo | Latest stable | Package manager and build tool |
| Docker | 24.0+ | Container runtime for testing |
| Elasticsearch | 8.x or 9.x | Target cluster for integration testing |
Initial Setup
``bash git clone https://github.com/elastic/mcp-server-elasticsearch.git cd mcp-server-elasticsearch ``
- Clone the repository
``bash cargo fetch ``
- Install Rust dependencies
``bash cargo build ``
- Verify the build compiles successfully
Running the Server Locally
The project provides a helper script for running the server during development:
./scripts/cargo-run.sh
This script wraps the cargo run command with appropriate environment configuration for local development.
Project Structure
mcp-server-elasticsearch/
├── src/
│ ├── servers/
│ │ └── elasticsearch/
│ │ ├── base_tools.rs # Core MCP tool implementations
│ │ └── mod.rs # Server configuration and initialization
│ ├── utils/
│ │ ├── interpolator.rs # Environment variable interpolation
│ │ ├── mod.rs # Utility module exports
│ │ └── rmcp_ext.rs # MCP SDK extensions
│ ├── cli.rs # Command-line interface
│ └── lib.rs # Library entry point
├── tests/ # Integration tests
├── Makefile # Build and development tasks
├── Cargo.toml # Rust dependencies and metadata
└── rustfmt.toml # Code formatting configuration
Code Style and Standards
Formatting
The project uses rustfmt for consistent code formatting. Configuration is defined in rustfmt.toml:
| Setting | Value | Description |
|---|---|---|
max_width | 100 | Maximum line length |
tab_spaces | 4 | Indentation size |
edition | 2021 | Rust edition |
Format your code before committing:
cargo fmt --check
Linting
Run clippy for linting checks:
cargo clippy --all-targets --all-features
Address all warnings and errors reported by clippy before submitting your pull request.
Code Organization
The codebase follows these architectural patterns:
- Tool Implementation: MCP tools are defined using the
#[tool]macro and implemented insrc/servers/elasticsearch/base_tools.rs - Configuration: Configuration parsing uses serde with JSON5 support for comments in config files
- Error Handling: The project uses
anyhow::Errorfor flexible error handling - HTTP Authentication: The
EsClientProvidersupports authorization header passthrough for HTTP transport mode
Testing
Unit Tests
Run unit tests with:
cargo test
Integration Testing
Integration tests require a running Elasticsearch cluster. Configure the test environment using environment variables:
| Environment Variable | Description | Required |
|---|---|---|
ES_URL | Elasticsearch cluster URL | Yes |
ES_API_KEY | API key for authentication | No* |
ES_USERNAME | Username for basic auth | No* |
ES_PASSWORD | Password for basic auth | No* |
*Either API key or username/password combination required
Build Verification
Use the Makefile to verify the complete build:
make build
MCP Tool Development
Defining New Tools
Tools are defined using the #[tool] attribute macro. The framework expects specific parameters:
#[tool(
description = "Tool description for LLM context",
annotations(title = "Display title", read_only_hint = true)
)]
async fn my_tool(
&self,
req_ctx: RequestContext<RoleServer>,
Parameters(ToolParams): Parameters<ToolParams>,
) -> Result<CallToolResult, rmcp::Error> {
// Implementation
}
Tool Parameters
Parameter types must implement serde::Deserialize and schemars::JsonSchema:
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
struct MyToolParams {
/// Parameter description for schema generation
param_name: String,
}
Response Format
Return tool results using CallToolResult::success() with a vector of Content items:
Ok(CallToolResult::success(vec![
Content::text("Description"),
Content::json(data)?,
]))
Submitting Changes
Branch Strategy
``bash git checkout -b feature/my-feature ``
- Create a feature branch from
main:
- Make your changes following the coding standards
``bash git commit -m "Add support for new ES|QL aggregation function" ``
- Commit with clear, descriptive messages:
Pull Request Checklist
- [ ] Code formatted with
cargo fmt - [ ] Clippy warnings resolved
- [ ] Tests pass with
cargo test - [ ] New tools include parameter documentation
- [ ] Configuration changes documented in relevant files
- [ ] Commit messages follow conventional format
Commit Message Format
<type>(<scope>): <description>
[optional body]
Types: feat, fix, docs, style, refactor, test, chore
Build and Release Process
Build Targets
The project supports multiple build targets:
| Target | Platform | Architecture |
|---|---|---|
| x86_64-unknown-linux-musl | Linux | x86_64 |
| x86_64-apple-darwin | macOS | x86_64 |
| aarch64-apple-darwin | macOS | ARM64 |
| x86_64-pc-windows-msvc | Windows | x86_64 |
Using Makefile
The Makefile provides convenient build targets:
make build # Build release binary
make test # Run test suite
make check # Run formatting and clippy
make clean # Clean build artifacts
Known Limitations
When contributing, be aware of the following known issues:
- Nested Mappings Issue: The
get_mappingstool may fail with "error decoding response body" when nested properties omit explicit type specification. See Issue #185.
- Basic Auth Configuration: Authentication via environment variables requires specific naming (
ES_USERNAME,ES_PASSWORD). See Issue #170.
- Platform Binaries: Currently, Linux ARM64 binaries are not published. See Issue #191.
Getting Help
If you encounter issues while contributing:
- Check existing GitHub Issues for similar problems
- Review the MCP Protocol Documentation for transport-related questions
- Join the Elastic community for support
License
By contributing to this project, you agree that your contributions will be licensed under the Apache License, Version 2.0. See the NOTICE.txt file for details.
Source: https://github.com/elastic/mcp-server-elasticsearch / Human Manual
Doramagic Pitfall Log
Source-linked risks stay visible on the manual page so the preview does not read like a recommendation.
May increase setup, validation, or first-run risk for the user.
May increase setup, validation, or first-run risk for the user.
May increase setup, validation, or first-run risk for the user.
May increase setup, validation, or first-run risk for the user.
Doramagic Pitfall Log
Found 10 structured pitfall item(s), including 0 high/blocking item(s). Top priority: Installation risk - Installation risk requires verification.
1. Installation risk: Installation risk requires verification
- Severity: medium
- Finding: Project evidence flags a installation risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Recommended check: Reproduce the official install and quickstart path in an isolated environment.
- Evidence: identity.distribution | github_repo:953992846 | https://github.com/elastic/mcp-server-elasticsearch
2. Installation risk: Installation risk requires verification
- Severity: medium
- Finding: Project evidence flags a installation risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Recommended check: Reproduce the official install and quickstart path in an isolated environment.
- Evidence: community_evidence:github | cevd_5573c160ecdd4e34be6dbf6549fe2529 | https://github.com/elastic/mcp-server-elasticsearch/issues/6
3. Configuration risk: Configuration risk requires verification
- Severity: medium
- Finding: Project evidence flags a configuration risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Recommended check: Reproduce the official install and quickstart path in an isolated environment.
- Evidence: capability.host_targets | github_repo:953992846 | https://github.com/elastic/mcp-server-elasticsearch
4. Capability evidence risk: Capability evidence risk requires verification
- Severity: medium
- Finding: README/documentation is current enough for a first validation pass.
- User impact: May increase setup, validation, or first-run risk for the user.
- Recommended check: Reproduce the official install and quickstart path in an isolated environment.
- Evidence: capability.assumptions | github_repo:953992846 | https://github.com/elastic/mcp-server-elasticsearch
5. Maintenance risk: Maintenance risk requires verification
- Severity: medium
- Finding: Project evidence flags a maintenance risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Recommended check: Reproduce the official install and quickstart path in an isolated environment.
- Evidence: evidence.maintainer_signals | github_repo:953992846 | https://github.com/elastic/mcp-server-elasticsearch
6. Security or permission risk: Security or permission risk requires verification
- Severity: medium
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Recommended check: Reproduce the official install and quickstart path in an isolated environment.
- Evidence: downstream_validation.risk_items | github_repo:953992846 | https://github.com/elastic/mcp-server-elasticsearch
7. Security or permission risk: Security or permission risk requires verification
- Severity: medium
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Recommended check: Reproduce the official install and quickstart path in an isolated environment.
- Evidence: risks.scoring_risks | github_repo:953992846 | https://github.com/elastic/mcp-server-elasticsearch
8. Security or permission risk: Security or permission risk requires verification
- Severity: medium
- Finding: Project evidence flags a security or permission risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Recommended check: Reproduce the official install and quickstart path in an isolated environment.
- Evidence: community_evidence:github | cevd_f8732a2deab341d6b739b122459d1243 | https://github.com/elastic/mcp-server-elasticsearch/issues/185
9. Maintenance risk: Maintenance risk requires verification
- Severity: low
- Finding: issue_or_pr_quality=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Recommended check: Reproduce the official install and quickstart path in an isolated environment.
- Evidence: evidence.maintainer_signals | github_repo:953992846 | https://github.com/elastic/mcp-server-elasticsearch
10. Maintenance risk: Maintenance risk requires verification
- Severity: low
- Finding: release_recency=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Recommended check: Reproduce the official install and quickstart path in an isolated environment.
- Evidence: evidence.maintainer_signals | github_repo:953992846 | https://github.com/elastic/mcp-server-elasticsearch
Source: Doramagic discovery, validation, and Project Pack records
Community Discussion Evidence
These external discussion links are review inputs, not standalone proof that the project is production-ready.
Count of project-level external discussion links exposed on this manual page.
Open the linked issues or discussions before treating the pack as ready for your environment.
Community Discussion Evidence
Doramagic exposes project-level community discussion separately from official documentation. Review these links before using mcp-server-elasticsearch with real data or production workflows.
- Dependency Dashboard - github / github_issue
- get_mappings tool fails with "error decoding response body" when nested - github / github_issue
- v0.4.6 - github / github_release
- v0.4.5 - github / github_release
- v0.4.4 - github / github_release
- v0.4.3 - github / github_release
- Installation risk requires verification - GitHub / issue
Source: Project Pack community evidence and pitfall evidence