mcp-opensearch
Provides read-only tools for searching, aggregating, and exploring log data in OpenSearch clusters, including index discovery, mapping retrieval, field sampling, full-text search, count, terms and multi-terms aggregations, histogram, and numeric stats.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-opensearchsearch for 'timeout' in logs from today"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-opensearch
Read-only MCP server for OpenSearch and OpenSearch Dashboards — search, aggregate, and explore your log data from Claude Code or any MCP-compatible AI assistant.
Features
12 tools covering connectivity checks, index/field discovery, full-text search, aggregations, time-series histograms, and numeric stats
Two backends: OpenSearch Dashboards proxy (preferred) or direct OpenSearch REST API
Hard limits on search result size (default 200) and histogram bucket count (default 2,000) to protect cluster health
Text field aggregation warnings (fielddata heap pressure)
No-time-range warnings on potentially expensive full-history queries
Read-only write guard — only safe endpoints are in the allowlist
Configurable via environment variables or
~/.config/mcp-opensearch/config.jsonDocker image or bare Python (no Docker required)
Related MCP server: OpenSearch Dashboards MCP Server
Requirements
Python 3.10+ or Docker
OpenSearch ≥ 2.x or OpenSearch Dashboards ≥ 2.x
Basic auth credentials
Quick Start
1. Clone
git clone https://github.com/bunnyiesart/mcp-opensearch.git
cd mcp-opensearch2. Configure
Create a .env file with your credentials:
# ~/.config/mcp-opensearch/.env
OPENSEARCH_DASHBOARDS_URL=https://opensearch.example.com
OPENSEARCH_USERNAME=myuser
OPENSEARCH_PASSWORD=mypassword
OPENSEARCH_VERIFY_SSL=trueOr run the interactive setup script:
./setup.sh3a. Docker (recommended)
make build
# Verify it works:
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"0"}}}' | \
docker run --rm -i --network host --env-file ~/.config/mcp-opensearch/.env opensearch-mcp:dev3b. Python (no Docker)
pip install -r requirements.txt
python3 server.py4. Register with Claude Code
Add the server to ~/.claude.json under your project path:
{
"projects": {
"/your/project": {
"mcpServers": {
"opensearch": {
"type": "stdio",
"command": "docker",
"args": [
"run", "--rm", "-i", "--network", "host",
"--env-file", "/home/youruser/.config/mcp-opensearch/.env",
"opensearch-mcp:dev"
],
"env": {}
}
}
}
}
}Restart Claude Code, then call opensearch_test to confirm the connection is healthy.
Configuration
Environment variables take priority over the config file. At least one of OPENSEARCH_DASHBOARDS_URL or OPENSEARCH_URL is required. The config file at ~/.config/mcp-opensearch/config.json must be chmod 600.
Variable | Config key | Default | Description |
|
| — | Dashboards URL, tried first (e.g. |
|
| — | Direct OpenSearch URL, used as fallback (e.g. |
|
| — | Basic auth username |
|
| — | Basic auth password |
|
|
| Set |
|
|
| Request timeout in seconds |
|
|
| Hard cap on search |
|
|
| Reject histogram requests exceeding this estimated bucket count |
Tool Reference
Connectivity
opensearch_test
Test connectivity and confirm the active backend.
No parameters.
{
"ok": true,
"backend": "dashboards",
"version": "2.19.3",
"url": "https://opensearch.example.com",
"username": "myuser"
}Start every session with this tool. The username field immediately explains why certain tools return 403.
Index Discovery
opensearch_cluster_health ⚠️
Requires
cluster:monitor/healthprivilege. Returns a permission error if the user lacks it — useopensearch_testfor basic connectivity instead.
No parameters. Returns cluster status (green/yellow/red), node count, and active/unassigned shard counts.
opensearch_list_indices ⚠️
Requires
_cat/indicesaccess via the Dashboards proxy. If you get 403, useopensearch_list_index_patternsinstead.
No parameters. Returns a list sorted by index name:
[
{"index": "wazuh-alerts-4.x-2026.06.24", "docs.count": "559359", "store.size": "1.2gb", "health": "green"}
]opensearch_list_index_patterns
List saved index patterns from OpenSearch Dashboards. Requires the Dashboards backend.
No parameters.
[
{"id": "abc123", "title": "wazuh-alerts-*", "timeFieldName": "@timestamp"}
]opensearch_get_mapping ⚠️
Requires
indices:admin/mappings/getprivilege. If you get 403, useopensearch_discover_fieldsinstead.
Parameter | Type | Default | Description |
| str | — | Index name or wildcard, e.g. |
Returns all fields flattened to dot-notation:
{
"wazuh-alerts-4.x-2026.06.24": {
"agent.name": "keyword",
"rule.level": "integer",
"@timestamp": "date"
}
}opensearch_discover_fields
Discover fields by sampling live documents. Works without mapping privileges.
Parameter | Type | Default | Description |
| str | — | Index name or wildcard |
| str |
| Lucene filter to narrow the sample |
| str | — | ISO 8601 UTC start time |
| str | — | ISO 8601 UTC end time |
| str |
| Timestamp field name |
| int |
| Documents to sample (max 100) |
Returns only fields present in the sampled documents, flattened to dot-notation:
{
"agent.id": "str",
"agent.name": "str",
"rule.level": "int",
"rule.description": "str",
"@timestamp": "str"
}Search
opensearch_search
Full-text search using Lucene query syntax — the same syntax as the OpenSearch Dashboards search bar.
Parameter | Type | Default | Description |
| str | — | Index name or wildcard |
| str |
| Lucene query, e.g. |
| str | — | ISO 8601 UTC start time |
| str | — | ISO 8601 UTC end time |
| str |
| Timestamp field name |
| int |
| Max documents to return (hard cap: 200) |
| int |
| Pagination offset — skip this many documents before returning results |
| str |
| Field to sort by |
| str |
|
|
| list | — | Fields to include, e.g. |
{
"total": 19824851,
"hits": [{"agent.name": "WIN-DC01", "rule.level": 12, "@timestamp": "2026-06-24T10:23:11Z"}],
"warning": "No time range specified — this query scans the full index history..."
}warning is present when the limit was capped or no time range was given. To paginate, increment offset by limit on each call.
opensearch_count
Count matching documents without fetching them. Much faster than opensearch_search when you only need the number.
Parameter | Type | Default | Description |
| str | — | Index name or wildcard |
| str |
| Lucene query |
| str | — | ISO 8601 UTC start time |
| str | — | ISO 8601 UTC end time |
| str |
| Timestamp field name |
{"count": 559359, "warning": "No time range specified..."}Aggregations
opensearch_terms
Top N unique values of a field with their document counts (frequency analysis).
Parameter | Type | Default | Description |
| str | — | Index name or wildcard |
| str | — | Field to aggregate. Must be a keyword field — for text fields, try appending |
| str |
| Lucene filter |
| str | — | ISO 8601 UTC start time |
| str | — | ISO 8601 UTC end time |
| str |
| Timestamp field name |
| int |
| Number of top values to return |
{
"WIN-DC01": 4821,
"srv-web01": 2103,
"db-primary": 987,
"_warning": "Field 'rule.description' looks like a text field. Try 'rule.description.keyword'..."
}_warning is present if the field name suggests an analyzed text type (fielddata heap pressure risk).
opensearch_multi_terms
Run multiple field frequency analyses in a single API call. Significantly faster than multiple opensearch_terms calls.
Parameter | Type | Default | Description |
| str | — | Index name or wildcard |
| list | — | List of aggregation specs (see below). Must not be empty. |
| str |
| Lucene filter |
| str | — | ISO 8601 UTC start time |
| str | — | ISO 8601 UTC end time |
| str |
| Timestamp field name |
Each item in aggregations:
{"id": "agents", "field": "agent.name", "size": 20}Example call:
{
"aggregations": [
{"id": "agents", "field": "agent.name", "size": 20},
{"id": "rules", "field": "rule.id", "size": 10},
{"id": "sources", "field": "data.srcip", "size": 30}
]
}{
"agents": {"WIN-DC01": 4821, "srv-web01": 2103},
"rules": {"550": 12000, "5710": 8400},
"sources": {"192.168.1.10": 3200}
}opensearch_histogram
Event count histogram over a time range.
Parameter | Type | Default | Description |
| str | — | Index name or wildcard |
| str | — | Required. ISO 8601 UTC start time |
| str | — | Required. ISO 8601 UTC end time |
| str |
| Timestamp field name |
| str |
| Bucket size. Format: |
| str |
| Lucene filter |
Requests that would produce more than 2,000 buckets are rejected before the query runs. A 1-minute interval over a 1-year range produces ~525,000 buckets and would cause a server-side timeout — the pre-check catches this instantly.
{
"interval_used": "1h",
"results": {
"2026-06-24T00:00:00.000Z": 1203,
"2026-06-24T01:00:00.000Z": 987,
"2026-06-24T02:00:00.000Z": 1541
}
}interval_used reflects the actual bucket size chosen by OpenSearch when interval="auto".
opensearch_stats
Numeric statistics for a field.
Parameter | Type | Default | Description |
| str | — | Index name or wildcard |
| str | — | Numeric field, e.g. |
| str |
| Lucene filter |
| str | — | ISO 8601 UTC start time |
| str | — | ISO 8601 UTC end time |
| str |
| Timestamp field name |
{
"count": 559359,
"min": 0,
"max": 15,
"avg": 7.4,
"sum": 4139834,
"std_deviation": 3.1
}Returns a clear error if a non-numeric field is passed.
Safety & Limits
Guard | Default | Override |
Max search results | 200 docs |
|
Max histogram buckets | 2,000 |
|
Max | 100 docs | hardcoded |
Write guard | all writes blocked | hardcoded |
Bucket pre-check — Histogram requests are validated before execution. The expected bucket count is calculated as (to_ts − from_ts) / interval. If it exceeds the limit, the request is rejected with an actionable error message instead of firing a query that would hold OpenSearch threads for minutes.
Text field warnings — opensearch_terms and opensearch_multi_terms detect field names that suggest analyzed text types and include a _warning in the response. Aggregating on unindexed text fields triggers fielddata loading on the OpenSearch heap.
No-time-range warnings — opensearch_search and opensearch_count include a warning when no from_ts/to_ts is given. A full-index scan on tens of millions of documents is slow and expensive; adding a time range typically reduces query time by 10–15×.
Known Limitations
Three tools require elevated privileges not available on all deployments:
Tool | Required privilege | Alternative |
|
|
|
|
|
|
|
|
|
These tools return a structured error message (not a raw stack trace) when the privilege is missing. The opensearch_test tool includes the authenticated username in its response, which immediately clarifies why specific calls fail.
Development
make build # build Docker image (opensearch-mcp:dev)
make run # run interactively (reads ~/.config/mcp-opensearch/.env)
make shell # open a bash shell inside the container for debuggingOverride the env file path:
make run ENV_FILE=/path/to/other.envLicense
MIT
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
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/bunnyiesart/mcp-opensearch'
If you have feedback or need assistance with the MCP directory API, please join our Discord server