- Model-facing (
triage-alerts,triage-attack-discoveries,manage-cases,manage-rules,threat-hunt,generate-sample-data,generate-attack-discovery): The LLM calls these. Each returns a compact text summary AND renders an interactive UI. - App-only (
poll-alerts,get-alert-context,investigate-entity,get-entity-detail,execute-esql,get-case-alerts,get-case-comments, etc.): Hidden from the LLM. The UI calls these for interactivity.
Each view is a React app bundled into a single self-contained HTML file using vite-plugin-singlefile. All CSS, JavaScript, and assets are inlined. The MCP server reads these HTML files and serves them as ui:// resources. The MCP host renders them in a sandboxed iframe.
The UI (React app in iframe) communicates with the MCP server through the host:
app.callServerTool(): UI calls app-only tools on the serverapp.ontoolresult: UI receives the tool result when the LLM calls the model-facing toolapp.ontoolinput: UI receives tool arguments as the LLM generates them (used for verdict streaming)app.updateModelContext(): UI pushes state back to the LLM's contextapp.sendMessage(): UI sends messages to the conversation (used for AI case actions)app.requestDisplayMode(): UI can request fullscreen
Model-facing tools return compact summaries (~1-5KB) to the LLM, not full Elasticsearch documents (800KB+). The UI independently loads full data via app-only tools.
The UI self-loads via callServerTool after connecting. The ontoolresult callback extracts filter parameters and verdicts from the LLM's tool call.
Multi-word search uses OR logic: "ransomware SRVWIN" matches alerts where any field contains "ransomware" OR "SRVWIN".
The triage-alerts tool accepts an optional verdicts array. Claude passes structured classifications that render as visual cards in the UI.
The graph starts with a single node. Click + to expand — each expansion runs pre-built ES|QL STATS ... BY queries. Hover to trace connections, drag to reposition, click for entity details from ES.
The generate-attack-discovery tool triggers Kibana's Attack Discovery API with any AI connector. Progress is polled via /api/attack_discovery/generations and shown as Kibana-style banners.
All Kibana API calls include elastic-api-version: 2023-10-31 headers, x-elastic-internal-origin: Kibana for internal APIs, and camelCase field names.
The MCP App emits a small set of anonymised usage events via @elastic/ebt, mirrored to the user's Kibana telemetry opt-in. The shipper is fail-closed: events never leave the process unless Kibana reports optIn === true. See docs/telemetry.md for the event catalog and opt-out story.