---
canonical_url: "https://docs.composio.dev/toolkits/clickhouse_mcp"
markdown_url: "https://docs.composio.dev/toolkits/clickhouse_mcp.md"
product: "platform"
version: null
last_updated: null
---

# ClickHouse MCP

Manage and query ClickHouse Cloud observability data through the managed ClickStack MCP server.

- **Category:** databases
- **Auth:** DCR_OAUTH
- **Composio-managed OAuth available?** No
- **Tools:** 32
- **Triggers:** 0
- **Slug:** `CLICKHOUSE_MCP`
- **Version:** 20260910_00

## Tools

### Clickstack delete dashboard

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_DELETE_DASHBOARD`

Permanently delete a dashboard by ID. Also removes any alerts attached to its tiles. Use clickstack_get_dashboard (without an ID) to list available dashboard IDs.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | string | Yes | Dashboard ID to delete. |

### Clickstack delete source

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_DELETE_SOURCE`

Permanently delete a data source by ID. Other sources may reference it (e.g. a trace source linked to a log source) — those links are left dangling, so check dependencies first. Use clickstack_list_sources to find available source IDs.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | string | Yes | Source ID to delete. |

### Clickstack delete webhook

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_DELETE_WEBHOOK`

Permanently delete a webhook by ID. Blocked while any alert still references it — reassign or delete those alerts first. Use clickstack_get_webhook to list available webhook IDs.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | string | Yes | Webhook ID to delete. |

### Clickstack describe metric

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_DESCRIBE_METRIC`

DRILL-DOWN: Use after clickstack_list_metrics (or after a clickstack_describe_source sample) to get attribute keys, sampled values, unit, and description for a specific (metricName, kind) pair. Attribute keys vary per metric — not per source — so always call this before clickstack_timeseries / clickstack_table for any metric you've never queried.

REQUIRES `kind` — pass the gauge/sum/histogram/exponential histogram/summary value emitted alongside the metric name by clickstack_list_metrics or clickstack_describe_source. A metric name can legitimately live in more than one kind (e.g. "container.cpu.usage" appears in both gauge and sum); call this tool once per kind you care about.

kind:"summary" is accepted for discovery (attribute keys, sampled values, unit, description), but summary metrics cannot be queried with clickstack_timeseries / clickstack_table — use clickstack_sql against the table in the source's metricTables.summary.

attributeValuesMeta on each kind reports sampledKeys (queried for values) and truncatedKeys (skipped by the per-call sampling cap) — a key in truncatedKeys was never queried, so query it directly if you need its values.

Workflow: clickstack_list_sources → clickstack_list_metrics → clickstack_describe_metric → clickstack_timeseries|clickstack_table.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `kind` | string ("gauge" | "sum" | "histogram" | "exponential histogram" | "summary") | Yes | Metric kind: "gauge" &#124; "sum" &#124; "histogram" &#124; "exponential histogram" &#124; "summary". Required. Discover via clickstack_list_metrics (which returns name + kind per entry) or clickstack_describe_source (which groups metric-name samples by kind). A metric name can legitimately live in more than one kind (e.g. "container.cpu.usage" appears in both gauge and sum) — pick the kind matching the value you want to query. |
| `endTime` | string | No | End of the discovery window as ISO 8601. Default: now. |
| `sourceId` | string | Yes | Source ID. Must reference a metric source — get IDs from clickstack_list_sources. |
| `startTime` | string | No | Restrict discovery to data points after this ISO 8601 timestamp. Default: 24 hours before endTime (or now). |
| `metricName` | string | Yes | OTel metric name to describe (e.g. "system.cpu.utilization", "http.server.request.duration"). Discover via clickstack_list_metrics or clickstack_describe_source. |
| `sampleValues` | boolean | No | When true (default), sample up to 10 distinct values per attribute key for the top 12 attributes. Set false to skip value sampling for faster responses on high-cardinality metrics. |

### Clickstack describe source

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_DESCRIBE_SOURCE`

CALL THIS BEFORE WRITING QUERIES — prevents unknown-column errors.

Returns the full column schema, map-attribute keys, and sampled low-cardinality values (e.g. SeverityText, StatusCode, ServiceName) for a single data source.

Workflow: call clickstack_list_sources first to get source IDs, then call this tool for each source you plan to query.

Returns:
- columns[]: column name, ClickHouse type, and JS type
- mapAttributeKeys: discovered keys in Map columns (e.g. SpanAttributes, ResourceAttributes)
- lowCardinalityValues: sampled values for LowCardinality(String) columns (SeverityText, StatusCode, ServiceName, etc.) — use these in filters instead of guessing
- mapAttributeValues: sampled top values for the most common map attribute keys (e.g. ResourceAttributes['service.name'] top values) — requires rollup tables
- requiredSourceFilters: when present, every query against this source MUST pass `sourceFilters` for each listed column. Sample values for each are returned as `sourceFilterValues` (capped); use clickstack_get_source_filter_values for the full paginated list.

Cost: one describe call prevents 3–5 exploratory queries against non-existent columns.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `sourceId` | string | Yes | The source ID to describe. Get this from clickstack_list_sources. |

### Clickstack emerging signals

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_EMERGING_SIGNALS`

Detect what is NEW or GONE between an earlier baseline window and a current window — log/event patterns that emerged, ramped up, or stopped. This answers "what changed / what is novel?" — NOT "what attribute value differs?".

USE THIS for status checks, health reports, post-deploy diffs, and any "call out anything new or worth a closer look" question. It mines event patterns (Drain) in BOTH windows and set-differences them:
  - emerging:    patterns whose share of the window is >= minShareRatio× higher now than in baseline (includes brand-new templates absent from baseline)
  - disappeared: patterns that were common in baseline but >= minShareRatio× rarer (or absent) now

WHY NOT clickstack_event_deltas: event_deltas compares ATTRIBUTE VALUE DISTRIBUTIONS between two row groups (e.g. "region shifted toward eu-west"). It CANNOT surface a brand-new log template or a new endpoint that simply did not exist before — a novel signal has no baseline distribution to shift. Use emerging_signals for novelty/emergence (set membership over time); use event_deltas for "what is different about these rows" (distribution shift within a shared population).

Requires sourceId — call clickstack_list_sources / clickstack_describe_source first. Provide two non-overlapping windows: the current window to characterize and an earlier baseline. Typically baselineEndTime == currentStartTime.

CALIBRATION: routine variance is not novelty. A pattern that merely wobbled in volume is NOT emerging; only report shifts past minShareRatio. An empty emerging list is a valid, informative answer ("nothing novel") — do not manufacture findings.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `topN` | number | No | Max emerging + disappeared patterns to return each. Default 20. |
| `where` | string | No | Row filter.  FIRST: pick a language (whereLanguage):   Lucene (default): Column:value          e.g. level:error   Lucene map attrs: Column.key:value      e.g. SpanAttributes.http.method:GET   SQL:              Column = 'value'       e.g. StatusCode = 500   SQL map attrs:    SpanAttributes['key'] = 'value'  MAP ATTRIBUTES:   Lucene uses DOT notation:    SpanAttributes.http.method:GET   SQL uses BRACKET notation:   SpanAttributes['http.method'] = 'GET'  WRONG: SpanAttributes['key']:value   (Lucene cannot parse bracket syntax) WRONG: level = "error"               (SQL syntax with whereLanguage:"lucene")  SUBSTRING TRAP: Lucene field:value matches ANY row containing "value" as a substring, not exact equality.   SpanKind:Server matches "Server", "ServerStreaming", "InternalServer", etc.   For exact match, use SQL: SpanKind = 'Server' |
| `sourceId` | string | Yes | Source ID (required). Call clickstack_list_sources to find available sources. |
| `sampleSize` | number | No | Rows sampled per window for pattern mining. Default 10000. |
| `minShareRatio` | number | No | How many times more frequent (by share of window) a pattern must be in the current window vs baseline to count as EMERGING (and vice-versa for DISAPPEARED). Default 3× — a pattern must have tripled its share to qualify as a shift, not just noise. |
| `sourceFilters` | object | No | Required filter values for sources whose configuration declares `requiredSourceFilters`. Keyed by column name (the values returned by clickstack_get_source_filter_values or the sampled `sourceFilterValues` from clickstack_describe_source).  FORMAT: { "<columnName>": ["<value1>", "<value2>"] } Multiple values for one column become a `column IN (...)` predicate.  ALL OPTION: a column may instead be pinned to every value with { "<columnName>": { "all": true } }. This is ONLY accepted for columns whose source marks them `allowAll: true` (see requiredSourceFilters in clickstack_list_sources / clickstack_describe_source, or the `allowAll` flag from clickstack_get_source_filter_values); using it on any other column is rejected. Prefer explicit values when they're known.  REQUIRED for any source whose summary in clickstack_list_sources or clickstack_describe_source includes `requiredSourceFilters`. Every column listed there must have at least one value set. Queries that omit a required column are rejected with an error telling you which columns are missing and how to recover.  PARTIAL FILTERS: when you supply values for some but not all required columns, the tool will try to auto-resolve the missing columns by looking up values cross-pruned against the ones you provided — if any missing column has exactly one remaining value it is filled in automatically. The resolved set is echoed back as `resolvedSourceFilters` in the response so you can supply it explicitly on the next call.  Ignored for sources without requiredSourceFilters: pass nothing. |
| `whereLanguage` | string ("lucene" | "sql") | No | Query language for the "where" filter. Default: lucene. IMPORTANT: the syntax in "where" MUST match "whereLanguage".   Lucene (default): Column:value, Column.mapKey:value, Column:>100   SQL:              Column = 'value', SpanAttributes['key'] = 'value'  Lucene supports comparisons (>= > < <=), wildcards (field:val*), ranges ([1 TO 5]), and map attributes via dot notation. Use "sql" for IN(...) lists, complex expressions, or function calls. IMPORTANT: Lucene field:value is a SUBSTRING match (ilike), not exact equality. field:val* is prefix-within-substring, not a true prefix match. For exact matching or reliable wildcards, use SQL: WHERE field = 'value' or WHERE field LIKE 'val%'.  Common mistake: writing Column:value (Lucene) but setting whereLanguage to "sql". If your filter uses colon syntax, leave whereLanguage as "lucene" (the default). |
| `bodyExpression` | string | No | Column expression to mine patterns from. Auto-detected from the source if omitted (Body for logs, SpanName for traces). Example: "Body", "SpanName", "SpanAttributes['http.url']". |
| `currentEndTime` | string | Yes | End of the CURRENT / report window (ISO 8601). Required — pass the current timestamp for a window ending now. |
| `baselineEndTime` | string | Yes | End of the BASELINE window (ISO 8601). Typically equals currentStartTime (the baseline ends where the current window begins). |
| `currentStartTime` | string | Yes | Start of the CURRENT / report window (ISO 8601). This is the window you want to characterize (e.g. the last hour). |
| `baselineStartTime` | string | Yes | Start of the BASELINE window (ISO 8601) — an earlier "normal" period to compare against. Should NOT overlap the current window. |

### Clickstack event deltas

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_EVENT_DELTAS`

Rank the properties of two row groups (logs or trace spans) by how much their value distributions differ. Same algorithm as the in-app Event Deltas view (DBDeltaChart). High-cardinality fields (IDs, request IDs, timestamps) are filtered out by default so the ranking surfaces the categorical attributes that actually separate the two groups. Score is computed after normalizing each group to 100% so it's robust to different group sizes.

USE THIS INSTEAD OF MANUAL PIVOTS. When two row sets visibly differ and you don't know which attribute(s) separate them, the standard agentic move is to run a GROUP BY for each candidate attribute and compare. event_deltas does this for ALL attributes in one call, ranked by signal strength — usually 1 call instead of 5–20.

NARROW the target to the specific outlier rows. A broad target mostly contains healthy rows, so the ranking comes back noisy. The narrower target — the sharper the ranking.

TYPICAL USES (any source — logs or traces):
  - Slow vs fast spans (MOST COMMON for latency triage):
      target = {where: <op-filter> AND Duration > <threshold>},
      baseline = {where: <op-filter> AND Duration <= <threshold>}
      Scope BOTH to the same operation/endpoint via `<op-filter>`; the ranked attribute(s) are then what discriminates the slow invocations of THAT operation from the fast ones. Skipping the op-filter gives a noisy "which operation is slow" answer instead of "which sub-set of one operation is slow".
  - Before vs after a deploy / incident onset:
      target = {window: after onset}, baseline = {window: before onset}
  - Failing vs succeeding rows:
      target = {where: <failing filter>}, baseline = {where: <succeeding filter>}
  - One service / endpoint vs the rest:
      target = {where: ServiceName=X}, baseline = {where: ServiceName != X}
  Any pair of row sets over the same source works — the tool just   asks "what is statistically different about target vs baseline".

WHEN NOT TO USE: when the question is already known to be about a specific attribute (use clickstack_table groupBy), when you want raw rows (use clickstack_search), or when you need a time-series shape (use clickstack_timeseries).

PREFER THIS over clickstack_sql. Only drop to raw SQL for things the builder tools cannot express — JOINs, sub-queries, CTEs, window functions, tables not registered as sources, or summary-type metrics (the metricTables.summary table on a metric source, which the builder tools cannot query).

OUTPUT SHAPE: an array of properties, each with rank, key, score, semanticBoost (true for well-known OTel attrs like service.name / http.method / error.type / status), targetCount and baselineCount (sample sizes), and topDeltas — the values whose share shifted most, each with `value`, `targetPct`, `baselinePct`, and `diffPct`. topDeltas already contains the full per-value comparison for that attribute, so there is no separate target/baseline distribution to consult.

IMPORTANT — DO NOT STOP AT RANK 1. The top ~5 ranked properties are often INDEPENDENT axes that together explain the population shift (e.g. a regression localized on the intersection of two attributes). Scan down the list until the score visibly drops to noise level; any property well above that floor is a candidate axis to combine with the others.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `topN` | number | No | Number of top-ranked properties to return in `properties`. Default: 20. |
| `target` | object | Yes | Target ("outlier") group — the rows you suspect are different (e.g. the recent slow window, the failing requests, the affected segment). |
| `baseline` | object | Yes | Baseline ("inlier") group — the rows considered normal (e.g. an earlier healthy window, the successful requests, the unaffected segment). |
| `sourceId` | string | Yes | Source ID. Works for trace and log sources. Call clickstack_list_sources for available sources. |
| `sampleSize` | number | No | Rows to sample from each group. Default: 500. Larger samples give more stable rankings at the cost of latency. |
| `includeHidden` | boolean | No | When true, include denylisted/high-cardinality properties in a separate `hidden` array. Default: false (mirrors UI behavior — these are noisy). |
| `sourceFilters` | object | No | Required filter values for sources whose configuration declares `requiredSourceFilters`. Keyed by column name (the values returned by clickstack_get_source_filter_values or the sampled `sourceFilterValues` from clickstack_describe_source).  FORMAT: { "<columnName>": ["<value1>", "<value2>"] } Multiple values for one column become a `column IN (...)` predicate.  ALL OPTION: a column may instead be pinned to every value with { "<columnName>": { "all": true } }. This is ONLY accepted for columns whose source marks them `allowAll: true` (see requiredSourceFilters in clickstack_list_sources / clickstack_describe_source, or the `allowAll` flag from clickstack_get_source_filter_values); using it on any other column is rejected. Prefer explicit values when they're known.  REQUIRED for any source whose summary in clickstack_list_sources or clickstack_describe_source includes `requiredSourceFilters`. Every column listed there must have at least one value set. Queries that omit a required column are rejected with an error telling you which columns are missing and how to recover.  PARTIAL FILTERS: when you supply values for some but not all required columns, the tool will try to auto-resolve the missing columns by looking up values cross-pruned against the ones you provided — if any missing column has exactly one remaining value it is filled in automatically. The resolved set is echoed back as `resolvedSourceFilters` in the response so you can supply it explicitly on the next call.  Ignored for sources without requiredSourceFilters: pass nothing. |
| `topValuesPerProperty` | number | No | How many top values per property to include in the response. Default: 6. |

### Clickstack event patterns

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_EVENT_PATTERNS`

Discover the most common log messages and event patterns. Samples random events, clusters them using the Drain algorithm, and returns patterns sorted by frequency with estimated counts and time trends.

PREFER THIS TOOL over clickstack_search or clickstack_table when the goal is to understand what kinds of messages, errors, or events exist — e.g. "sample logs", "what errors are happening", "show me common messages", "what does this service log". It returns frequency-ranked patterns instead of raw rows, giving a much better overview.

Also use when asked about "top patterns", "common logs", "noisy services", "recurring messages", or log noise analysis.

Each pattern includes a "whereSnippet" — use it as the "where" parameter in a follow-up clickstack_search call to browse matching raw events.

Requires sourceId — call clickstack_list_sources then clickstack_describe_source first.

PREFER THIS over clickstack_sql. Only drop to raw SQL for things the builder tools cannot express — JOINs, sub-queries, CTEs, window functions, tables not registered as sources, or summary-type metrics (the metricTables.summary table on a metric source, which the builder tools cannot query).

When to use which tool:
  - clickstack_event_patterns: clustering / recurring shapes / noise analysis
  - clickstack_search: raw individual rows
  - clickstack_table: aggregated metrics / counts / top-N

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `topN` | number | No | Number of top patterns to return (1-100). Default: 20. |
| `where` | string | No | Row filter.  FIRST: pick a language (whereLanguage):   Lucene (default): Column:value          e.g. level:error   Lucene map attrs: Column.key:value      e.g. SpanAttributes.http.method:GET   SQL:              Column = 'value'       e.g. StatusCode = 500   SQL map attrs:    SpanAttributes['key'] = 'value'  MAP ATTRIBUTES:   Lucene uses DOT notation:    SpanAttributes.http.method:GET   SQL uses BRACKET notation:   SpanAttributes['http.method'] = 'GET'  WRONG: SpanAttributes['key']:value   (Lucene cannot parse bracket syntax) WRONG: level = "error"               (SQL syntax with whereLanguage:"lucene")  SUBSTRING TRAP: Lucene field:value matches ANY row containing "value" as a substring, not exact equality.   SpanKind:Server matches "Server", "ServerStreaming", "InternalServer", etc.   For exact match, use SQL: SpanKind = 'Server' |
| `endTime` | string | No | End of the query window as ISO 8601. Default: now. |
| `sourceId` | string | Yes | Source ID (required). Call clickstack_list_sources to find available sources. |
| `startTime` | string | No | Start of the query window as ISO 8601. Default: 15 minutes ago. If results are empty, try a wider range (e.g. 24 hours). |
| `sampleSize` | number | No | Number of random rows to sample for pattern mining. Default: 10000. Higher values produce more accurate patterns but take longer. |
| `trendBuckets` | number | No | Number of time buckets for per-pattern trend sparklines (0-60). Default: 24. Compare bucket counts at start vs end to spot patterns that started or stopped mid-window. Set to 0 to disable trend computation (smaller response). |
| `sourceFilters` | object | No | Required filter values for sources whose configuration declares `requiredSourceFilters`. Keyed by column name (the values returned by clickstack_get_source_filter_values or the sampled `sourceFilterValues` from clickstack_describe_source).  FORMAT: { "<columnName>": ["<value1>", "<value2>"] } Multiple values for one column become a `column IN (...)` predicate.  ALL OPTION: a column may instead be pinned to every value with { "<columnName>": { "all": true } }. This is ONLY accepted for columns whose source marks them `allowAll: true` (see requiredSourceFilters in clickstack_list_sources / clickstack_describe_source, or the `allowAll` flag from clickstack_get_source_filter_values); using it on any other column is rejected. Prefer explicit values when they're known.  REQUIRED for any source whose summary in clickstack_list_sources or clickstack_describe_source includes `requiredSourceFilters`. Every column listed there must have at least one value set. Queries that omit a required column are rejected with an error telling you which columns are missing and how to recover.  PARTIAL FILTERS: when you supply values for some but not all required columns, the tool will try to auto-resolve the missing columns by looking up values cross-pruned against the ones you provided — if any missing column has exactly one remaining value it is filled in automatically. The resolved set is echoed back as `resolvedSourceFilters` in the response so you can supply it explicitly on the next call.  Ignored for sources without requiredSourceFilters: pass nothing. |
| `whereLanguage` | string ("lucene" | "sql") | No | Query language for the "where" filter. Default: lucene. IMPORTANT: the syntax in "where" MUST match "whereLanguage".   Lucene (default): Column:value, Column.mapKey:value, Column:>100   SQL:              Column = 'value', SpanAttributes['key'] = 'value'  Lucene supports comparisons (>= > < <=), wildcards (field:val*), ranges ([1 TO 5]), and map attributes via dot notation. Use "sql" for IN(...) lists, complex expressions, or function calls. IMPORTANT: Lucene field:value is a SUBSTRING match (ilike), not exact equality. field:val* is prefix-within-substring, not a true prefix match. For exact matching or reliable wildcards, use SQL: WHERE field = 'value' or WHERE field LIKE 'val%'.  Common mistake: writing Column:value (Lucene) but setting whereLanguage to "sql". If your filter uses colon syntax, leave whereLanguage as "lucene" (the default). |
| `bodyExpression` | string | No | Column expression to mine patterns from. Auto-detected from the source if omitted (Body for logs, SpanName for traces). Example: "Body", "SpanName", "SpanAttributes['http.url']" |

### Clickstack get alert

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_GET_ALERT`

Without an ID: list all alerts as a high-level summary (id, name, displayName, tags, state, source, interval). Optionally filter by state (e.g. state="ALERT" for firing alerts). With an ID: get full alert detail including configuration, displayName, tags, and recent evaluation history.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | string | No | Alert ID. Omit to list all alerts, provide to get full detail. |
| `state` | string ("ALERT" | "OK" | "DISABLED" | "INSUFFICIENT_DATA") | No | Filter list by alert state (only applies when id is omitted). Use "ALERT" to find currently firing alerts. |

### Clickstack get dashboard

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_GET_DASHBOARD`

Without an ID: list all dashboards (returns IDs, names, tags). With an ID: get full dashboard detail including all tiles and configuration.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | string | No | Dashboard ID. Omit to list all dashboards, provide to get full detail. |

### Clickstack get dashboard tile

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_GET_DASHBOARD_TILE`

Retrieve a single tile from a dashboard by tileId. Useful for inspecting one tile without loading the full dashboard. Use clickstack_get_dashboard (without an ID) to list dashboards, then clickstack_get_dashboard (with an ID) to see all tile IDs.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `tileId` | string | Yes | Tile ID within the dashboard. Obtain from clickstack_get_dashboard. |
| `dashboardId` | string | Yes | Dashboard ID. |

### Clickstack get saved search

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_GET_SAVED_SEARCH`

Without an ID: list all saved searches as a high-level summary (id, name, tags). With an ID: get full saved search detail including query, source, filters, and configuration.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | string | No | Saved search ID. Omit to list all saved searches, provide to get full detail. |

### Clickstack get source filter values

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_GET_SOURCE_FILTER_VALUES`

List the available values for the required source filter columns on a single source. Use this BEFORE calling any query tool against a source whose configuration declares `requiredSourceFilters`. ONLY use this for sources that have `requiredSourceFilters`. Every such source requires values for every declared column on every query call, and this tool tells you which values exist.

Workflow:
  1. clickstack_list_sources or clickstack_describe_source to see whether a source declares requiredSourceFilters and which columns it requires.
  2. clickstack_get_source_filter_values to fetch the values for those columns.
  3. Call your query tool with the sourceFilters parameter set, e.g.
     sourceFilters: { "<columnA>": ["val1"], "<columnB>": ["val2"] }

CROSS-FILTER PRUNING: pass `selectedFilters` with the values you have already chosen for some columns to narrow the option list for the others.

PAGINATION: each column reports `totalAvailable`, `truncated`, and (when truncated) `nextOffset`. Use `limit` + `offset` to step through high-cardinality columns instead of pulling everything at once.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `limit` | integer | No | Maximum number of values to return per column (1-1000). Default: 20. Combine with offset to paginate columns that have many distinct values. |
| `offset` | integer | No | Number of values to skip per column. Default: 0. Use with limit to paginate when a column has more distinct values than the limit allows. |
| `columns` | array | No | Optional subset of filter column names to look up. Defaults to every column declared on the source's requiredSourceFilters. Unknown column names return an error listing the valid ones so the agent can retry. |
| `sourceId` | string | Yes | Source ID (required). Must be a source whose configuration declares `requiredSourceFilters`. Use clickstack_list_sources or clickstack_describe_source to see which sources have requiredSourceFilters and the column names they expose. |
| `selectedFilters` | object | No | Already-chosen filter values, keyed by column name. Pass the partial sourceFilters you alreadyhave so the response reflects only values that are co-occurring with those selections. |

### Clickstack get webhook

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_GET_WEBHOOK`

List available webhook destinations (id, name, service type). Use the returned id as the webhookId when creating alerts with clickstack_save_alert.

### Clickstack list metrics

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_LIST_METRICS`

DISCOVERY: Use this after clickstack_describe_source when you need more metric names than the per-kind sample shows, or when you want to narrow by kind / name pattern / time window. Returns paginated metric names per kind (gauge/sum/histogram/exponential histogram/summary) with optional unit and description (when the OTel-default columns are present). Pass the returned `nextCursor` back unchanged to fetch the next page.

Summary metrics are listed for discovery only — they cannot be passed to clickstack_timeseries / clickstack_table; query them with clickstack_sql against the table in the source's metricTables.summary.

Workflow: clickstack_list_sources → clickstack_describe_source → clickstack_list_metrics → clickstack_describe_metric → clickstack_timeseries|clickstack_table.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `kind` | string ("gauge" | "sum" | "histogram" | "exponential histogram" | "summary") | No | Optional metric kind filter. Omit to scan every populated kind on the source (gauge, sum, histogram, exponential histogram, summary). Set to narrow results to one kind. NOTE: summary metrics are discovery-only — they cannot be passed to clickstack_timeseries / clickstack_table; query them with clickstack_sql. |
| `limit` | integer | No | Max metrics returned per page. Default 50, max 500. |
| `cursor` | string | No | Opaque pagination cursor returned by a previous call as `nextCursor`. Pass it back unchanged to get the next page. |
| `endTime` | string | No | End of the time window as ISO 8601. Default: now. |
| `sourceId` | string | Yes | Source ID. Must reference a metric source — get IDs from clickstack_list_sources. |
| `startTime` | string | No | Restrict to metrics with data points after this ISO 8601 timestamp. Default: 24 hours before endTime (or now). |
| `namePattern` | string | No | Optional ClickHouse ILIKE pattern applied to MetricName server-side. Use % as the wildcard. Examples: "system.cpu.%", "%duration%", "http.server.%". |

### Clickstack list sources

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_LIST_SOURCES`

List all data sources (logs, metrics, traces) and database connections available to this team. Returns source IDs, names, kinds, and connection IDs as a lightweight catalog. Metric sources additionally include metricNamesPreview — a sample of recently-reported metric names per kind — so metrics can be queried immediately.

NEXT STEP: After identifying the source(s) you need, call clickstack_describe_source with the sourceId to get the full column schema, attribute keys, and sampled values. This two-step approach avoids fetching expensive schema details for sources you do not need.

REQUIRED SOURCE FILTERS: when a source has `requiredSourceFilters`, every query against it MUST supply values for every column in `requiredSourceFilters` via the `sourceFilters` parameter. Call clickstack_get_source_filter_values to discover the available values, or check the sampled `sourceFilterValues` returned by clickstack_describe_source.

NOTE: For most queries, use source IDs with clickstack_timeseries, clickstack_table, clickstack_search, or clickstack_event_patterns. Connection IDs are only needed for clickstack_sql (raw ClickHouse SQL).

Metric sources may list a "summary" table in metricTables. Summary metrics are not supported by the builder tools — use clickstack_sql to look at them.

### Clickstack list teams

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_LIST_TEAMS`

List all teams the current user belongs to and identify which team is active for this session. Use this to discover available teams when the user works across multiple teams.

To switch teams, the MCP client must include the `x-hdx-team` HTTP header set to the target team ID on subsequent requests. The header is validated against the user’s team memberships — requests for teams the user does not belong to will be rejected.

### Clickstack patch dashboard

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_PATCH_DASHBOARD`

Make targeted updates to a dashboard without resubmitting the full object. You can update dashboard-level fields (name, tags) and/or replace a single tile by tileId — all in one call. Unmentioned tiles and fields are preserved. Use clickstack_get_dashboard_tile to inspect a tile before patching it. IMPORTANT: After patching a tile, run clickstack_query_tile to confirm the query still works.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | No | New dashboard name. Omit to keep the current name. |
| `tags` | array | No | New tags array (replaces all existing tags). Omit to keep the current tags. Up to 50 tags, each at most 32 characters. |
| `tile` | string | No | The full replacement tile definition. Replaces the tile matched by tileId. Layout fields (x, y, w, h), name, and containerId/tabId default to the existing tile's values when omitted, so you only need to specify what changed. |
| `tileId` | string | No | ID of the tile to replace. Must be paired with `tile`. Obtain tile IDs from clickstack_get_dashboard. |
| `dashboardId` | string | Yes | Dashboard ID. |

### Clickstack query tile

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_QUERY_TILE`

Execute the query for a specific tile on an existing dashboard. Useful for validating that a tile returns data or for spot-checking results without rebuilding the query from scratch. Use clickstack_get_dashboard with an ID to find tile IDs.

SOURCE FILTERS: when the tile reads a source that declares `requiredSourceFilters` (core filters) but carries no tile-level `sourceFilters` of its own (which is the recommended default), pass `sourceFilters` here to stand in for the dropdown selection so the validation query has values to run with. The values you pass are used only for this query and are NOT persisted on the tile. If the tile already pins its own tile-level `sourceFilters`, do NOT pass `sourceFilters` here: omit it so the validation runs with the tile's own values. Anything you pass here takes precedence over the tile-level values (the two are never merged), so passing a different set would validate something other than what the tile will actually use.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `tileId` | string | Yes | Tile ID within the dashboard. Obtain from clickstack_get_dashboard. |
| `endTime` | string | No | End of the query window as ISO 8601. Default: now. |
| `startTime` | string | No | Start of the query window as ISO 8601. Default: 15 minutes ago. If results are empty, try a wider range (e.g. 24 hours). |
| `dashboardId` | string | Yes | Dashboard ID. |
| `sourceFilters` | object | No | Source-filter values to apply to this tile query, keyed by column name, e.g. { "ServiceName": ["api"] }. These stand in for the dashboard-level source-filter dropdown selection when the tile's source declares `requiredSourceFilters` but the tile itself carries no `sourceFilters`. They are used only for this query (NOT persisted). These take precedence over any tile-level sourceFilters (the two are never merged), so the set that applies must satisfy every required column on its own. If the tile already pins its own tile-level sourceFilters, OMIT this so the query validates the tile as it will actually run. Call clickstack_get_source_filter_values to discover valid values. Ignored for sources without requiredSourceFilters. |
| `variableValues` | array | No | Optional simulated dashboard variable selection for this run. Every variable the dashboard declares defaults to EMPTY, which is the state a freshly-opened dashboard is in. Supply values here to check that a tile narrows as expected once the user picks something. Variables not listed stay empty. |

### Clickstack query tiles

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_QUERY_TILES`

Run the queries for many tiles of a dashboard in ONE call and return a compact per-tile success/failure summary. This is the efficient way to validate an entire dashboard after clickstack_save_dashboard — prefer it over calling clickstack_query_tile once per tile. Accepts a dashboard ID and an optional list of tile IDs. Markdown tiles are excluded by default; a markdown tile passed explicitly in tileIds is returned with status "skipped". A tile that fails is reported inline with its error and the overall call still succeeds, so one broken tile does not hide the rest, and unrecognized tile IDs come back as unknownTileIds rather than failing. At most 50 tiles run per call; any beyond that are returned as unrunTileIds — call again with those as tileIds to run the remainder. Drill into a specific failing tile with clickstack_query_tile.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `endTime` | string | No | End of the query window as ISO 8601. Default: now. |
| `tileIds` | array | No | Tile IDs to run. Default: every non-markdown tile on the dashboard. Obtain IDs from clickstack_get_dashboard. At most 500 IDs per call. |
| `startTime` | string | No | Start of the query window as ISO 8601. Default: 15 minutes ago. If results are empty, try a wider range (e.g. 24 hours). |
| `dashboardId` | string | Yes | Dashboard ID. |
| `variableValues` | array | No | Optional simulated dashboard variable selection for this run. Every variable the dashboard declares defaults to EMPTY, which is the state a freshly-opened dashboard is in. Supply values here to check that a tile narrows as expected once the user picks something. Variables not listed stay empty. |

### Clickstack save alert

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_SAVE_ALERT`

Create a new alert (omit id) or update an existing one (provide id). Alerts monitor a saved search, a dashboard tile, or an inline chart config (source "inline" + chartConfig — no saved search or dashboard needed) and fire when the metric crosses a threshold. At least one webhook notification channel is required: pass "channels" for 1-10 targets, or the legacy singular "channel" for one. Updates replace the alert configuration rather than merging it, so read the alert first and resend its full "channels" array to avoid dropping channels you did not mean to remove.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | string | No | Alert ID. Omit to create a new alert, provide to update an existing one. |
| `name` | string | No | Alert title template (supports Handlebars syntax). |
| `tags` | array | No | Tags for the alert. Defaults to the tags of the referenced saved search or dashboard when omitted; inline alerts default to an empty list. |
| `source` | string ("saved_search" | "tile" | "inline") | Yes | Alert source type: "saved_search" monitors a saved search, "tile" monitors a dashboard tile, and "inline" carries its own chartConfig without requiring a saved search or dashboard. |
| `tileId` | string | No | Tile ID within the dashboard (required when source is tile). Must be a line, stacked bar, or number tile. |
| `channel` | object | No | Alert notification channel configuration. |
| `groupBy` | string | No | Group-by key for saved search alerts. |
| `message` | string | No | Alert message template (supports Handlebars syntax). |
| `channels` | array | No | Notification channels (1-10). Provide this or "channel"; if both, "channel" must match the first entry. |
| `interval` | string ("30s" | "1m" | "5m" | "15m" | "30m" | "1h" | "6h" | "12h" | "1d") | Yes | Evaluation interval. |
| `threshold` | number | Yes | Threshold value for triggering the alert. |
| `chartConfig` | string | No | Chart configuration for inline alerts (required when source is "inline"). Same shape as a dashboard tile config, limited to displayType line, stacked_bar, or number. Omit configType for the builder variant (sourceId + select); set configType to "sql" for the Raw SQL variant (connectionId + sqlTemplate; the template must use the $__timeFilter and $__timeInterval macros so each evaluation window can be queried). |
| `dashboardId` | string | No | Dashboard ID (required when source is tile). |
| `displayName` | string | No | Display name shown for the alert in the UI. Defaults to the saved search name, dashboard tile name, or inline chartConfig name when omitted. Overridden by the "name" field in alert notifications when provided. |
| `thresholdMax` | number | No | Upper bound (required when thresholdType is between or not_between, must be >= threshold). |
| `savedSearchId` | string | No | Saved search ID (required when source is saved_search). |
| `thresholdType` | string ("above" | "below" | "above_exclusive" | "below_or_equal" | "equal" | "not_equal" | "between" | "not_between") | Yes | How the metric value is compared against the threshold. |
| `scheduleStartAt` | string | No | Absolute UTC anchor for window alignment (ISO 8601). |
| `scheduleOffsetMinutes` | integer | No | Offset from the interval boundary in minutes (must be < interval). |

### Clickstack save dashboard

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_SAVE_DASHBOARD`

