developmentTechArticle

Building an Elasticsearch MCP Server

Expose full-text search over Elasticsearch as an MCP tool, restricted to search-only queries against a fixed set of indices.

Quick Answer / TL;DR

An Elasticsearch MCP server uses the official @elastic/elasticsearch client to run search queries via the Query DSL, restricted to a fixed list of indices and forbidding any query that would delete or modify documents.

Key Takeaways

  • Restrict the tool to a fixed, server-side list of allowed indices - never let a model-supplied index name reach the client unchecked.
  • Use the _search endpoint exclusively; the client can also delete indices and documents, so those methods should never be wired into the tool at all.
  • A match query is usually the right default for natural-language search; reserve term queries for exact-value filters like status codes or IDs.

Why not just copy a reference implementation?

The official MCP servers repository says directly: "The servers in this repository are intended as reference implementations to demonstrate MCP features and SDK usage... not as production-ready solutions." Applied naively to Elasticsearch, that pattern would wire up the full client - including delete and index-management methods - rather than the search-only surface used below.

A restricted search tool

The official client exposes the full Elasticsearch API, including destructive operations, so the safety boundary here is which client methods the tool code calls - only .search() - combined with validating the requested index against an allowlist before the call is made.

typescript
import { Client } from "@elastic/elasticsearch";

const client = new Client({
  node: process.env.ELASTICSEARCH_URL!,
  auth: { apiKey: process.env.ELASTICSEARCH_API_KEY! },
});

const ALLOWED_INDICES = (process.env.ELASTICSEARCH_ALLOWED_INDICES ?? "").split(",").filter(Boolean);

async function searchTool(index: string, query: string, size = 20) {
  if (!ALLOWED_INDICES.includes(index)) {
    throw new Error(`Index "${index}" is not in the allowed list.`);
  }

  const result = await client.search({
    index,
    query: { match: { content: query } },
    size,
  });

  return result.hits.hits.map((hit) => ({ score: hit._score, ...hit._source as object }));
}

Building an Elasticsearch MCP Server FAQs

Direct answers for developers, operators, and Indian teams evaluating MCP.

M
MCPserver Team

MCP documentation and protocol implementation team

Published: 2026-07-21
Updated: 2026-07-21