list_connections
List Elasticsearch connections with the layer each comes from: project (.esmole.json, found by walking up from the project dir) or global (user store). Also shows invalid entries and broken files. Secrets are omitted.
無需輸入參數。

io.github.ed-smartass由 GitHub 帳號 ed-smartass 發布v0.2.0更新於 Nov 2, 2026
Elasticsearch 7.x/8.x with connections managed at runtime by the agent, SSH tunnels, readonly.
針對 Elasticsearch 7.x/8.x 的本機 stdio MCP 伺服器,讓助理在執行時新增、修改、移除具名叢集連線,並進行搜尋與查詢。
Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。
參照 儲存庫 中的啟動說明。
Elasticsearch 7.x and 8.x for AI agents that manage their own connections: add, edit and remove clusters through MCP tools at runtime, per project, hot-reloaded, through SSH.
[npm version] [npm downloads] [CI] [MIT license] [Glama score]
esmole-mcp is a stdio MCP server for Elasticsearch. Tell
your agent "connect to the logs cluster through bastion.example.com, readonly" and it calls
add_connection. The connection lands in a per-project .esmole.json or in your global store,
and both are re-read on every tool call, so a change made by the agent, by you in an editor,
or by another session applies on the next call. No client config edits, no restarts.
known_hosts or a
pinned fingerprint.readonly connections limited to an allowlist of read endpoints, absolute request
timeouts, a 10 MB response cap, and results capped at 40 KB so a big aggregation cannot
flood the context.Need PostgreSQL or MySQL? dbmole-mcp is the same design for SQL databases.
[The agent adds a readonly cluster to the project and searches it]
Requires Node.js 22 or newer. The server needs no configuration to start; connections are added later.
Claude Code
Add --scope user to enable it in every project. Or install it as a plugin (Claude Code
2.1.275 or later):
Codex
or in ~/.codex/config.toml:
Cursor
VS Code
Claude Desktop
Download esmole-mcp-<version>.mcpb from the
latest release and open it, or add
the JSON below to claude_desktop_config.json. Claude Desktop does not start servers inside a
project, so keep connections in the global store (the default scope).
Any other MCP client
Docker
The image is built from source:
The container runs as node with cwd /work: mount a project there to use its
.esmole.json, and the named volume keeps the global store. Mount known_hosts for SSH
connections (or pin ssh.hostKey), and any private key you reference by privateKeyPath, at
the path the connection names; for ssh.agent: true, forward the agent socket
(-v "$SSH_AUTH_SOCK:/ssh-agent" -e SSH_AUTH_SOCK=/ssh-agent). To reach hosts that only your machine can reach, add
--add-host=host.docker.internal:host-gateway (Linux) and use host.docker.internal. The
image has no git, so the "not ignored by git" check always warns.
A 0.1 setup that still sets ESMOLE_CONNECTIONS, ESMOLE_CONFIG or --config starts fine:
the server prints one line on stderr saying they are ignored. See the
changelog.
Ask the agent, for example:
Add a readonly elasticsearch connection
logsto this project: https on 10.0.0.20, API key VnVhQ2ZH…, through ssh as [email protected] with my ssh agent. Then show the five biggest indices.
It calls add_connection with:
then test_connection and es_list_indices. Credentials you type in chat pass through the
model's context; for production secrets, write the entry into the file yourself.
list_connections reports each connection's
source (project or global) and, when it hides an entry of the same name in the other
layer, shadows..esmole.json it finds. In a linked git worktree with no file of its own, it falls
back to the main checkout's .esmole.json, so every worktree of a repo shares one file.0600, written atomically. Writes keep entries they
cannot parse verbatim, and refuse to overwrite a file that is not valid JSON.add_connection takes the connection fields plus scope: "global" (default) or
"project". With project and no file yet, the file is created at the main checkout root
(or the git root, or the working directory outside git). If git does not ignore it, the
response warns you; add .esmole.json to .gitignore so credentials are not committed.add_connection fails when the name already exists in the target layer. The same name in
the other layer is allowed; the response says which entry now shadows which.update_connection patches the connection where it lives (the winning layer). A null
value removes an optional field. An ssh object is merged into the stored one field by
field, so { "ssh": { "hostKey": "SHA256:..." } } keeps the stored SSH secrets.remove_connection deletes it from that layer and tells you if that uncovers a same-name
entry in the other layer.list_connections shows scheme, host, port, the auth
kind, verifyTls, readonly, source and whether SSH is used.connection 'x' in <file> is invalid: ...) instead of falling through to a same-name entry
in the global store, and list_connections lists it as invalid. A name defined twice in one
file is an error too. remove_connection can delete an invalid entry.list_connections (brokenLayers). A broken project file blocks every connection; a broken
global store blocks only names the project file does not define. A missing file is just
empty.Unknown fields are rejected. Authentication is required: an apiKey, or both user and
password.
Example .esmole.json:
Host keys are verified. With hostKey set, the bastion's key must match it (trailing =
padding is accepted) and known_hosts is not consulted. Otherwise the key must match an entry
for the host in ~/.ssh/known_hosts (host on port 22, [host]:port otherwise):
!*.example.com) is ignored.@revoked is rejected for every host, whatever the line's host pattern.
@cert-authority lines and unknown markers are skipped.There is no trust-on-first-use: an unknown key fails the connection with an error that shows
the presented fingerprint. Check it against a trusted source, then either add the host to
known_hosts or pin it:
and ask the agent to update_connection with ssh.hostKey set to that SHA256:... value.
Tunnels send keepalives every 15 s and are rebuilt after 3 missed replies. Not supported:
ProxyJump and ~/.ssh/config, SSH certificates. With scheme: "https" through a tunnel,
the certificate is checked against host, not the tunnel's local address.
readonly: true protects against accidental writes. Every tool, es_request included,
checks method and path against an allowlist before anything is sent:
GET and HEAD: allowed.POST: only _search, _msearch, _count, _field_caps and _pit (optionally under one
index or pattern, e.g. /logs-*/_search), and /_search/scroll.DELETE: only /_search/scroll and /_pit (closing read sessions)..., %2e, %2f, %5c, \ or
#, whatever the method. So on a readonly connection es_request cannot use date-math
index names with / rounding, such as <logs-{now/d}>; writable connections are not
affected./, \, ?, #,
%, whitespace, ..); comma lists, wildcards, -exclusions and cluster:index work.
Date-math names need %-encoding, so they only work through es_request.GET passes, GET-only maintenance endpoints such as GET /_flush pass too.
They change no documents, but they are not reads.It is not a security boundary: the agent can switch it off with update_connection, by
design. For a hard guarantee, use an Elasticsearch role or API key with read privileges only.
es_search size defaults to 10 and is capped at 1000, also when it comes from a nested
body; with aggs and no query it is 0. Tool arguments are checked strictly: a wrong key is
rejected with a hint, e.g. unknown key '_source' — did you mean 'source'?.
A 4xx or 5xx from the cluster, es_request included, comes back as a tool error:
HTTP <status> <root_cause[0].type>: <reason> on the first line, then the trimmed response
body. test_connection fails on any non-2xx answer (e.g. 401 from a secured cluster).
The ten tool definitions cost about 2.1k tokens of context (tools/list is about 8,200
characters; measured with npm run smoke, at roughly 4 characters per token).
…[+N bytes]; if the body still does not fit, its longest array (hits, buckets) is halved
until it does. The result says so in truncated and stays valid JSON. Ask for less with
size and source on es_search, or filter_path on es_request.queryTimeoutMs (default 60 s) is an absolute deadline per request, from connecting to
reading the last byte of the body.✓ documented, ✗ not offered, — not mentioned or not tested. Checked October 2026; corrections welcome.
Connection files hold plaintext credentials (mode 0600). Keep .esmole.json out of git.
Cluster errors are passed to the client verbatim.
Whoever can change a connection, through update_connection or by editing a connection file,
can redirect its credentials. Changing host keeps the stored password or API key, and
changing ssh.host keeps the stored SSH password or key, so the next call sends them to the
new host. Treat write access to the connection tools and files like access to the credentials.
A project's .esmole.json is trusted as is: in a cloned or untrusted repository it can add
connections or shadow a global one of the same name and send your queries to another host.
Discovery walks up to /, so a file in a parent directory (such as ~) applies to every
project below it. Check list_connections before working in an unfamiliar repository.
Each open SSH tunnel listens on 127.0.0.1 at a random port without authentication: while it
is open, any local process, including other users on a shared host, can connect through it
to the target host. The cluster still asks for its own credentials.
See SECURITY.md for the threat model and how to report a vulnerability.
The shared connection core is @dbmole/core,
developed in the dbmole-mcp repo and bundled into this package at build time. See
CONTRIBUTING.md.
來源:README.md,提交 1968561
List Elasticsearch connections with the layer each comes from: project (.esmole.json, found by walking up from the project dir) or global (user store). Also shows invalid entries and broken files. Secrets are omitted.
無需輸入參數。
Add a named Elasticsearch connection. scope "global" (default) saves it to the user store; "project" saves it to .esmole.json in the project root. Auth via user/password or apiKey; optional ssh tunnel, readonly flag, scheme and verifyTls.
sshhost必填name必填porttypeuserscopeapiKeyschemepasswordreadonlyverifyTls另有 1 個Patch an ES connection by name in the layer it lives in (project or global). Set a field to null to remove it (e.g. "ssh": null); an ssh object is merged field by field, e.g. { "ssh": { "hostKey": "SHA256:..." } }. queryTimeoutMs sets the per-request deadline (null restores the 60 s default).
name必填patch必填Remove an ES connection by name from the layer it lives in (invalid entries too) and drop its cached client/tunnel.
name必填Verify an ES connection works end to end (including ssh tunnel). Reports server version and latency.
name必填Generic Elasticsearch REST passthrough: any method, any path. Returns {status, body}. A 4xx/5xx answer is returned as an error: "HTTP <status> <type>: <reason>" plus the trimmed body. Use a string body for NDJSON (_bulk, _msearch). On readonly connections only reads pass: GET, HEAD, POST [/<index>]/_search|_msearch|_count|_field_caps|_pit, POST /_search/scroll, DELETE /_pit and /_search/scroll.
bodypath必填method必填paramsconnection必填Search an index. Wraps POST /{index}/_search. query is the query DSL only; sort, from, search_after, track_total_hits, highlight, script_fields and source (_source) map to the same-named body keys. size defaults to 10; with aggs and no query it is forced to 0. Page with from+size or sort+search_after. Use source to project fields and keep responses small. Returns {status, body}; a 4xx/5xx is an error with the ES root cause.
aggsfromsizesortindex必填querysourcehighlightconnection必填search_afterscript_fieldstrack_total_hitsList indices matching a pattern with health, doc count and store size. Wraps GET /_cat/indices.
patternconnection必填Return an index mapping as a flat field:type list (compact, token-cheap). Pass raw=true for the nested ES JSON.
rawindex必填connection必填Cluster health summary (status, node and shard counts). Wraps GET /_cluster/health.
connection必填已滿足適用檢查項的 90%
該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。
請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。
claude mcp add esmole -- npx -y esmole-mcp/plugin install esmole --marketplace ed-smartass/esmole-mcpcodex mcp add esmole -- npx -y esmole-mcp[mcp_servers.esmole]command = "npx"args = ["-y", "esmole-mcp"]{ "mcpServers": { "esmole": { "command": "npx", "args": ["-y", "esmole-mcp"] } }}git clone https://github.com/ed-smartass/esmole-mcp.git && cd esmole-mcpdocker 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" ] } }}{ "name": "logs", "host": "10.0.0.20", "apiKey": "VnVhQ2ZH…", "readonly": true, "ssh": { "host": "bastion.example.com", "user": "deploy", "agent": true }, "scope": "project"}{ "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 } } ]}{ "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" }ssh-keyscan -p 22 bastion.example.com 2>/dev/null | ssh-keygen -lf -# 256 SHA256:EXAMPLEexampleEXAMPLEexampleEXAMPLEexample1 bastion.example.com (ED25519)npm cinpm run lint # Biomenpm run typecheck # tscnpm run test:unit # unit tests, mocked IOnpm 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