关于 esmole
esmole 是面向 AI Agent 的 stdio MCP 服务,用于管理 Elasticsearch 7.x 和 8.x 集群。它支持运行时修改连接、项目级或全局存储,以及带只读限制的 SSH 隧道。
主要功能
- 01运行时管理命名集群
- 02支持 Elasticsearch 7.x 和 8.x
- 03项目级与全局连接存储
- 04SSH 隧道与主机密钥验证
- 05只读请求白名单与响应限制
适用场景
- 01在 Agent 会话中添加或修改项目集群
- 02通过 SSH 堡垒机查询 Elasticsearch
- 03将 Agent 连接限制为获准的读取端点
MCP 相关
它向 MCP 客户端提供 Elasticsearch 连接管理和查询工具。每次工具调用都会重新读取连接配置,因此修改无需重启即可生效。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
10收录的工具列表 · 更新于 3 天前
此工具无需参数。
详细介绍
esmole-mcp
面向可自行管理连接的 AI Agent,支持 Elasticsearch 7.x 和 8.x:通过 MCP 工具在运行时按项目添加、编辑和移除集群,并通过 SSH 热重载。
esmole-mcp 是一个用于 Elasticsearch 的 stdio MCP 服务。告诉
你的 Agent:“通过 bastion.example.com 连接到日志集群,只读”,它就会调用
add_connection。连接会保存到项目专属的 .esmole.json 或全局存储中,
并且每次调用工具时都会重新读取这两处配置。因此,无论是 Agent、你在编辑器中的修改,
还是其他会话做出的更改,都会在下一次调用时生效。无需修改客户端配置,也无需重启。
- 支持 Elasticsearch 7.x 和 8.x,可连接任意数量的命名集群,并支持 API 密钥或基本身份验证。
- 支持通过密码、密钥或 Agent 身份验证建立 SSH 隧道;主机密钥会通过
known_hosts或 固定指纹进行验证。 readonly连接仅可访问允许列表中的读取端点;请求超时为绝对时限,响应上限为 10 MB,结果限制为 40 KB,避免大型聚合结果淹没上下文。
需要 PostgreSQL 或 MySQL?dbmole-mcp 为 SQL 数据库采用了相同的设计。
演示

