diff --git a/README.md b/README.md index 6c95b6e..9df4a5a 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ The Elasticsearch MCP Server supports configuration options to connect to your E | `ES_USERNAME` | Elasticsearch username for basic authentication | No | | `ES_PASSWORD` | Elasticsearch password for basic authentication | No | | `ES_CA_CERT` | Path to custom CA certificate for Elasticsearch SSL/TLS | No | +| `ES_PATH_PREFIX` | Path prefix for Elasticsearch instance exposed at a non-root path | No | ### Developing Locally diff --git a/index.ts b/index.ts index 8551906..94ec3c1 100644 --- a/index.ts +++ b/index.ts @@ -7,10 +7,35 @@ import { z } from "zod"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { Client, estypes, ClientOptions } from "@elastic/elasticsearch"; +import { + Client, + estypes, + ClientOptions, + Transport, + TransportRequestOptions, + TransportRequestParams +} from "@elastic/elasticsearch"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import fs from "fs"; +// Prepend a path prefix to every request path +class CustomTransport extends Transport { + private readonly pathPrefix: string; + + constructor(opts: ConstructorParameters[0], pathPrefix: string) { + super(opts); + this.pathPrefix = pathPrefix; + } + + async request( + params: TransportRequestParams, + options?: TransportRequestOptions + ): Promise { + const newParams = { ...params, path: this.pathPrefix + params.path }; + return super.request(newParams, options); + } +} + // Configuration schema with auth options const ConfigSchema = z .object({ @@ -40,6 +65,11 @@ const ConfigSchema = z .string() .optional() .describe("Path to custom CA certificate for Elasticsearch"), + + pathPrefix: z + .string() + .optional() + .describe("Path prefix for Elasticsearch"), }) .refine( (data) => { @@ -74,12 +104,21 @@ export async function createElasticsearchMcpServer( config: ElasticsearchConfig ) { const validatedConfig = ConfigSchema.parse(config); - const { url, apiKey, username, password, caCert } = validatedConfig; + const { url, apiKey, username, password, caCert, pathPrefix } = validatedConfig; const clientOptions: ClientOptions = { node: url, }; + if (pathPrefix) { + const verifiedPathPrefix = pathPrefix; + clientOptions.Transport = class extends CustomTransport { + constructor(opts: ConstructorParameters[0]) { + super(opts, verifiedPathPrefix); + } + }; + } + // Set up authentication if (apiKey) { clientOptions.auth = { apiKey }; @@ -417,6 +456,7 @@ const config: ElasticsearchConfig = { username: process.env.ES_USERNAME || "", password: process.env.ES_PASSWORD || "", caCert: process.env.ES_CA_CERT || "", + pathPrefix: process.env.ES_PATH_PREFIX || "", }; async function main() {