# https://github.com/elastic/mcp-server-elasticsearch Project Manual

Generated at: 2026-05-31 01:04:06 UTC

## Table of Contents

- [Project Introduction](#page-project-introduction)
- [Available MCP Tools](#page-available-tools)
- [System Architecture](#page-architecture)
- [MCP Protocol Configuration](#page-protocols)
- [Docker Deployment](#page-deployment)
- [Authentication and Security](#page-authentication)
- [Configuration Reference](#page-configuration-reference)
- [Troubleshooting Guide](#page-troubleshooting)
- [Monitoring and Health Checks](#page-monitoring)
- [Contributing Guide](#page-contributing)

<a id='page-project-introduction'></a>

## Project Introduction

### Related Pages

Related topics: [System Architecture](#page-architecture), [Available MCP Tools](#page-available-tools), [Docker Deployment](#page-deployment)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [README.md](https://github.com/elastic/mcp-server-elasticsearch/blob/main/README.md)
- [catalog-info.yaml](https://github.com/elastic/mcp-server-elasticsearch/blob/main/catalog-info.yaml)
- [src/servers/elasticsearch/mod.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/mod.rs)
- [src/servers/elasticsearch/base_tools.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/base_tools.rs)
- [src/cli.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/cli.rs)
- [src/lib.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/lib.rs)
- [src/protocol/http.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/protocol/http.rs)
- [src/utils/interpolator.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/utils/interpolator.rs)
- [renovate.json](https://github.com/elastic/mcp-server-elasticsearch/blob/main/renovate.json)
- [NOTICE.txt](https://github.com/elastic/mcp-server-elasticsearch/blob/main/NOTICE.txt)
</details>

# 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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/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](https://ela.st/agent-builder-docs) [MCP endpoint](https://ela.st/agent-builder-mcp), which is available in Elastic 9.2.0+ and Elasticsearch Serverless projects.

Source: [README.md](https://github.com/elastic/mcp-server-elasticsearch/blob/main/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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/catalog-info.yaml)

## Architecture

### High-Level Architecture

```mermaid
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 --> M
```

### Server 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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/cli.rs)

### Configuration Structure

```mermaid
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]
    end
```

Source: [src/lib.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/lib.rs) and [src/cli.rs:63-80](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/cli.rs)

## 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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/mod.rs)

### EsClientProvider

A wrapper around the Elasticsearch client that provides request-context-aware client instances.

```rust
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 `Authorization` header from incoming HTTP requests

Source: [src/servers/elasticsearch/mod.rs:88-106](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/mod.rs)

## 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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/mod.rs)

### 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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/mod.rs)

## Data Models

### Search Result

```rust
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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/base_tools.rs)

### ES|QL Response

```rust
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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/base_tools.rs)

## Transport Protocols

### Stdio Transport

Used for local MCP client connections. Compatible with Claude Desktop, Cursor, and VS Code.

```json
{
  "mcpServers": {
    "elasticsearch": {
      "command": "npx",
      "args": ["-y", "@elastic/mcp-server-elasticsearch"]
    }
  }
}
```

Source: [src/cli.rs:42-55](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/cli.rs)

### 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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/protocol/http.rs)

## Environment Variable Interpolation

The configuration file supports environment variable interpolation using `${VAR}` or `${VAR:default}` syntax.

```rust
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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/utils/interpolator.rs)

## CLI Commands

### Stdio Command

```bash
mcp-server-elasticsearch stdio --config <path>
```

Source: [src/cli.rs:36-41](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/cli.rs)

### HTTP Command

```bash
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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/cli.rs)

## 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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/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](https://github.com/elastic/mcp-server-elasticsearch/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

```json
{
  "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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/lib.rs)

### 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](https://github.com/elastic/mcp-server-elasticsearch/releases)

---

<a id='page-available-tools'></a>

## Available MCP Tools

### Related Pages

Related topics: [System Architecture](#page-architecture), [Troubleshooting Guide](#page-troubleshooting)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [src/servers/elasticsearch/base_tools.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/base_tools.rs)
- [src/servers/elasticsearch/mod.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/mod.rs)
- [src/cli.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/cli.rs)
- [src/protocol/http.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/protocol/http.rs)
- [src/lib.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/lib.rs)
</details>

# 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.

```mermaid
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| A
```

**Source:** [src/servers/elasticsearch/base_tools.rs:30-36](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/base_tools.rs#L30-L36)

## 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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/base_tools.rs#L1-L40)

### 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.

```rust
#[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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/base_tools.rs#L70-L92)

### 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.

```rust
#[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](https://github.com/elastic/mcp-server-elasticsearch/issues/185).

**Source:** [src/servers/elasticsearch/base_tools.rs:95-115](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/base_tools.rs#L95-L115)

### 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:

```rust
#[derive(Serialize, Deserialize)]
pub struct SearchResult {
    pub hits: Hits,
    #[serde(default)]
    pub aggregations: IndexMap<String, Value>,
}
```

```rust
#[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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/base_tools.rs#L118-L165)

### 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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/mod.rs#L40-L55)

### 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.

```rust
#[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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/base_tools.rs#L168-L200)

## 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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/mod.rs#L15-L30)

### Tool Definition Structure

```rust
#[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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/mod.rs#L32-L55)

### 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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/mod.rs#L57-L72)

## Tool Configuration

Tools are configured in the server configuration file under the `tools` section:

```json
{
  "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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/lib.rs#L45-L65)

## Server Capabilities

Each tool set reports its capabilities through the `get_info()` method:

```rust
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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/base_tools.rs#L27-L36)

## 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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/cli.rs#L20-L35)

## Response Structure

All tools return results wrapped in `CallToolResult`:

```rust
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](https://github.com/elastic/mcp-server-elasticsearch/issues/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](https://github.com/elastic/mcp-server-elasticsearch/issues/185).
- **HTTP transport limitations**: When accessing via HTTP/SSE, certain tool combinations may return errors. This is discussed in [issue #173](https://github.com/elastic/mcp-server-elasticsearch/issues/173).

---

<a id='page-architecture'></a>

## System Architecture

### Related Pages

Related topics: [MCP Protocol Configuration](#page-protocols), [Available MCP Tools](#page-available-tools)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [src/lib.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/lib.rs)
- [src/servers/elasticsearch/mod.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/mod.rs)
- [src/servers/elasticsearch/base_tools.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/base_tools.rs)
- [src/cli.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/cli.rs)
- [src/utils/interpolator.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/utils/interpolator.rs)
- [catalog-info.yaml](https://github.com/elastic/mcp-server-elasticsearch/blob/main/catalog-info.yaml)
</details>

# 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:

```mermaid
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 --> ES
```

## Transport 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.

```rust
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:

```rust
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:

```rust
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:

```rust
#[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

```rust
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:

```rust
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:

```rust
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:

```rust
#[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:

```rust
#[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

```rust
#[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:

```rust
#[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

```rust
#[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

```rust
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

```rust
#[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

```mermaid
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 requests
```

**Source:** [src/lib.rs:1-100]()

## Known Limitations

### Community-Reported Issues

1. **Nested Mapping Parsing (Issue #185)**: The `get_mappings` tool 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.

2. **HTTP Transport Complexity (Issue #17)**: Support for streamable HTTP is a requested enhancement. Currently, the server supports stdio and HTTP/SSE transports only.

3. **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:

1. **Transport Layer**: Handles protocol-agnostic communication (stdio/HTTP/SSE)
2. **Protocol Layer**: Implements MCP specification for tool discovery and invocation
3. **Service Layer**: Manages Elasticsearch connections and authentication
4. **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.

---

<a id='page-protocols'></a>

## MCP Protocol Configuration

### Related Pages

Related topics: [Docker Deployment](#page-deployment), [System Architecture](#page-architecture)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [src/cli.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/cli.rs)
- [src/protocol/http.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/protocol/http.rs)
- [src/lib.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/lib.rs)
- [src/servers/elasticsearch/mod.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/mod.rs)
- [README.md](https://github.com/elastic/mcp-server-elasticsearch/blob/main/README.md)
</details>

# 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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/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.

---

## Protocol Types

The server supports three transport protocols, defined as an enum in the CLI module:

| Protocol | Description | Use Case |
|----------|-------------|----------|
| `Stdio` | Direct stdin/stdout communication | Local MCP clients (Claude Desktop, Cursor, VS Code) |
| `StreamableHttp` | HTTP-based streaming protocol | Web integrations, remote access, concurrent clients |
| `Sse` | Server-Sent Events (deprecated) | Legacy HTTP support |

Source: [src/cli.rs:28-33](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/cli.rs)

### Configuration Structure

```rust
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
#[serde(tag = "type")]
pub enum McpServer {
    Sse(Http),
    StreamableHttp(Http),
    Stdio(Stdio),
}
```

#### Stdio Configuration

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `command` | String | Yes | Command to run (e.g., "npx", "docker") |
| `args` | Vec<String> | Yes | Command arguments |
| `env` | HashMap<String, String> | No | Environment variables |

Source: [src/cli.rs:6-14](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/cli.rs)

#### HTTP Configuration

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `url` | String | Yes | URL of the server |
| `headers` | HashMap<String, String> | No | HTTP headers to send with requests |

Source: [src/cli.rs:17-25](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/cli.rs)

---

## CLI Commands

The server provides two main command variants for starting the server:

```rust
#[derive(Debug, Subcommand)]
pub enum Command {
    Stdio(StdioCommand),
    Http(HttpCommand),
}
```

Source: [src/cli.rs:45-49](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/cli.rs)

### Stdio Command

```rust
#[derive(Debug, Args)]
pub struct StdioCommand {
    /// Config file
    #[clap(short, long)]
    pub config: Option<PathBuf>,
}
```

Source: [src/cli.rs:57-62](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/cli.rs)

**Usage:**

```bash
docker run -i --rm \
  -e ES_URL \
  -e ES_API_KEY \
  docker.elastic.co/mcp/elasticsearch \
  stdio
```

### HTTP Command

```rust
#[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:52-56](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/cli.rs)

**Usage:**

```bash
docker run -p 8080:8080 \
  -e ES_URL \
  -e ES_API_KEY \
  docker.elastic.co/mcp/elasticsearch \
  http --address 0.0.0.0:8080
```

---

## HTTP Server Configuration

The HTTP protocol implementation provides fine-grained control over server behavior through the `HttpServerConfig` struct:

```rust
pub struct HttpServerConfig<M: SessionManager = LocalSessionManager> {
    /// TCP address to bind to
    pub bind: SocketAddr,

    /// Parent cancellation token. `serve_with_config` will return a child token
    pub ct: CancellationToken,

    /// Streamable http server option
    pub keep_alive: Option<Duration>,

    /// Streamable http server option
    pub stateful_mode: bool,

    /// Streamable http server option
    pub session_manager: Arc<M>,
}
```

Source: [src/protocol/http.rs:9-21](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/protocol/http.rs)

### Configuration Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `bind` | SocketAddr | 127.0.0.1:8080 | TCP address to bind to |
| `ct` | CancellationToken | - | Parent cancellation token for graceful shutdown |
| `keep_alive` | Option<Duration> | None | SSE keep-alive interval for connection maintenance |
| `stateful_mode` | bool | false | Enable stateful session management |
| `session_manager` | Arc<M> | NeverSessionManager | Session manager implementation |

Source: [src/lib.rs:75-88](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/lib.rs)

### Protocol Implementation

The `HttpProtocol` struct provides the server startup logic:

```rust
pub struct HttpProtocol {}

impl HttpProtocol {
    pub async fn serve_with_config<S: Service<RoleServer>, M: SessionManager>(
        server_provider: impl Into<ServerProvider<S>>,
        config: HttpServerConfig<M>,
    ) -> std::io::Result<CancellationToken> { ... }
}
```

Source: [src/protocol/http.rs:25-37](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/protocol/http.rs)

---

## Server Setup Flow

The following diagram shows how the server initializes and configures the MCP protocol based on user input:

```mermaid
graph TD
    A[Start Server] --> B{Command Type?}
    B -->|stdio| C[Initialize StdioCommand]
    B -->|http| D[Initialize HttpCommand]
    
    C --> E[Read Config File]
    D --> F[Parse CLI Args]
    F --> G{Config File Provided?}
    G -->|Yes| H[Load from File]
    G -->|No| I[Use Environment Variables]
    E --> J[setup_services]
    H --> J
    I --> J
    
    J --> K[Create ElasticsearchMcpConfig]
    K --> L[Build ServerProvider]
    L --> M{Protocol Type?}
    M -->|Stdio| N[Start Stdio Server]
    M -->|Http| O[HttpProtocol::serve_with_config]
    
    O --> P[Create HttpServerConfig]
    P --> Q[Initialize StreamableHttpServer]
    Q --> R[Bind to Address]
    R --> S[Listen for Connections]
```

---

## Configuration File Format

The server accepts JSON configuration files that can include both Elasticsearch connection settings and MCP server definitions:

```json
{
  "elasticsearch": {
    "url": "${ES_URL}",
    "api_key": "${ES_API_KEY:}",
    "username": "${ES_USERNAME:}",
    "password": "${ES_PASSWORD:}",
    "ssl_skip_verify": "${ES_SSL_SKIP_VERIFY:false}"
  },
  "mcp_servers": {
    "my-server": {
      "type": "streamableHttp",
      "url": "http://localhost:8080",
      "headers": {}
    }
  }
}
```

Source: [src/lib.rs:95-106](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/lib.rs)

### Environment Variable Expansion

Configuration files support environment variable interpolation using `${VAR}` or `${VAR:default}` syntax:

| Variable | Description | Default |
|----------|-------------|---------|
| `ES_URL` | Elasticsearch cluster URL | Required |
| `ES_API_KEY` | API key for authentication | None |
| `ES_USERNAME` | Username for basic auth | None |
| `ES_PASSWORD` | Password for basic auth | None |
| `ES_SSL_SKIP_VERIFY` | Skip SSL certificate verification | false |
| `HTTP_ADDRESS` | HTTP server bind address | 127.0.0.1:8080 |

Source: [src/servers/elasticsearch/mod.rs:28-46](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/mod.rs)

---

## Available Tools

Regardless of protocol configuration, the server exposes these tools to connected MCP clients:

| Tool | Description | Read Only |
|------|-------------|-----------|
| `list_indices` | List all available Elasticsearch indices | Yes |
| `get_mappings` | Get field mappings for a specific index | Yes |
| `search` | Perform Elasticsearch search with Query DSL | Yes |
| `esql` | Execute ESQL queries | No |
| `get_shards` | Get shard information for indices | Yes |

Source: [README.md](https://github.com/elastic/mcp-server-elasticsearch/blob/main/README.md)

---

## Deployment Considerations

### Local Development

For local development with stdio protocol, use the default configuration with environment variables:

```bash
export ES_URL=https://localhost:9200
export ES_API_KEY=your-api-key
docker run -i --rm \
  -e ES_URL -e ES_API_KEY \
  docker.elastic.co/mcp/elasticsearch \
  stdio
```

### Production HTTP Deployment

For production deployments requiring remote access:

```bash
docker run -p 8080:8080 \
  -e ES_URL=https://your-cluster.es.amazonaws.com:9200 \
  -e ES_API_KEY=your-api-key \
  docker.elastic.co/mcp/elasticsearch \
  http --address 0.0.0.0:8080
```

### Address Binding Behavior

| Mode | Default Address | Container Mode Default |
|------|-----------------|------------------------|
| HTTP | 127.0.0.1:8080 | 0.0.0.0:8080 |
| Stdio | N/A (uses stdin/stdout) | N/A |

Source: [src/lib.rs:68-76](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/lib.rs)

---

## Known Issues and Limitations

### Authentication Issues

Issue #170 documents cases where basic authentication fails with 401 Unauthorized despite correct credentials. Users should verify:

1. `ES_USERNAME` and `ES_PASSWORD` are both set
2. The Elasticsearch cluster supports basic authentication
3. No conflicting authentication headers in MCP client configuration

### HTTP Transport Status

As documented in Issue #17, the server previously only supported stdio mode. Streamable-HTTP support was added to address modern deployment requirements. Users requiring HTTP transport should ensure their MCP client also supports the streamable-HTTP protocol specification.

---

## References

- [Model Context Protocol Specification](https://modelcontextprotocol.io/specification/latest/basic/transports)
- [Streamable HTTP Transport](https://modelcontextprotocol.io/docs/concepts/transports#streamable-http)
- [Elasticsearch MCP Server Releases](https://github.com/elastic/mcp-server-elasticsearch/releases)

---

<a id='page-deployment'></a>

## Docker Deployment

### Related Pages

Related topics: [MCP Protocol Configuration](#page-protocols), [Authentication and Security](#page-authentication), [Configuration Reference](#page-configuration-reference)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [src/lib.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/lib.rs) - Main library entry point with container mode and service setup
- [src/cli.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/cli.rs) - Command-line interface definitions
- [src/servers/elasticsearch/mod.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/mod.rs) - Elasticsearch server implementation
- [README.md](https://github.com/elastic/mcp-server-elasticsearch/blob/main/README.md) - Main documentation with deployment instructions
- [src/servers/elasticsearch/base_tools.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/base_tools.rs) - Base tools implementation
</details>

# 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.

```mermaid
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] --> C
```

## Container 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()](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/lib.rs)

### 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

```bash
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:

```json
{
  "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

```bash
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()](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/lib.rs)

### HTTP-SSE Mode

For environments that require Server-Sent Events (SSE) for backwards compatibility, the HTTP server can be started with SSE support enabled:

```bash
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:

```bash
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()](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/mod.rs)

If `localhost` is detected and no alias resolves successfully, the server logs a warning but continues operation:

```rust
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

```json5
{
  "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

```bash
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:

1. **Check container logs**: View logs using `docker logs <container-id>` to identify authentication or connection errors.

2. **Test Elasticsearch connectivity**: From within the container:
   ```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>
   ```

3. **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:

```bash
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:

```bash
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:

```bash
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_mappings` tool 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](https://github.com/elastic/mcp-server-elasticsearch/issues/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](https://github.com/elastic/mcp-server-elasticsearch/issues/191)) but not yet available.

---

<a id='page-authentication'></a>

## Authentication and Security

### Related Pages

Related topics: [Docker Deployment](#page-deployment), [Troubleshooting Guide](#page-troubleshooting)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [src/servers/elasticsearch/mod.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/mod.rs)
- [src/lib.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/lib.rs)
- [src/servers/elasticsearch/base_tools.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/base_tools.rs)
- [src/cli.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/cli.rs)
- [README.md](https://github.com/elastic/mcp-server-elasticsearch/blob/main/README.md)
</details>

# 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:

```rust
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:

```rust
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:

```json
{
    "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.

```rust
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:

1. Extracting the header value from the incoming HTTP request
2. Stripping `Bearer ` prefixes that some MCP clients (like the MCP Inspector) automatically prepend
3. Creating a new Elasticsearch client transport with the extracted credentials
4. 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.

```rust
#[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.

```rust
#[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

```mermaid
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 Response
```

## SSL/TLS Configuration

### Certificate Verification

By default, the server validates SSL/TLS certificates when connecting to Elasticsearch. To disable verification (not recommended for production):

```bash
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:

```rust
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_PASSWORD` are 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:**

1. Verify all environment variables are correctly set
2. Test connectivity directly to Elasticsearch with the same credentials
3. 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:

```rust
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](https://modelcontextprotocol.io/docs/concepts/transports)
- [Elasticsearch API Keys](https://www.elastic.co/docs/deploy-manage/api-keys)
- [Elasticsearch User Management](https://www.elastic.co/docs/deploy-manage/users-roles)
- [Docker Deployment Guide](#) - For container-specific authentication configuration

---

<a id='page-configuration-reference'></a>

## Configuration Reference

### Related Pages

Related topics: [Docker Deployment](#page-deployment), [Authentication and Security](#page-authentication)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [src/lib.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/lib.rs)
- [src/cli.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/cli.rs)
- [src/servers/elasticsearch/mod.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/mod.rs)
- [src/utils/interpolator.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/utils/interpolator.rs)
- [.env-example](https://github.com/elastic/mcp-server-elasticsearch/blob/main/.env-example)
</details>

# 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:

1. **Configuration file** (JSON or JSON5 format) specified via CLI
2. **Environment variables** for containerized deployments
3. **Default values** built into the binary

Source: [src/lib.rs:66-85](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/lib.rs#L66-L85)

## 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.

```json5
{
  "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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/lib.rs#L66-L79)

## 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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/mod.rs#L101-L129)

## 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

```bash
# 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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/utils/interpolator.rs#L1-L50)

```mermaid
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| B
```

## Authentication Configuration

The server supports multiple authentication methods. Only one should be configured per deployment.

### API Key Authentication

```json5
{
  "elasticsearch": {
    "url": "https://elasticsearch:9200",
    "api_key": "${ES_API_KEY}"
  }
}
```

### Username/Password Authentication

```json5
{
  "elasticsearch": {
    "url": "https://elasticsearch:9200",
    "username": "${ES_USERNAME}",
    "password": "${ES_PASSWORD}"
  }
}
```

### Authentication Priority

When multiple authentication methods are specified:

1. `api_key` takes precedence over `username`/`password`
2. Both `username` and `password` must be present for basic auth

Source: [src/servers/elasticsearch/mod.rs:70-81](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/mod.rs#L70-L81)

## CLI Configuration

### Stdio Mode

Run the server in stdio mode for local integration with MCP clients:

```bash
elastic-mcp stdio [OPTIONS]
```

| Option | Description |
|--------|-------------|
| `-c, --config <PATH>` | Path to configuration file |

Source: [src/cli.rs:38-45](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/cli.rs#L38-L45)

### HTTP Mode

Run the server as an HTTP server for remote access:

```bash
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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/cli.rs#L26-L37)

### 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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/lib.rs#L90-L99)

## 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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/mod.rs#L158-L175)

```mermaid
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:

```json5
{
  "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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/mod.rs#L35-L68)

## Complete Configuration Example

```json5
{
  "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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/.env-example)

## Known Configuration Issues

### Basic Auth 401 Unauthorized

If using basic authentication fails with 401 errors, verify that:
1. Both `ES_USERNAME` and `ES_PASSWORD` are set
2. The Elasticsearch user has sufficient permissions
3. 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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/mod.rs#L169-L170)

## Configuration Schema

The complete JSON Schema for validation:

```json
{
  "$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: [src/servers/elasticsearch/mod.rs:101-129](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/mod.rs#L101-L129)

---

<a id='page-troubleshooting'></a>

## Troubleshooting Guide

### Related Pages

Related topics: [Authentication and Security](#page-authentication), [Monitoring and Health Checks](#page-monitoring), [Available MCP Tools](#page-available-tools)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [src/servers/elasticsearch/mod.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/mod.rs)
- [src/servers/elasticsearch/base_tools.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/base_tools.rs)
- [src/lib.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/lib.rs)
- [src/cli.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/cli.rs)
- [src/utils/interpolator.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/utils/interpolator.rs)
</details>

# 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`:

```rust
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:**

1. Enter the container and confirm environment variables:
   ```bash
   docker exec -it <container_name> env | grep ES_
   ```

2. Verify the correct variable names are used:
   - `ES_URL` - Elasticsearch cluster URL
   - `ES_USERNAME` - Username (NOT `ES_lOGIN`)
   - `ES_PASSWORD` - Password
   - `ES_API_KEY` - API key (alternative to username/password)

3. 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:**

```json
{
  "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`:

```rust
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:**

```json
{
  "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`:

```rust
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:**

```json
{
  "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:**

```json
{
  "elasticsearch": {
    "url": "http://localhost:9200"
    // Missing comma above causes parsing error
  }
}
```

**Source:** `src/lib.rs:58-63`:

```rust
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`:

```rust
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:

```json
{
  "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`:

```rust
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`:

```rust
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`:

```rust
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`:

```rust
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`:

```rust
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:**

1. Check logs: `docker logs <container_name>`
2. Verify environment variables are set
3. Ensure Elasticsearch URL is accessible from container

**Port already in use:**

```bash
# 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`:

```rust
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_URL` is set and points to a reachable Elasticsearch cluster
- [ ] Authentication credentials (API key or username/password) are correct
- [ ] If using Basic auth, both `ES_USERNAME` and `ES_PASSWORD` are set (not `ES_lOGIN`)
- [ ] SSL certificate issues: set `ES_SSL_SKIP_VERIFY=true` for self-signed certs
- [ ] Docker networking allows container to reach Elasticsearch
- [ ] Index name is correct (use `list_indices` tool to verify)
- [ ] User has necessary Elasticsearch permissions for the requested operations

## Getting Help

For additional support:

1. Search [existing GitHub issues](https://github.com/elastic/mcp-server-elasticsearch/issues)
2. Check the [Dependency Dashboard](https://github.com/elastic/mcp-server-elasticsearch/issues/6) for known dependency-related issues
3. Enable debug logging by setting `RUST_LOG=debug` when running the server

---

<a id='page-monitoring'></a>

## Monitoring and Health Checks

### Related Pages

Related topics: [Troubleshooting Guide](#page-troubleshooting), [Docker Deployment](#page-deployment)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [src/protocol/http.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/protocol/http.rs)
- [src/servers/elasticsearch/base_tools.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/servers/elasticsearch/base_tools.rs)
- [src/bin/start_http.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/bin/start_http.rs)
- [README.md](https://github.com/elastic/mcp-server-elasticsearch/blob/main/README.md)
- [src/lib.rs](https://github.com/elastic/mcp-server-elasticsearch/blob/main/src/lib.rs)
</details>

# 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:

```http
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](src/protocol/http.rs)

### Liveness Probe

The liveness endpoint confirms the server process is running:

```http
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](src/protocol/http.rs)

### Ping Endpoint

The ping endpoint provides a quick health verification:

```http
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](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:

```json
{
  "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](src/servers/elasticsearch/base_tools.rs)

### Index Listing

The `list_indices` tool enumerates all indices accessible to the authenticated user:

```json
{
  "name": "list_indices",
  "arguments": {
    "index_pattern": "*"
  }
}
```

**Response Structure:**

```json
[
  {
    "index": "index-name",
    "status": "open",
    "doc_count": 12345
  }
]
```

### Mappings Retrieval

The `get_mappings` tool retrieves field mappings for index analysis:

```json
{
  "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](https://github.com/elastic/mcp-server-elasticsearch/issues/185)

## Architecture

### Health Check Request Flow

```mermaid
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:

```mermaid
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](src/protocol/http.rs)

## Troubleshooting

### Common Issues

#### Authentication Failures

**Symptom:** 401 Unauthorized errors when the server attempts to connect to Elasticsearch.

**Diagnostic Steps:**

1. Verify environment variables inside the container:
   ```bash
   docker exec <container-id> env | grep ES_
   ```

2. Confirm credentials are correctly set:
   - `ES_API_KEY` for API key authentication
   - `ES_USERNAME` and `ES_PASSWORD` for basic auth

3. Test connectivity directly from the container:
   ```bash
   # Basic auth
   docker exec <container-id> curl -k -u <username>:<password> <ES_URL>
   
   # API key
   docker exec <container-id> curl -k -H "Authorization: ApiKey <api-key>" <ES_URL>
   ```

Source: [README.md](README.md)

#### Container Logs Analysis

View logs to identify connection or authentication issues:

```bash
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:

```bash
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:
```bash
# Environment variable
HTTP_ADDRESS=0.0.0.0:8080

# CLI argument
--address 0.0.0.0:8080
```

Source: [src/cli.rs](src/cli.rs)

## Best Practices

### Kubernetes Probes

Configure Kubernetes probes to use the health check endpoints:

```yaml
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 `/ping` for load balancer health checks
- Use `/_health/ready` for orchestration readiness signals
- Use `/_health/live` for process liveness detection
- Leverage `get_shards` tool 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 `/ping` endpoint returns minimal information suitable for public exposure

## Configuration Reference

### Health Router Configuration

The health router is mounted at `/_health` within the main application router:

```rust
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](src/protocol/http.rs)

### Server Initialization

Health endpoints become available after successful server initialization:

```rust
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](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](https://ela.st/agent-builder-docs) MCP endpoint available in Elastic 9.2.0+ and Elasticsearch Serverless projects. Source: [README.md](README.md)

---

<a id='page-contributing'></a>

## Contributing Guide

### Related Pages

Related topics: [System Architecture](#page-architecture)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [Cargo.toml](https://github.com/elastic/mcp-server-elasticsearch/blob/main/Cargo.toml)
- [Makefile](https://github.com/elastic/mcp-server-elasticsearch/blob/main/Makefile)
- [docs/CONTRIBUTING.md](https://github.com/elastic/mcp-server-elasticsearch/blob/main/docs/CONTRIBUTING.md)
- [rustfmt.toml](https://github.com/elastic/mcp-server-elasticsearch/blob/main/rustfmt.toml)
- [scripts/cargo-run.sh](https://github.com/elastic/mcp-server-elasticsearch/blob/main/scripts/cargo-run.sh)
</details>

# 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

1. **Clone the repository**
   ```bash
   git clone https://github.com/elastic/mcp-server-elasticsearch.git
   cd mcp-server-elasticsearch
   ```

2. **Install Rust dependencies**
   ```bash
   cargo fetch
   ```

3. **Verify the build compiles successfully**
   ```bash
   cargo build
   ```

### Running the Server Locally

The project provides a helper script for running the server during development:

```bash
./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:**
```bash
cargo fmt --check
```

### Linting

Run clippy for linting checks:
```bash
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 in `src/servers/elasticsearch/base_tools.rs`
- **Configuration**: Configuration parsing uses serde with JSON5 support for comments in config files
- **Error Handling**: The project uses `anyhow::Error` for flexible error handling
- **HTTP Authentication**: The `EsClientProvider` supports authorization header passthrough for HTTP transport mode

## Testing

### Unit Tests

Run unit tests with:
```bash
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:

```bash
make build
```

## MCP Tool Development

### Defining New Tools

Tools are defined using the `#[tool]` attribute macro. The framework expects specific parameters:

```rust
#[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`:

```rust
#[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:

```rust
Ok(CallToolResult::success(vec![
    Content::text("Description"),
    Content::json(data)?,
]))
```

## Submitting Changes

### Branch Strategy

1. Create a feature branch from `main`:
   ```bash
   git checkout -b feature/my-feature
   ```

2. Make your changes following the coding standards

3. Commit with clear, descriptive messages:
   ```bash
   git commit -m "Add support for new ES|QL aggregation function"
   ```

### 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:

```bash
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:

1. **Nested Mappings Issue**: The `get_mappings` tool may fail with "error decoding response body" when nested properties omit explicit type specification. See [Issue #185](https://github.com/elastic/mcp-server-elasticsearch/issues/185).

2. **Basic Auth Configuration**: Authentication via environment variables requires specific naming (`ES_USERNAME`, `ES_PASSWORD`). See [Issue #170](https://github.com/elastic/mcp-server-elasticsearch/issues/170).

3. **Platform Binaries**: Currently, Linux ARM64 binaries are not published. See [Issue #191](https://github.com/elastic/mcp-server-elasticsearch/issues/191).

## Getting Help

If you encounter issues while contributing:

- Check existing [GitHub Issues](https://github.com/elastic/mcp-server-elasticsearch/issues) for similar problems
- Review the [MCP Protocol Documentation](https://modelcontextprotocol.io/docs/concepts/transports) 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](https://github.com/elastic/mcp-server-elasticsearch/blob/main/NOTICE.txt) file for details.

---

<!-- evidence_pipeline_checked: true -->
<!-- evidence_injected: true -->

---

## Pitfall Log

Project: elastic/mcp-server-elasticsearch

Summary: 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
- Evidence strength: runtime_trace
- 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.
- Suggested check: Reproduce the official install and quickstart path in an isolated environment.
- Repro command: `docker run -i --rm -e ES_URL -e ES_API_KEY docker.elastic.co/mcp/elasticsearch stdio`
- Evidence: identity.distribution | github_repo:953992846 | https://github.com/elastic/mcp-server-elasticsearch

## 2. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- 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.
- Suggested 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
- Evidence strength: source_linked
- 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.
- Suggested 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
- Evidence strength: source_linked
- Finding: README/documentation is current enough for a first validation pass.
- User impact: May increase setup, validation, or first-run risk for the user.
- Suggested 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
- Evidence strength: source_linked
- 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.
- Suggested 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
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Suggested 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
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Suggested 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
- Evidence strength: source_linked
- 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.
- Suggested 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
- Evidence strength: source_linked
- Finding: issue_or_pr_quality=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Suggested 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
- Evidence strength: source_linked
- Finding: release_recency=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Suggested 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

<!-- canonical_name: elastic/mcp-server-elasticsearch; human_manual_source: deepwiki_human_wiki -->