安装
需要 Node.js 22 或更高版本。服务启动时无需配置;连接可以稍后添加。
Claude Code
claude mcp add esmole -- npx -y esmole-mcp
添加 --scope user,即可在每个项目中启用。也可以将其安装为插件(Claude Code 2.1.275 或更高版本):
/plugin install esmole --marketplace ed-smartass/esmole-mcp
Codex
codex mcp add esmole -- npx -y esmole-mcp
或添加到 ~/.codex/config.toml:
[mcp_servers.esmole]
command = "npx"
args = ["-y", "esmole-mcp"]
Cursor
VS Code
Claude Desktop
从最新版本下载 esmole-mcp-<version>.mcpb 并打开,或者将下面的 JSON 添加到 claude_desktop_config.json。Claude Desktop 不会在项目内启动服务,因此请将连接保存在全局存储中(默认作用域)。
其他 MCP 客户端
{
"mcpServers": {
"esmole": { "command": "npx", "args": ["-y", "esmole-mcp"] }
}
}
Docker
镜像由源代码构建:
git clone https://github.com/ed-smartass/esmole-mcp.git && cd esmole-mcp
docker build -t esmole-mcp .
{
"mcpServers": {
"esmole": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-v", "esmole-store:/home/node/.config/esmole",
"-v", "/abs/path/to/project:/work",
"-v", "/home/me/.ssh/known_hosts:/home/node/.ssh/known_hosts:ro",
"esmole-mcp"
]
}
}
}
容器以 node 用户运行,工作目录为 /work:将项目挂载到该目录,即可使用项目中的 .esmole.json;命名卷则会保留全局存储。对于 SSH 连接,请挂载 known_hosts(或固定 ssh.hostKey),并将通过 privateKeyPath 引用的任何私钥挂载到连接指定的路径;若设置 ssh.agent: true,请转发 Agent 套接字(-v "$SSH_AUTH_SOCK:/ssh-agent" -e SSH_AUTH_SOCK=/ssh-agent)。若要访问只有本机才能连接的主机,请添加 --add-host=host.docker.internal:host-gateway(Linux),并使用 host.docker.internal。镜像中没有 git,因此“未被 git 忽略”检查总会发出警告。
即使仍设置了 ESMOLE_CONNECTIONS、ESMOLE_CONFIG 或 --config,0.1 版的配置也能正常启动:服务会在 stderr 输出一行提示,说明这些配置已被忽略。请参阅变更日志。
快速开始
例如,可以向 Agent 提出请求:
在此项目中添加一个名为
logs的只读 elasticsearch 连接:通过 ssh,以 [email protected] 身份和我的 ssh agent 连接到 10.0.0.20 上的 https 服务,使用 API 密钥 VnVhQ2ZH…。然后显示最大的五个索引。
它会调用 add_connection,并传入:
{
"name": "logs",
"host": "10.0.0.20",
"apiKey": "VnVhQ2ZH…",
"readonly": true,
"ssh": { "host": "bastion.example.com", "user": "deploy", "agent": true },
"scope": "project"
}
然后调用 test_connection 和 es_list_indices。你在聊天中输入的凭据会经过模型的上下文;对于生产环境的密钥,请自行将条目写入文件。
连接
两层配置
| 层级 | 文件 | 结构 |
|---|---|---|
project | .esmole.json,从服务的工作目录开始查找 | { "connections": [ ... ] } |
global | ~/.config/esmole/connections.json,或 ESMOLE_STORE 中指定的路径 | [ ... ] |
- 两层中名称相同:项目条目优先。
list_connections会报告每个连接的source(project或global);当一个条目遮蔽了另一层中同名的条目时,还会报告shadows。 - 热重载。 每次调用工具时都会重新读取这两个文件。在会话期间编辑文件后,下次调用就会使用新设置;发生变化的连接会创建新的客户端,并关闭旧客户端。
- 项目文件发现。 esmole 从服务的工作目录开始(Claude Code 和 Codex 会在项目目录中启动服务),向上遍历至文件系统根目录,并使用找到的第一个
.esmole.json。如果链接的 git worktree 中没有自己的文件,则会回退到主检出目录中的.esmole.json,因此同一仓库的所有 worktree 共用一个文件。 - 两个文件都是权限模式为
0600的普通 JSON 文件,并以原子方式写入。写入时会原样保留无法解析的条目,并拒绝覆盖无效 JSON 文件。
使用工具管理连接
add_connection接受连接字段以及scope:"global"(默认值)或"project"。选择project且尚无文件时,文件会创建在主检出目录的根目录 (或 git 根目录;如果不在 git 仓库中,则创建在工作目录)。如果 git 未忽略该文件, 响应会发出警告;请将.esmole.json添加到.gitignore,以免提交凭据。- 如果目标层中已存在同名连接,
add_connection会失败。另一层中存在同名连接是允许的; 响应会说明现在由哪个条目覆盖哪个条目。 update_connection会更新连接所在的层(优先层)。将可选字段设为null可移除该字段。ssh对象会逐字段合并到已存储的对象中,因此{ "ssh": { "hostKey": "SHA256:..." } }会保留已存储的 SSH 密钥。remove_connection会从该层删除连接,并告知删除后是否会显露另一层中的同名条目。- 凭据绝不会回显:
list_connections会显示 scheme、host、port、认证类型、verifyTls、readonly、source以及是否使用 SSH。
损坏和无效条目
- 未通过验证的条目不会悄然消失:其名称会解析为错误
(
connection 'x' in <file> is invalid: ...),而不会回退到全局存储中的同名条目;list_connections会将其列为无效条目。在同一文件中定义两个同名条目也会报错。remove_connection可以删除无效条目。 - 如果文件存在但无法读取、不是 JSON 格式或结构不正确,
list_connections会在brokenLayers中报告。损坏的项目文件会阻止所有连接;损坏的全局存储只会阻止 项目文件中未定义的名称。文件缺失时则视为空。
连接字段
不接受未知字段。必须提供身份验证信息:apiKey,或同时提供 user 和 password。
| 字段 | 类型 | 说明 |
|---|---|---|
name | string | [a-zA-Z0-9_-]+ |
type | elasticsearch | 默认值为 elasticsearch |
host | string | 设置 ssh 时,从 SSH 堡垒机看到的主机 |
port | number | 默认值为 9200 |
scheme | http | https | 默认值为 https |
verifyTls | boolean | 默认值为 true;false 会跳过证书验证 |
apiKey | string | API 密钥(base64 格式的 id:key) |
user, password | string | 基本身份验证 |
readonly | boolean | 默认值为 false,参见 只读 |
queryTimeoutMs | integer | 毫秒数,默认值为 60000,最大为 2147483647;参见 限制和超时 |
ssh | object | 参见 SSH 隧道 |
.esmole.json 示例:
{
"connections": [
{ "name": "local", "host": "localhost", "scheme": "http", "user": "elastic", "password": "changeme" },
{
"name": "logs",
"host": "10.0.0.20",
"apiKey": "VnVhQ2ZHY0JDZGJrUW0t...",
"readonly": true,
"ssh": { "host": "bastion.example.com", "user": "deploy", "agent": true }
}
]
}
SSH 隧道
ssh 字段 | 说明 |
|---|---|
host、port | 堡垒机;port 默认为 22 |
user | SSH 用户 |
password | 密码身份验证 |
privateKey / privateKeyPath | 密钥内容或路径(会展开 ~) |
passphrase | 用于加密密钥 |
agent | 设为 true 可使用 SSH_AUTH_SOCK 中的 Agent |
hostKey | 固定的主机密钥指纹,SHA256:...(OpenSSH 格式) |
{ "host": "bastion.example.com", "user": "deploy", "privateKeyPath": "~/.ssh/id_ed25519" }
{ "host": "bastion.example.com", "user": "deploy", "password": "…" }
{ "host": "bastion.example.com", "port": 2222, "user": "deploy", "agent": true }
{ "host": "bastion.example.com", "user": "deploy", "agent": true, "hostKey": "SHA256:EXAMPLEexampleEXAMPLEexampleEXAMPLEexample1" }
主机密钥会经过验证。 设置 hostKey 后,堡垒机的密钥必须与其匹配(接受末尾带有 = 填充的情况),并且不会查询 known_hosts。否则,密钥必须与 ~/.ssh/known_hosts 中该主机对应的条目匹配(端口为 22 时使用 host,否则使用 [host]:port):
- 普通和哈希主机名均可匹配。通配符模式永不匹配,带有否定通配符(
!*.example.com)的行会被忽略。 - 对于任何主机,凡是在
@revoked下列出的密钥都会被拒绝,无论该行的主机模式是什么。@cert-authority行和未知标记会被跳过。
不支持首次使用时自动信任:遇到未知密钥时,连接会失败并显示该密钥的指纹。请通过可信来源核验该指纹,然后将主机添加到 known_hosts,或固定该密钥:
ssh-keyscan -p 22 bastion.example.com 2>/dev/null | ssh-keygen -lf -
# 256 SHA256:EXAMPLEexampleEXAMPLEexampleEXAMPLEexample1 bastion.example.com (ED25519)
并让 Agent 使用该 SHA256:... 值设置 ssh.hostKey,调用 update_connection。
隧道每隔 15 s 发送一次保活信号,连续 3 次未收到回复后会重建。不支持:ProxyJump 和 ~/.ssh/config、SSH 证书。通过隧道使用 scheme: "https" 时,证书会针对 host 而非隧道的本地地址进行验证。
只读
readonly: true 可防止意外写入。每个工具(包括 es_request)都会先根据允许列表检查方法和路径,再发送任何内容:
GET和HEAD:允许。POST:仅允许_search、_msearch、_count、_field_caps和_pit(可选地位于一个索引或模式下,例如/logs-*/_search),以及/_search/scroll。DELETE:仅允许/_search/scroll和/_pit(关闭读取会话)。- 其他所有请求都会被拒绝,路径中包含
..、%2e、%2f、%5c、\或#的请求也会被拒绝,无论使用什么方法。因此,在只读连接上,es_request无法使用带/取整的日期数学索引名称,例如<logs-{now/d}>;可写连接不受影响。 - 辅助工具的索引和模式参数也会经过验证(不能包含
/、\、?、#、%、空白字符或..);支持逗号分隔列表、通配符、-exclusions和cluster:index。日期数学名称需要经过%编码,因此只能通过es_request使用。 - 由于所有
GET请求都会通过,仅用于GET的维护端点(例如GET /_flush)也会通过。它们不会更改任何文档,但并非读取操作。
这不是安全边界:Agent 可以通过 update_connection 将其关闭,这是有意如此设计的。如需强制保证,请使用仅具有读取权限的 Elasticsearch 角色或 API 密钥。
工具
| 工具 | 功能 |
|---|---|
list_connections | 列出连接及其 source、被覆盖的名称、无效条目和损坏的文件 |
add_connection | 将连接添加到 project 文件或 global 存储 |
update_connection | 修改连接所在位置的连接;null 会移除字段 |
remove_connection | 移除连接;报告因此显露的同名条目 |
test_connection | 端到端连接(包括隧道),并报告集群版本和延迟 |
es_request | 支持任意方法、路径、请求体和查询参数;受只读保护;用于 NDJSON(_bulk、_msearch)的字符串请求体 |
es_search | 在索引或模式上支持 query、aggs、size、source(作为 _source 发送)、sort、from、search_after、track_total_hits、highlight、script_fields;整个请求体若嵌套在 query 中,会被提取出来,并附带一条 note |
es_list_indices | 列出与模式匹配的索引(默认 *),并显示健康状态、文档数和存储大小 |
es_get_mapping | 将映射扁平化为 field: type;设置 raw: true 可获取原始映射 |
es_cluster_health | 集群健康状态 |
es_search 的 size 默认值为 10,上限为 1000,嵌套请求体中的 size 也不例外;如果有 aggs 但没有 query,则为 0。工具参数会严格检查:错误的键会被拒绝并给出提示,例如 unknown key '_source' — did you mean 'source'?。
集群返回的 4xx 或 5xx 错误(包括 es_request 的响应)会作为工具错误返回:第一行显示 HTTP <status> <root_cause[0].type>: <reason>,后面附上经过修剪的响应体。test_connection 遇到任何非 2xx 响应都会失败(例如,受到保护的集群返回 401)。
这十个工具定义约占用 2.1k 个上下文 token(tools/list 约 8,200 个字符;使用 npm run smoke 测量,按每个 token 约 4 个字符计算)。
限制和超时
- 每个结果最多为 40 KB 的 JSON。超过 2 KB 的字符串会被截断并标记为
…[+N bytes];如果请求体仍然过大,则会将最长的数组(hits、buckets)减半,直到符合限制。结果会在truncated中说明,并保持为有效 JSON。可通过在es_search中设置size和source,或在es_request中设置filter_path,来减少结果。 - 超过 10 MB 的响应会被中止并报错(“narrow it with size/_source”),不会返回部分内容。
queryTimeoutMs(默认 60 s)是每个请求的绝对截止时间,从连接开始到读取完响应体最后一个字节为止。
对比
| esmole-mcp | elastic/mcp-server-elasticsearch | cr7258/elasticsearch-mcp-server | |
|---|---|---|---|
| 状态 | 维护中 | 已弃用;Elastic 指向 Agent Builder MCP 端点(Elasticsearch 9.2+) | 维护中 |
| Elasticsearch 7.x | ✓ | — | ✓ |
| Elasticsearch 8.x | ✓ | ✓ | ✓ |
| OpenSearch | — | ✗ | ✓ |
| 内置 SSH 隧道 | ✓ | ✗ | ✗ |
| Agent 通过 MCP 工具添加和编辑连接 | ✓ | ✗ | ✗ |
✓ 有文档说明,✗ 不提供,— 未提及或未测试。信息核对时间:2026 年 10 月;欢迎指正。
安全性
连接文件以明文形式保存凭据(权限模式为 0600)。请勿将 .esmole.json 提交到 git。
集群错误会原样传递给客户端。
任何能够通过 update_connection 或编辑连接文件来更改连接的人,都可以重定向该连接的凭据。
更改 host 会保留已存储的密码或 API 密钥,更改 ssh.host 会保留已存储的 SSH 密码或密钥,因此下一次调用会将它们发送到新主机。请将连接工具和文件的写入权限视同凭据的访问权限。
项目的 .esmole.json 会被直接信任:在克隆的或不受信任的代码库中,它可以添加连接,或覆盖同名的全局连接,并将你的查询发送到另一台主机。发现过程会一路向上遍历到 /,因此父目录(例如 ~)中的文件会应用于其下的所有项目。在不熟悉的代码库中工作前,请检查 list_connections。
每个打开的 SSH 隧道都会在 127.0.0.1 上使用随机端口监听,且不需要身份验证:隧道打开期间,任何本地进程(包括共享主机上的其他用户)都可以通过它连接到目标主机。集群仍会要求提供其自身的凭据。
有关威胁模型以及如何报告漏洞,请参阅 SECURITY.md。
开发
npm ci
npm run lint # Biome
npm run typecheck # tsc
npm run test:unit # unit tests, mocked IO
npm run test:int # integration tests against Elasticsearch 7.17 and 8.x (needs Docker)
npm run smoke # build, npm pack, install the tarball in a temp dir, boot, tools/list
共享的连接核心是 @dbmole/core,
该核心在 dbmole-mcp 仓库中开发,并在构建时打包到此软件包中。请参阅
CONTRIBUTING.md。
许可证
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。