subtext_url, opens the session, and walks the decisive moments with screenshots, the component tree, network, and console. ClickHouse detects; Subtext diagnoses.
This guide assumes the Subtext capture snippet is installed and capturing sessions, and that your clickstream events are already flowing into a ClickHouse table carrying a
subtext_url property. The value is stamped onto events at capture time by whatever collector feeds your warehouse (Snowplow, Hightouch, Segment, or your own inserts). ClickHouse is the store, not the source — it holds the link, it doesn’t generate it.Attach the session URL
The job here is to landsubtext_url as a first-class column and keep it fast to filter on, so any SQL result row carries a clickable session alongside it.
Give the link its own column
Add a plainString column. The value is a long, high-cardinality signed URL, so never wrap it in LowCardinality — that builds a dictionary the size of your session count and wins nothing.
When you land raw JSON
If you land whole event payloads as a JSON string and shred fields later, pullsubtext_url into its own column with a MATERIALIZED expression so it is computed once at insert and indexes like any other column:
ALTER, ClickHouse computes the expression on the fly at read time from properties; run ALTER TABLE events MATERIALIZE COLUMN subtext_url to physically write the values into existing parts so reads stop paying the extraction cost.
Query for sessions
Every filter you already run to find interesting behavior now returns a clickable session alongside it:From signal to session
However the query first surfaces a problem — a dashboard tile, an ad-hoc triage, a scheduled model — the path is the same: get from the result row to thesubtext_url, then hand it to your agent for a Detect-vs-Diagnose read. The query says these users errored or dropped; the session shows what they actually hit.
A dashboard tile spikes
A dashboard tile spikes
An error-event or drop-off tile on a ClickHouse-backed dashboard (Grafana, Metabase, or an internal panel) jumps above trend for a specific event.Pull the offending sessions straight from the events table:Grab a few links, then hand them to your agent:
Ad-hoc triage of a suspicious state
Ad-hoc triage of a suspicious state
A customer says “the page just froze,” and you want to see everyone who reached the same state.Filter to the affected users and grab the links:
A scheduled retention or funnel query flags a cohort
A scheduled retention or funnel query flags a cohort
A cohort converts or retains below trend in your weekly model, and you want the human story before you write it up.Join the cohort back to the events table for their session links:
For agents
An autonomous agent can run the whole loop against ClickHouse directly: discover the friction with SQL, extract thesubtext_url from the same rows, and hand each session to the Subtext MCP. Unlike most tools, ClickHouse ships a first-party data MCP — mcp-clickhouse (ClickHouse/mcp-clickhouse, on PyPI). It connects to your cluster with CLICKHOUSE_HOST / CLICKHOUSE_USER / CLICKHOUSE_PASSWORD and is read-only by default (CLICKHOUSE_ALLOW_WRITE_ACCESS=false), which is exactly what a review agent wants: it can select session links but cannot mutate data.
1
Discover who is affected
list_databases and list_tables enumerate the cluster and find the events table (paginated, with like / not_like filters). Then run_query aggregates to find the worst hotspot:2
Extract the session URL
run_query does discover and extract at once — the filter that finds the behavior also returns the link:3
Hand off to Subtext
For each URL, call
review-open(session_url=<subtext_url>) on the Subtext MCP and step through the session for a Detect-vs-Diagnose read: the query says they errored; the session shows what they actually hit.subtext_url only round-trips if the collector attached it at capture time — rows written before the collector started stamping subtext_url carry an empty string, so always filter subtext_url != ''. Keep run_query in read-only mode; a review agent never needs write access. And because it is a high-cardinality column, always LIMIT and order your queries — don’t pull every session.Related
- Session Review overview — what your agent does once a session is open.
- Install the capture snippet — required before any session URL exists to attach.

