Skip to main content
Glama
Octodet

octodet-elasticsearch-mcp

search

Query Elasticsearch using DSL to retrieve, highlight, and script fields for specified indices, enabling precise data extraction and analysis.

Instructions

Perform an Elasticsearch search with the provided query DSL, highlighting, and script fields

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
indexYesName of the Elasticsearch index to search
queryBodyYesComplete Elasticsearch query DSL object (can include query, size, from, sort, etc.)
scriptFieldsNoScript fields to evaluate and include in the response

Implementation Reference

  • The main handler function for the 'search' MCP tool. It enhances the query body with script_fields if provided, performs the search using ElasticsearchService, processes the results including metadata, aggregations, highlights, script fields, and source data, and formats them into MCP-compliant text content fragments.
    async ({ index, queryBody, scriptFields }, extra) => { try { // Add script_fields to the query body if provided const enhancedQueryBody = { ...queryBody }; if (scriptFields && Object.keys(scriptFields).length > 0) { enhancedQueryBody.script_fields = scriptFields; } const result = await esService.search(index, enhancedQueryBody); // Extract the 'from' parameter from queryBody, defaulting to 0 if not provided const from = queryBody.from ?? 0; const contentFragments: TextContent[] = []; // Add metadata about the search results contentFragments.push({ type: "text", text: `Total results: ${ typeof result.hits.total === "number" ? result.hits.total : result.hits.total?.value ?? 0 }, showing ${result.hits.hits.length} from position ${from}`, }); // Add aggregation results if present if (result.aggregations) { contentFragments.push({ type: "text", text: `Aggregations: ${JSON.stringify( result.aggregations, null, 2 )}`, }); } // Process and add individual hit results result.hits.hits.forEach((hit: any) => { const highlightedFields = hit.highlight ?? {}; const sourceData = hit._source ?? {}; const scriptFieldsData = hit.fields ?? {}; let content = `Document ID: ${hit._id}\nScore: ${hit._score}\n\n`; // Add script fields results for (const [field, value] of Object.entries(scriptFieldsData)) { content += `${field} (script): ${JSON.stringify(value)}\n`; } // Add highlighted fields for (const [field, highlights] of Object.entries(highlightedFields)) { if (Array.isArray(highlights) && highlights.length > 0) { content += `${field} (highlighted): ${( highlights as string[] ).join(" ... ")}\n`; } } // Add source fields that weren't highlighted for (const [field, value] of Object.entries(sourceData)) { if (!(field in highlightedFields)) { content += `${field}: ${JSON.stringify(value)}\n`; } } contentFragments.push({ type: "text", text: content.trim(), }); }); const response: ResponseContent = { content: contentFragments, }; return response; } catch (error) { console.error( `Search failed: ${ error instanceof Error ? error.message : String(error) }` ); return { content: [ { type: "text", text: `Error: ${ error instanceof Error ? error.message : String(error) }`, }, ], }; } }
  • Zod schema defining the input parameters for the 'search' tool: required 'index' string, 'queryBody' as arbitrary record for Elasticsearch DSL, and optional 'scriptFields' for computed fields with script definitions.
    index: z .string() .trim() .min(1, "Index name is required") .describe("Name of the Elasticsearch index to search"), queryBody: z .record(z.any()) .describe( "Complete Elasticsearch query DSL object (can include query, size, from, sort, etc.)" ), scriptFields: z .record( z.object({ script: z.object({ source: z .string() .min(1, "Script source is required") .describe("Painless script source code"), params: z .record(z.any()) .optional() .describe("Optional parameters for the script"), lang: z .string() .optional() .default("painless") .describe("Script language (defaults to painless)"), }) }) ) .optional() .describe("Script fields to evaluate and include in the response"), },
  • src/index.ts:191-324 (registration)
    Registration of the 'search' tool on the MCP server using server.tool(), including name, description, input schema, and handler function.
    server.tool( "search", "Perform an Elasticsearch search with the provided query DSL, highlighting, and script fields", { index: z .string() .trim() .min(1, "Index name is required") .describe("Name of the Elasticsearch index to search"), queryBody: z .record(z.any()) .describe( "Complete Elasticsearch query DSL object (can include query, size, from, sort, etc.)" ), scriptFields: z .record( z.object({ script: z.object({ source: z .string() .min(1, "Script source is required") .describe("Painless script source code"), params: z .record(z.any()) .optional() .describe("Optional parameters for the script"), lang: z .string() .optional() .default("painless") .describe("Script language (defaults to painless)"), }) }) ) .optional() .describe("Script fields to evaluate and include in the response"), }, async ({ index, queryBody, scriptFields }, extra) => { try { // Add script_fields to the query body if provided const enhancedQueryBody = { ...queryBody }; if (scriptFields && Object.keys(scriptFields).length > 0) { enhancedQueryBody.script_fields = scriptFields; } const result = await esService.search(index, enhancedQueryBody); // Extract the 'from' parameter from queryBody, defaulting to 0 if not provided const from = queryBody.from ?? 0; const contentFragments: TextContent[] = []; // Add metadata about the search results contentFragments.push({ type: "text", text: `Total results: ${ typeof result.hits.total === "number" ? result.hits.total : result.hits.total?.value ?? 0 }, showing ${result.hits.hits.length} from position ${from}`, }); // Add aggregation results if present if (result.aggregations) { contentFragments.push({ type: "text", text: `Aggregations: ${JSON.stringify( result.aggregations, null, 2 )}`, }); } // Process and add individual hit results result.hits.hits.forEach((hit: any) => { const highlightedFields = hit.highlight ?? {}; const sourceData = hit._source ?? {}; const scriptFieldsData = hit.fields ?? {}; let content = `Document ID: ${hit._id}\nScore: ${hit._score}\n\n`; // Add script fields results for (const [field, value] of Object.entries(scriptFieldsData)) { content += `${field} (script): ${JSON.stringify(value)}\n`; } // Add highlighted fields for (const [field, highlights] of Object.entries(highlightedFields)) { if (Array.isArray(highlights) && highlights.length > 0) { content += `${field} (highlighted): ${( highlights as string[] ).join(" ... ")}\n`; } } // Add source fields that weren't highlighted for (const [field, value] of Object.entries(sourceData)) { if (!(field in highlightedFields)) { content += `${field}: ${JSON.stringify(value)}\n`; } } contentFragments.push({ type: "text", text: content.trim(), }); }); const response: ResponseContent = { content: contentFragments, }; return response; } catch (error) { console.error( `Search failed: ${ error instanceof Error ? error.message : String(error) }` ); return { content: [ { type: "text", text: `Error: ${ error instanceof Error ? error.message : String(error) }`, }, ], }; } } );
  • Supporting helper method in ElasticsearchService class that wraps the Elasticsearch client's search method, spreading the queryBody into the search parameters.
    async search(index: string, queryBody: any): Promise<any> { return await this.client.search({ index, ...queryBody, }); }

Other Tools

Related Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Octodet/elasticsearch-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server