Agent skill

Kql Query Authoring

by SCStelz in SCStelz/security-investigator

A skill your agent uses when asked to write, create, or help with KQL (Kusto Query Language) queries for Microsoft Sentinel, Defender XDR, or Azure Data Explorer.

MITAuto-check passedData & Analytics

Install Kql Query Authoring

skills CLI
$ npx skills add SCStelz/security-investigator --skill kql-query-authoring -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install SCStelz/security-investigator kql-query-authoring --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/SCStelz/security-investigator.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.github/skills/kql-query-authoring .claude/skills/kql-query-authoring && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
kql-query-authoring
GitHub stars
249
Token cost
~5.7k tokens
SKILL.md length
2,133 words
Files
1
Skills in repo
22
Repo updated
First seen
Licence
MIT

At a glance

A skill your agent uses when asked to write, create, or help with KQL (Kusto Query Language) queries for Microsoft Sentinel, Defender XDR, or Azure Data Explorer.

  • Works in 8 steps: Understand User Requirements → Check Local Query Library → Get Table Schema (MANDATORY) → …
  • Help with KQL (Kusto Query Language) queries for Microsoft Sentinel
  • SKILL.md covers Purpose, Prerequisites, ⚠️ Known Issues and ⚠️ CRITICAL WORKFLOW RULES -…, plus 5 more sections
  • Calls python and npm

What it does

Kql Query Authoring is an agent skill from SCStelz/security-investigator. Use this skill when asked to write, create, or help with KQL (Kusto Query Language) queries for Microsoft Sentinel, Defender XDR, or Azure Data Explorer. Triggers on keywords like "write KQL", "create KQL query", "help with KQL", "query [table]", "KQL for [scenario]", or when a user requests queries for specific data analysis scenarios. This skill uses schema validation, Microsoft Learn documentation, and community examples to generate production-ready KQL queries.

Its SKILL.md is about 5.7k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Data & Analytics, covering Forms and validation and Data analysis. It works with Microsoft Sentinel, Microsoft Defender, Microsoft Azure and Model Context Protocol. The repository describes itself as: Automated security investigation tool using Microsoft MCP Servers, GitHub Copilot, Python Modules and custom copilot-instructions. The licence is MIT.

When your agent uses it

  • Help with KQL (Kusto Query Language) queries for Microsoft Sentinel
  • Azure Data Explorer
  • Keywords like write KQL
  • Create KQL query

Example prompts

  • “write KQL”
  • “create KQL query”
  • “help with KQL”
  • “/kql-query-authoring”

Requirements

  • Python 3
  • Node.js

Workflow steps

8 steps, taken from the step headings in SKILL.md.

  1. Understand User Requirements
  2. Check Local Query Library
  3. Get Table Schema (MANDATORY)
  4. Get Official Code Samples
  5. Get Community Examples
  6. Generate Query
  7. Validate and Test (MANDATORY)
  8. Format and Deliver Output

What it can do on your machine

Read from SKILL.md and the folder at commit 51e1385. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • python
    • npm

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    Links to these hosts (documentation or services it may open):

    • npmjs.com
    • github.com
    • learn.microsoft.com

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Kql Query Authoring loads about 5.7k tokens when it runs. Until then it costs about 122 tokens; SKILL.md has 2,133 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~122
When it runs · the whole SKILL.md, loaded when a task matches
~5.7k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from SCStelz/security-investigator at commit 51e1385, republished under its MIT licence (© SCStelz). 2,133 words, ~5,749 tokens.

