esmole-mcp
Provides an MCP server for Elasticsearch 7.x and 8.x clusters, enabling AI agents to add, edit, remove, and test named cluster connections at runtime; list indices; and run searches and aggregations, with API key or basic auth, SSH tunnels, and readonly safeguards.
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.
Elasticsearch 7.x and 8.x, any number of named clusters, API key or basic auth.
SSH tunnels with password, key or agent auth; host keys verified against
known_hostsor a pinned fingerprint.readonlyconnections 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.
Demo

Related MCP server: octodet-elasticsearch-mcp
Install
Requires Node.js 22 or newer. The server needs no configuration to start; connections are added later.
Claude Code
claude mcp add esmole -- npx -y esmole-mcpAdd --scope user to enable it in every project. Or install it as a plugin (Claude Code
2.1.275 or later):
/plugin install esmole --marketplace ed-smartass/esmole-mcpCodex
codex mcp add esmole -- npx -y esmole-mcpor in ~/.codex/config.toml:
[mcp_servers.esmole]
command = "npx"
args = ["-y", "esmole-mcp"]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
{
"mcpServers": {
"esmole": { "command": "npx", "args": ["-y", "esmole-mcp"] }
}
}Docker
The image is built from source:
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"
]
}
}
}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.
Quick start
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 deploy@bastion.example.com with my ssh agent. Then show the five biggest indices.
It calls add_connection with:
{
"name": "logs",
"host": "10.0.0.20",
"apiKey": "VnVhQ2ZH…",
"readonly": true,
"ssh": { "host": "bastion.example.com", "user": "deploy", "agent": true },
"scope": "project"
}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.
Connections
Two layers
Layer | File | Shape |
|
|
|
|
|
|
Same name in both: the project entry wins.
list_connectionsreports each connection'ssource(projectorglobal) and, when it hides an entry of the same name in the other layer,shadows.Hot reload. Both files are re-read on every tool call. Edit a file mid-session and the next call uses the new settings; a changed connection gets a fresh client, the old one is closed.
Project file discovery. Starting from the server's working directory (Claude Code and Codex start it in the project directory), esmole walks up to the filesystem root and uses the first
.esmole.jsonit 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.Both files are plain JSON with mode
0600, written atomically. Writes keep entries they cannot parse verbatim, and refuse to overwrite a file that is not valid JSON.
Managing connections with tools
add_connectiontakes the connection fields plusscope:"global"(default) or"project". Withprojectand 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.jsonto.gitignoreso credentials are not committed.add_connectionfails 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_connectionpatches the connection where it lives (the winning layer). Anullvalue removes an optional field. Ansshobject is merged into the stored one field by field, so{ "ssh": { "hostKey": "SHA256:..." } }keeps the stored SSH secrets.remove_connectiondeletes it from that layer and tells you if that uncovers a same-name entry in the other layer.Credentials are never echoed back:
list_connectionsshows scheme, host, port, the auth kind,verifyTls,readonly,sourceand whether SSH is used.
Broken and invalid entries
An entry that fails validation does not silently disappear: its name resolves to an error (
connection 'x' in <file> is invalid: ...) instead of falling through to a same-name entry in the global store, andlist_connectionslists it as invalid. A name defined twice in one file is an error too.remove_connectioncan delete an invalid entry.A file that exists but is unreadable, not JSON, or the wrong shape is reported in
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.
Connection fields
Unknown fields are rejected. Authentication is required: an apiKey, or both user and
password.
Field | Type | Notes |
| string |
|
|
| default |
| string | as seen from the SSH bastion when |
| number | default |
|
| default |
| boolean | default |
| string | API key (the base64 |
| string | basic auth |
| boolean | default |
| integer | milliseconds, default |
| object | see SSH tunnels |
Example .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 tunnels
| Notes |
| bastion; port defaults to |
| SSH user |
| password auth |
| key contents, or a path ( |
| for an encrypted key |
|
|
| pinned host key fingerprint, |
{ "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" }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):
Plain and hashed host names match. Wildcard patterns never match, and a line with a negated wildcard (
!*.example.com) is ignored.A key listed under
@revokedis rejected for every host, whatever the line's host pattern.@cert-authoritylines 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:
ssh-keyscan -p 22 bastion.example.com 2>/dev/null | ssh-keygen -lf -
# 256 SHA256:EXAMPLEexampleEXAMPLEexampleEXAMPLEexample1 bastion.example.com (ED25519)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
readonly: true protects against accidental writes. Every tool, es_request included,
checks method and path against an allowlist before anything is sent:
GETandHEAD: allowed.POST: only_search,_msearch,_count,_field_capsand_pit(optionally under one index or pattern, e.g./logs-*/_search), and/_search/scroll.DELETE: only/_search/scrolland/_pit(closing read sessions).Everything else is refused, as is any path containing
..,%2e,%2f,%5c,\or#, whatever the method. So on a readonly connectiones_requestcannot use date-math index names with/rounding, such as<logs-{now/d}>; writable connections are not affected.Index and pattern arguments of the helper tools are validated too (no
/,\,?,#,%, whitespace,..); comma lists, wildcards,-exclusionsandcluster:indexwork. Date-math names need%-encoding, so they only work throughes_request.Because every
GETpasses, GET-only maintenance endpoints such asGET /_flushpass 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.
Tools
Tool | What it does |
| connections with their |
| add a connection to the |
| patch a connection where it lives; |
| remove a connection; reports a same-name entry it uncovers |
| connect end to end (including the tunnel) and report cluster version and latency |
| any method, path, body and query params; readonly-guarded; a string body for NDJSON ( |
|
|
| indices matching a pattern (default |
| mapping flattened to |
| cluster health |
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).
Limits and timeouts
Every result is at most 40 KB of JSON. Strings longer than 2 KB are cut and marked
…[+N bytes]; if the body still does not fit, its longest array (hits, buckets) is halved until it does. The result says so intruncatedand stays valid JSON. Ask for less withsizeandsourceones_search, orfilter_pathones_request.Responses larger than 10 MB are aborted with an error ("narrow it with size/_source"), never returned partially.
queryTimeoutMs(default 60 s) is an absolute deadline per request, from connecting to reading the last byte of the body.
How it compares
esmole-mcp | |||
Status | maintained | deprecated; Elastic points to the Agent Builder MCP endpoint (Elasticsearch 9.2+) | maintained |
Elasticsearch 7.x | ✓ | — | ✓ |
Elasticsearch 8.x | ✓ | ✓ | ✓ |
OpenSearch | — | ✗ | ✓ |
SSH tunnel built in | ✓ | ✗ | ✗ |
Agent adds and edits connections through MCP tools | ✓ | ✗ | ✗ |
✓ documented, ✗ not offered, — not mentioned or not tested. Checked October 2026; corrections welcome.
Security
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.
Development
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/listThe shared connection core is @dbmole/core,
developed in the dbmole-mcp repo and bundled into this package at build time. See
CONTRIBUTING.md.
License
Available Tools
10 toolsadd_connectionA
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.
| Name | Required | Description | Default |
|---|---|---|---|
| ssh | No | ||
| host | Yes | ||
| name | Yes | ||
| port | No | ||
| type | No | elasticsearch | |
| user | No | ||
| scope | No | global | |
| apiKey | No | ||
| scheme | No | https | |
| password | No | ||
| readonly | No | ||
| verifyTls | No | ||
| queryTimeoutMs | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the disclosure burden and does add valuable behavior: it reveals where the connection is persisted for each scope and that credentials are stored acceptably via two auth schemes, plus optional ssh tunneling and TLS verification. It omits important traits: whether an existing name is overwritten or rejected, whether credentials are written in plaintext to .esmole.json, and whether the connection is validated on add.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three dense sentences, no filler, and the most decision-relevant fact (scope semantics) is front-loaded immediately after the purpose statement. Every clause carries distinct information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 13-parameter mutation tool with a nested object, no annotations and no output schema, the description covers the top-level shape but not overwrite/idempotency behavior or the nested ssh credential options. An agent could call it, but might pick the wrong auth fields or silently clobber an existing connection.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% across 13 parameters (including a nested ssh object), so the description must compensate. It covers scope, user/password, apiKey, readonly, scheme, verifyTls and the existence of ssh, but leaves name, host, port, type, queryTimeoutMs, and all nested ssh fields (agent, hostKey, privateKeyPath, passphrase) undocumented.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource — "Add a named Elasticsearch connection" — which clearly separates it from update_connection, remove_connection, list_connections and test_connection. It does not name those siblings explicitly, but the create semantics are unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives real guidance on the scope choice (default 'global' writes to the user store, 'project' writes to .esmole.json in the project root) and lists auth alternatives (user/password vs apiKey). However, it never says when to use this versus update_connection (e.g. what happens if the name already exists) or test_connection (validate before saving).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
es_cluster_healthB
Cluster health summary (status, node and shard counts). Wraps GET /_cluster/health.
| Name | Required | Description | Default |
|---|---|---|---|
| connection | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden, and 'Wraps GET /_cluster/health' does useful work by signalling a read-only, non-mutating call. It does not disclose failure behavior (unreachable cluster, bad connection id) or whether the result is cached/real-time, which leaves real gaps for an unannotated tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with the purpose and follow-up with the API mapping. No filler and nothing buried.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description roughly covers the return shape (status, node and shard counts), which partially offsets the absent output schema. However, for a tool with zero annotations and an undocumented required parameter, it leaves the agent without enough to call it confidently across multiple clusters/connections.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The single required parameter 'connection' has 0% schema description coverage and the description says nothing about it — not whether it is a connection name from list_connections, a URL, or an alias. With the schema silent, the description needed to compensate and does not.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States the specific resource and what it returns (status, node and shard counts) and identifies the underlying API endpoint, so an agent knows immediately this is a health/diagnostic read. It is distinguishable from siblings like es_list_indices or es_search, though it doesn't explicitly say so.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No statement of when to call this versus alternatives such as es_request or es_list_indices, and no prerequisites. An agent must infer the diagnostic use case from the name alone.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
es_get_mappingB
Return an index mapping as a flat field:type list (compact, token-cheap). Pass raw=true for the nested ES JSON.
| Name | Required | Description | Default |
|---|---|---|---|
| raw | No | ||
| index | Yes | ||
| connection | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full behavioral burden. It usefully discloses the default output behavior ('flat field:type list', 'compact, token-cheap') and the raw alternative, but does not explicitly state read-only safety, permission requirements, or error behavior for missing indices/connections.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, front-loaded with the core purpose and followed immediately by the only non-obvious output option. No wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple read retrieval tool with no output schema, the description adequately explains the returned shape and the raw alternative. The main omission is enough detail about the connection and index parameters to fully compensate for the zero schema descriptions.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description should compensate for all three parameters. It explains only the 'raw' parameter meaningfully; 'index' is only implied by 'an index mapping', and 'connection' is not addressed at all.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states a specific verb and resource: 'Return an index mapping'. It also specifies the output shape ('flat field:type list') and an alternative mode ('raw=true'), which distinguishes it in practice from a generic request tool. It does not explicitly name which sibling to use instead, so it stops short of full sibling differentiation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by 'Return an index mapping' and the raw=true option gives a clear conditional for output mode. However, there is no explicit guidance on when to prefer this over es_request or es_list_indices, and no when-not-to-use guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
es_list_indicesB
List indices matching a pattern with health, doc count and store size. Wraps GET /_cat/indices.
| Name | Required | Description | Default |
|---|---|---|---|
| pattern | No | * | |
| connection | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden. It does disclose the underlying endpoint ('GET /_cat/indices') and the return fields, which implies a read-only operation, but it omits permissions, failure behavior, and whether the pattern supports wildcards or regex.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two compact clauses with the operation front-loaded and no wasted wording. The endpoint note adds useful context without bloat.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple two-parameter read tool, the description covers the operation and return metrics, but it leaves the required 'connection' parameter undocumented and does not clarify pattern syntax. Given no annotations and no output schema, that is adequate but with clear gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate fully. It only indirectly explains the 'pattern' parameter via 'matching a pattern' and says nothing about the required 'connection' parameter, leaving half the inputs undocumented.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ('List indices') plus scope ('matching a pattern') and return contents ('health, doc count and store size'). It does not explicitly differentiate from siblings like es_search or es_cluster_health, but the purpose is clear enough to select the tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied: use this when you need to list Elasticsearch indices. However, the description gives no explicit when-to-use guidance, no exclusions, and never mentions alternatives such as es_request or es_search.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
es_requestA
Generic Elasticsearch REST passthrough: any method, any path. Returns {status, body}. A 4xx/5xx answer is returned as an error: "HTTP : " plus the trimmed body. Use a string body for NDJSON (_bulk, _msearch). On readonly connections only reads pass: GET, HEAD, POST [/]/_search|_msearch|_count|_field_caps|_pit, POST /_search/scroll, DELETE /_pit and /_search/scroll.
| Name | Required | Description | Default |
|---|---|---|---|
| body | No | ||
| path | Yes | path from the ES root, e.g. '/idx/_search' | |
| method | Yes | ||
| params | No | ||
| connection | Yes | connection name, see list_connections |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Without annotations, the description carries the full burden and does so well: it discloses the return shape ({status, body}), the error format for 4xx/5xx ('HTTP <status> <type>: <reason>' plus trimmed body), the requirement for string bodies with NDJSON, and the readonly connection restrictions. It omits details like timeout behavior or retries, but covers the essentials.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense and front-loaded: it opens with the core purpose, then details return and error formats, and finally lists readonly restrictions. Every sentence provides useful information with no filler, though it could be slightly more structured for faster scanning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the lack of an output schema and annotations, the description compensates by explaining the return value and error format. It also covers the readonly whitelist, which is critical for safe invocation. The main gap is the undocumented `params` parameter, but overall it is sufficiently complete for an agent to use the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 40%, so the description must compensate. It adds value for the `body` parameter by explaining the string requirement for NDJSON. However, the `params` parameter is completely undocumented in both description and schema, and `connection` is only briefly referenced. The description adds meaningful context for one parameter but leaves significant gaps.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states a specific verb and resource: 'Generic Elasticsearch REST passthrough: any method, any path.' It distinguishes itself from specialized siblings like `es_search` and `es_get_mapping` by being a generic escape hatch, though it doesn't explicitly recommend when to choose this over the specialized tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage through examples (e.g., string body for NDJSON) and constraints (readonly connections allow only reads), but it does not provide explicit guidance on when to use this tool versus the specialized sibling tools like `es_search` or `es_get_mapping`. The agent must infer that this is the fallback option.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
es_searchA
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.
| Name | Required | Description | Default |
|---|---|---|---|
| aggs | No | ||
| from | No | ||
| size | No | ||
| sort | No | e.g. [{"@timestamp": "desc"}] | |
| index | Yes | index, alias, comma list or wildcard, e.g. logs-*,metrics | |
| query | No | query DSL only, e.g. {"bool": {...}} | |
| source | No | _source projection: list of fields or false | |
| highlight | No | ||
| connection | Yes | ||
| search_after | No | sort values of the last hit | |
| script_fields | No | ||
| track_total_hits | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does substantial work: it discloses the size default of 10, the non-obvious rule that aggs with no query forces size to 0, the {status, body} return envelope, and that 4xx/5xx surfaces an ES root cause error. Authentication/connection assumptions are the main undisclosed trait.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Dense and front-loaded: endpoint mapping first, then parameter mapping, then pagination and projection guidance, then return/error behavior. Every clause conveys a distinct fact with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 12-parameter nested-object tool with no output schema and no annotations, the description covers return shape, error semantics, defaulting behavior, and param mapping. The remaining gap is the relationship to es_request and the meaning of the required connection parameter.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is only 42%, and the description compensates by mapping the undocumented body keys (sort, from, search_after, track_total_hits, highlight, script_fields, _source) and clarifying that query is DSL only, not a full body. It omits the size ceiling of 1000 and leaves the connection parameter entirely unexplained.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Search an index') and immediately grounds it in the underlying endpoint ('Wraps POST /{index}/_search'), so an agent knows exactly what it does. It does not explicitly differentiate itself from the sibling es_request, which could also issue a _search body, so it stops short of a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives operative advice on how to use the tool ('Page with from+size or sort+search_after', 'Use source to project fields and keep responses small'), which implies the intended usage pattern. However it never says when to pick this over es_request, nor names prerequisites, so alternative selection is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_connectionsA
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.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the load and does so well: it discloses the project-layer resolution mechanism (walking up from the project dir to find .esmole.json), the global fallback to the user store, that invalid entries and broken files appear in output, and that secrets are redacted. It stops short of stating ordering, deduplication between layers, or error behavior, which keeps it from a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, no redundancy, and the core purpose is front-loaded before the parenthetical path-resolution detail. Every clause adds information the agent could not get from structured fields.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema, params, or annotations exist, so the description must explain what comes back, and it largely does (layer name, invalid entries, broken files, redacted secrets). Slightly short of complete because the shape of an "invalid entry" or "broken file" record is left unstated.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing for the description to disambiguate; the baseline of 4 applies. The description correctly implies a parameterless, whole-store listing.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb plus resource ("List Elasticsearch connections") with the added dimension of which layer each connection comes from. It clearly reads as the non-mutating inventory tool among siblings like add_connection, update_connection, and remove_connection, and it is distinct from the es_* query tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied by the verb "List"; there is no explicit statement of when to call this versus test_connection or the es_* tools, and no prerequisites or exclusions. An agent can infer it is the discovery step before mutation, but it must do the inferring itself.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
remove_connectionA
Remove an ES connection by name from the layer it lives in (invalid entries too) and drop its cached client/tunnel.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations at all, the description carries the full burden and does disclose real behavior: it removes even invalid entries and drops the cached client/tunnel, which tells the agent this is destructive to runtime state. It omits failure behavior when the name doesn't exist and whether the removal is reversible.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with no filler. The primary action comes first and the side effects follow in the same breath.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive, annotation-free, no-output-schema mutation tool, the description covers the action and two side effects but leaves out error behavior, idempotency, and confirmation that the effect is permanent. An agent still has to guess at the edges.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate, and it only partially does. 'By name' confirms the sole parameter is the connection identifier and that invalid entries are still matched, but it doesn't clarify whether this is a display name, ID, or the registered key, nor the expected format.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource: remove an ES connection by name. This clearly separates it from add_connection, update_connection and test_connection in the sibling list. The phrase 'from the layer it lives in' is somewhat opaque domain jargon, which keeps it short of a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied: an agent infers it should call this when it wants a connection gone. There is no statement of when to prefer update_connection, no prerequisites, and no note about what to do if the connection might still be in use.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
test_connectionA
Verify an ES connection works end to end (including ssh tunnel). Reports server version and latency.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It usefully discloses that the check traverses the ssh tunnel and that the response contains server version and latency, but says nothing about failure behavior (error vs. negative result), authentication needs, or timeout characteristics for what is inherently a network round trip.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, front-loaded with the verb and resource, with the scope qualifier and return payload following. No filler text.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple, one-parameter read-only diagnostic tool with no output schema, the description conveys what is checked and roughly what comes back. The only real omission is disambiguating the 'name' parameter and stating what a failed test looks like.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The single required parameter 'name' has 0% schema description coverage and the description never clarifies whether it is a stored connection identifier, a host, or a URI. The description mentions 'an ES connection' only obliquely, so the agent must guess which value to supply.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (verify) and resource (an ES connection), plus the end-to-end scope including the ssh tunnel. None of the sibling tools (update/list/add/remove_connection, the es_* operations) perform this check, so an agent can select it unambiguously.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The nature of the call implies usage (confirm a connection is healthy, e.g. after add_connection or before es_search), but the description never states when to use it versus es_cluster_health or when not to bother. No prerequisites or exclusions are given.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_connectionA
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 | Required | Description | Default |
|---|---|---|---|
| name | Yes | ||
| patch | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description bears the full burden and does substantial work: it discloses that null deletes a field, that ssh merges field-by-field rather than replacing wholesale, and that queryTimeoutMs controls the per-request deadline with null restoring the 60 s default. Undisclosed: permission requirements, behavior when the named connection is missing from both layers, and whether changes take effect on live connections.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three dense sentences, front-loaded with the verb and scope, then the mutation semantics, then the one non-obvious field. The inline JSON examples earn their place, though the sentence structure is somewhat crammed with parentheticals.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a nested-object mutation with no annotations and no output schema, the merge/delete semantics are well covered, but return behavior (does it return the updated connection?) and failure modes (connection not found, invalid hostKey pattern, conflicting ssh fields) are absent. An agent knows how to shape the patch but not what to expect back.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate; it explains the null-deletion convention, the nested ssh merge rule, and queryTimeoutMs semantics, which covers the trickiest parts. However, the patch object contains a second `name` field (rename, with its own regex) that the description never mentions and whose relationship to the top-level `name` identifier is left ambiguous; port, user, scheme, apiKey, verifyTls and readonly are also unaddressed.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb+resource: 'Patch an ES connection by name', plus the scope clarification that it applies to the layer the connection lives in (project or global). This distinguishes it from add_connection and remove_connection without naming them explicitly, though a direct sibling reference would have sealed a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the word 'patch' (partial update of an existing connection, not creation), but there is no explicit 'use this instead of add_connection when...' or prerequisite guidance. The null-removal example describes mechanics rather than when to choose this tool over test_connection or add_connection.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
10 tool updates
v0.2.0- First observed
add_connection - First observed
es_cluster_health - First observed
es_get_mapping - First observed
es_list_indices - First observed
es_request - First observed
es_search - First observed
list_connections - First observed
remove_connection - First observed
test_connection - First observed
update_connection
TDQS
Scored across 10 tools
The connection group (add/list/update/remove/test) is crisply separated, and the ES group mostly targets distinct facets (search, indices, mapping, health). The main overlap is es_request, a generic passthrough that can duplicate every specialized ES tool, but the descriptions frame it explicitly as the escape hatch and the others as convenience wrappers.
Connection tools follow a clean verb_noun pattern, and the ES tools are consistently namespaced with an es_ prefix that clearly separates the two domains. Minor deviation: es_cluster_health is noun-only while es_list_indices and es_get_mapping include verbs, so ordering is not perfectly uniform.
Ten tools is well within the sweet spot, split sensibly between five connection-management operations and five ES query operations. Each tool covers a distinct need and no obvious padding is present.
Connection lifecycle is fully covered (add, list, update, remove, test) with good detail on scopes and secrets. The ES side covers search, indices, mapping, and health, while document-level and index-level CRUD (get/put doc, create/delete index) exist only via the generic es_request escape hatch, which is workable but less discoverable.
Maintenance
Related MCP Connectors
Secure and manage MCP servers through the Speakeasy AI Control Plane from your agent.
Syslog receiver and MCP server for homelab log intelligence.
Syslog receiver and MCP server for homelab log intelligence.
Governed MCP gateway: one endpoint for your tools, with credential custody and audit log.
Related MCP Servers
- AlicenseCqualityDmaintenanceProvides an MCP protocol interface for interacting with Elasticsearch 7.x databases, supporting comprehensive search functionality including aggregations, highlighting, and sorting.311Apache 2.0
- AlicenseAqualityCmaintenanceRead/write Elasticsearch mcp server with many tools1613 npm3MIT
- AlicenseNot gradedqualityFmaintenanceA read-only MCP server for Elasticsearch 7.10 clusters, providing tools for cluster info, health, index listing, and Query DSL search with Google OAuth and Bearer token authentication.2 npmMIT
- FlicenseNot gradedqualityDmaintenanceAn MCP server that downloads Elasticsearch logs from Elastic Cloud via the Admin API.-