kibana-esql/SKILL.md
Runs ES|QL queries against Elastic Cloud / Kibana by reusing the user's authenticated browser session via the claude-in-chrome MCP, without requiring an API key. Use when the user mentions Kibana, ES|QL, Elasticsearch, Elastic Cloud, asks to query logs or indices, or wants to explore data in their cluster and only has SSO access.
npx skillsauth add juanjosegongi/skills kibana-esqlInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
*.found.io, *.cloud.es.io, *.kb.<region>.aws.elastic-cloud.com)Do NOT use this skill when:
@elastic/mcp-server-elasticsearch) insteadkibana-dashboards, kibana-vegaPOST /internal/search/esql_async
Headers:
Content-Type: application/json
kbn-xsrf: true
Elastic-Api-Version: 1
x-elastic-internal-origin: Kibana ← REQUIRED. Without it: 400.
Credentials: include
Body: { "params": { "query": "<ES|QL>" } }
x-elastic-internal-origin: Kibana is the gate for /internal/* endpoints in Kibana 8.11+. If missing, Kibana returns 400 "uri ... exists but is not available with the current configuration" — misleading, but it IS a header problem, not a permissions problem.
Small/fast queries complete inline:
{
"rawResponse": {
"columns": [{"name": "greeting", "type": "keyword"}, {"name": "n", "type": "integer"}],
"values": [["hello", 42]],
"took": 15,
"is_running": false,
"is_partial": false
},
"isRunning": false,
"warning": "299 Elasticsearch ... \"No limit defined, adding default limit of [1000]\""
}
Slow queries return { id, isRunning: true } and must be polled at POST /internal/search/ese/<id> with the same four headers until isRunning: false.
NEVER extract session cookies into the conversation context. Keep every API call inside the browser tab via mcp__claude-in-chrome__javascript_tool. The cookie is HttpOnly anyway — cookies stay in the Chrome process, the agent receives only JSON results.
Find or create the tab
mcp__claude-in-chrome__tabs_context_mcp (with createIfEmpty: true if no group exists)Verify authentication
window.location.pathname via javascript_tool/app/ (e.g. /app/discover) → authenticated, proceed/login or redirects to amazon.com / identitycenter → ask user to complete SSO in that window and wait for confirmationRun the query
assets/run-esql.js pattern: single fetch with the four headersparams.queryrawResponse.columns and rawResponse.values for the resultHandle async
isRunning: true in the first response, extract id and use assets/poll-async.jsisRunning: falseDELETE /internal/search/<id> when done to release cluster resourcesRender for the user
took ms and warnings (default LIMIT 1000 is the common one)Minimum viable query (sync path):
const r = await fetch('/internal/search/esql_async', {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
'kbn-xsrf': 'true',
'Elastic-Api-Version': '1',
'x-elastic-internal-origin': 'Kibana'
},
body: JSON.stringify({ params: { query: 'FROM logs-* | LIMIT 5' } })
});
const j = await r.json();
// j.rawResponse.columns, j.rawResponse.values, j.rawResponse.took
Full self-contained snippet lives in assets/run-esql.js and handles both sync and async completions. For polling-only, see assets/poll-async.js.
| HTTP | Body contains | Cause | Fix |
|------|---------------|-------|-----|
| 400 | not available with the current configuration | Missing x-elastic-internal-origin: Kibana | Add the header |
| 400 | Please specify a version via Elastic-Api-Version header | Missing version header | Add Elastic-Api-Version: 1 |
| 401 | security_exception or redirect to login | Session expired | Ask user to re-login via SSO in the tab |
| 403 | security_exception with index name | User's role lacks read on that index | Try a different index or FROM <other-index> |
| 404 | Not Found on /internal/search/esql_async | Kibana version < 8.11 — endpoint did not exist | Fall back to /api/console/proxy?path=_query&method=POST if console is enabled, otherwise unsupported |
| 400 | parsing_exception with line/col | ES|QL syntax error | Show the parser message to the user — it's precise |
Deep debugging: see references/troubleshooting.md.
If endpoints in this skill stop working (Kibana upgrade, different cluster topology), use the interceptor discovery approach rather than guessing:
assets/install-interceptor.js via javascript_toolwindow.__cap to see the exact URL, method, and body Kibana usedThis is faster and more reliable than reading Kibana source or trying documented endpoints.
run-esql.js — run one ES|QL query, handles sync + asyncpoll-async.js — poll an async search id until completeprobe-auth.js — check whether the tab has a live Kibana sessioninstall-interceptor.js — capture Kibana's internal API calls for endpoint discoverylist-indices.js — enumerate available indices via autocompletedevelopment
Guides Dockerfile creation and optimization. Use when Dockerfile or Docker Compose is detected. Supports multi-stage builds, cache optimization, security hardening, and image size minimization.
development
Provides comprehensive testing and TDD guidance. Use for writing tests before implementing new features (TDD, test-driven development, red-green-refactor), creating reproduction tests for bug fixes, running regression tests during refactoring, and checking test coverage during code reviews. Enforces AAA pattern, test-first workflow, and 100% business logic coverage goal. Also covers testing anti-patterns, mock discipline, and testable design.
data-ai
A completely different skill for database operations. Use when working with PostgreSQL queries, schema design, or database migrations.
testing
Another sample skill for testing. Use when the user wants to create widgets with advanced features or mentions beta testing.