一键导入
opensearch
Guide for efficiently querying OpenSearch logs via MCP tools with minimal context consumption
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Guide for efficiently querying OpenSearch logs via MCP tools with minimal context consumption
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | opensearch |
| description | Guide for efficiently querying OpenSearch logs via MCP tools with minimal context consumption |
You have access to an OpenSearch MCP server that queries container logs via OpenSearch Dashboards. The cluster has billions of documents. Always include time ranges to avoid timeouts.
| Tool | Purpose |
|---|---|
opensearch_search | Search logs with Lucene/KQL query syntax (primary tool) |
opensearch_search_raw | Raw Query DSL for advanced queries |
opensearch_aggregate | Aggregations (counts, terms, histograms) |
opensearch_get_indices | List indices with doc counts |
opensearch_get_mappings | Get field names/types from a sample doc |
opensearch_cluster_health | Basic cluster health |
opensearch_switch_cluster | Switch to a different cluster on-the-fly (no restart needed) |
opensearch_get_active_cluster | Show currently active cluster name, URL, and cookie age |
The MCP server applies operations to reduce response size. Always optimize for minimal context usage.
opensearch_search(index="container-logs-*", query_string="...", summary_only=true)
This returns only total_hits and time_range — costs ~100 tokens.
opensearch_search(
index="container-logs-*",
query_string="...",
fields=["@timestamp", "log", "kubernetes.namespace_name", "kubernetes.pod_name"],
size=10
)
Only returns specified fields — saves 70-80% context vs full documents.
opensearch_aggregate(
index="container-logs-*",
aggs={"by_namespace": {"terms": {"field": "kubernetes.namespace_name.keyword", "size": 20}}},
query={"bool": {"must": [...], "filter": [{"range": {"@timestamp": {"gte": "now-1h"}}}]}}
)
| Parameter | Default | Purpose |
|---|---|---|
summary_only | false | Set true to get only hit count, no documents |
auto_prune | true | Strips kubernetes.labels and kubernetes.annotations automatically |
fields | null | Array of specific fields to return (e.g., ["log", "@timestamp"]) |
max_chars_per_hit | 2000 | Truncates individual hits exceeding this size |
size | 100 | Number of docs to return (max 1000) |
time_from | now-15m | Start time (ISO 8601 or relative like now-1h) |
time_to | now | End time |
Every response includes a _meta.applied_operations array showing what the server did:
| Flag | Meaning |
|---|---|
summary_only | Only counts returned, no documents |
field_filter:field1,field2 | Only these fields were returned |
auto_prune:kubernetes.labels,kubernetes.annotations | Verbose k8s fields were removed |
hits_truncated:N/M | N out of M hits exceeded max_chars_per_hit and were truncated |
partial_results:100_of_50000 | Only 100 of 50000 total hits returned |
response_truncated_at_15KB | Entire response exceeded 15KB and was cut off |
response_truncated_at_15KB:size (e.g., size=5)fields to select only needed fieldssummary_only=true if you only need countsopensearch_aggregate for analysis insteadpartial_results:The query matched more documents than returned. If the user needs broader analysis, use aggregations.
hits_truncated:Individual log entries were too large. Use fields to pick only the fields you need, or increase max_chars_per_hit.
log Field (Cluster-Specific Strategy)The log field search strategy depends on the cluster type:
For onprem clusters, use query_string with analyze_wildcard: true and quoted wildcard patterns:
opensearch_search_raw(
index="container-logs-*",
body={
"query": {"bool": {"must": [
{"query_string": {
"query": "log:\"*level*error*\"",
"analyze_wildcard": true,
"time_zone": "Asia/Colombo"
}}
], "filter": [
{"range": {"@timestamp": {"gte": "now-5m", "lte": "now"}}}
]}},
"size": 20,
"_source": ["@timestamp", "log", "kubernetes.namespace_name", "kubernetes.pod_name"]
}
)
opensearch_search_raw(
index="container-logs-*",
body={
"query": {"bool": {"must": [
{"query_string": {
"query": "log:\"*1d4867ac-65cb-4de8-8d46-aaef62f6b5fb*\"",
"analyze_wildcard": true,
"time_zone": "Asia/Colombo"
}}
], "filter": [
{"range": {"@timestamp": {"gte": "now-1h", "lte": "now"}}}
]}},
"size": 100,
"sort": [{"@timestamp": "asc"}],
"_source": ["@timestamp", "log", "kubernetes.namespace_name", "kubernetes.pod_name", "kubernetes.container_name"]
}
)
Key points for OnPrem:
query_string with analyze_wildcard: true"*pattern*" not *pattern*time_zone: "Asia/Colombo" for consistency with dashboardFor cloud clusters (AWS/Azure), the log field is mapped as keyword (not analyzed text). Use wildcard queries:
opensearch_search_raw(
index="container-logs-*",
body={
"query": {"bool": {"must": [
{"range": {"@timestamp": {"gte": "now-5m", "lte": "now"}}},
{"wildcard": {"log": "*level*error*"}}
]}},
"size": 20,
"_source": ["@timestamp", "log", "kubernetes.namespace_name", "kubernetes.pod_name"]
}
)
opensearch_search_raw(
index="container-logs-*",
body={
"query": {"bool": {"must": [
{"range": {"@timestamp": {"gte": "now-1h", "lte": "now"}}},
{"wildcard": {"log": "*77e71a17-2e52-404a-86d2-eed997fd2a57*"}}
]}},
"size": 20,
"_source": ["@timestamp", "log", "kubernetes.namespace_name", "kubernetes.pod_name"]
}
)
Key points for Cloud:
wildcard query (NOT query_string)query_string with log:*pattern* returns 0 hits on these clustersopensearch_aggregate(
index="container-logs-*",
query={"bool": {"must": [
{"range": {"@timestamp": {"gte": "now-5m", "lte": "now"}}},
{"wildcard": {"log": "*level*error*"}}
]}},
aggs={"namespaces": {"terms": {"field": "kubernetes.namespace_name", "size": 10}}}
)
Check the active cluster name using opensearch_get_active_cluster:
onprem → use query_string with analyze_wildcard: truewildcard queriesNote: query_string and opensearch_search work fine for non-keyword fields like kubernetes.namespace_name, stream, kubernetes.pod_name, etc. on all clusters.
query_string: 'kubernetes.namespace_name:"my-namespace"'
query_string: 'kubernetes.pod_name:"my-pod-abc123"'
query_string: 'stream:stderr'
opensearch_search_raw(
index="container-logs-*",
body={
"query": {"bool": {"must": [
{"range": {"@timestamp": {"gte": "now-5m", "lte": "now"}}},
{"term": {"kubernetes.namespace_name": "my-namespace"}},
{"wildcard": {"log": "*timeout*"}}
]}},
"size": 20,
"_source": ["@timestamp", "log", "kubernetes.pod_name"]
}
)
fields Parameter| Field | Description |
|---|---|
@timestamp | Log timestamp |
log | The actual log message |
stream | stdout or stderr |
kubernetes.namespace_name | K8s namespace |
kubernetes.pod_name | K8s pod name |
kubernetes.container_name | Container name |
kubernetes.host | Node name |
kubernetes.pod_ip | Pod IP address |
kubernetes.labels.organization_id | Org ID (when auto_prune=false or use aggs) |
kubernetes.labels.env_name | Environment name |
kubernetes.labels.component_name | Component name |
now-5m, now-1h, now-24hIMPORTANT: When the user mentions a cluster, first read clusters.py to get the available clusters and their short names. The cluster registry is built from the .env file (URLs) and the DESCRIPTIONS dict in clusters.py.
Users configure their cluster URLs in .env (copied from .env.example). Example entries:
CLUSTER_DEV_AWS_EU_CP=https://opensearch-dashboard.dev.example.com
CLUSTER_PROD_AWS_EU_CDP=https://opensearch-dashboard.prod.example.com
At runtime, clusters.py loads these into the CLUSTERS dict as (url, description) tuples.
When the user says any of these, map to the corresponding cluster:
prod-azure-us-cdpdev-azure-us-cdpstg-us-cdpdev-onprem-cp or dev-onprem-dp (ask which)prod-eu-cdpdev-eu-cdpprod-tenant-a-userprodprod-tenant-cprod-tenant-dprod-tenant-bIf the user asks to query a cluster that has No OpenSearch, inform them it uses Azure Log Analytics Workspace instead and is not queryable through this MCP.
When the user wants to query a different cluster, use the opensearch_switch_cluster tool:
opensearch_switch_cluster(cluster="prod-azure-eu-cdp")
This automatically fetches cookies via headless SSO and switches all subsequent queries to the new cluster. No restart needed.
If the tool returns an error (SSO session expired), instruct the user to run:
cd /path/to/opensearch-agent/opensearch-mcp
./get-cookies.py <cluster-short-name>
This opens a browser for manual login. After login, retry — no restart needed.
Use opensearch_get_active_cluster to check which cluster is currently active before switching.
The MCP server has automatic cookie refresh. Here's how it works:
cookies.json and retries the requestWhen you call opensearch_switch_cluster, it fetches fresh cookies for the target cluster via headless SSO. If that fails, the tool returns an error with manual instructions.
If the SSO session itself has expired (user hasn't logged in via browser recently), auto-refresh fails.
The server returns a structured error with action_required and a command to run.
When you see this error, instruct the user:
The OpenSearch cookies have expired and automatic refresh failed (SSO session expired).
To fix, run:
cd /path/to/opensearch-agent/opensearch-mcp
./get-cookies.py <cluster-name>
This opens a browser for you to log in. After login completes, cookies are
saved automatically. **No Claude Code restart needed** — just retry the query.
Available clusters: ./get-cookies.py --list
Important: After the user runs the script, you CAN retry the query immediately — no restart needed. The server reads cookies.json fresh on every request.
For any user request, follow this order:
summary_only=true to understand volumesize=5, fields=[...] to understand shapeopensearch_aggregate for breakdowns