Download SKILL.mdSave it as .claude/skills/kql-query-authoring/SKILL.md (or your agent's skills folder).
name
kql-query-authoring
description
Use this skill when asked to write, create, or help with KQL (Kusto Query Language) queries for Microsoft Sentinel, Defender XDR, or Azure Data Explorer. Triggers on keywords like "write KQL", "create KQL query", "help with KQL", "query [table]", "KQL for [scenario]", or when a user requests queries for specific data analysis scenarios. This skill uses schema validation, Microsoft Learn documentation, and community examples to generate production-ready KQL queries.

KQL Query Authoring - Instructions

Purpose

Generate validated, production-ready KQL queries by combining schema validation (331+ indexed tables), Microsoft Learn documentation, community examples, and performance best practices.


Prerequisites

Required MCP Servers:

  1. KQL Search MCP Server — Schema validation, query examples, table discovery

    • Install: npm install -g kql-search-mcp (npm)
  2. Microsoft Docs MCP Server — Official Microsoft Learn documentation and code samples

Verification: Tools should be available as mcp_kql-search_* and mcp_microsoft-lea_*.


⚠️ Known Issues

search_favorite_repos Bug (v1.0.5)

❌ Broken — ERROR_TYPE_QUERY_PARSING_FATAL. Use mcp_kql-search_search_github_examples_fallback instead.


⚠️ CRITICAL WORKFLOW RULES - READ FIRST ⚠️

  1. Validate table schema FIRST — mcp_kql-search_get_table_schema to verify table exists, column names, and data types.

  2. Check platform schema — Sentinel uses TimeGenerated; Defender XDR uses Timestamp. Microsoft Learn examples default to XDR syntax — always convert before testing on Sentinel.

  3. Check local query library FIRST — Use the discovery manifest (.github/manifests/discovery-manifest.yaml) for domain/MITRE lookups and grep_search for table-name/keyword lookups. See the KQL Pre-Flight Checklist in copilot-instructions.md for the full priority order.

  4. Query file structure: NO placeholder TOC — When creating a new query file, do NOT add a ## Quick Reference — Query Index heading or placeholder. scripts/generate_tocs.py creates the heading and table itself. Pre-creating it confuses the strip-and-reinsert logic and produces duplicated content. See ## Creating Query Files below for full file structure rules.

  5. Use multiple sources — Schema (authoritative column names) + Microsoft Learn (official patterns) + community queries (real-world examples).

  6. Test using the correct execution tool — Follow the Tool Selection Rule in copilot-instructions.md:

    • Sentinel-native tables → Data Lake or AH
    • XDR tables ≤ 30d → Advanced Hunting (free); > 30d → Data Lake
    • XDR-only tables (DeviceTvm*, Exposure*) → Advanced Hunting only
    • Adapt timestamp column when switching tools
  7. Test queries before presenting to user — Run with | take 5 via live execution. Use mcp_kql-search_validate_kql_query as fallback if live testing unavailable.

  8. Provide context — Explain what the query does, expected results, and any limitations.

  9. Read the complete workflow below before starting.

📋 Inherited rules: This skill inherits the KQL Pre-Flight Checklist, Tool Selection Rule (Data Lake vs Advanced Hunting), and Known Table Pitfalls from copilot-instructions.md. Those rules are authoritative — do not contradict them here.


Query Authoring Workflow

Step 1: Understand User Requirements

Extract key information:

  • Table(s) needed: Which data source? (e.g., EntraIdSignInEvents, EmailEvents, SecurityAlert)
  • Time range: How far back? (e.g., last 7 days, specific date range)
  • Filters: What specific conditions? (e.g., user, IP, threat type)
  • Output: Statistics, detailed records, time series, aggregations?
  • Platform: Sentinel or Defender XDR? (affects column names)
  • Deployment target: Custom detection rule? (see below)

Custom Detection Intent Detection:

If the user mentions "custom detection", "detection rule", "deploy as detection", "CD rule", "author detections for", or "deploy to Defender":

  1. Read the detection-authoring skill (.github/skills/detection-authoring/SKILL.md) — Critical Rules and CD Metadata Contract sections
  2. Design queries with CD constraints — row-level output, mandatory columns (TimeGenerated, DeviceName, ReportId), no bare summarize
  3. Include cd-metadata blocks in the output file (see Step 8)
  4. Still write queries in Sentinel format (with let variables, 7d lookback) — adaptation to CD format happens at deployment time via the detection-authoring skill
Step 2: Check Local Query Library

Search for existing verified queries before writing from scratch. Use two complementary methods:

  1. Manifest lookup (domain/MITRE): Read .github/manifests/discovery-manifest.yaml and match by domain tag (e.g., identity, endpoint, email) or MITRE technique ID (e.g., T1078, T1566). Best when you know the security domain or ATT&CK technique.
  2. Targeted grep_search (table/keyword): grep_search for the specific table name (e.g., CloudAppEvents, OfficeActivity) or operation keyword (e.g., New-InboxRule, SecretGet) scoped to queries/** and .github/skills/**. The manifest lacks table-name and keyword fields — grep fills this gap.
  3. Check the Ad-Hoc Query Examples appendix in copilot-instructions.md

When to use which: Domain/technique known → manifest first. Table name/operation known → grep first. Both can be used together — manifest for breadth, grep for precision.

If a suitable query is found, adapt it and skip to Step 6. These queries encode known pitfalls and schema quirks.

Step 3: Get Table Schema (MANDATORY)
mcp_kql-search_get_table_schema("<table_name>")

Returns: category, description, all columns with data types, and example queries. Use this to verify column names and understand data types.

Step 4: Get Official Code Samples
mcp_microsoft-lea_microsoft_code_sample_search(
  query: "<table_name> <scenario description>",
  language: "kusto"
)

Include table name + scenario in the query (e.g., "EmailEvents phishing detection").

Step 5: Get Community Examples
mcp_kql-search_search_github_examples_fallback(
  table_name: "<table_name>",
  description: "<goal description>"
)

Also available: mcp_kql-search_search_kql_repositories to find KQL-focused repos.

Step 6: Generate Query

Combine insights: schema for column names, Learn for patterns, community for techniques.

Standalone queries rule: When generating MULTIPLE separate queries, each must start directly with the table name — never use shared let variables across separate queries (they run independently). Use let variables only within a single complex query.

Step 7: Validate and Test (MANDATORY)

Test queries against live data before presenting to the user.

  1. Convert Timestamp → TimeGenerated if adapting MS Learn examples for Sentinel
  2. Test via mcp_sentinel-data_query_lake or RunAdvancedHuntingQuery with | take 5
  3. Verify results are sensible — check for empty results (wrong table/time/filters)
  4. Fix schema mismatches or syntax errors, re-test
  5. Remove test limits, present to user

Common errors:

ErrorFix
Failed to resolve column 'Timestamp'Use TimeGenerated (Sentinel)
Failed to resolve column 'TimeGenerated'Use Timestamp (XDR AH)
Table not foundVerify with get_table_schema; try the other execution tool
expected string expressionAdd tostring() after mv-expand or parse_json
Query timeout / too many resultsAdd datetime filter + take or summarize

Fallback validation: mcp_kql-search_validate_kql_query("<query>") — syntax/schema check only, no live data.

Step 8: Format and Deliver Output

Single query: Provide directly in chat with brief explanation and expected results.

Multiple queries (3+): Create a markdown file in queries/<subfolder>/ with the standardized metadata header. This header is mandatory — build_manifest.py parses it to index the file for discovery by threat-pulse and other skills.

File naming: queries/<subfolder>/<topic>.md — e.g., queries/email/email_threat_detection.md

Required metadata header template (first 10 lines of every query file):

markdown
# <Descriptive Title>

**Created:** YYYY-MM-DD  
**Platform:** Microsoft Sentinel | Microsoft Defender XDR | Both  
**Tables:** <comma-separated exact KQL table names>  
**Keywords:** <comma-separated searchable terms — attack techniques, scenarios, field names>  
**MITRE:** <comma-separated technique IDs, e.g., T1098.001, T1136.003, TA0008>  
**Domains:** <comma-separated domain tags from the valid set below>  
**Timeframe:** Last N days (configurable)  

Valid domain tags: incidents, identity, spn, endpoint, email, admin, cloud, exposure

FieldPurposeParsed By
Tables:Exact KQL table names for grep_search discoverybuild_manifest.py (full manifest)
Keywords:Searchable terms for attack scenarios, operations, field namesbuild_manifest.py (full manifest)
MITRE:ATT&CK technique/tactic IDs for cross-referencingbuild_manifest.py (slim + full)
Domains:Domain tags for threat-pulse cross-referencingbuild_manifest.py (slim + full) — missing = validation error

After creating a new query file: Run python .github/manifests/build_manifest.py to regenerate the discovery manifest, then run python scripts/generate_tocs.py to auto-generate the Quick Reference TOC. The validator will flag any missing required fields.

Subfolder selection: Place files in the subfolder matching the primary data source: identity/, endpoint/, email/, network/, cloud/.

Include per-query documentation with Purpose, Thresholds, Expected Results, and Tuning guidance.

Heading format for TOC compatibility: The generate_tocs.py script auto-generates a Quick Reference TOC by scanning ### and ## Query headings that have a KQL code block within 40 lines. To ensure clean TOC output:

  • ✅ DO use ### Query N: <Title> or ## Query N: <Title> for query headings — the number prefix ensures proper TOC ordering
  • ✅ DO add a ## heading (e.g., ## Queries, ## Part A:, ## Hunts) immediately before the first ### Query N: if the file has preamble content (Overview, Table Selection, etc.). The TOC generator uses a --- → ## heading pair as its insertion anchor — without it, the script inserts the TOC at the bottom of the file.
  • ✅ DO start non-query section headings with a non-query keyword (e.g., ### Deployment, ### Tuning, ### References) — these are automatically filtered out by the TOC generator
  • ❌ DO NOT add a ## Quick Reference — Query Index heading or placeholder yourself — the script creates the heading and table. Pre-existing placeholders cause duplicated content and a broken file structure. (This is also enforced as Critical Rule #4 above.)
  • ❌ DO NOT use ### headings for non-query content that contains a KQL code block within 40 lines — the TOC generator uses KQL proximity to detect query headings and will incorrectly include them

Investigation shortcuts (optional): Query files can include an **Investigation shortcuts:** bulleted list between the ## Quick Reference heading and the TOC table. These document recommended query combos for common investigation scenarios (e.g., "Delivered phishing drill-down: Q2.4 + Q7.6 + Q3.3"). Shortcuts are preserved by generate_tocs.py across re-runs. Don't add them to new files — they're a refinement added after real investigations reveal which query combos work best together.

Show full SKILL.md (833 more words)Show less
CD-Aware Output

When CD intent is detected (Step 1), each query MUST include a <!-- cd-metadata --> HTML comment block. The full schema is in .github/skills/detection-authoring/SKILL.md under CD Metadata Contract.

Valid cd-metadata fields (exhaustive list):

FieldRequiredNotes
cd_readyAlwaystrue or false
scheduleIf cd_ready"0" (NRT), "1H", "3H", "12H", "24H"
categoryIf cd_readyMITRE tactic (e.g., Persistence, CredentialAccess)
titleOptionalDynamic title with {{Column}} placeholders (max 3 unique columns across title + description)
impactedAssetsIf cd_readyArray of type + identifier pairs
recommendedActionsOptionalTriage and response guidance string
adaptation_notesOptionalWhat needs to change for CD format

⛔ responseActions is NOT a valid cd-metadata field. It shares a name with the Graph API field that is explicitly prohibited in LLM-authored detections ("responseActions": [] is mandatory). Do not include it. Put incident response guidance in recommendedActions instead.

markdown
<!-- cd-metadata
cd_ready: true
schedule: "1H"
category: "Persistence"
title: "Suspicious Scheduled Task on {{DeviceName}}"
impactedAssets:
  - type: device
    identifier: DeviceName
recommendedActions: "Investigate the task XML and decode any encoded payloads."
adaptation_notes: "Remove let blocks, add mandatory columns"
-->

For queries not suitable for CD (baseline/statistical):

markdown
<!-- cd-metadata
cd_ready: false
adaptation_notes: "Statistical baseline — requires bare summarize, not CD-compatible"
-->

Summary table: Include a CD column in the Implementation Priority table: ✅ 1H / ❌.


Tool Quick Reference

ToolPurpose
mcp_kql-search_get_table_schemaGet table columns, types, example queries (Step 3)
mcp_microsoft-lea_microsoft_code_sample_searchOfficial MS Learn KQL samples — use language: "kusto" (Step 4)
mcp_kql-search_search_github_examples_fallbackCommunity KQL examples by table name (Step 5)
mcp_kql-search_search_kql_repositoriesFind GitHub repos with KQL collections
mcp_kql-search_validate_kql_querySyntax/schema validation (fallback for Step 7)
mcp_kql-search_find_columnFind which tables contain a specific column
mcp_kql-search_generate_kql_queryAuto-generate schema-validated query from natural language
mcp_sentinel-data_query_lakeExecute KQL against live Sentinel (primary validation)
mcp_sentinel-data_search_tablesDiscover tables using natural language

Schema Differences

PlatformTimestamp ColumnNotes
Sentinel / Log AnalyticsTimeGeneratedAll ingested logs
Defender XDR (Advanced Hunting)TimestampXDR-native tables only; Sentinel tables in AH still use TimeGenerated

Other common differences: Identity/UserPrincipalName (Sentinel) vs AccountUpn/AccountName (XDR); IPAddress (Sentinel) vs RemoteIP/LocalIP (XDR). Always verify with get_table_schema.

Sign-In Table Selection (High-Frequency Queries)

Sign-in queries are the most common query type. Use this decision rule:

ScenarioTableKey Differences
AH query, ≤30dEntraIdSignInEvents (single table)Covers both interactive + non-interactive. ErrorCode (int), AccountUpn, Country/City (direct strings), LogonType (JSON array — use has), Timestamp
Data Lake / >30dSigninLogs + AADNonInteractiveUserSignInLogs (union)ResultType (string), UserPrincipalName, parse_json(LocationDetails) needed for geo, IsInteractive (bool), TimeGenerated

Common mistakes:

  • Using union SigninLogs, AADNonInteractiveUserSignInLogs in AH queries — unnecessary, EntraIdSignInEvents covers both
  • Using LogonType == "nonInteractiveUser" — values are JSON arrays (["nonInteractiveUser"]), use has
  • Using ResultType on EntraIdSignInEvents — column is ErrorCode (int), not string

Full details: See copilot-instructions.md → Known Table Pitfalls → EntraIdSignInEvents (AH table preference rule) for complete column mapping and additional pitfalls.

Full table pitfalls (dynamic field parsing, immutable fields, table casing, deprecated tables) are documented in copilot-instructions.md under Known Table Pitfalls. Refer there for SecurityAlert.Status, AuditLogs.InitiatedBy, SigninLogs.DeviceDetail, and 20+ other table-specific gotchas.


Best Practices

Performance Optimization

Reference: KQL Best Practices — Microsoft Learn

1. Filter on datetime columns first

The most important optimization. Datetime predicates use efficient index-based shard elimination, skipping entire data partitions without scanning.

kql
// ✅ Correct — datetime first, then selective string filters
SigninLogs
| where TimeGenerated > ago(7d)
| where UserPrincipalName =~ "user@domain.com"

// ❌ Wrong — string filter before datetime
SigninLogs
| where UserPrincipalName =~ "user@domain.com"
| where TimeGenerated > ago(7d)
2. Use has over contains for token matching

has uses the term index for full-token lookup. contains scans every character — dramatically slower on large tables.

kql
// ✅ Faster — term-level index lookup
| where UserPrincipalName has "admin"

// ❌ Slower — full substring scan
| where UserPrincipalName contains "admin"

Use contains only when you genuinely need substring matching (e.g., fragments inside URL paths).

3. Prefer case-sensitive operators

Case-sensitive comparisons (==, in, has_cs) are faster than case-insensitive (=~, in~, has). Use case-insensitive only when casing is unpredictable.

kql
// ✅ Faster — ActionType, Operation, OfficeWorkload have consistent casing
| where ActionType == "LogonFailed"
| where Operation in ("New-InboxRule", "Set-InboxRule")
| where OfficeWorkload == "Exchange"

// 🔵 Use =~ only when casing varies (e.g., user-entered UPNs)
| where UserPrincipalName =~ "user@domain.com"

Common fields with consistent casing (always use == / in): ActionType, Operation, OfficeWorkload, EventID, ResultType, DeliveryAction, EmailDirection, LogonType, Severity, Status, Classification.

4. Filter tables BEFORE joins

Pre-filter both sides of a join to reduce data volume. Move where clauses into subqueries.

kql
// ✅ Correct — filter KB table before joining
DeviceTvmSoftwareVulnerabilities
| join kind=inner (
    DeviceTvmSoftwareVulnerabilitiesKB
    | where IsExploitAvailable == true
    | where CvssScore >= 8.0
) on CveId

// ❌ Wrong — joins full tables, filters after
DeviceTvmSoftwareVulnerabilities
| join kind=inner DeviceTvmSoftwareVulnerabilitiesKB on CveId
| where IsExploitAvailable == true

Join sizing rules:

  • Smaller table on the left (or hint.strategy=broadcast when left is small)
  • in instead of left semi join for single-column filtering
  • lookup instead of join when right side is small (<50 MB)
  • hint.shufflekey=<key> when both sides are large with high-cardinality join key
5. Use materialize() for multi-referenced let statements

Without materialize(), the engine may recompute the let expression each time it's referenced.

kql
// ✅ Computed once, reused twice
let SprayFailures = materialize(
    EntraIdSignInEvents
    | where Timestamp > ago(7d)
    | where ErrorCode in (50126, 50053, 50057)
    | summarize FailedAttempts = count(), TargetUsers = dcount(AccountUpn)
        by SourceIP = IPAddress
    | where TargetUsers >= 5);
6. Narrow arg_max to only needed columns

arg_max(TimeGenerated, *) materializes every column. Specify only what you use.

kql
// ✅ Only 5 columns materialized
SecurityAlert
| where TimeGenerated > ago(30d)
| summarize arg_max(TimeGenerated, Entities, Tactics, Techniques, AlertName, AlertSeverity) by SystemAlertId

// ❌ Materializes all 30+ columns
SecurityAlert
| summarize arg_max(TimeGenerated, *) by SystemAlertId
7. Pre-filter before JSON parsing

For rare key/value lookups in dynamic columns, use has to eliminate rows before expensive parse_json().

kql
// ✅ Term filter first, JSON parse on survivors
AuditLogs
| where tostring(TargetResources) has "MyApp"
| extend Target = tostring(parse_json(tostring(TargetResources[0])).displayName)
| where Target == "MyApp"
8. Filter on table columns, not calculated columns

Filtering on native columns enables index usage; calculated columns force full scans.

kql
// ✅ Filter on native column
SecurityEvent | where EventID == 4625

// ❌ Filter on calculated column
SecurityEvent | extend Cat = case(EventID == 4625, "Fail", ...) | where Cat == "Fail"
9. Project only needed columns early

Drop unnecessary columns before expensive operators (join, summarize, mv-expand) to reduce memory and shuffling.

10. Use take or summarize to limit results

Unbounded queries on large tables consume excessive resources.

11. Platform-specific dynamic column access

In AH, AuditLogs.InitiatedBy and TargetResources are native dynamic — use direct dot-notation. In Data Lake, they may be string-typed requiring parse_json().

kql
// ✅ Advanced Hunting — direct access
| extend Actor = tostring(InitiatedBy.user.userPrincipalName)

// ✅ Data Lake — parse_json wrapper
| extend Actor = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)

// 🔵 Safe in both — stringify full field
| where tostring(InitiatedBy) has "user@domain.com"
Security and Privacy
  • Limit sensitive data exposure — redact PII with strcat(substring(UPN, 0, 3), "***") when appropriate
  • Filter early — reduce dataset before projecting sensitive columns
Code Quality
  • Comments — explain what the query does and why key filters are applied
  • Meaningful variable names — let SuspiciousIPs = ... not let x = ...
  • Standalone queries — when providing multiple separate queries, each MUST start with the table name directly. Never share let variables across queries the user will run independently

Dynamic Type Casting

Common "expected string expression" error: After mv-expand, parse_json, or split, values are dynamic — string functions fail. Always convert first:

kql
// After mv-expand
| mv-expand AuthDetails
| extend AuthMethod = tostring(AuthDetails.authenticationMethod)

// After split
| extend Parts = split(UPN, "@")
| extend Domain = tostring(Parts[1])

Rule of thumb: If you get "expected string expression", add tostring().

© SCStelz, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .github/skills/kql-query-authoring of SCStelz/security-investigator.

Open the folder on GitHubat commit 51e1385

Compare with similar skills

Kql Query Authoring next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Kql Query Authoring compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Kql Query Authoring this skillSCStelz/security-investigator249—~5.7kAutomated safety check: PassMIT
Apex Azure Kustojonathan-vella/apex217—~984Automated safety check: PassMIT
Kqlmicrosoft/fabric-rti-mcp131—~6.2kAutomated safety check: PassMIT
Azure Kustomicrosoft/GitHub-Copilot-for-Azure2551 repos~2.1kAutomated safety check: PassMIT
Kqlmicrosoft/skills3.1k—~4.7kAutomated safety check: PassMIT
Posthog Product Health Auditboardsesh/boardsesh164—~2.1kAutomated safety check: PassApache-2.0

Similar skills

  • Apex Azure Kusto

    jonathan-vella/apex

    ANALYSIS SKILL — Query and analyze data in Azure Data Explorer (Kusto/ADX) using KQL.

    217 GitHub stars~984 tokensUpdated today
    Data & AnalyticsAuto-check passed
  • Kql

    microsoft/fabric-rti-mcp

    Official

    KQL language expertise for writing correct, efficient Kusto queries using the Fabric RTI MCP tools.

    131 GitHub stars~6.2k tokensUpdated 9 days ago
    Data & AnalyticsAuto-check passed
  • Azure Kusto

    microsoft/GitHub-Copilot-for-Azure

    Official

    Query and analyze data in Azure Data Explorer (Kusto/ADX) using KQL for log analytics, telemetry, and time series analysis.

    255 GitHub starsUsed in 1 repo~2.1k tokens
    Data & AnalyticsAuto-check passed
  • Kql

    microsoft/skills

    Official

    KQL language expertise for writing correct, efficient Kusto Query Language queries.

    3.1k GitHub stars~4.7k tokensUpdated today
    Data & AnalyticsAuto-check passed
  • Posthog Product Health Audit

    boardsesh/boardsesh

    Mine Boardsesh's PostHog telemetry (error tracking, session recordings, product analytics) with a multi-agent workflow, then file verified, deduplicated, severity-labelled GitHub issues.

    164 GitHub stars~2.1k tokensUpdated today
    Data & AnalyticsAuto-check passed
  • Cao Workflow

    awslabs/cli-agent-orchestrator

    Official

    Author and run CAO Python workflow scripts — multi-step, parameterized, fan-out orchestrations executed by cao workflow run.

    1.4k GitHub stars~4.5k tokensUpdated today
    Data & AnalyticsAuto-check passed

More from SCStelz/security-investigator

All 22 skills in this repo
  • Ca Policy Investigation

    SCStelz/security-investigator

    A skill your agent uses when asked to investigate Conditional Access policy changes, sign-in failures related to CA policies (error codes 53000, 50074, 530032), or suspected policy…

    249 GitHub stars~3.8k tokensUpdated yesterday
    Auto-check passed
  • Context Memory Review

    SCStelz/security-investigator

    Weekly review of an investigation tenant-context memory file against the most recent SOC scan reports (e.g.

    249 GitHub stars~3.7k tokensUpdated yesterday
    Auto-check passed
  • Heatmap Visualization

    SCStelz/security-investigator

    A skill your agent uses when asked to create heatmaps, visualize patterns over time, show activity grids, or display aggregated data in a matrix format.

    249 GitHub stars~3.4k tokensUpdated yesterday
    Auto-check passed
  • AI Agent Activity

    SCStelz/security-investigator

    Report/investigate RUNTIME ACTIVITY of AI agents (Agent 365 / Copilot Studio / M365 Copilot / Work IQ) — agents used, tools/connectors, channels, tokens, prompt/reply content, and Prompt Shield…

    249 GitHub stars~17k tokensUpdated yesterday
    Auto-check passed
  • AI Agent Posture

    SCStelz/security-investigator

    Audit or report on AI agent security posture across Copilot Studio, Microsoft 365 Copilot, Microsoft Foundry, and third-party agents.

    249 GitHub stars~21k tokensUpdated yesterday
    Auto-check passed
  • App Registration Posture

    SCStelz/security-investigator

    Audit Entra ID app registration and service principal security posture.

    249 GitHub stars~21k tokensUpdated yesterday
    Auto-check passed

Questions about Kql Query Authoring

What does Kql Query Authoring do?

A skill your agent uses when asked to write, create, or help with KQL (Kusto Query Language) queries for Microsoft Sentinel, Defender XDR, or Azure Data Explorer. Kql Query Authoring is an agent skill from SCStelz/security-investigator. Use this skill when asked to write, create, or help with KQL (Kusto Query Language) queries for Microsoft Sentinel, Defender XDR, or Azure Data Explorer.

When should I use Kql Query Authoring?

Kql Query Authoring fits situations like: help with KQL (Kusto Query Language) queries for Microsoft Sentinel; azure Data Explorer; keywords like write KQL; create KQL query.

How do I install Kql Query Authoring in Claude Code?

Run `npx skills add SCStelz/security-investigator --skill kql-query-authoring -a claude-code`. Or copy the skill folder (.github/skills/kql-query-authoring in SCStelz/security-investigator) into .claude/skills/kql-query-authoring in your project. Claude Code loads it when a task matches its description.

How do I install Kql Query Authoring in Codex?

Run `npx skills add SCStelz/security-investigator --skill kql-query-authoring -a codex`. Or copy the skill folder (.github/skills/kql-query-authoring in SCStelz/security-investigator) into .agents/skills/kql-query-authoring in your project. Codex loads it when a task matches its description.

Can I use Kql Query Authoring in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add SCStelz/security-investigator --skill kql-query-authoring -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/kql-query-authoring, .gemini/skills/kql-query-authoring, .github/skills/kql-query-authoring and .opencode/skills/kql-query-authoring in your project.

What does Kql Query Authoring need to run?

Going by SKILL.md and its folder, Kql Query Authoring needs the command-line tools its instructions call (python and npm). Our summary lists: Python 3; Node.js.

Does Kql Query Authoring access the network?

SKILL.md names 3 domains. As links in the text: npmjs.com, github.com and learn.microsoft.com. This is read from the text; nothing was executed.

Is Kql Query Authoring safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Kql Query Authoring use?

Kql Query Authoring is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Kql Query Authoring use?

About 5.7k tokens (SKILL.md is roughly 23k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Kql Query Authoring?

Skills that share tags, products or a category with Kql Query Authoring: Apex Azure Kusto (jonathan-vella/apex, 217 stars), Kql (microsoft/fabric-rti-mcp, 131 stars), Azure Kusto (microsoft/GitHub-Copilot-for-Azure, 255 stars) and Kql (microsoft/skills, 3.1k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Kql Query Authoring?

SCStelz (a GitHub user) maintains it in SCStelz/security-investigator, which has 249 GitHub stars. The repository holds 22 skills in this directory. The repository was last updated on October 8, 2026.

Source: SCStelz/security-investigator on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.