kibana-dashboard
Create Kibana dashboards from descriptions
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Create Kibana dashboards from descriptions
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Debug and analyze LLM eval runs — view traces, compare runs, investigate failures, track costs. Use when debugging @kbn/evals failures, comparing eval runs, or analyzing LLM performance.
Start your security session with a personalized briefing — attacks, alerts, cases, rules, threat intel. Use as the first thing when starting security work.
Guide users from zero to a working Elastic cluster — Cloud or on-prem, connection config, first queries, and next steps.
Interactive guide for creating an APM service overview dashboard — discovers service data, presents metrics, and creates a tailored dashboard.
Interactive guide for creating SLOs from discovered APM and metric data — identifies candidates, lets user configure targets, and creates SLOs.
Create, configure, and manage Elasticsearch indices — mappings, settings, templates, data streams, and lifecycle policies.
| name | kibana-dashboard |
| description | Create Kibana dashboards from descriptions |
Use when the user wants to create or customize Kibana dashboards programmatically via the kibana_create_dashboard tool.
list_indices and get_mappings to understand available fields.esql_query or search to sample data and verify field names.kibana_list_data_views and kibana_list_dashboards to check existing resources."aggs": { "ts": { "stats": { "field": "@timestamp" } } } to confirm when data exists. Dashboards with now-1h won't show data older than 1 hour.Call kibana_create_dashboard with a title, panels array, and time range.
The tool uses a simplified panel format — it translates panels into the raw Kibana as-code API format (POST /api/dashboards with Elastic-Api-Version: 1). The tool also handles:
type: "lens" + config.attributes for the raw APIlabel properties from ES|QL XY y-axis columnslens.apiFormat, dashboardAgent.enabled, lens.enable_esql) dynamicallyKibana 9.4+ needs these in kibana.yml or as Docker env vars:
feature_flags.overrides:
dashboardAgent.enabled: true
lens.apiFormat: true
lens.enable_esql: true # Required for ES|QL datasets
Also set coreApp.allowDynamicConfigOverrides: true and server.restrictInternalApis: false to allow the tool to enable flags at runtime.
w:48, half = w:24, third = w:16, quarter = w:12.h:6, charts h:12–14, gauge h:10–12, tables h:10, markdown headers h:3–4.KPI row (top of dashboard): 6 metric panels across (w:8 each) with h:6. Use ROUND() and readable column aliases so subtitles show "Tank Level (%)" not "avg_val".
Section headers: Use markdown panels (h:3) with ### 🏭 Section Name to group related charts. Organize by domain (e.g. Chemical Systems, Water Systems, Sanitation).
Chart grouping: 3 charts per row at w:16 each. Use consistent chart types within a section (line for precision, area for flow/continuous, bar_stacked for volume).
Summary tables: Place at the bottom. Split into side-by-side tables (w:24 each) — e.g. "Metrics by Site" + "Metrics by Device Type" — rather than one huge table.
Time range: Always check actual data timestamps before setting time_from. For demo data that spans minutes, use now-1h; for live data, now-24h is typical. Match BUCKET() interval to data density (30s for 2-min data, 5min for hourly data).
Markdown panel:
{ "type": "DASHBOARD_MARKDOWN", "content": "# Title\nBody text", "grid": { "x": 0, "w": 48, "h": 4 } }
Metric panel (ES|QL):
dataset at panel level: { "type": "esql", "query": "..." }metrics is an array (not a single object): [{ "type": "primary", "operation": "value", "column": "col_name" }]"type": "secondary"breakdown_by: { "operation": "value", "column": "col_name" }{
"type": "metric",
"title": "Avg Dosing Rate",
"dataset": { "type": "esql", "query": "FROM my-index | STATS `L/min` = AVG(field)" },
"metrics": [{ "type": "primary", "operation": "value", "column": "L/min" }]
}
XY panel (ES|QL):
breakdown_by (NOT breakdown — the wrong name silently causes validation failure).y columns accept ONLY operation + column — no label property (causes hard validation error).dosing not avg_val).line, area, area_stacked, area_percentage, bar, bar_stacked, bar_horizontal, bar_horizontal_stacked, bar_percentage, bar_horizontal_percentage{
"type": "xy",
"title": "Trend Over Time by Category",
"layers": [{
"dataset": { "type": "esql", "query": "FROM my-index | WHERE field IS NOT NULL | STATS val = AVG(field) BY @timestamp = BUCKET(@timestamp, 5 minute), category = attributes.category" },
"type": "line",
"x": { "operation": "value", "column": "@timestamp" },
"y": [{ "operation": "value", "column": "val" }],
"breakdown_by": { "operation": "value", "column": "category" }
}]
}
Gauge panel (ES|QL):
dataset at panel levelmetric is a single object (not an array): { "operation": "value", "column": "col" }{
"type": "gauge",
"title": "Water pH (target 6.5–8.5)",
"dataset": { "type": "esql", "query": "FROM my-index | STATS pH = AVG(metrics.water.ph)" },
"metric": { "operation": "value", "column": "pH" }
}
Datatable panel (ES|QL):
dataset at panel levelrows + metrics arrays (NOT columns — that key is rejected){ "operation": "value", "column": "col" }{
"type": "datatable",
"title": "Summary Table",
"dataset": { "type": "esql", "query": "FROM my-index | STATS `Count` = COUNT(*), `Avg Value` = AVG(f) BY `Category` = cat_field | SORT `Category`" },
"rows": [{ "operation": "value", "column": "Category" }],
"metrics": [{ "operation": "value", "column": "Count" }, { "operation": "value", "column": "Avg Value" }]
}
\L/min`or`Avg pH`in STATS, not generic names likeavg_val`.BUCKET(@timestamp, 10 second). If data spans hours, use 5 minute. Too-large buckets collapse everything into one point.site = attributes.site.name instead of raw attributes.site.name.WHERE field IS NOT NULL to avoid empty data points.counter_double) reject all aggs (AVG, SUM, MAX, MIN). Cast first: EVAL c = TO_DOUBLE(counter_field) | STATS avg_c = AVG(c). This applies to fields with time_series_metric: "counter" (e.g. metrics.sanitation.cycle_count).water.ph for WaterSystem only), aggregating across all device types shows null for rows without that metric. This is expected.| Mistake | Symptom | Fix |
|---|---|---|
dataset at XY panel root | Validation error on all layers | Move dataset into each layer |
breakdown instead of breakdown_by | Validation error | Use breakdown_by |
label on ES | QL XY y-axis columns | Validation error |
Single metric for metric panels | Validation error | Use metrics array with type: "primary" |
columns array on datatable | Validation error | Use rows + metrics arrays |
time_range: "now-1h" on old data | Empty panels | Check actual data timestamps, set absolute range |
BUCKET(@timestamp, 5 minute) on 1-min data | Single flat point per series | Use smaller bucket (e.g. 10 second) |
SUM/AVG/MAX on OTel counter metrics | ES | QL verification_exception |
| Raw field names as ES | QL aliases | Ugly subtitles like avg_val |
Missing lens.enable_esql feature flag | esql dataset rejected with "expected dataView or index" | Add flag to kibana.yml or Docker env |
For non-ES|QL panels, use { "type": "dataView", "id": "data-view-id" } or { "type": "index", "index": "my-index-*" }. Operations use aggregation names: "count", "average", "sum", "unique_count", "last_value", "percentile", "terms", "date_histogram" with a field property.
If calling POST /api/dashboards directly (without kibana_create_dashboard), the panel structure differs:
{
"title": "Dashboard Title",
"panels": [
{
"type": "lens",
"uid": "unique_id",
"grid": { "x": 0, "y": 0, "w": 24, "h": 10 },
"config": {
"attributes": {
"type": "metric",
"title": "Panel Title",
"dataset": { "type": "esql", "query": "..." },
"metrics": [{ "type": "primary", "operation": "value", "column": "col" }]
}
}
}
]
}
Note: Lens panels are wrapped in type: "lens" with chart config inside config.attributes. Markdown panels use type: "DASHBOARD_MARKDOWN" with config.content. The kibana_create_dashboard tool handles this translation automatically.