{
  "slug": "io-github-cr7258-elasticsearch-mcp-server",
  "name": "io.github.cr7258/elasticsearch-mcp-server",
  "title": "elasticsearch-mcp-server",
  "description": "MCP server for interacting with Elasticsearch",
  "version": "2.0.15",
  "publishedAt": "2025-09-15T14:34:34.758628Z",
  "updatedAt": "2025-09-15T14:34:34.758628Z",
  "repoUrl": "https://github.com/cr7258/elasticsearch-mcp-server",
  "websiteUrl": null,
  "namespace": {
    "type": "github",
    "owner": "cr7258"
  },
  "kind": "local",
  "packages": [
    {
      "registryType": "pypi",
      "identifier": "elasticsearch-mcp-server",
      "version": "2.0.15",
      "transport": "stdio",
      "runtimeHint": null
    }
  ],
  "remotes": [],
  "envVars": [
    {
      "name": "ELASTICSEARCH_HOSTS",
      "isSecret": false,
      "isRequired": false,
      "description": "Comma-separated list of Elasticsearch hosts (e.g., https://localhost:9200)"
    },
    {
      "name": "ELASTICSEARCH_API_KEY",
      "isSecret": true,
      "isRequired": false,
      "description": "API key for Elasticsearch or Elastic Cloud authentication (recommended)"
    },
    {
      "name": "ELASTICSEARCH_USERNAME",
      "isSecret": false,
      "isRequired": false,
      "description": "Username for basic authentication (alternative to API key)"
    },
    {
      "name": "ELASTICSEARCH_PASSWORD",
      "isSecret": true,
      "isRequired": false,
      "description": "Password for basic authentication (used with ELASTICSEARCH_USERNAME)"
    },
    {
      "name": "ELASTICSEARCH_VERIFY_CERTS",
      "isSecret": false,
      "isRequired": false,
      "description": "Whether to verify SSL certificates (true/false)"
    }
  ],
  "category": "databases",
  "subcategory": "nosql",
  "company": "",
  "categoryAuto": true,
  "checks": [
    {
      "id": "source-linked",
      "label": "Source repository linked",
      "result": "pass",
      "detail": "https://github.com/cr7258/elasticsearch-mcp-server"
    },
    {
      "id": "version-pinned",
      "label": "Version pinned",
      "result": "pass",
      "detail": "Every package has an explicit version"
    },
    {
      "id": "secrets-requested",
      "label": "Secrets requested",
      "result": "info",
      "detail": "Asks for 2 secrets: ELASTICSEARCH_API_KEY, ELASTICSEARCH_PASSWORD"
    },
    {
      "id": "known-registry",
      "label": "Known package registry",
      "result": "pass",
      "detail": "All packages come from npm, PyPI, OCI, NuGet or MCPB"
    },
    {
      "id": "namespace",
      "label": "Namespace ownership",
      "result": "info",
      "detail": "Name verified via GitHub account cr7258"
    },
    {
      "id": "meta-text",
      "label": "Registry text aimed at the AI",
      "result": "pass",
      "detail": "Description and settings contain no hidden characters or instructions to the AI"
    },
    {
      "id": "install-scripts",
      "label": "Install scripts",
      "result": "pass",
      "detail": "elasticsearch-mcp-server 2.0.15 has no setup.py; nothing runs at install"
    },
    {
      "id": "code-flags",
      "label": "Code red flags",
      "result": "pass",
      "detail": "Read 39 files in elasticsearch-mcp-server 2.0.15: no obfuscation, encoded execution, credential reads or data-collection endpoints found"
    },
    {
      "id": "ai-text",
      "label": "Text aimed at the AI",
      "result": "pass",
      "detail": "No hidden characters or instructions to the AI in 39 files, including comments and the README"
    }
  ],
  "scannedAt": "2026-10-29",
  "level": "Scanned",
  "install": [
    {
      "label": "Claude Desktop (pypi)",
      "kind": "claude_desktop_json",
      "text": "{\n  \"mcpServers\": {\n    \"elasticsearch-mcp-server\": {\n      \"command\": \"uvx\",\n      \"args\": [\n        \"elasticsearch-mcp-server==2.0.15\"\n      ]\n    }\n  }\n}"
    }
  ],
  "curated": true,
  "stars": 314,
  "archived": false,
  "pushedAt": "2026-10-19T15:14:17Z",
  "review": {
    "package": "elasticsearch-mcp-server",
    "version": "2.0.15",
    "at": "2026-10-31",
    "files": 22,
    "lines": 867,
    "verdict": "pass-with-notes",
    "summary": "Lets an AI search and manage an Elasticsearch or OpenSearch cluster you point it at; it talks only to that cluster. Watch out: it skips TLS certificate checks unless you set VERIFY_CERTS=true, and it includes delete tools and a raw API tool with no read-only switch, so use a limited-permission account.",
    "hosts": [
      {
        "host": "ELASTICSEARCH_HOSTS / OPENSEARCH_HOSTS (default https://localhost:9200)",
        "when": "only when a tool is called (client objects are created at startup but do not send until used)",
        "sends": "search queries, documents, index/alias/data-stream operations, and arbitrary REST calls from general_api_request, with basic auth or ApiKey header"
      }
    ],
    "env": [
      {
        "name": "ELASTICSEARCH_HOSTS / OPENSEARCH_HOSTS",
        "sent_to": "local (target cluster URLs)"
      },
      {
        "name": "ELASTICSEARCH_USERNAME / ELASTICSEARCH_PASSWORD (or OPENSEARCH_*)",
        "sent_to": "the configured cluster (basic auth)"
      },
      {
        "name": "ELASTICSEARCH_API_KEY / OPENSEARCH_API_KEY",
        "sent_to": "the configured cluster (Authorization: ApiKey)"
      },
      {
        "name": "ELASTICSEARCH_VERIFY_CERTS / OPENSEARCH_VERIFY_CERTS",
        "sent_to": "local (TLS setting, default false)"
      }
    ],
    "local": "Calls load_dotenv(), so a .env file in the working directory is read into the environment. No other file reads or writes.",
    "exec": "None. No subprocess, eval or exec.",
    "telemetry": "None.",
    "tools": [
      "list_indices",
      "get_index",
      "create_index",
      "delete_index",
      "search_documents",
      "index_document",
      "get_document",
      "delete_document",
      "delete_by_query",
      "get_cluster_health",
      "get_cluster_stats",
      "list_aliases",
      "get_alias",
      "put_alias",
      "delete_alias",
      "create_data_stream",
      "get_data_stream",
      "delete_data_stream",
      "general_api_request"
    ],
    "toolText": "clean",
    "deps": [
      "fastmcp",
      "mcp",
      "elasticsearch",
      "opensearch-py (opensearchpy)",
      "httpx",
      "python-dotenv",
      "urllib3 (optional import)"
    ],
    "findings": [
      {
        "severity": "note",
        "at": "src/clients/__init__.py:28 + src/clients/base.py:28-39",
        "what": "TLS certificate verification is OFF by default (VERIFY_CERTS defaults to 'false') and the insecure-request warnings are silenced, so credentials can be intercepted on a hostile network unless the user sets *_VERIFY_CERTS=true."
      },
      {
        "severity": "note",
        "at": "src/tools/index.py:36-44, document.py:44-64, data_stream.py:37-47, alias.py:36-45",
        "what": "Destructive tools (delete_index, delete_document, delete_by_query, delete_data_stream, delete_alias) are always registered; this version has no read-only mode or confirmation step."
      },
      {
        "severity": "note",
        "at": "src/tools/general.py:9-20",
        "what": "general_api_request lets the AI send any HTTP method to any path on the configured cluster (e.g. cluster settings, snapshot deletes). The base URL is fixed, so it cannot reach other hosts."
      },
      {
        "severity": "note",
        "at": "src/clients/__init__.py:19",
        "what": "load_dotenv() reads a .env from whatever directory the server is started in."
      },
      {
        "severity": "info",
        "at": "src/server.py:90-92",
        "what": "HTTP transports bind 127.0.0.1 by default (safe); there is no auth on the MCP endpoint if the user binds wider."
      },
      {
        "severity": "info",
        "at": "package",
        "what": "No pyproject/METADATA in the reviewed copy; dependencies are taken from imports. No obfuscation, install hooks, dynamic code, or hard-coded secrets."
      },
      {
        "severity": "info",
        "at": "manifest.tsv",
        "what": "lines_read (867) is the real line count of every file (Read tool / splitlines). The manifest's 886 counts one extra empty 'line' after the trailing newline of 19 files; no file or line was skipped."
      }
    ]
  },
  "health": {
    "score": 76,
    "parts": {
      "popularity": 0.6002218172622296,
      "activity": 0.8635011718265692,
      "issues": 0.6666666666666666,
      "security": 1
    },
    "fail": 0,
    "warn": 0,
    "raw": {
      "stars": 314,
      "forks": 67,
      "downloads": 1924,
      "hn": {
        "n": 0,
        "pts": 0,
        "t": []
      },
      "issuesOpen": 8,
      "issuesClosed": 16,
      "prsMerged": 46,
      "commits90": 14,
      "release": "2026-10-19T15:14:21Z",
      "pushed": "2026-10-19T15:14:17Z",
      "growth": null,
      "growthDays": null,
      "archived": false
    },
    "at": "2026-10-31T07:42:38.200Z"
  }
}
