Skip to main content
Glama
ed-smartass

esmole-mcp

by ed-smartass

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

Demo

The agent adds a readonly cluster to the project and searches it

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

Add --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-mcp

Codex

codex mcp add esmole -- npx -y esmole-mcp

or in ~/.codex/config.toml:

[mcp_servers.esmole]
command = "npx"
args = ["-y", "esmole-mcp"]

Cursor

Add esmole to Cursor

VS Code

Install in 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 logs to 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

project

.esmole.json, found from the server's working directory

{ "connections": [ ... ] }

global

~/.config/esmole/connections.json, or the path in ESMOLE_STORE

[ ... ]

  • Same name in both: the project entry wins. 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.

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

  • 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_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.

  • Credentials are never echoed back: list_connections shows scheme, host, port, the auth kind, verifyTls, readonly, source and 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, 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.

  • 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

name

string

[a-zA-Z0-9_-]+

type

elasticsearch

default elasticsearch

host

string

as seen from the SSH bastion when ssh is set

port

number

default 9200

scheme

http | https

default https

verifyTls

boolean

default true; false skips certificate verification

apiKey

string

API key (the base64 id:key form)

user, password

string

basic auth

readonly

boolean

default false, see Readonly

queryTimeoutMs

integer

milliseconds, default 60000, at most 2147483647; see Limits and timeouts

ssh

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

ssh field

Notes

host, port

bastion; port defaults to 22

user

SSH user

password

password auth

privateKey / privateKeyPath

key contents, or a path (~ is expanded)

passphrase

for an encrypted key

agent

true to use the agent at SSH_AUTH_SOCK

hostKey

pinned host key fingerprint, SHA256:... (OpenSSH format)

{ "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 @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:

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:

  • 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).

  • Everything else is refused, as is any path containing .., %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.

  • Index and pattern arguments of the helper tools are validated too (no /, \, ?, #, %, whitespace, ..); comma lists, wildcards, -exclusions and cluster:index work. Date-math names need %-encoding, so they only work through es_request.

  • Because every 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.

Tools

Tool

What it does

list_connections

connections with their source, shadowed names, invalid entries and broken files

add_connection

add a connection to the project file or the global store

update_connection

patch a connection where it lives; null removes a field

remove_connection

remove a connection; reports a same-name entry it uncovers

test_connection

connect end to end (including the tunnel) and report cluster version and latency

es_request

any method, path, body and query params; readonly-guarded; a string body for NDJSON (_bulk, _msearch)

es_search

query, aggs, size, source (sent as _source), sort, from, search_after, track_total_hits, highlight, script_fields on an index or pattern; a whole body nested into query is lifted, with a note

es_list_indices

indices matching a pattern (default *) with health, doc count and store size

es_get_mapping

mapping flattened to field: type; raw: true for the original

es_cluster_health

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 in truncated and stays valid JSON. Ask for less with size and source on es_search, or filter_path on es_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

elastic/mcp-server-elasticsearch

cr7258/elasticsearch-mcp-server

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/list

The shared connection core is @dbmole/core, developed in the dbmole-mcp repo and bundled into this package at build time. See CONTRIBUTING.md.

License

MIT

Available Tools

10 tools
add_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.

NameRequiredDescriptionDefault
sshNo
hostYes
nameYes
portNo
typeNoelasticsearch
userNo
scopeNoglobal
apiKeyNo
schemeNohttps
passwordNo
readonlyNo
verifyTlsNo
queryTimeoutMsNo

TDQS

A3.5/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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.

NameRequiredDescriptionDefault
connectionYes

TDQS

B3.1/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters2/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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.

NameRequiredDescriptionDefault
rawNo
indexYes
connectionYes

TDQS

B3.4/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters2/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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.

NameRequiredDescriptionDefault
patternNo*
connectionYes

TDQS

B3.3/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters2/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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.

NameRequiredDescriptionDefault
bodyNo
pathYespath from the ES root, e.g. '/idx/_search'
methodYes
paramsNo
connectionYesconnection name, see list_connections

TDQS

A3.7/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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.

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.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.2/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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.

NameRequiredDescriptionDefault
nameYes

TDQS

A3.7/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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.

NameRequiredDescriptionDefault
nameYes

TDQS

A3.7/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters2/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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

NameRequiredDescriptionDefault
nameYes
patchYes

TDQS

A3.6/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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.

  1. 10 tool updatesv0.2.0
    • First observedadd_connection
    • First observedes_cluster_health
    • First observedes_get_mapping
    • First observedes_list_indices
    • First observedes_request
    • First observedes_search
    • First observedlist_connections
    • First observedremove_connection
    • First observedtest_connection
    • First observedupdate_connection

TDQS

A3.7/5.0

Scored across 10 tools

Disambiguation4/5

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.

Naming Consistency4/5

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.

Tool Count5/5

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.

Completeness4/5

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

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers