Provides KQL expertise for writing, debugging, and optimizing Kusto queries via MCP.

Install

mkdir -p .claude/skills/kql && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/13416" && unzip -o skill.zip -d .claude/skills/kql && rm skill.zip

Installs to .claude/skills/kql

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

KQL language expertise for writing correct, efficient Kusto queries using the Fabric RTI MCP tools. Covers syntax gotchas, join patterns, dynamic types, datetime pitfalls, regex patterns, serialization, memory management, result-size discipline, and advanced functions (geo, vector, graph). USE THIS SKILL whenever writing, debugging, or reviewing KQL queries — even simple ones — because the gotchas section prevents the most common errors that waste tool calls and cause expensive retry cascades. Trigger on: KQL, Kusto, ADX, Azure Data Explorer, Fabric Eventhouse, log analysis, data exploration, time series, anomaly detection, summarize, where clause, join, extend, project, let statement, parse operator, extract function, any mention of pipe-forward query syntax.
770 charsno explicit “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)
Intermediate

Key capabilities

  • Execute KQL queries on a database
  • Execute Kusto management commands
  • List entities (databases, tables, functions, graphs)
  • Get schema for databases and specific entities
  • Get sample data from tables or other entities
  • Execute graph queries using snapshots or transient graphs

How it works

The skill uses Fabric RTI MCP tools to execute KQL queries and management commands, providing access to Kusto functionality for data exploration, schema description, and data sampling.

Inputs & outputs

You give it
KQL query or Kusto management command, cluster URI, and database name
You get back
Query results, command output, schema details, sample data, or graph query results

When to use kql

  • Write optimized KQL queries
  • Debug Kusto query performance
  • Explore database schema
  • Analyze time-series data

About this skill

KQL Mastery

Try it yourself: All examples in this skill can be run against the public help cluster: https://help.kusto.windows.net, database Samples (contains StormEvents, SimpleGraph_Nodes/Edges, nyc_taxi, and more).

1. Running KQL with Fabric RTI MCP

Fabric RTI MCP exposes Kusto functionality as MCP tools. Authentication is handled transparently using Azure Identity.

Available tools

ToolPurpose
kusto_queryExecute a KQL query on a database
kusto_commandExecute a management command (.show, .create, etc.)
kusto_list_entitiesList databases, tables, external tables, materialized views, functions, graphs
kusto_describe_databaseGet schema for all entities in a database
kusto_describe_database_entityGet schema for a specific entity (table, function, etc.)
kusto_sample_entityGet sample data from a table or other entity
kusto_graph_queryExecute a graph query using snapshots or transient graphs
kusto_ingest_inline_into_tableIngest inline CSV data into a table
kusto_known_servicesList configured Kusto services
kusto_get_shotsRetrieve semantically similar shots from a shots table
kusto_deeplink_from_queryBuild a deeplink URL to open a query in the web explorer
kusto_show_queryplanGet the execution plan for a query without running it
kusto_diagnosticsGet a best-effort cluster health and capacity summary

Query vs management commands

KQL has two execution planes, each with its own MCP tool:

PlaneToolStarts withExamples
Querykusto_queryTable name, let, print, datatableStormEvents | where State == "TEXAS"
Managementkusto_command.show, .create, .set, .drop, .alter.show tables, .show table T schema

Basic usage

# Query plane — use kusto_query
kusto_query(
    cluster_uri="https://help.kusto.windows.net",
    database="Samples",
    query="StormEvents | summarize count() by EventType | top 5 by count_ desc"
)

# Management plane — use kusto_command
kusto_command(
    cluster_uri="https://help.kusto.windows.net",
    database="Samples",
    command=".show tables"
)

# Schema exploration — use kusto_describe_database or kusto_describe_database_entity
kusto_describe_database(
    cluster_uri="https://help.kusto.windows.net",
    database="Samples"
)

# Sample data — use kusto_sample_entity
kusto_sample_entity(
    cluster_uri="https://help.kusto.windows.net",
    database="Samples",
    entity_name="StormEvents",
    entity_type="table",
    sample_size=5
)

# Graph queries — use kusto_graph_query
kusto_graph_query(
    cluster_uri="https://mycluster.kusto.windows.net",
    database="MyDB",
    graph_name="MyGraph",
    query="| graph-match (node) project labels=labels(node)"
)

# Deeplinks — use kusto_deeplink_from_query
kusto_deeplink_from_query(
    cluster_uri="https://help.kusto.windows.net",
    database="Samples",
    query="StormEvents | count"
)

Exploration workflow

When encountering a new cluster or database:

  1. List entities: kusto_list_entities(cluster_uri, entity_type="tables", database="MyDB")
  2. Get schema: kusto_describe_database_entity(entity_name="MyTable", entity_type="table", cluster_uri=..., database=...)
  3. Sample data: kusto_sample_entity(entity_name="MyTable", entity_type="table", cluster_uri=..., sample_size=5)
  4. Count rows: kusto_query(query="MyTable | count", cluster_uri=..., database=...)
  5. Run analysis: kusto_query(query="MyTable | where ... | summarize ...", cluster_uri=..., database=...)

2. Dynamic Type Discipline

KQL's dynamic type is flexible but strict in certain contexts. A common mistake is using a dynamic column in summarize by, order by, or join on without casting.

The rule: Any time you use a dynamic-typed column in by, on, or order by, wrap it in an explicit cast.

// ❌ ERROR: "Summarize group key 'Partners' is of a 'dynamic' type"
| summarize count() by Partners

// ✅ FIX
| summarize count() by tostring(Partners)
// ❌ ERROR: "order operator: key can't be of dynamic type"
| order by Area desc

// ✅ FIX
| order by tostring(Area) desc
// ❌ ERROR in join: dynamic join key
| join kind=inner other on $left.Area == $right.Area

// ✅ FIX — cast both sides
| extend Area_str = tostring(Area)
| join kind=inner (other | extend Area_str = tostring(Area)) on Area_str

Self-correction: When you see "is of a 'dynamic' type" in an error, add tostring(), tolong(), or todouble().

3. Join Patterns & Pitfalls

KQL joins have constraints that differ from SQL.

Equality only

KQL join conditions support only ==. No <, >, !=, or function calls in join predicates.

// ❌ ERROR: "Only equality is allowed in this context"
| join on geo_distance_2points(a.Lat, a.Lon, b.Lat, b.Lon) < 1000

// ✅ WORKAROUND — pre-bucket into spatial cells, then join on cell ID
| extend cell = geo_point_to_s2cell(Lon, Lat, 8)
| join kind=inner (other | extend cell = geo_point_to_s2cell(Lon, Lat, 8)) on cell

For range joins, pre-bin values: | extend bin_val = bin(Value, 100), then join on bin_val.

Left/right attribute matching

Both sides of a join on clause must reference column entities only — not expressions, not aggregates.

// ❌ ERROR: "for each left attribute, right attribute should be selected"
| join kind=inner other on $left.col1

// ✅ FIX — specify both sides explicitly
| join kind=inner other on $left.col1 == $right.col1

Cardinality check before large joins

Always check cardinality before joining tables with >10K rows. A cross-join explosion was the source of the single E_RUNAWAY_QUERY error (25K × 195 = potential 4.8M rows).

// Before joining, check how many rows each side contributes
TableA | summarize dcount(JoinKey)  // → 25,000? Too many for an unconstrained join
TableB | summarize dcount(JoinKey)  // → 195? OK if filtered first

4. Regex in KQL

KQL handles regex natively — no need for Python.

The extract_all gotcha

Unlike Python's re.findall(), KQL's extract_all requires capturing groups in the regex:

// ❌ ERROR: "extractall(): argument 2 must be a valid regex with [1..16] matching groups"
| extend words = extract_all(@"[a-zA-Z]{3,}", Text)

// ✅ FIX — add parentheses around the pattern
| extend words = extract_all(@"([a-zA-Z]{3,})", Text)

Regex toolkit — don't fall back to Python

FunctionUse caseExample
extract(regex, group, source)Single matchextract(@"User '([^']+)'", 1, Msg)
extract_all(regex, source)All matches (needs ())extract_all(@"(\w+)", Text)
parseStructured extractionparse Msg with * "User '" Sender "' sent" *
matches regexBoolean filterwhere Url matches regex @"^https?://"
replace_regexFind and replacereplace_regex(Text, @"\s+", " ")

5. Serialization Requirements

Window functions need serialized (ordered) input.

// ❌ ERROR: "Function 'row_cumsum' cannot be invoked. The row set must be serialized."
| summarize Online = sum(Direction) by bin(Timestamp, 5m)
| extend CumulativeOnline = row_cumsum(Online)

// ✅ FIX — add | serialize (or | order by, which implicitly serializes)
| summarize Online = sum(Direction) by bin(Timestamp, 5m)
| order by Timestamp asc
| extend CumulativeOnline = row_cumsum(Online)

Functions requiring serialization: row_number(), row_cumsum(), prev(), next(), row_window_session().

6. Memory-Safe Query Patterns

The most common memory error. Caused by scanning too much data without pre-filtering.

The progression of safety

