Skip to content

feat: support username/pass auth + self-signed ca cert #20

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 25 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ This server connects agents to your Elasticsearch data using the Model Context P
<img width="380" height="200" src="https://glama.ai/mcp/servers/@elastic/mcp-server-elasticsearch/badge" alt="Elasticsearch Server MCP server" />
</a>

## Features
## Available Tools

* **List Indices**: View all available Elasticsearch indices
* **Get Mappings**: Inspect field mappings for specific indices
* **Search**: Execute Elasticsearch queries using full Query DSL capabilities with automatic highlighting
* `list_indices`: List all available Elasticsearch indices
* `get_mappings`: Get field mappings for a specific Elasticsearch index
* `search`: Perform an Elasticsearch search with the provided query DSL

## Prerequisites

* An Elasticsearch instance
* Elasticsearch API key with appropriate permissions
* Elasticsearch authentication credentials (API key or username/password)
* MCP Client (e.g. Claude Desktop)

## Demo
Expand Down Expand Up @@ -59,6 +59,23 @@ https://github.com/user-attachments/assets/5dd292e1-a728-4ca7-8f01-1380d1bebe0c
- The MCP server should connect automatically
- You can now ask questions about your Elasticsearch data

### Configuration Options

The Elasticsearch MCP Server supports configuration options to connect to your Elasticsearch:

> [!NOTE]
> You must provide either an API key or both username and password for authentication.


| Environment Variable | Description | Required |
|---------------------|-------------|----------|
| `ES_URL` | Your Elasticsearch instance URL | Yes |
| `ES_API_KEY` | Elasticsearch API key for authentication | No |
| `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 |


### Developing Locally

> [!NOTE]
Expand Down Expand Up @@ -106,7 +123,7 @@ https://github.com/user-attachments/assets/5dd292e1-a728-4ca7-8f01-1380d1bebe0c
ES_URL=your-elasticsearch-url ES_API_KEY=your-api-key npm run inspector
```

This will start the MCP Inspector, allowing you to debug and analyze requests. Ensure that the necessary environment variables (`ES_URL` and `ES_API_KEY`) are exposed when starting the inspector. You should see output similar to:
This will start the MCP Inspector, allowing you to debug and analyze requests. You should see:

```bash
Starting MCP inspector...
Expand Down Expand Up @@ -167,10 +184,10 @@ POST /_security/api_key

## Troubleshooting


* Ensure your MCP configuration is correct.
* Verify that your Elasticsearch URL is accessible from your machine.
* Check that your API key has the necessary permissions.
* Check that your authentication credentials (API key or username/password) have the necessary permissions.
* If using SSL/TLS with a custom CA, verify that the certificate path is correct and the file is readable.
* Look at the terminal output for error messages.

If you encounter issues, feel free to open an issue on the GitHub repository.
98 changes: 74 additions & 24 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,90 @@

import { z } from "zod";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { Client, estypes } from "@elastic/elasticsearch";
import { Client, estypes, ClientOptions } from "@elastic/elasticsearch";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

// Simplified configuration schema with only URL and API key
const ConfigSchema = z.object({
url: z
.string()
.trim()
.min(1, "Elasticsearch URL cannot be empty")
.url("Invalid Elasticsearch URL format")
.describe("Elasticsearch server URL"),

apiKey: z
.string()
.trim()
.min(1, "API key is required")
.describe("API key for Elasticsearch authentication"),
});
import fs from "fs";

// Configuration schema with auth options
const ConfigSchema = z
.object({
url: z
.string()
.trim()
.min(1, "Elasticsearch URL cannot be empty")
.url("Invalid Elasticsearch URL format")
.describe("Elasticsearch server URL"),

apiKey: z
.string()
.optional()
.describe("API key for Elasticsearch authentication"),

username: z
.string()
.optional()
.describe("Username for Elasticsearch authentication"),

password: z
.string()
.optional()
.describe("Password for Elasticsearch authentication"),

caCert: z
.string()
.optional()
.describe("Path to custom CA certificate for Elasticsearch"),
})
.refine(
(data) => {
// Either apiKey is present, or both username and password are present
return !!data.apiKey || (!!data.username && !!data.password);
},
{
message:
"Either ES_API_KEY or both ES_USERNAME and ES_PASSWORD must be provided",
path: ["apiKey", "username", "password"],
}
);

type ElasticsearchConfig = z.infer<typeof ConfigSchema>;

export async function createElasticsearchMcpServer(
config: ElasticsearchConfig
) {
const validatedConfig = ConfigSchema.parse(config);
const { url, apiKey } = validatedConfig;
const { url, apiKey, username, password, caCert } = validatedConfig;

const esClient = new Client({
const clientOptions: ClientOptions = {
node: url,
auth: {
apiKey: apiKey,
},
});
};

// Set up authentication
if (apiKey) {
clientOptions.auth = { apiKey };
} else if (username && password) {
clientOptions.auth = { username, password };
}

// Set up SSL/TLS certificate if provided
if (caCert) {
try {
const ca = fs.readFileSync(caCert);
clientOptions.tls = { ca };
} catch (error) {
console.error(
`Failed to read certificate file: ${
error instanceof Error ? error.message : String(error)
}`
);
}
}

const esClient = new Client(clientOptions);

const server = new McpServer({
name: "elasticsearch-mcp-server",
version: "0.1.0",
version: "0.1.1",
});

// Tool 1: List indices
Expand Down Expand Up @@ -274,6 +321,9 @@ export async function createElasticsearchMcpServer(
const config: ElasticsearchConfig = {
url: process.env.ES_URL || "",
apiKey: process.env.ES_API_KEY || "",
username: process.env.ES_USERNAME || "",
password: process.env.ES_PASSWORD || "",
caCert: process.env.ES_CA_CERT || "",
};

async function main() {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@elastic/mcp-server-elasticsearch",
"description": "Elasticsearch MCP Server",
"version": "0.1.0",
"version": "0.1.1",
"license": "Apache-2.0",
"author": "Elastic",
"type": "module",
Expand Down