About kubesense-rum
A skill for analyzing real-user monitoring data through the KubeSense MCP server, intended for investigating web application experience and performance. It distinguishes six event signals and directs users to discover fields before choosing aggregate analysis or raw-event search.
Key features
- 01Covers six RUM event signals
- 02Discover field catalogs before filtering
- 03Aggregate counts, percentiles, and trends
- 04Search and page through raw events
- 05WHERE filters and custom attributes
Use cases
- 01Analyze page load time and LCP
- 02Find errors, crashes, and related sessions
- 03Locate slow or failing network requests
- 04Inspect long tasks blocking the UI
MCP support
Requires the KubeSense MCP server and explains how to use its RUM tools, including get-fields, analyze-telemetry, and search-rum. RUM queries require an API key with the appropriate RBAC grant.
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ kubesense-ai/ kubesense-mcp-skills --skill kubesense-rumAfter installing, type/kubesense-rumin Claude Code to confirm it loaded.
Bundled filesreferences/dashboard-recipes.mdreferences/field-catalog.md
Details
From the project READMEKubeSense RUM
Requires the KubeSense MCP server; see kubesense-mcp for
connection, auth, and the shared WHERE-clause reference. RUM queries resolve RBAC against
the RUM module (rbac(rum)). A key without it fails with
rbac(rum): this API key does not have access to this resource; ask for the RUM grant
rather than retrying. search-rum needs the same grant. So do the RUM list routes it reads through
(POST /rum/explorer/{sessions,views,actions,errors,resources,longtasks}), which also
guard the RUM pages.
Signals
RUM has six signals, one per event type. Each reads its own table.
| Signal | One row is | Typical question |
|---|---|---|
rum_session | a user session | how many sessions, how many had errors |
rum_view | a finished page or screen view | LCP, load time, views per page |
rum_action | a click, tap or scroll | which actions are slow or frustrating |
rum_error | a captured error or crash | which errors, how many crashes |
rum_resource | an HTTP or asset request | slow or failing requests, by host |
rum_long_task | a main-thread long task | what blocks the UI |
rum_view counts only ended views of non-system sessions. Every row is placed in time by
its own start_timestamp, so a long session is counted in the window where it started.
Tools
| Tool | Use when | Notes |
|---|---|---|
get-fields (signal: "rum_view", …) | Always, first | Field catalog for that event type |
analyze-telemetry | Counts, rates, percentiles, trends | One sub-query per signal, set with selectedMode |
search-rum | Reading individual sessions, errors, requests | Raw rows for one signal; see search-rum |
There is no analyze-rum. analyze-logs and analyze-traces do not take RUM signals.
To compare RUM with logs or traces, or to compute a rate, put several sub-queries in one
analyze-telemetry call and add a formula; see
multi-query.
Discover First
Call get-fields with the signal you will query. The field set differs per event type:
lcp exists only on rum_view, error_type only on rum_error, and view_name allows
LIKE on rum_view but not elsewhere. The full catalog for every event type is in
references/field-catalog.md; use it to check a name
before the call, and get-fields as the authority for the deployment you are talking to.
get-fields for RUM returns the static catalog only, in the usual TSV:
- The header line is
# signal=rum_view total_count=<N> truncated=<bool> note=RUM has no attribute-key scan; .... There is noattribute_scan_error. The note says to type a custom attribute as@key(no LIKE on attributes) and a feature flag asfeature_flag.<key>. - Operators come as sets (
# ops.o1=EQ|NEQ|IN|NIN|LT|GT|LTE|GTE). A field that allows LIKE has a set that includesLIKE/ILIKE; an exact field such asuser_emaildoes not. Trust the set over any assumption about the field. - A trailing
notescolumn appears when some field has a note, for examplefilter-only: use it in WHERE only (milliseconds); aggregate the base field lcp instead; platform: browseronlcp_msandfilter-only: not groupable; platform: browseronlcp_target_selector. enum_valuescarriesseverity'scrashanderror.- Pass
searchto narrow the list (search=lcpreturnslcp,lcp_ms,lcp_sandlcp_target_selector).
There is no attribute-key discovery. When the user needs a custom attribute, ask for the
key or infer it from their instrumentation, and type it with the @ prefix. A wrong key
matches nothing and returns an empty result, not an error.
search-rum
search-rum reads raw rows for one signal: the same rows the Data Explorer List panel
shows. Use it to look at individual events (which sessions crashed, what the slowest
requests were, the text of an error). Use analyze-telemetry for anything that is a number:
counts, rates, percentiles and trends. Reading rows to compute a count is slower and cuts
off at the page limit.
Call get-fields with the same signal first. where and required_fields take catalog
labels, @key for an attribute and feature_flag.<key> for a flag (views and errors only).
from_time, to_time and signal are required. There is no clusters argument; passing
one is refused. Scope to an app with application_id = "<id>" or env = production.
The slowest failed requests in the last hour, 20 per page
{
"signal": "rum_resource",
"from_time": "2026-10-20T10:00:00Z",
"to_time": "2026-10-20T11:00:00Z",
"where": "status_code >= 500",
"sort_by": "duration_ms",
"sort_order": "desc",
"page_size": 20
}
Arguments beyond the shared search ones: sort_by (catalog label; default
start_timestamp), sort_order (asc or desc, default desc) and cursor.
required_fields picks columns; empty gives the type's defaults.
| Signal | Default columns |
|---|---|
rum_session | start_timestamp, session_type, time_spent_ms, view_count, error_count, action_count, frustration_count, first_view_name, last_view_name |
rum_view | start_timestamp, view_name, frustration_count, loading_type, loading_time_ms, view_time_spent_ms, country, browser_name |
rum_action | start_timestamp, action_type, action_name, frustration_type, view_name |
rum_error | start_timestamp, view_name, error_source, error_message |
rum_resource | start_timestamp, view_name, resource_url, duration_ms, response_size, status_code, resource_type |
rum_long_task | start_timestamp, duration_ms, view_name |
The defaults use the _ms twins. A raw duration you request by name (duration) comes
back in nanoseconds; ask for duration_ms or duration_s instead.
Output is TSV. The first line is
# total_count=unknown returned_rows=<N> truncated=<bool> next_cursor=<c> window_clamped=<bool> ...,
followed by # notice=<n> lines when something needs saying, a header, and one line per
row. Every row also carries application_id, session_id, row_id (the event's own id),
timestamp, has_replay (set when the session has a stored replay) and severity (errors: error or crash).
Whatever required_fields you ask for, every row also carries the columns that identify it, always present: sessions application_id, session_id, session_start, session_has_replay; views add view_id, view_start; actions action_id, action_start; errors error_id, error_start, is_crash; resources resource_id, resource_start; long tasks task_id, task_start (each with view_id except sessions).
Paging. There is no total (total_count=unknown). While next_cursor is non-empty,
call again with the same from_time, to_time, where, sort_by, sort_order,
page_size and required_fields, and cursor=<next_cursor>. The cursor is a page token
bound to the query, so a cursor replayed under a different query is refused.
- Paging is page-based with no depth limit, for every
sort_by. There is no keyset. page_sizedefaults to 10 and is clamped to 200. The Data Explorer List panel loads 50 rows per page.
What a row is
- Rows match the RUM page tables. Sessions and views are deduplicated. Live (still open) views are listed.
- Actions, errors, resources and long tasks are grouped by id: one row per id, start is the first occurrence, resource durations are medians and long-task durations are averages.
rum_errorlists only errors (type = 3).- Blob fields cannot be listed or sorted by:
error_stack,handling_stack,threads,graphql,display,tags. - Array fields (
service,state,city,network_connectivity,ip,action_id) cannot be sorted by. - Each cell in the TSV output is cut at 2048 bytes. The list API itself does not cut strings.
Window. The window is clamped and snapped by the RUM data window. A from_time older
than raw retention (30 days for sessions and views, 7 days for the rest) is moved up to the
retention edge, and the window widens to the bucket boundaries of the RUM data. The header
says window_clamped=true when the window changed. A window entirely older than retention
is left as asked.
No read-budget errors. The list reads have no read-budget limit. A slow read can still
time out; shorten the window or filter on application_id, env or session_id.
WHERE Syntax
The shared syntax from kubesense-mcp applies, with the field names from the RUM catalog.
env = production AND browser_name = Chrome
severity = crash AND country IN (US, DE)
view_name ILIKE "%checkout%" (rum_view only)
lcp_ms > 2500 AND browser_name = Chrome (rum_view)
status_code >= 500 AND resource_host = api.example.com (rum_resource)
error_count > 0 (rum_session)
feature_flag.new_checkout = true (any event type)
@checkout.step = payment (attribute)
Quote values that contain /, :, % or a space. Write NOT (x IN (...)), not
x NOT IN (...).
Rules
- LIKE and ILIKE only on fields whose
LIKEcolumn saysyesin the catalog. Those are the low-cardinality columns (env,country,error_type,action_name,view_nameonrum_view). Everything else (user_id,view_url,resource_url,error_message,view_nameon the other event types) takes=andIN. A LIKE on them is refused withLIKE is not supported on <field>; use = or IN. - No LIKE on
@attributefields. Attributes take=,!=,IN,<,>,<=and>=. Comparisons need a number.@plan LIKE ...is refused withoperator "LIKE" not allowed for rum_view attribute field "plan". - Array fields filter only.
service,state,city,network_connectivityandiponrum_session,iponrum_view, andaction_idonrum_error,rum_resourceandrum_long_taskhold lists.=andINtest membership. They cannot be grouped or measured. - Durations are nanoseconds. Raw columns (
lcp,duration,loading_time,time_spent, …) are stored and returned in nanoseconds, and cannot be a group-by. Each has an_msand an_stwin (lcp_ms,duration_s) that is filter-only: use it inwhere(lcp_ms > 2500filters at 2.5 seconds), never as an aggregation field, group-by or list column. To aggregate a duration, use the base field (p75oflcp); the result is nanoseconds. - Group-by is
nofor free-search ids and text (session_id,view_id,trace_id, …). Group by a low-cardinality field instead. Five are groupable and return the top N groups (the page size):error_message(grouped by its first 200 characters),fingerprint,issue_id, andview_url/resource_url(grouped by the path, without query string or fragment). Filters still read the full value, so a URL group label is a path, andview_url = "<path>"matches only URLs that have no query string. feature_flag.<key>filters on every event type. It groups or measures only onrum_viewandrum_error, where the flag value is stored on the row. On the other four it is stored per session, so a group-by there is refused. Keys are case-sensitive and values are text (true,dark,5). A group-by on a flag key (feature_flag.new-checkout) is confirmed onrum_view. Thewhereform follows the engine source and has not been run against a live server.- Attributes need
@inwhere(@checkout.step) andis_attribute: truewithout@ingroup_by_fields/fields. There is no discovery for the key. severityonrum_erroriscrashorerror, lowercase. It is the label foris_crash; typeseverity, notis_crashas a string.- Boolean fields (
session_is_active,slow_rendered) taketrueorfalse. - Platform-specific fields are not enforced.
lcpis browser-only andbattery_levelis mobile-only. Filtering on one for the wrong application type matches nothing and raises no error. value_operationisrow_count,unique_count,avg,sum,min,max,p50,p75,p90,p95orp99.row_counttakes no field. Everything else takes exactly onefieldsentry. The numeric operations need a number field or an attribute;unique_counttakes any non-list field.alert_mode,group_by_present_onlyandlookback_secondsare refused for RUM.
analyze-telemetry
Set selectedMode to the signal. Omitting it makes the sub-query a metrics query.
p90 LCP per page, Chrome only, in milliseconds
{
"from_time": "2026-10-20T10:00:00Z",
"to_time": "2026-10-20T11:00:00Z",
"query_type": "instant",
"queries": {
"A": {
"selectedMode": "rum_view",
"where": "browser_name = Chrome",
"group_by_fields": [ { "field": "view_name", "is_attribute": false } ],
"value_operation": "p90",
"fields": [ { "field": "lcp", "is_attribute": false } ],
"sort_direction": "DESC",
"limit": 10
}
}
}
Crashes per hour by error type
{
"from_time": "2026-10-20T00:00:00Z",
"to_time": "2026-10-21T00:00:00Z",
"query_type": "range",
"queries": {
"A": {
"selectedMode": "rum_error",
"where": "severity = crash AND env = production",
"group_by_fields": [ { "field": "error_type", "is_attribute": false } ],
"value_operation": "row_count"
}
}
}
Share of sessions that hit an error — two queries and a formula, both ungrouped:
{
"from_time": "2026-10-20T10:00:00Z",
"to_time": "2026-10-20T11:00:00Z",
"query_type": "instant",
"queries": {
"A": { "selectedMode": "rum_session", "value_operation": "row_count" },
"B": { "selectedMode": "rum_session", "where": "error_count > 0", "value_operation": "row_count" },
"C": { "query_type": "formula", "expression": "(B/A)*100" }
}
}
Slowest hosts by p95 request time, failed requests only
selectedMode: rum_resource, where: "status_code >= 500",
group_by_fields: [{"field":"resource_host"}], value_operation: p95,
fields: [{"field":"duration"}] (nanoseconds)
Errors by feature flag value
selectedMode: rum_error, group_by_fields: [{"field":"feature_flag.new_checkout"}],
value_operation: row_count
Sessions by a custom attribute
selectedMode: rum_session, group_by_fields: [{"field":"checkout.step","is_attribute":true}],
where: "@checkout.step IN (cart, payment)"
Dashboard Panels
A stored panel query and the analyze call name the event type differently:
| Field | Value | |
|---|---|---|
| analyze-telemetry sub-query | selectedMode | rum_view |
| dashboard panel query | selectedMode + rumEventType | "rum" + "view" |
A panel with selectedMode: "rum_view" fails validation, and "rum" without
rumEventType is refused on save. The panel shape, with a _ms measure and its unit, is
in kubesense-dashboards. Test the query
with analyze-telemetry first, then translate it.
Panel recipes for the usual RUM questions (unique users, crash-free users, Web Vitals good/poor shares, Apdex, top errors and URLs, ANR rate) are in references/dashboard-recipes.md.
Limits
- Retention. Raw RUM rows are kept 7 days for
rum_action,rum_error,rum_resourceandrum_long_task, and 30 days forrum_sessionandrum_view, by default. A longer window returns only what is still stored (search-rumreports it aswindow_clamped=true); deployments can change the retention. - Row reads are bounded per call.
search-rumreturns at most 200 rows a call and has no total. Paging has no depth limit. For questions that are a number, aggregate instead. - No alerts, SQL or SPL. RUM is not a datasource for alert rules,
execute-sqlorexecute-spl. Say so rather than approximating with logs. - No attribute-key discovery. See Discover First.
Failure Messages
Seen from a live server:
| Message | Cause |
|---|---|
unknown field "services" for signal=rum_session ... | A storage name; type the catalog name (service) |
analysis failed: cannot group by lcp_ms | Rule 4: _ms / _s twins are filter-only; aggregate the base field |
analysis failed: LIKE is not supported on user_email; use = or IN | Rule 1 |
operator "LIKE" not allowed for rum_view attribute field "plan" — use one of: EQ, NEQ, ... | Rule 2 |
rbac(rum): this API key does not have access to this resource | The key lacks the RUM module |
Worded from the kubeapi source, not seen live:
| Message | Cause |
|---|---|
<field> is not a filterable field for <type> | The field belongs to another event type, or is hidden |
<field> holds a list and cannot be grouped or measured; filter on it instead | Rule 3 |
cannot group by <field>; it is a duration in nanoseconds | Rule 4, on a raw column such as lcp |
cannot group by <field> | Rule 5 |
feature flags are stored per session for <type>; filter on them instead | Rule 6 |
<op> needs a numeric field; <field> is not numeric | Measure on a string or boolean |
rum is not configured | The deployment has no RUM database |