内容
# elastic-mcp
[](https://www.npmjs.com/package/elastic-mcp)
[](https://github.com/csimi/elastic-mcp/actions/workflows/build.yml)
> ⚠️ **Disclaimer: this MCP server was vibe coded.** It was built iteratively with an AI assistant and is
> typechecked and unit-tested, but review it yourself before relying on it — especially the write tools —
> and use it at your own risk.
一个适用于Elasticsearch的[模型上下文协议](https://modelcontextprotocol.io)服务器,使用JavaScript编写,并通过TypeScript(`@ts-check` / `checkJs`)进行类型检查。默认情况下为只读,可选写工具。
它通过 **stdio** 传输暴露搜索、索引检查和集群信息工具,并且透明地支持:
- 在反向代理后面提供Elasticsearch部署,位于 **基准路径**下(例如 `https://host/elasticsearch`)——官方客户端无法直接处理这种情况。
- 适用于 **Elasticsearch 8.x 和 9.x** 集群——v9客户端使用 `compatible-with=9` 并被8.x集群拒绝,因此匹配的客户端主版本在启动时选择(自动检测或固定)。
> **在企业环境中测试**,其中API密钥通常颁发给具有有限权限的受限用户(例如,仅限索引 `read`,无集群 `monitor` 或 `view_index_metadata`)。服务器设计为在这些设置中优雅地降级,而不是直接失败——它尽可能地回退到较低权限的API,并在必要时显示明确的授权错误。有关详细信息,请参见
> [Elasticsearch 版本兼容性](#elasticsearch-版本兼容性)和
> [权限回退表](#creating-an-api-key)。
## 用法
直接从npm使用 `npx`运行(无需安装 — 先设置环境变量) :
```sh
ELASTICSEARCH_URL=http://localhost:9200/elastic npx elastic-mcp
```
或全局安装以在 `PATH` 上获取 `elastic-mcp` 命令:
```sh
npm install -g elastic-mcp
ELASTICSEARCH_URL=http://localhost:9200/elastic elastic-mcp
```
### Claude Desktop / Claude Code
将服务器添加到MCP客户端配置中:
```json
{
"mcpServers": {
"elastic": {
"command": "npx",
"args": ["-y", "elastic-mcp"],
"env": {
"ELASTICSEARCH_URL": "https://host/elasticsearch",
"ELASTICSEARCH_API_KEY": "<base64-api-key>"
}
}
}
}
```
## 工具
### 读取工具
始终注册,所有工具都标记为只读(`readOnlyHint`)。
| 工具 | 描述 |
| --- | --- |
| `search` | 运行查询DSL搜索(查询、聚合、排序、大小、偏移、_source)。 |
| `count` | 统计与可选查询匹配的文档。 |
| `get_document` | 通过索引和ID获取单个文档。 |
| `list_indices` | 列出索引及其健康状态、状态、文档计数和大小;如果没有集群 `monitor` 权限,则回退到仅名称的 `_resolve/index`。 |
| `get_mapping` | 获取一个或多个索引的字段映射;如果没有 `view_index_metadata` 权限,则回退到读取级别的 `_field_caps`(字段名称 + 类型)。 |
| `get_settings` | 获取一个或多个索引的设置。 |
| `get_aliases` | 列出索引别名。 |
| `list_shards` | 列出分片及其角色、状态、文档计数、存储大小和节点(等效于 `_cat/shards`);如果没有集群 `monitor` 权限,则文档计数和存储大小返回 null。 |
| `get_kibana_object` | 通过 `<type>:<id>` 获取Kibana保存的对象;解码内联JSON字符串字段(visState、searchSourceJSON等)并将 `references[]` 解析为标题。读取 `.kibana_analytics` / `.kibana`,因此密钥需要对这些索引具有 `read` 权限。 |
| `cluster_health` | 集群健康状况,可选地限定在索引范围内。 |
| `cluster_stats` | 集群范围的统计信息。 |
| `cluster_info` | 集群名称、UUID和版本。 |
| `list_nodes` | 列出节点及其角色、堆、CPU和负载。 |
### 写工具
**默认情况下关闭**。仅当 `ELASTICSEARCH_ENABLE_WRITES=true` 时注册。每个工具都标记为 `destructiveHint`(`create_index` 除外),因此MCP客户端可以提示确认,并且仅在API密钥具有相应的写入权限时才有效。
| 工具 | 描述 |
| --- | --- |
| `index_document` | 创建或替换文档(如果省略,则自动生成ID;`createOnly` 以避免覆盖)。 |
| `update_document` | 部分更新文档,附带可选的upsert。 |
| `delete_document` | 通过ID删除单个文档。 |
| `create_index` | 创建索引,可选地带有映射、设置和别名。 |
| `delete_index` | 永久删除一个或多个索引 — 无法撤销。 |
## 配置
从环境变量中读取配置(参见 [`.env.example`](./.env.example)):
| 变量 | 必需 | 描述 |
| --- | --- | --- |
| `ELASTICSEARCH_URL` | 是 | 端点URL,可选地包含基准路径。 |
| `ELASTICSEARCH_API_KEY` | 否 | Base64 API密钥;优先于基本身份验证。 |
| `ELASTICSEARCH_USERNAME` | 否 | 基本身份验证用户名。 |
| `ELASTICSEARCH_PASSWORD` | 否 | 基本身份验证密码。 |
| `ELASTICSEARCH_CA_FINGERPRINT` | 否 | CA证书的SHA-256指纹。 |
| `ELASTICSEARCH_TLS_VERIFY` | 否 | 默认情况下为 `true`;设置为 `false` 以接受自签名/无效证书(仅限开发)。 |
| `ELASTICSEARCH_API_VERSION` | 否 | `8`、`9` 或 `auto`(默认)。选择客户端主版本;参见下文。 |
| `ELASTICSEARCH_ENABLE_WRITES` | 否 | 设置为 `true` 以注册写工具。默认情况下为只读。 |
### Elasticsearch 版本兼容性
官方客户端始终发送一个 `compatible-with=<major>` 媒体类型,与其自身的major版本相匹配,而不同major版本的集群会拒绝它。为了与 8.x 和 9.x 集群配合使用,两个客户端major版本都已捆绑,启动时选择正确的客户端major版本:
- `auto`(默认)— 探测集群的根端点(使用一个纯 `application/json` 请求,绕过兼容性头部),并选择匹配的客户端major版本。任何低于 8.x 的版本都使用 v8 客户端;9.x 及更高版本使用 v9 客户端。
- `8` / `9` — 显式固定客户端major版本并跳过探测。
> **注意:** 根探测需要 `cluster:monitor/main` 权限。索引范围的API密钥通常缺乏它(只能由管理员授予),在这种情况下,探测失败并出现
> `action [cluster:monitor/main] is unauthorized` 错误。当发生这种情况时,服务器记录一个警告并 **回退到v8客户端**,该客户端使用 `compatible-with=8` — 被 8.x 和 9.x 集群接受 — 因此搜索和索引检查继续工作。显式设置 `ELASTICSEARCH_API_VERSION` 以跳过探测(和警告)。
### 基准路径支持
当 `ELASTICSEARCH_URL` 包含路径(例如 `https://host/elasticsearch`)时,URL被分成其源(`https://host`)和前缀(`/elasticsearch`)。自定义连接类将前缀添加到每个请求路径中,因为官方客户端仅针对源路由请求并静默删除路径。
### 创建 API 密钥
默认情况下,服务器是只读的,因此它只需要集群 **`monitor`** 权限(用于 `cluster_health`、`cluster_stats`、`cluster_info`、`list_nodes` 和 `_cat` 列表)以及您要公开的索引上的 **`read`** 和 **`view_index_metadata`**。使用 [创建API密钥API](https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-create-api-key.html) 创建一个最小权限密钥:
```http
POST /_security/api_key
{
"name": "elastic-mcp",
"role_descriptors": {
"elastic_mcp_read_only": {
"cluster": ["monitor"],
"indices": [
{
"names": ["*"],
"privileges": ["read", "view_index_metadata"]
}
]
}
}
}
```
或使用 `curl`:
```sh
curl -u elastic -X POST "$ELASTICSEARCH_URL/_security/api_key" \
-H 'Content-Type: application/json' \
-d '{
"name": "elastic-mcp",
"role_descriptors": {
"elastic_mcp_read_only": {
"cluster": ["monitor"],
"indices": [
{ "names": ["*"], "privileges": ["read", "view_index_metadata"] }
]
}
}
}'
```
响应中包含一个 `encoded` 字段 — 该base64值正是 `ELASTICSEARCH_API_KEY` 所期望的:
```json
{
"id": "VuaCfGcBCdbkQm-e5aOx",
"name": "elastic-mcp",
"api_key": "ui2lp2axTNmsyakw9tvNnw",
"encoded": "VnVhQ2ZHY0JDZGJrUW0tZTVhT3g6dWkybHAyYXhUTm1zeWFrdzl0dk5udw=="
}
```
```sh
ELASTICSEARCH_API_KEY=VnVhQ2ZHY0JDZGJrUW0tZTVhT3g6dWkybHAyYXhUTm1zeWFrdzl0dk5udw==
```
将 `indices[].names` 限制为特定的索引名称或模式,以进一步缩小访问范围。
#### Kibana 保存对象访问(仅限 `get_kibana_object`)
`get_kibana_object` 直接从 `.kibana_analytics` 和 `.kibana` 索引读取Kibana的保存对象。这些是 **受限系统索引**,因此对 `*` 的普通 `read` 权限无法访问它们 — 角色需要一个专门的条目,允许使用 `allow_restricted_indices`。在现有读取条目旁边添加它(将通配符条目保留为 `false`,以便密钥无法读取其他系统索引,如 `.security-*`):
```json
{
"name": "elastic-mcp",
"role_descriptors": {
"elastic_mcp_read_only": {
"cluster": ["monitor"],
"indices": [
{ "names": ["*"], "privileges": ["read", "view_index_metadata"], "allow_restricted_indices": false },
{ "names": [".kibana*"], "privileges": ["read"], "allow_restricted_indices": true }
]
}
}
}
```
要扩大现有的 **密钥** 而不旋转它, [更新API密钥API](https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-update-api-key.html)
(`PUT /_security/api_key/<id>`)在原地重写其 `role_descriptors` — ID和编码值保持不变,因此 `ELASTICSEARCH_API_KEY` 无需更新。如果不使用 `get_kibana_object`,则完全省略此条目;该工具然后返回授权错误,服务器的其余部分不受影响。
#### 写权限(仅限 `ELASTICSEARCH_ENABLE_WRITES=true`)
上面的只读密钥无法修改数据 — 写工具将返回授权错误。要允许它们,请添加相关索引权限: **`write`**(涵盖 `index_document`、`update_document`、`delete_document`)、 **`create_index`** 和 **`delete_index`**:
```json
{
"name": "elastic-mcp",
"role_descriptors": {
"elastic_mcp_read_write": {
"cluster": ["monitor"],
"indices": [
{
"names": ["*"],
"privileges": ["read", "view_index_metadata", "write", "create_index", "delete_index"]
}
]
}
}
}
```
仅授予所需的权限 — 例如,如果从不删除索引,则可以删除 `delete_index`。集群 `monitor` 仍然是可选的(参见下面的回退表)。
#### 权限降级
一些工具在缺少权限时,会降级为较低权限的API,并记住这一点以便在后续重试中不再尝试被拒绝的端点:
| 工具 | 需要 | 缺失时的回退 | 授予者 |
| --- | --- | --- | --- |
| `list_indices` | 集群 `monitor` | `_resolve/index`(仅限名称) | `view_index_metadata` |
| `get_mapping` | `view_index_metadata` | `_field_caps`(字段名称 + 类型) | `read` |
因此,具有 `read` + `view_index_metadata`(甚至只是 `read`)的密钥仍然可以从 `search`、`count`、`get_document`、`get_mapping` 和 `list_indices` 中获得有用的结果。没有较低权限等效的工具 — `cluster_health`、`cluster_stats`、`cluster_info`、`list_nodes`(集群 `monitor`)、`get_settings`、`get_aliases` 和 `get_kibana_object`(受限 `.kibana*` 读取,参见上文)— 如果没有权限,则返回授权错误。
## 贡献
参见 [CLAUDE.md](./CLAUDE.md),了解项目布局、架构说明和编码约定。
## 许可证
ISC — 参见 [LICENSE](./LICENSE)。