Create a new dashboard (omit id) or update an existing one (provide id). Call clickstack_list_sources first to obtain sourceId and connectionId values. IMPORTANT: After saving a dashboard, always run clickstack_query_tiles to validate every tile in one call (or clickstack_query_tile for a single tile) and confirm the queries work and return expected data. Tiles can silently fail due to incorrect filter syntax, missing attributes, or wrong column names. TIP: To update a single tile without resubmitting all tiles, use clickstack_patch_dashboard instead.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | string | No | Dashboard ID. Omit to create a new dashboard, provide to update an existing one. |
| `name` | string | Yes | Dashboard name |
| `tags` | array | No | Dashboard tags. Up to 50 tags, each at most 32 characters. |
| `tiles` | array | Yes | Array of dashboard tiles. Each tile needs a name, optional layout (x/y/w/h), and a config block. The config block varies by displayType – use clickstack_list_sources for sourceId and connectionId values.  Example tiles: 1. Line chart: { "name": "Error Rate", "config": { "displayType": "line", "sourceId": "<from list_sources>", "groupBy": "ResourceAttributes['service.name']", "select": [{ "aggFn": "count", "where": "StatusCode:STATUS_CODE_ERROR", "alias": "Errors" }] } } 2. Table: { "name": "Top Endpoints", "config": { "displayType": "table", "sourceId": "<from list_sources>", "groupBy": "SpanAttributes['http.route']", "select": [{ "aggFn": "count", "alias": "Requests" }, { "aggFn": "avg", "valueExpression": "Duration", "alias": "Avg Duration", "numberFormat": { "output": "duration", "factor": 0.000000001 } }] } }    (per-series numberFormat lets one column render as a duration while a sibling count column stays a plain number) 3. Number: { "name": "Total Requests", "config": { "displayType": "number", "sourceId": "<from list_sources>", "select": [{ "aggFn": "count", "alias": "Requests" }], "numberFormat": { "output": "number", "average": true } } } 4. Number (duration): { "name": "P95 Latency", "config": { "displayType": "number", "sourceId": "<from list_sources>", "select": [{ "aggFn": "quantile", "level": 0.95, "valueExpression": "Duration", "alias": "P95 Latency" }], "numberFormat": { "output": "duration", "factor": 0.000000001 } } } 5. Heatmap: { "name": "Latency Heatmap", "config": { "displayType": "heatmap", "sourceId": "<from list_sources, must be a Trace source>", "select": [{ "valueExpression": "Duration" }], "numberFormat": { "output": "duration", "factor": 0.000000001 } } } |
| `filters` | array | No | Optional dashboard-level filters. These define the dropdowns in the dashboard filter bar AND the expressions that table-tile row-click navigation can populate. If another tile's onClick targets THIS dashboard with `filters: [{ expression: "X", ... }]`, this array MUST declare a filter whose `expression` is "X". Otherwise the value is dropped on arrival and the destination opens unfiltered.  Two filter types: QUERY_EXPRESSION queries its dropdown values from a source column, while STATIC_LIST declares them inline via `options` and is always variable-only (it has no expression to broadcast).  A QUERY_EXPRESSION filter runs in one of two modes (or both). BROADCAST (the default) applies the selected value as a condition on matching tiles with no per-tile wiring. VARIABLE (isVariableEnabled) exposes the value to tile queries as $variableName, which the tile must reference explicitly. Prefer broadcast; reach for a variable in advanced cases. Enabling both on one filter applies the value twice.  By default, a broadcast filter applies to every tile on the dashboard. On mixed-source dashboards, use variable mode or the optional `appliesToSourceIds` field to restrict a filter to only the tiles whose source carries the referenced column — leave `appliesToSourceIds` omitted to keep the broadcast-to-all-tiles default. `appliesToSourceIds` does NOT scope variable references; any tile may reference any variable.  Example (broadcast to every tile, the common case): [   { "type": "QUERY_EXPRESSION", "name": "Service", "expression": "ServiceName",     "sourceId": "<trace-source-id>", "whereLanguage": "sql" } ]  Example (scoped on a mixed log/trace/metric dashboard): [   { "type": "QUERY_EXPRESSION", "name": "Service", "expression": "SpanName",     "sourceId": "<trace-source-id>", "whereLanguage": "sql",     "appliesToSourceIds": ["<trace-source-id>"] } ]  Example (variable only, referenced by tiles as $service): [   { "type": "QUERY_EXPRESSION", "name": "Service", "expression": "ServiceName",     "sourceId": "<trace-source-id>", "whereLanguage": "sql",     "isBroadcastEnabled": false, "isVariableEnabled": true, "variableName": "service" } ] A tile then uses it as $__filter(ServiceName, $service) in a SQL where clause, which expands to 1=1 while nothing is selected.  Example (static list: fixed hand-authored values, always a variable, no source query): [   { "type": "STATIC_LIST", "name": "Environment",     "options": ["prod", "staging", "dev"], "variableName": "env" } ] A tile references it as $__filter(ResourceAttributes['deployment.environment'], $env). Always pass the column explicitly: the one-argument $__filter($env) form reuses the filter's own expression, which a static filter does not have. Prefer STATIC_LIST when the values are a fixed business list or a curated subset rather than derivable from the data.  Example (dependent filter: the Endpoint dropdown only lists the selected service's endpoints): [   { "type": "QUERY_EXPRESSION", "name": "Service", "expression": "ServiceName",     "sourceId": "<trace-source-id>", "whereLanguage": "sql",     "isVariableEnabled": true, "variableName": "service" },   { "type": "QUERY_EXPRESSION", "name": "Endpoint", "expression": "SpanName",     "sourceId": "<trace-source-id>", "whereLanguage": "sql",     "where": "$__filter(ServiceName, $service)" } ] A filter's `where` scopes only the values ITS dropdown offers; it is not applied to any tile. That is why Service keeps broadcasting here: its variable is read by the Endpoint dropdown, not by a tile, so the value is never applied twice. |
| `containers` | array | No | Optional dashboard organization layer. Each container groups one or more tiles visually and may carry a tab bar. Tiles join a container by setting tile.containerId; tiles further select a tab by setting tile.tabId.  Rules: - Container ids must be unique on the dashboard. - Tab ids must be unique within a container. - A tile.containerId must reference a container id in this array. - A tile.tabId must reference a tab id on the same container. - tile.tabId requires tile.containerId.  Container shape: { id, title, collapsed, collapsible?, bordered?, tabs? }. `collapsed` is required. `collapsible` and `bordered` are optional and persisted absent when omitted; the renderer treats absence as true. Two or more tabs render as a tab bar; zero or one tab renders as a plain group header.  Example: [   { "id": "service-health", "title": "Service Health", "collapsed": false,     "tabs": [ { "id": "errors", "title": "Errors" }, { "id": "latency", "title": "Latency" } ] },   { "id": "overview", "title": "Overview", "collapsed": true } ] |

### Clickstack save saved search

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_SAVE_SAVED_SEARCH`

Create a new saved search (omit id) or update an existing one (provide id). A saved search stores a reusable query against a data source. Use clickstack_list_sources to find the sourceId.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | string | No | Saved search ID. Omit to create a new saved search, provide to update an existing one. |
| `name` | string | Yes | Human-friendly name for the saved search. |
| `tags` | array | No | Tags for organizing saved searches. Up to 50 tags, each at most 32 characters. |
| `where` | string | No | Filter condition string in the language specified by whereLanguage. Example (lucene): "level:error", Example (sql): "StatusCode = 'Error'" |
| `select` | string | No | Columns/fields to retrieve. Leave empty for defaults. Example: "body,service.name,duration" |
| `filters` | array | No | Additional structured filters. |
| `orderBy` | string | No | Sort expression. Example: "Timestamp DESC" |
| `sourceId` | string | Yes | Source ID — call clickstack_list_sources to find available sources. |
| `sourceFilters` | object | No | Source filter values keyed by column name. Only available for sources with filterSettings defined. Format: { "<columnName>": ["<value>"] }. A column may instead be pinned to every value with { "<columnName>": { "all": true } }, but only for columns whose source marks them `allowAll: true`; using it on any other column is rejected. |
| `whereLanguage` | string ("sql" | "lucene") | No | Language for the where filter. Default: lucene. |

### Clickstack save source

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_SAVE_SOURCE`

Create a new data source (omit id) or update an existing one (provide id) so shipped telemetry becomes queryable. Update is a full replace of the source definition. Required for all kinds: kind, name, connection, databaseName, tableName, timestampValueExpression. Kind-specific requirements: log & trace need defaultTableSelectExpression; trace also needs durationExpression, traceIdExpression, spanIdExpression, parentSpanIdExpression, spanNameExpression, spanKindExpression; session needs traceSourceId; metric needs metricTables and resourceAttributesExpression. Get connection and source IDs from clickstack_list_sources.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | string | No | Source ID. Omit to create a new source, provide to update an existing one. |
| `kind` | string ("log" | "trace" | "session" | "metric" | "promql") | Yes | Source kind. Determines which fields are required. |
| `name` | string | Yes | Human-friendly source name. |
| `section` | string | No | Optional grouping section label. |
| `disabled` | boolean | No | When true, the source is hidden from source selectors in the UI. Defaults to false. On update this is part of the full replace: omitting it resets the source to enabled. |
| `tableName` | string | Yes | ClickHouse table name (maps to from.tableName). Required for all kinds except metric (where it may be empty) and promql (use a placeholder). |
| `connection` | string | Yes | Connection ID this source reads from. Get IDs from clickstack_list_sources. |
| `logSourceId` | string | No |  |
| `databaseName` | string | Yes | ClickHouse database name (maps to from.databaseName). |
| `metricTables` | object | No | Metric source only. Map of OTel metric kind -> ClickHouse table name. At least one entry is required for metric sources. |
| `querySettings` | array | No | Optional ClickHouse query settings ({ setting, value }) applied when querying this source (max 10). |
| `traceSourceId` | string | No | Correlated trace source ID. Required for session sources. |
| `bodyExpression` | string | No | Log body expression. |
| `filterSettings` | object | No | Optional required-source-filter definition (log/trace sources): databaseName, tableName, and up to 10 columns exposed as filters prepended to every query. Each column's `name` is the column of the source table that selected values are matched against. Set the optional `valueExpression` to a SQL expression over the filter-values table when the available options live in a differently-named column, or must be transformed to match the source column (e.g. `lower(service_name)`). |
| `metricSourceId` | string | No |  |
| `sessionSourceId` | string | No |  |
| `spanIdExpression` | string | No |  |
| `durationPrecision` | number | No | Trace duration precision (0-9). Defaults to 3. |
| `materializedViews` | array | No |  |
| `orderByExpression` | string | No |  |
| `traceIdExpression` | string | No |  |
| `durationExpression` | string | No |  |
| `spanKindExpression` | string | No |  |
| `spanNameExpression` | string | No |  |
| `sampleRateExpression` | string | No |  |
| `statusCodeExpression` | string | No |  |
| `serviceNameExpression` | string | No |  |
| `tableFilterExpression` | string | No |  |
| `parentSpanIdExpression` | string | No |  |
| `severityTextExpression` | string | No |  |
| `statusMessageExpression` | string | No |  |
| `implicitColumnExpression` | string | No |  |
| `spanLinksValueExpression` | string | No |  |
| `timestampValueExpression` | string | Yes | Expression/column used as the primary timestamp. |
| `eventAttributesExpression` | string | No |  |
| `metadataMaterializedViews` | object | No |  |
| `spanEventsValueExpression` | string | No |  |
| `knownColumnsListExpression` | string | No |  |
| `defaultTableSelectExpression` | string | No | Default columns to select. Required for log and trace sources. |
| `resourceAttributesExpression` | string | No | Required for metric sources; optional for others. |
| `useTextIndexForImplicitColumn` | string ("auto" | "enabled" | "disabled") | No | Whether to use the text index for the implicit column. |
| `displayedTimestampValueExpression` | string | No |  |
| `highlightedRowAttributeExpressions` | string | No |  |
| `highlightedTraceAttributeExpressions` | array | No |  |

### Clickstack save webhook

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_SAVE_WEBHOOK`

Create a new webhook (omit id) or update an existing one (provide id). Use the returned id as the webhookId when creating alerts with clickstack_save_alert. Required: name, service (slack, generic, or incidentio), and url. For the slack service the url host must end in slack.com and headers/queryParams/body are not supported. On update, readable fields (description, body) are a full replace while write-only headers/queryParams are preserved when omitted (send {} to clear); changing the destination clears omitted write-only secrets.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | string | No | Webhook ID. Omit to create a new webhook, provide to update an existing one. |
| `url` | string | Yes | Destination URL. For the slack service the host must end in slack.com. Private/reserved IPs and internal hosts are rejected. |
| `body` | string | No | Optional request body template. Not supported for the slack service. On update this is a full replace: omitting it clears the stored body. |
| `name` | string | Yes | Human-friendly webhook name. |
| `headers` | object | No | Optional HTTP headers (key -> value). Not supported for the slack service. On update these are write-only: omitting them PRESERVES the stored values; send an empty object ({}) to clear them. If the destination (url or service) changes, omitted headers are cleared. |
| `service` | string ("slack" | "generic" | "incidentio") | Yes | Webhook service type. "slack" posts a fixed Block Kit payload and does not support headers/queryParams/body. |
| `description` | string | No | Optional description shown in the UI. On update this is a full replace: omitting it clears the stored description. |
| `queryParams` | object | No | Optional query parameters (key -> value). Not supported for the slack service. Same write-only update semantics as headers. |

### Clickstack search

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_SEARCH`

Browse individual log/event/trace rows. Use this when you need to see raw events, investigate specific log lines, or drill into individual records matching a filter.

PREFER THIS over clickstack_sql. Only drop to raw SQL for things the builder tools cannot express — JOINs, sub-queries, CTEs, window functions, tables not registered as sources, or summary-type metrics (the metricTables.summary table on a metric source, which the builder tools cannot query).

Requires sourceId — call clickstack_list_sources then clickstack_describe_source first.

For aggregated metrics, use clickstack_table instead. For pattern discovery, use clickstack_event_patterns instead.

Set denoise=true to automatically filter out high-frequency repetitive patterns, surfacing only unusual or interesting events.

Column naming: top-level columns are PascalCase (Duration, StatusCode). Map attributes use bracket syntax: SpanAttributes['http.method'].

SOURCE FILTERS: when the source declares `requiredSourceFilters` (see clickstack_describe_source / clickstack_list_sources), you MUST pass `sourceFilters` with at least one value for every required column.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `where` | string | No | Row filter.  FIRST: pick a language (whereLanguage):   Lucene (default): Column:value          e.g. level:error   Lucene map attrs: Column.key:value      e.g. SpanAttributes.http.method:GET   SQL:              Column = 'value'       e.g. StatusCode = 500   SQL map attrs:    SpanAttributes['key'] = 'value'  MAP ATTRIBUTES:   Lucene uses DOT notation:    SpanAttributes.http.method:GET   SQL uses BRACKET notation:   SpanAttributes['http.method'] = 'GET'  WRONG: SpanAttributes['key']:value   (Lucene cannot parse bracket syntax) WRONG: level = "error"               (SQL syntax with whereLanguage:"lucene")  SUBSTRING TRAP: Lucene field:value matches ANY row containing "value" as a substring, not exact equality.   SpanKind:Server matches "Server", "ServerStreaming", "InternalServer", etc.   For exact match, use SQL: SpanKind = 'Server' |
| `columns` | string | No | Comma-separated columns to include in search results. Leave empty for defaults. Example: "Body,ServiceName,Duration" |
| `denoise` | boolean | No | When true, automatically removes events matching high-frequency patterns (those accounting for >10% of sampled events) from the results. Useful for cutting through log noise to find unusual or interesting events. Adds ~1-2s of latency for the pattern sampling queries. |
| `endTime` | string | No | End of the query window as ISO 8601. Default: now. |
| `sourceId` | string | Yes | Source ID (required). Call clickstack_list_sources to find available sources. |
| `startTime` | string | No | Start of the query window as ISO 8601. Default: 15 minutes ago. If results are empty, try a wider range (e.g. 24 hours). |
| `maxResults` | number | No | Maximum number of rows to return (1-200). Default: 50. Use smaller values to reduce response size. |
| `sourceFilters` | object | No | Required filter values for sources whose configuration declares `requiredSourceFilters`. Keyed by column name (the values returned by clickstack_get_source_filter_values or the sampled `sourceFilterValues` from clickstack_describe_source).  FORMAT: { "<columnName>": ["<value1>", "<value2>"] } Multiple values for one column become a `column IN (...)` predicate.  ALL OPTION: a column may instead be pinned to every value with { "<columnName>": { "all": true } }. This is ONLY accepted for columns whose source marks them `allowAll: true` (see requiredSourceFilters in clickstack_list_sources / clickstack_describe_source, or the `allowAll` flag from clickstack_get_source_filter_values); using it on any other column is rejected. Prefer explicit values when they're known.  REQUIRED for any source whose summary in clickstack_list_sources or clickstack_describe_source includes `requiredSourceFilters`. Every column listed there must have at least one value set. Queries that omit a required column are rejected with an error telling you which columns are missing and how to recover.  PARTIAL FILTERS: when you supply values for some but not all required columns, the tool will try to auto-resolve the missing columns by looking up values cross-pruned against the ones you provided — if any missing column has exactly one remaining value it is filled in automatically. The resolved set is echoed back as `resolvedSourceFilters` in the response so you can supply it explicitly on the next call.  Ignored for sources without requiredSourceFilters: pass nothing. |
| `whereLanguage` | string ("lucene" | "sql") | No | Query language for the "where" filter. Default: lucene. IMPORTANT: the syntax in "where" MUST match "whereLanguage".   Lucene (default): Column:value, Column.mapKey:value, Column:>100   SQL:              Column = 'value', SpanAttributes['key'] = 'value'  Lucene supports comparisons (>= > < <=), wildcards (field:val*), ranges ([1 TO 5]), and map attributes via dot notation. Use "sql" for IN(...) lists, complex expressions, or function calls. IMPORTANT: Lucene field:value is a SUBSTRING match (ilike), not exact equality. field:val* is prefix-within-substring, not a true prefix match. For exact matching or reliable wildcards, use SQL: WHERE field = 'value' or WHERE field LIKE 'val%'.  Common mistake: writing Column:value (Lucene) but setting whereLanguage to "sql". If your filter uses colon syntax, leave whereLanguage as "lucene" (the default). |

### Clickstack search dashboards

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_SEARCH_DASHBOARDS`

Search dashboards by name and/or tags. Returns matching dashboards with their IDs, names, and tags. More targeted than clickstack_get_dashboard (which lists all dashboards). At least one of query or tags must be provided.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `tags` | array | No | Filter to dashboards that have ALL of these tags. |
| `query` | string | No | Search term to match against dashboard names (case-insensitive substring match). |

### Clickstack sql

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_SQL`

Execute raw ClickHouse SQL. LAST-RESORT TOOL — do NOT reach for this first.

Default to the builder tools for querying; they are more reliable and produce richer, structured results:
  • clickstack_table — aggregations, top-N, single-value KPIs, breakdowns
  • clickstack_timeseries — trends / metrics over time
  • clickstack_search — browsing individual log/trace rows
  • clickstack_event_patterns — recurring log/event pattern discovery
  • clickstack_event_deltas — attributes that differ between two row groups
  • clickstack_emerging_signals — patterns new or gone vs a baseline window
  • clickstack_trace_waterfall — one trace as a parent/child span tree
  • clickstack_trace_top_time_consuming_operations — slowest child operations in a trace

ONLY use raw SQL when the query genuinely cannot be expressed by a builder tool — i.e. it requires JOINs, sub-queries, CTEs, window functions, tables not registered as sources, or summary-type metrics (the metricTables.summary table on a metric source, which the builder tools cannot query). A single-table aggregation, top-N, time-series, or row browse is ALWAYS a builder-tool job, never raw SQL. If you are unsure, try the builder tool first and only fall back to SQL if it cannot express what you need.

Requires connectionId — call clickstack_list_sources to find connections. Call clickstack_describe_source to discover column names before writing SQL.

SOURCE FILTERS: when querying a table backed by a registered source, pass `sourceId` so the `$__sourceTable` and `$__filters` macros resolve. If that source declares `requiredSourceFilters`, you MUST also pass `sourceFilters` AND include `$__filters` in your WHERE clause. Source filter values are rendered as `<column> IN (<value>, <value2>, ...)` conditions.

Results are always returned as table rows — for time-series semantics, include a time column and ORDER BY it in your SQL.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `sql` | string | Yes | Raw ClickHouse SQL query. Always include a LIMIT clause to avoid excessive data.  TIME FILTERING (preferred path):   $__timeFilter(column)    — column >= <start> AND column <= <end> (DateTime precision)   $__timeFilter_ms(column) — same with DateTime64 millisecond precision   {startDateMilliseconds:Int64} / {endDateMilliseconds:Int64} — epoch ms parameters  LITERAL-CAST FALLBACKS:   parseDateTime64BestEffortOrNull('2024-01-01T00:00:00Z')   toDateTime64('2024-01-01 00:00:00', 9)  OTHER MACROS:   $__dateFilter(column) — Date precision   $__dateTimeFilter(dateCol, timeCol) — filters on Date + DateTime columns   $__dt(dateCol, timeCol) — alias for $__dateTimeFilter   $__fromTime / $__toTime — start/end as DateTime values   $__fromTime_ms / $__toTime_ms — start/end as DateTime64 values   $__timeInterval(column) — time bucket expression: toStartOfInterval(toDateTime(column), INTERVAL ...)   $__timeInterval_ms(column) — same with millisecond precision   $__interval_s — raw interval in seconds   $__filters — replaced with the `column IN (…)` predicates derived from the provided `sourceFilters`. Resolves to `(1=1)` when there are no source filters. REQUIRED in the WHERE clause when querying a source with `requiredSourceFilters` (see SOURCE FILTERS below). Requires sourceId.  $__sourceTable — expands to the source's database.table. Requires sourceId.   {intervalSeconds:Int64} / {intervalMilliseconds:Int64} — bucket size parameters  Example (time-series):   SELECT $__timeInterval(TimestampTime) AS ts, ServiceName, count() FROM $__sourceTable WHERE $__timeFilter(TimestampTime) AND $__filters GROUP BY ServiceName, ts ORDER BY ts  Example (table):   SELECT ServiceName, count() AS n FROM otel_logs WHERE TimestampTime >= fromUnixTimestamp64Milli({startDateMilliseconds:Int64}) AND TimestampTime < fromUnixTimestamp64Milli({endDateMilliseconds:Int64}) GROUP BY ServiceName ORDER BY n DESC LIMIT 20 |
| `endTime` | string | No | End of the query window as ISO 8601. Default: now. |
| `sourceId` | string | No | Source ID. OPTIONAL but recommended when the query targets a table backed by a registered source. Required when the source declares `requiredSourceFilters` (see clickstack_list_sources / clickstack_describe_source) or when using the `$__filters` or `$__sourceTable` macros. When set, sourceId's `connection` must match `connectionId`. |
| `startTime` | string | No | Start of the query window as ISO 8601. Default: 15 minutes ago. If results are empty, try a wider range (e.g. 24 hours). |
| `connectionId` | string | Yes | Connection ID (required). This is the only split tool that needs a connectionId instead of sourceId. Call clickstack_list_sources to find available connections. |
| `sourceFilters` | object | No | Required filter values for sources whose configuration declares `requiredSourceFilters`. Keyed by column name (the values returned by clickstack_get_source_filter_values or the sampled `sourceFilterValues` from clickstack_describe_source).  FORMAT: { "<columnName>": ["<value1>", "<value2>"] } Multiple values for one column become a `column IN (...)` predicate.  ALL OPTION: a column may instead be pinned to every value with { "<columnName>": { "all": true } }. This is ONLY accepted for columns whose source marks them `allowAll: true` (see requiredSourceFilters in clickstack_list_sources / clickstack_describe_source, or the `allowAll` flag from clickstack_get_source_filter_values); using it on any other column is rejected. Prefer explicit values when they're known.  REQUIRED for any source whose summary in clickstack_list_sources or clickstack_describe_source includes `requiredSourceFilters`. Every column listed there must have at least one value set. Queries that omit a required column are rejected with an error telling you which columns are missing and how to recover.  PARTIAL FILTERS: when you supply values for some but not all required columns, the tool will try to auto-resolve the missing columns by looking up values cross-pruned against the ones you provided — if any missing column has exactly one remaining value it is filled in automatically. The resolved set is echoed back as `resolvedSourceFilters` in the response so you can supply it explicitly on the next call.  Ignored for sources without requiredSourceFilters: pass nothing. |

### Clickstack table

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_TABLE`

Compute aggregated metrics as a table, single number, pie chart, or bar chart. Use this for grouped aggregations, top-N queries, single-value KPIs, or proportional breakdowns.

PREFER THIS over clickstack_sql. Only drop to raw SQL for things the builder tools cannot express — JOINs, sub-queries, CTEs, window functions, tables not registered as sources, or summary-type metrics (the metricTables.summary table on a metric source, which the builder tools cannot query).

Requires sourceId — call clickstack_list_sources then clickstack_describe_source first.

Use the top-level "where" to scope the entire query (e.g. filter by service). Each select item can also have its own "where" for per-metric cohort comparisons (compiles to <aggFn>If(...)). Both can be used together.

Column naming: top-level columns are PascalCase (Duration, StatusCode). Map attributes use bracket syntax: SpanAttributes['http.method']. Map attributes work in groupBy and valueExpression, including toFloat64OrZero(SpanAttributes['key']).

Shape auto-upgrade: if shape is "number", "pie", or "bar" but select has >1 item, it is transparently upgraded to "table".

── METRIC SOURCES ──
When sourceId is a metric source, each select item MUST set metricType ("gauge"|"sum"|"histogram"|"exponential histogram") and metricName (the OTel metric name). valueExpression defaults to "Value" — set it explicitly only to transform the value.
Discovery: clickstack_describe_source returns a per-kind metric-name sample; clickstack_list_metrics paginates the full catalog; clickstack_describe_metric returns attribute keys + sampled values for a single metric.
Per kind: gauge uses last_value/avg/min/max; sum uses aggFn:"increase" for counter increase (top-N capped at 20 groups when combined with groupBy), or sum/avg on the rate; histogram and exponential histogram use aggFn:"quantile" + level for percentiles, or aggFn:"count" for total bucket count.
summary metrics are not supported by the query renderer — query them with clickstack_sql against the table in the source's metricTables.summary.

SOURCE FILTERS: when the source declares `requiredSourceFilters` (see clickstack_describe_source / clickstack_list_sources), you MUST pass `sourceFilters` with at least one value for every required column.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `shape` | string ("table" | "number" | "pie" | "bar") | No | Output shape: "table" (grouped rows, default), "number" (single scalar), "pie" (pie chart), or "bar" (categorical bar chart). If "number", "pie", or "bar" is set with select.length > 1, it is auto-upgraded to "table". groupBy is ignored when shape is "number". |
| `where` | string | No | Row filter applied to ALL select items. This scopes the entire query — use it to restrict by service, severity, etc. Each select item can also have its own per-metric "where" for cohort comparisons. When both are set, they are ANDed together.  Lucene example: "ServiceName:frontend" SQL example: "ServiceName = 'frontend'" |
| `select` | array | Yes | Metrics to compute. Each item defines one aggregation column. For "number" shape, provide exactly 1 item. Example: [{ aggFn: "count" }, { aggFn: "avg", valueExpression: "Duration" }] |
| `endTime` | string | No | End of the query window as ISO 8601. Default: now. |
| `groupBy` | string | No | Column(s) or ClickHouse expression(s) to group/split by. Accepts a SINGLE entry or MULTIPLE entries as a comma-delimited list — multi-column groupBy expresses multi-dimensional breakdowns in one query (e.g. "ServiceName, SpanName, StatusMessage") instead of running one query per dimension. For "table" displayType, the result has one row per distinct combination of group values. Top-level columns use PascalCase ("SpanName", "StatusCode"). Map attributes: SpanAttributes['key'], ResourceAttributes['key'].  Arbitrary ClickHouse expressions are also allowed in groupBy — useful when you need to group by a derived column without falling back to raw SQL:   - "substring(Body, 1, 80)" — group by body prefix (log pattern bucketing)   - "toStartOfInterval(Timestamp, INTERVAL 5 MINUTE)" — explicit time bucketing in a table view, alongside another dimension (granularity only works for line/stacked_bar)   - "JSONExtractString(Body, 'event')" — parse a JSON field from the body   - "if(Duration > 1e9, 'slow', 'fast')" — coarse boolean buckets Comma splitting is bracket-aware, so multi-arg function calls work as single entries. |
| `orderBy` | string | No | Sort results by this column. When ordering by an alias that contains spaces or special characters, wrap the alias in quotes: e.g. '"P95 Latency" DESC'. |
| `sourceId` | string | Yes | Source ID (required). Call clickstack_list_sources to find available sources. |
| `startTime` | string | No | Start of the query window as ISO 8601. Default: 15 minutes ago. If results are empty, try a wider range (e.g. 24 hours). |
| `sourceFilters` | object | No | Required filter values for sources whose configuration declares `requiredSourceFilters`. Keyed by column name (the values returned by clickstack_get_source_filter_values or the sampled `sourceFilterValues` from clickstack_describe_source).  FORMAT: { "<columnName>": ["<value1>", "<value2>"] } Multiple values for one column become a `column IN (...)` predicate.  ALL OPTION: a column may instead be pinned to every value with { "<columnName>": { "all": true } }. This is ONLY accepted for columns whose source marks them `allowAll: true` (see requiredSourceFilters in clickstack_list_sources / clickstack_describe_source, or the `allowAll` flag from clickstack_get_source_filter_values); using it on any other column is rejected. Prefer explicit values when they're known.  REQUIRED for any source whose summary in clickstack_list_sources or clickstack_describe_source includes `requiredSourceFilters`. Every column listed there must have at least one value set. Queries that omit a required column are rejected with an error telling you which columns are missing and how to recover.  PARTIAL FILTERS: when you supply values for some but not all required columns, the tool will try to auto-resolve the missing columns by looking up values cross-pruned against the ones you provided — if any missing column has exactly one remaining value it is filled in automatically. The resolved set is echoed back as `resolvedSourceFilters` in the response so you can supply it explicitly on the next call.  Ignored for sources without requiredSourceFilters: pass nothing. |
| `whereLanguage` | string ("lucene" | "sql") | No | Query language for the "where" filter. Default: lucene. IMPORTANT: the syntax in "where" MUST match "whereLanguage".   Lucene (default): Column:value, Column.mapKey:value, Column:>100   SQL:              Column = 'value', SpanAttributes['key'] = 'value'  Lucene supports comparisons (>= > < <=), wildcards (field:val*), ranges ([1 TO 5]), and map attributes via dot notation. Use "sql" for IN(...) lists, complex expressions, or function calls. IMPORTANT: Lucene field:value is a SUBSTRING match (ilike), not exact equality. field:val* is prefix-within-substring, not a true prefix match. For exact matching or reliable wildcards, use SQL: WHERE field = 'value' or WHERE field LIKE 'val%'.  Common mistake: writing Column:value (Lucene) but setting whereLanguage to "sql". If your filter uses colon syntax, leave whereLanguage as "lucene" (the default). |

### Clickstack timeseries

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_TIMESERIES`

Plot metrics over time as a line or stacked bar chart. Use this when you need to visualize trends, compare time-series, or monitor metric changes over a time window.

PREFER THIS over clickstack_sql. Only drop to raw SQL for things the builder tools cannot express — JOINs, sub-queries, CTEs, window functions, tables not registered as sources, or summary-type metrics (the metricTables.summary table on a metric source, which the builder tools cannot query).

Requires sourceId — call clickstack_list_sources then clickstack_describe_source first. Each select item defines one plotted series.

Column naming: top-level columns are PascalCase (Duration, StatusCode). Map attributes use bracket syntax: SpanAttributes['http.method'].

── METRIC SOURCES ──
When sourceId is a metric source, each select item MUST set metricType ("gauge"|"sum"|"histogram"|"exponential histogram") and metricName (the OTel metric name). valueExpression defaults to "Value" — set it explicitly only to transform the value.
Discovery: clickstack_describe_source returns a per-kind metric-name sample; clickstack_list_metrics paginates the full catalog; clickstack_describe_metric returns attribute keys + sampled values for a single metric.
Per kind: gauge uses last_value/avg/min/max (or aggFn:any + isDelta:true for Prometheus-style delta); sum uses aggFn:"increase" for the counter increase, or sum/avg on the computed rate; histogram and exponential histogram use aggFn:"quantile" + level for percentiles, or aggFn:"count" for total bucket count.
TOP-N CAP: aggFn:"increase" + groupBy is capped at 20 groups by the renderer (top by max bucket sum). Narrow with where/groupBy to see other groups.
summary metrics are not supported by the query renderer — query them with clickstack_sql against the table in the source's metricTables.summary.

SOURCE FILTERS: when the source declares `requiredSourceFilters` (see clickstack_describe_source / clickstack_list_sources), you MUST pass `sourceFilters` with at least one value for every required column.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `shape` | string ("line" | "stacked_bar") | No | Chart shape. "line" for line chart (default), "stacked_bar" for stacked bar chart. |
| `where` | string | No | Row filter applied to ALL select items. Scopes the entire query — use to restrict by service, severity, etc. Each select item can also have its own per-metric "where" for cohort comparisons. When both are set, they are ANDed together. |
| `select` | array | Yes | Metrics to plot on the chart. Each item defines one series. Example: [{ aggFn: "count" }, { aggFn: "avg", valueExpression: "Duration" }] |
| `endTime` | string | No | End of the query window as ISO 8601. Default: now. |
| `groupBy` | string | No | Column(s) or ClickHouse expression(s) to group/split by. Accepts a SINGLE entry or MULTIPLE entries as a comma-delimited list — multi-column groupBy expresses multi-dimensional breakdowns in one query (e.g. "ServiceName, SpanName, StatusMessage") instead of running one query per dimension. For "table" displayType, the result has one row per distinct combination of group values. Top-level columns use PascalCase ("SpanName", "StatusCode"). Map attributes: SpanAttributes['key'], ResourceAttributes['key'].  Arbitrary ClickHouse expressions are also allowed in groupBy — useful when you need to group by a derived column without falling back to raw SQL:   - "substring(Body, 1, 80)" — group by body prefix (log pattern bucketing)   - "toStartOfInterval(Timestamp, INTERVAL 5 MINUTE)" — explicit time bucketing in a table view, alongside another dimension (granularity only works for line/stacked_bar)   - "JSONExtractString(Body, 'event')" — parse a JSON field from the body   - "if(Duration > 1e9, 'slow', 'fast')" — coarse boolean buckets Comma splitting is bracket-aware, so multi-arg function calls work as single entries. |
| `orderBy` | string | No | Sort results by this column. When ordering by an alias that contains spaces or special characters, wrap the alias in quotes: e.g. '"P95 Latency" DESC'. |
| `sourceId` | string | Yes | Source ID (required). Call clickstack_list_sources to find available sources. |
| `startTime` | string | No | Start of the query window as ISO 8601. Default: 15 minutes ago. If results are empty, try a wider range (e.g. 24 hours). |
| `granularity` | string | No | Time bucket size for the chart. Format: "<number> <unit>" where unit is second, minute, hour, or day. CORRECT: "1 minute", "5 minute", "1 hour", "1 day" WRONG:   "1m", "5min", "1h" (abbreviations are not supported)  Omit to let HyperDX pick automatically based on the time range. |
| `sourceFilters` | object | No | Required filter values for sources whose configuration declares `requiredSourceFilters`. Keyed by column name (the values returned by clickstack_get_source_filter_values or the sampled `sourceFilterValues` from clickstack_describe_source).  FORMAT: { "<columnName>": ["<value1>", "<value2>"] } Multiple values for one column become a `column IN (...)` predicate.  ALL OPTION: a column may instead be pinned to every value with { "<columnName>": { "all": true } }. This is ONLY accepted for columns whose source marks them `allowAll: true` (see requiredSourceFilters in clickstack_list_sources / clickstack_describe_source, or the `allowAll` flag from clickstack_get_source_filter_values); using it on any other column is rejected. Prefer explicit values when they're known.  REQUIRED for any source whose summary in clickstack_list_sources or clickstack_describe_source includes `requiredSourceFilters`. Every column listed there must have at least one value set. Queries that omit a required column are rejected with an error telling you which columns are missing and how to recover.  PARTIAL FILTERS: when you supply values for some but not all required columns, the tool will try to auto-resolve the missing columns by looking up values cross-pruned against the ones you provided — if any missing column has exactly one remaining value it is filled in automatically. The resolved set is echoed back as `resolvedSourceFilters` in the response so you can supply it explicitly on the next call.  Ignored for sources without requiredSourceFilters: pass nothing. |
| `whereLanguage` | string ("lucene" | "sql") | No | Query language for the "where" filter. Default: lucene. IMPORTANT: the syntax in "where" MUST match "whereLanguage".   Lucene (default): Column:value, Column.mapKey:value, Column:>100   SQL:              Column = 'value', SpanAttributes['key'] = 'value'  Lucene supports comparisons (>= > < <=), wildcards (field:val*), ranges ([1 TO 5]), and map attributes via dot notation. Use "sql" for IN(...) lists, complex expressions, or function calls. IMPORTANT: Lucene field:value is a SUBSTRING match (ilike), not exact equality. field:val* is prefix-within-substring, not a true prefix match. For exact matching or reliable wildcards, use SQL: WHERE field = 'value' or WHERE field LIKE 'val%'.  Common mistake: writing Column:value (Lucene) but setting whereLanguage to "sql". If your filter uses colon syntax, leave whereLanguage as "lucene" (the default). |

### Clickstack trace top time consuming operations

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_TRACE_TOP_TIME_CONSUMING_OPERATIONS`

Given a parent-span filter and a time window, return the child operations contributing the most cumulative time across all traces matching the parent filter. Same algorithm as the in-app "Top Most Time Consuming Operations" chart on the service dashboard.

WHAT IT DOES (two-stage, runs as one SQL):
  1. Pick distinct TraceIds where the parent span matches `parentFilter` in the window. Optionally restrict to `minParentDurationMs` to focus on slow parents.
  2. Aggregate ALL spans across those traces (excluding the matching root span itself) by (ServiceName, SpanName), ranked by `total_time_ms` DESC.

USE WHEN: investigating "where is the time going" for a slow operation. Filter to a specific (ServiceName, SpanName) pair and set `minParentDurationMs` to the threshold above which a parent span counts as "slow" for your investigation.

MULTIPLE OPERATIONS SLOW: when more than one operation shows elevated latency, call this tool ONCE PER (service, operation) and compare the top child rows across the result sets. Operations with the same top child likely share a cause; operations with different top children are independent regressions that happen to co-occur. DO NOT merge multiple operations into a single parentFilter — the cumulative rank then conflates independent investigations into one noisy answer.

RANKING METRIC: `total_time_ms = sum(Duration)` across all matching child spans. This captures the true contribution to elapsed time — a fast-but-frequent child can dominate the latency even if its p99 is unremarkable.

RETURNS: array of rows, each with `service`, `operation`, `total_time_ms`, `calls`, `in_parents` (how many parent traces contained at least one such span), `p50_ms`, `p99_ms`. Plus a `summary` block with the matched-parent count.

NEXT STEP after this tool: once a dominant slow child operation is identified, the canonical follow-up is clickstack_event_deltas with slow-vs-fast spans of THAT child operation as target/baseline (target = {where: SpanName='<slow-child>' AND Duration > X}, baseline = {where: SpanName='<slow-child>' AND Duration <= Y}). The ranked attributes surface what distinguishes slow invocations of the child operation from fast ones.

CROSS-SERVICE BREAKDOWN: this tool does NOT scope children to the parent's service. Slow cross-service calls (database, cache, upstream HTTP) surface naturally — useful for triage.

PAIR TOOL: clickstack_trace_waterfall returns ONE concrete trace as a parent/child tree. Use it for an example after this tool's aggregate breakdown has pointed you at the slow downstream operation.

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `topN` | number | No | Number of top operations to return, ranked by total_time_ms DESC. Default 20. |
| `endTime` | string | Yes | End of the parent-filter time window as ISO 8601. REQUIRED. |
| `sourceId` | string | Yes | Trace source ID. Must be a source of kind="trace". Call clickstack_list_sources to find available sources. |
| `startTime` | string | Yes | Start of the parent-filter time window as ISO 8601. REQUIRED. |
| `parentFilter` | string | Yes | SQL WHERE clause that selects the PARENT spans you want to break down. The tool finds the distinct TraceIds matching this filter, then aggregates all spans within those traces. Use SQL syntax (not Lucene). Example: "ServiceName = '<your-service>' AND SpanName = '<your-operation>'".  SCOPE TO A SPECIFIC OPERATION, not just a service. A service-only filter mixes every endpoint together and returns whatever has the loudest child-time across all parents, which usually isn't what you want. Always include a SpanName (or another operation-level discriminator). To compare two slow operations, call this tool once per operation and diff the results — two operations with similar p99 don't necessarily share a slow child. |
| `sourceFilters` | object | No | Required filter values for sources whose configuration declares `requiredSourceFilters`. Keyed by column name (the values returned by clickstack_get_source_filter_values or the sampled `sourceFilterValues` from clickstack_describe_source).  FORMAT: { "<columnName>": ["<value1>", "<value2>"] } Multiple values for one column become a `column IN (...)` predicate.  ALL OPTION: a column may instead be pinned to every value with { "<columnName>": { "all": true } }. This is ONLY accepted for columns whose source marks them `allowAll: true` (see requiredSourceFilters in clickstack_list_sources / clickstack_describe_source, or the `allowAll` flag from clickstack_get_source_filter_values); using it on any other column is rejected. Prefer explicit values when they're known.  REQUIRED for any source whose summary in clickstack_list_sources or clickstack_describe_source includes `requiredSourceFilters`. Every column listed there must have at least one value set. Queries that omit a required column are rejected with an error telling you which columns are missing and how to recover.  PARTIAL FILTERS: when you supply values for some but not all required columns, the tool will try to auto-resolve the missing columns by looking up values cross-pruned against the ones you provided — if any missing column has exactly one remaining value it is filled in automatically. The resolved set is echoed back as `resolvedSourceFilters` in the response so you can supply it explicitly on the next call.  Ignored for sources without requiredSourceFilters: pass nothing. |
| `maxParentTraces` | number | No | Safety cap on the number of distinct parent TraceIds considered. A breakdown over more than this many parent traces gets truncated to the first N. Default 100000. |
| `minParentDurationMs` | number | No | When set, only break down parent spans whose Duration ≥ this value. Use to focus the breakdown on slow parents (e.g. 1000 to only look at parents that took ≥ 1 second). |

### Clickstack trace waterfall

**Slug:** `CLICKHOUSE_MCP_CLICKSTACK_TRACE_WATERFALL`

Fetch all spans in ONE trace and return them as a parent/child waterfall, pre-ordered for human-readable display. Use this for "show me a concrete example trace" or "what happened in trace X" investigations — the tool walks the cascade for you instead of forcing the model to write self-JOINs in raw SQL.

NOT THE RIGHT TOOL when the question is "where does the time go across MANY slow traces of operation X" — that is the aggregate question, and the answer is clickstack_trace_top_time_consuming_operations (called per affected (service, operation) with `minParentDurationMs`). Use this tool only when you want a single concrete example to inspect, or after the aggregate breakdown has already identified a suspicious operation.

Two modes:
  1. Specific trace: pass `traceId`. Returns every span in that trace.
  2. Auto-pick: pass `pickFilter` + `pickBy`. The tool finds one matching trace (slowest / first_error / most_recent) and returns its full tree.

Each returned span has: depth (root=0), spanId, parentSpanId, serviceName, spanName, spanKind, durationMs, statusCode, statusMessage, timestamp, and spanAttributes. Spans are pre-order DFS — child spans follow their parent in execution order. The tool also surfaces a `summary` section with the picked TraceId, total span count, and root span info.

When the trace source has a linked logSourceId (the standard config), the response also includes a `logs[]` array of correlated log rows that share the same TraceId — sorted by timestamp, each carrying its `spanId` so the agent can attribute messages to specific spans. Disable with `includeLogs:false`. If the log source declares `requiredSourceFilters`, include its columns in `sourceFilters` too (one map shared with the trace source, matched by column name).

Prefer this over running raw SQL with JOINs on TraceId — it uses the source's configured traceIdExpression / parentSpanIdExpression / spanIdExpression so attribute extraction stays consistent with the rest of the platform.

PAIR TOOL: clickstack_trace_top_time_consuming_operations is the aggregate counterpart — given a parent-span filter, it ranks child operations by total time across MANY matching traces. Use that when the question is "where does time go in slow X" (aggregate), and use THIS tool when the question is "show me one example of a slow X" (single trace).

#### Input Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `pickBy` | string ("slowest" | "first_error" | "most_recent") | No | How to choose which trace to return when traceId is not provided.   slowest – trace with the longest single span (use for latency investigations)   first_error – earliest trace containing a span with STATUS_CODE_ERROR   most_recent – the most recent trace matching pickFilter |
| `endTime` | string | No | End of the search window as ISO 8601. Default: now. |
| `maxLogs` | number | No | Cap on correlated log rows returned (1–1000). Default 100. Has no effect when includeLogs is false or the trace source has no logSourceId. |
| `traceId` | string | No | Specific TraceId to look up. When provided, the tool fetches every span in this trace and returns them as a parent/child tree. When omitted, the tool auto-picks one trace using pickFilter + pickBy. |
| `maxSpans` | number | No | Cap on spans returned in the tree (1–2000). Default 500. A trace with more spans than this will be truncated; the response notes the truncation. |
| `sourceId` | string | Yes | Trace source ID. Must be a source of kind "trace". Call clickstack_list_sources to find available sources. |
| `startTime` | string | No | Start of the search window as ISO 8601. Default: 15 minutes ago. |
| `pickFilter` | string | No | Filter (Lucene by default, SQL via pickFilterLanguage) used to narrow which trace to auto-pick when traceId is not provided. Example shapes (Lucene): `ServiceName:<some-service>`, `ServiceName:<svc> AND SpanName:"<some-operation>"`, `StatusCode:<status-value>` (status values come from the source schema's keyColumns). Ignored when traceId is provided. |
| `includeLogs` | boolean | No | When true and the trace source has a linked logSourceId, also fetch log rows that share the same TraceId and inline them in the response as `logs[]`. Each log row carries its spanId so the agent can attribute messages to specific spans. No-op if the trace source has no logSourceId configured. |
| `sourceFilters` | object | No | Required filter values for BOTH the trace source and its linked log source (if includeLogs is true), in one map keyed by column name: { "<columnName>": ["<value1>", "<value2>"] }. Each entry is matched to each source by name against that source's `requiredSourceFilters`. Every column the trace source requires must have at least one value Logs will be skipped if any log source required filter is not provided.Call clickstack_get_source_filter_values to discover valid values. Ignored for sources without requiredSourceFilters. |
| `pickFilterLanguage` | string ("lucene" | "sql") | No | Query language for pickFilter. Default: lucene |
