From elastic-agent-skills
Triages Elastic Security alerts: fetches next alert, runs context queries, classifies threats, creates cases, and acknowledges. For SOC analysis and detections.
npx claudepluginhub elastic/agent-skills --plugin elastic-cloudThis skill uses the workspace's default tool permissions.
Analyze Elastic Security alerts one at a time: gather context, classify, create a case, and acknowledge. This skill
Conducts multi-round deep research on GitHub repos via API and web searches, generating markdown reports with executive summaries, timelines, metrics, and Mermaid diagrams.
Dynamically discovers and combines enabled skills into cohesive, unexpected delightful experiences like interactive HTML or themed artifacts. Activates on 'surprise me', inspiration, or boredom cues.
Generates images from structured JSON prompts via Python script execution. Supports reference images and aspect ratios for characters, scenes, products, visuals.
Analyze Elastic Security alerts one at a time: gather context, classify, create a case, and acknowledge. This skill
depends on the case-management skill for case creation.
Install dependencies before first use from the skills/security directory:
cd skills/security && npm install
Set the required environment variables (or add them to a .env file in the workspace root):
export ELASTICSEARCH_URL="https://your-cluster.es.cloud.example.com:443"
export ELASTICSEARCH_API_KEY="your-api-key"
export KIBANA_URL="https://your-cluster.kb.cloud.example.com:443"
export KIBANA_API_KEY="your-kibana-api-key"
All commands from workspace root. Always fetch → investigate → document → acknowledge. Call the tools directly — do not read the skill file or explore the workspace first.
node skills/security/alert-triage/scripts/fetch-next-alert.js
node skills/security/case-management/scripts/case-manager.js find --tags "agent_id:<id>"
node skills/security/alert-triage/scripts/run-query.js --query-file query.esql --type esql
node skills/security/case-management/scripts/case-manager.js create --title "..." --description "..." --tags "classification:..." "agent_id:<id>" --severity <level> --yes
node skills/security/case-management/scripts/case-manager.js attach-alert --case-id <id> --alert-id <id> --alert-index <index> --rule-id <uuid> --rule-name "<name>" --yes
node skills/security/alert-triage/scripts/acknowledge-alert.js --related --agent <id> --timestamp <ts> --window 60 --yes
| Task | Tools to call (in order) |
|---|---|
| End-to-end triage | fetch_next_alert → run_query (context) → case_manager create (case) → acknowledge_alert |
| Gather context | run_query (process tree, network, related alerts) |
| Create case after classification | case_manager create → case_manager attach-alert |
| Acknowledge after triage | acknowledge_alert (related mode for batch) |
Always complete the full workflow: fetch → investigate → document → acknowledge. Do not stop after gathering context — create or update a case with findings before acknowledging.
Critical execution rules:
.esql file then pass it via --query-file. Do not use edit_file
— use a single shell call with echo "..." > query.esql && node ... --query-file query.esql.When triaging multiple alerts, group first, then triage each group:
- [ ] Step 0: Group alerts by agent/host and time window
- [ ] Step 1: Check existing cases
- [ ] Step 2: Gather full context (DO NOT SKIP)
- [ ] Step 3: Create or update case (only AFTER context gathered)
- [ ] Step 4: Acknowledge alert and all related alerts
- [ ] Step 5: Fetch next alert group and repeat
When the user asks about multiple open alerts, group them first to avoid redundant investigation: query open alerts,
group by agent.id, sub-group by time window (~5 min = likely one incident), triage each group as a single unit.
Use ES|QL for an overview (write to file first for PowerShell):
FROM .alerts-security.alerts-*
| WHERE kibana.alert.workflow_status == "open" AND @timestamp >= "<start>"
| STATS alert_count=COUNT(*), rules=VALUES(kibana.alert.rule.name) BY agent.id
| SORT alert_count DESC
For full query templates, see references/classification-guide.md.
Before creating a new case, check if this alert belongs to an existing one. Use the case-management skill:
node skills/security/case-management/scripts/case-manager.js find --tags "agent_id:<agent_id>"
node skills/security/case-management/scripts/case-manager.js cases-for-alert --alert-id <alert_id>
Look for cases with the same agent ID, user, or related detection rule within a similar time window.
Note:
find --searchmay return 500 errors on Serverless. Usefind --tagsorlistinstead.
This is the most important step. Do not skip or shortcut it. Complete ALL substeps before forming any classification opinion.
Time range warning: Alerts may be days or weeks old. NEVER use relative time like NOW() - 1 HOUR. Extract the
alert's @timestamp and build queries around that time with +/- 1 hour window.
Substeps: (2a) Related alerts on same agent/user; (2b) Rule frequency across env (high = FP-prone); (2c) Entity context — process tree, network, registry, files; (2d) Behavior investigation — persistence, C2, lateral movement, credential access.
Example — process tree (use ES|QL with KEEP; avoid --full which produces 10K+ lines):
FROM logs-endpoint.events.process-*
| WHERE agent.id == "<agent_id>" AND @timestamp >= "<alert_time - 5min>" AND @timestamp <= "<alert_time + 10min>"
AND process.parent.name IS NOT NULL
AND process.name NOT IN ("svchost.exe", "conhost.exe", "agentbeat.exe")
| KEEP @timestamp, process.name, process.command_line, process.pid, process.parent.name, process.parent.pid
| SORT @timestamp | LIMIT 80
| Data type | Index pattern |
|---|---|
| Alerts | .alerts-security.alerts-* |
| Processes | logs-endpoint.events.process-* |
| Network | logs-endpoint.events.network-* |
| Logs | logs-* |
For full query templates and classification criteria, see references/classification-guide.md.
After gathering context, create a case and attach alert(s). Use --rule-id and --rule-name (required; 400 error
without them):
node skills/security/case-management/scripts/case-manager.js create \
--title "<concise summary>" \
--description "<findings, IOCs, attack chain, MITRE techniques>" \
--tags "classification:<benign|unknown|malicious>" "confidence:<0-100>" "mitre:<technique>" "agent_id:<id>" \
--severity <low|medium|high|critical>
node skills/security/case-management/scripts/case-manager.js attach-alert \
--case-id <case_id> --alert-id <alert_id> --alert-index <index> \
--rule-id <rule_uuid> --rule-name "<rule name>"
# Multiple alerts: attach-alerts --alert-ids <id1> <id2>
# Add notes: add-comment --case-id <id> --comment "Findings..."
Case description: Summary (1-2 sentences); Attack chain; IOCs (hashes, IPs, paths); MITRE techniques; Behavioral findings; Response context (remediation, credentials at risk).
Acknowledge ALL related alerts together. Use --dry-run first to confirm scope, then run without it:
# By host name — preferred when triaging a host
node skills/security/alert-triage/scripts/acknowledge-alert.js --query --host <hostname> --dry-run
node skills/security/alert-triage/scripts/acknowledge-alert.js --query --host <hostname> --yes
# By agent ID — preferred when agent.id is known
node skills/security/alert-triage/scripts/acknowledge-alert.js --related --agent <id> --timestamp <ts> --window 60 --dry-run
node skills/security/alert-triage/scripts/acknowledge-alert.js --related --agent <id> --timestamp <ts> --window 60 --yes
Increase --window for longer attack chains (e.g., 300 for 5 minutes). Report the exact count of acknowledged alerts
from the tool output. Pass --yes to skip the confirmation prompt (required when called by an agent).
node skills/security/alert-triage/scripts/fetch-next-alert.js
Fetches the oldest unacknowledged Elastic Security alert.
node skills/security/alert-triage/scripts/fetch-next-alert.js [--days <n>] [--json] [--full] [--verbose]
Runs KQL or ES|QL queries against Elasticsearch.
PowerShell warning: ES|QL queries contain pipe characters (|) which PowerShell interprets as shell pipes. ALWAYS
use --query-file for ES|QL:
# Write query to file, then run
node skills/security/alert-triage/scripts/run-query.js --query-file query.esql --type esql
KQL queries without pipes can be passed directly:
node skills/security/alert-triage/scripts/run-query.js "agent.id:<id>" --index "logs-*" --days 7
| Arg | Description |
|---|---|
query | KQL query (positional) |
--query-file, -q | Read query from file (required for ES|QL on PowerShell) |
--type, -t | kql or esql (default: kql) |
--index, -i | Index pattern (default: logs-*) |
--size, -s | Max results (default: 100) |
--days, -d | Limit to last N days |
--json | Raw JSON output |
--full | Full document source |
Acknowledges alerts by updating workflow_status to acknowledged.
| Mode | Command |
|---|---|
| Single | node skills/security/alert-triage/scripts/acknowledge-alert.js <alert_id> --index <index> --yes |
| Related | node skills/security/alert-triage/scripts/acknowledge-alert.js --related --agent <id> --timestamp <ts> [--window 60] --yes |
| By host | node skills/security/alert-triage/scripts/acknowledge-alert.js --query --host <hostname> [--time-start <ts>] [--time-end <ts>] --yes |
| Query | node skills/security/alert-triage/scripts/acknowledge-alert.js --query --agent <id> [--time-start <ts>] [--time-end <ts>] --yes |
| Dry run | Add --dry-run to any mode (no confirmation needed) |
| Confirm | All write modes prompt for confirmation; pass --yes to skip |
acknowledge-alert.js) prompt for confirmation. Pass --yes or -y to skip when called by an
agent.--dry-run before bulk acknowledgments to preview scope without modifying data.| Variable | Required | Description |
|---|---|---|
ELASTICSEARCH_URL | Yes | Elasticsearch URL |
ELASTICSEARCH_API_KEY | Yes | Elasticsearch API key |
KIBANA_URL | Yes | Kibana URL (for case management) |
KIBANA_API_KEY | Yes | Kibana API key (for case management) |