Safest ──────────────────────────────────────────────── Most dangerous
| count    | take 10    | where + summarize    | summarize (no filter)    | full scan

Rules for large tables (>1M rows)

  1. Always start with | count to understand table size
  2. Always | where before | summarize — filter time range, partition key, or category first
  3. Never dcount() on high-cardinality columns without pre-filtering
  4. Check join cardinality before executing (see Section 3)
  5. Use materialize() for subqueries referenced multiple times
// ❌ OUT OF MEMORY — 24M rows, no filter, dcount on every column
Consumption
| summarize dcount(Consumed), count() by Timestamp, HouseholdId, MeterType
| where dcount_Consumed > 1

// ✅ SAFE — filter first, then aggregate
Consumption
| where Timestamp between (datetime(2023-04-15) .. datetime(2023-04-16))
| summarize dcount(Consumed) by HouseholdId, MeterType
| where dcount_Consumed > 1

When you see E_LOW_MEMORY_CONDITION

The query touched too much data. Your options:

  • Add | where filters (time range, partition key)
  • Reduce the number of by columns in summarize
  • Break into smaller time windows and union results
  • Use | sample 10000 for exploratory work instead of full scans

When you see E_RUNAWAY_QUERY

A join or aggregation produced too many output rows. Check join cardinality — one or both sides is too large.

7. Result Size Discipline

Large results slow down analysis. Prevention:

Query typeSafeguard
ExploratoryAlways end with | take 10 or | take 20
AggregationUse | top 20 by ... not unbounded summarize
Wide rows (vectors, JSON)| project only needed columns
make_list() / make_set()Avoid on high-cardinality groups (produces huge cells)
Unknown sizeRun | count first

The vector trap: Tables with embedding columns (1536-dim float arrays) produce ~30KB per row. Even | take 20 yields 600KB. Always | project away vector columns unless you specifically need them.

With MCP tools: Use `kusto_sample_ent


Content truncated.

When not to use it

  • When KQL queries are not required for data analysis
  • When not interacting with Fabric RTI MCP tools
  • When the user is not interested in Kusto-specific syntax or performance optimization

Limitations

  • KQL join conditions support only '==' for equality.
  • Dynamic-typed columns require explicit casting in 'by', 'on', or 'order by' clauses.
  • Query IDs are cached and persist across script calls.

How it compares

This skill provides direct access to Kusto functionality through specialized MCP tools, handling authentication transparently, unlike manual KQL execution or generic database interaction.

Compared to similar skills

kql side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
kql (this skill)04moNo flagsIntermediate
data-quality-monitor-designer01moNo flagsAdvanced
kql04moNo flagsIntermediate
sql-queries185moNo flagsIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

More by microsoft

View all by microsoft

You might also like

data-quality-monitor-designer

nguyenpv1980-wq

Design data-quality monitoring for production pipelines and stores — checks across the six dimensions (freshness, completeness/volume, uniqueness, validity, consistency/referential integrity, distribution drift), placed at the right pipeline stage (ingest, transform, serving), each with severity, an

00

kql

microsoft

KQL language expertise for writing correct, efficient Kusto Query Language queries. Covers syntax gotchas, join patterns, dynamic types, datetime pitfalls, regex patterns, serialization, memory management, result-size discipline, and advanced functions (geo, vector, graph). USE THIS SKILL whenever w

00

sql-queries

anthropics

Write correct, performant SQL across all major data warehouse dialects (Snowflake, BigQuery, Databricks, PostgreSQL, etc.). Use when writing queries, optimizing slow SQL, translating between dialects, or building complex analytical queries with CTEs, window functions, or aggregations.

1888

senior-data-engineer

davila7

World-class data engineering skill for building scalable data pipelines, ETL/ELT systems, and data infrastructure. Expertise in Python, SQL, Spark, Airflow, dbt, Kafka, and modern data stack. Includes data modeling, pipeline orchestration, data quality, and DataOps. Use when designing data architectures, building data pipelines, optimizing data workflows, or implementing data governance.

2179

powerbi-modeling

github

Power BI semantic modeling assistant for building optimized data models. Use when working with Power BI semantic models, creating measures, designing star schemas, configuring relationships, implementing RLS, or optimizing model performance. Triggers on queries about DAX calculations, table relationships, dimension/fact table design, naming conventions, model documentation, cardinality, cross-filter direction, calculation groups, and data model best practices. Always connects to the active model first using power-bi-modeling MCP tools to understand the data structure before providing guidance.

1161

data-quality-frameworks

wshobson

Implement data quality validation with Great Expectations, dbt tests, and data contracts. Use when building data quality pipelines, implementing validation rules, or establishing data contracts.

628

Search skills

Search the agent skills registry