kubesense-mcp
The KubeSense MCP tool layer — connection and auth, the full 38-tool inventory, tool selection, the discovery-first rule, the catalog-label field contract shared by every logs/traces query, WHERE syntax, multi-datasource formula queries, and how to read the TSV/columnar output formats. Read this when a tool returns a field-name or WHERE error.
How do I install this agent skill?
npx skills add https://github.com/kubesense-ai/kubesense-mcp-skills --skill kubesense-mcpIs this agent skill safe to install?
- Gen Agent Trust Hubpass
This skill provides technical documentation and instructions for the KubeSense Model Context Protocol (MCP) layer. It defines tool schemas, query syntax, and authentication procedures for interacting with observability data. No malicious patterns or security risks were identified.
- Socketpass
No alerts
- Snykpass
Risk: LOW · No issues
What does this agent skill do?
KubeSense MCP
The tool layer every KubeSense query skill sits on. This skill owns the shared contract — field names, WHERE syntax, output formats — so the per-signal skills don't each have to redefine it.
Routing
| The user is asking… | Read |
|---|---|
| logs, error messages, grep | kubesense-logs |
| latency, p99, requests, spans, which hop broke | kubesense-traces |
| CPU, memory, disk, PromQL | kubesense-metrics |
| what's running, pod restarts, what changed | kubesense-infra |
| alerts — list, investigate, create | kubesense-alerts |
Connection
Served by kubeapi at /mcp over Streamable HTTP, mounted outside the /api group.
Same credentials as the REST API.
# API key — recommended for agents, does not expire
claude mcp add --scope user --transport http kubesense \
https://<your-kubesense-host>/mcp \
--header "x-api-key: <your-api-key>"
# JWT access token — expires
claude mcp add --scope user --transport http kubesense \
https://<your-kubesense-host>/mcp \
--header "Authorization: Bearer <access-token>"
Liveness: GET /mcp_health. To confirm who you are authenticated as, call
get-current-user — no arguments, cheap, resolved from the request credentials.
RBAC is resolved per tool call against the modules logs, traces,
infrastructure (metrics + nodes/pods), and alerts. A permission error names the
module. analyze-telemetry resolves every module its sub-queries touch.
Set MCP_LOG_LEVEL=debug server-side to log per-call argument payloads without raising
the global log level.
Tools (38)
Discovery — call before querying
| Tool | Returns |
|---|---|
get-trace-or-log-fields | Field catalog for logs or traces in a window |
get-available-metrics | Metric names |
get-metric-labels | Label names on one metric |
Query
| Tool | Returns |
|---|---|
search-logs | Raw log rows (default 10) |
search-traces | Raw span rows (default 10) |
analyze-logs | Aggregated log series |
analyze-traces | Aggregated span series |
analyze-metrics | PromQL result |
analyze-telemetry | Multiple queries + formulas in one call |
get-distributed-trace | Full span tree for one trace_id |
execute-sql | Rows from a raw ClickHouse SELECT over logs or traces |
validate-sql | Dry-run verdict for a SQL query, plus the SQL that would run |
validate-spl | Dry-run verdict for an SPL query, plus the SQL it compiles to |
execute-spl | Rows from an SPL pipeline over logs |
execute-sql / validate-sql are the escape hatch for what analyze-* cannot express —
joins, CTE chains, window functions, arithmetic across aggregates. They take the same
catalog labels as everything else, plus a few fields discovery does not list. Refused for
scope-restricted roles. See kubesense-sql.
A filter is never the reason to reach for SQL. If the condition fits the where string
(the Advanced query language), use search-* / analyze-*: the server compiles it
to use the indexes, while SQL runs it as typed — a bare body LIKE '%…%' in SQL scans
every log line in the window.
execute-spl / validate-spl are the Logs explorer's piped SPL language, logs only. Validate
first: SPL's parser accepts unknown fields AND unknown functions, so a wrong name compiles and only
fails once execute-spl has paid for the scan. Note SPL takes STORAGE column names
(level, pod_name, cluster) — the inverse of the catalog labels every other tool here requires —
and its parser accepts unknown fields and functions, so validating is the only way to find out a
query is broken before it is run. See kubesense-spl.
Inventory — see kubesense-infra
list-clusters · list-nodes · get-node-detail · list-pods · get-pod-detail ·
list-workloads · get-workload-detail · list-issues · get-infra-issues ·
get-recent-changes
Alerts — see kubesense-alerts
list-alerts (external Alertmanager) · list-active-alerts (native store, firing) ·
list-alert-rules (rule definitions) · get-alert-details · get-alert-history ·
list-notification-channels · find-investigation-for-alert
Identity
get-current-user — returns username, name, email, role, auth_type. Note
username and email are frequently different; ownership fields such as
list-alert-rules' created_by match on username.
Validation — checks a document, stores nothing
validate-alert-json · validate-dashboard-json
Both take a document (a JSON object, or a string containing one) and report every
schema problem at once as path / rule / message, where path is a JSON Pointer to
the value to fix. Read-only and free to call repeatedly. Run whatever you assembled by
hand through the matching one before creating it or handing it over.
Write — mutates state
create-alert · create-dashboard
Both carry write annotations and expect user approval. State exactly what you are about to create before calling. Each validates against the same schema as the tool above and refuses with the same findings, so validating first turns a refusal into a fix.
Discovery-First Rule
Never guess a name. Not a field, metric, cluster, or label.
logs / traces: get-trace-or-log-fields → search-* / analyze-*
metrics: get-available-metrics → get-metric-labels → analyze-metrics
clusters: list-clusters → anything with a "clusters" filter
Two reasons this matters more than usual here:
- Field names are rejected with an error naming the correct label — good, but only if you read it rather than retrying.
- A wrong cluster name or metric name is not an error — it returns an empty result indistinguishable from "the value is zero".
Pass the same time window to get-trace-or-log-fields that you will use for the
query: attribute keys are window-scoped and differ across windows.
The Field-Name Contract
Logs/traces inputs — where, required_fields, group_by_fields, fields, sort_by —
accept catalog labels only. Storage column names are rejected:
field "pod_name" is a storage column; use the catalog label "instance" instead
(call get-trace-or-log-fields to see all labels)
unknown field "service" for signal=logs; call get-trace-or-log-fields to discover
valid fields (attributes carry an @ prefix)
| Concept | Logs | Traces |
|---|---|---|
| severity / outcome | type (ERROR — UPPER) | status (error — lower) |
| pod | instance | instance |
| container | container | container |
| node | node | node_name |
| service identity | not available — use workload | service |
| workload | workload | workload |
| HTTP method | — | method |
| HTTP status | — | status_code |
| endpoint | — | resource |
| span role | — | role |
| protocol | — | protocol |
| message text | body | not available |
| latency | — | duration |
The full per-signal tables, including hidden-but-accepted fields and every storage mapping, are in references/field-catalog.md.
[!WARNING] The two signals disagree on
node/node_nameand on severity name and casing, andserviceexists only on traces. Carrying a field name from one signal to the other is the most common failure.
Read enum casing from the enum_values column of discovery output, never the
example column — the example generator silently returns blanks for several enum fields
(logs type; traces method, protocol, status_code).
WHERE Syntax
type = ERROR AND namespace = production
status = error AND role = server
namespace IN (production, staging)
NOT (env_type = dev)
body ILIKE "%timeout%"
duration > 500
@user.id = abc-123
Operators the parser accepts: = != < > <= >= LIKE ILIKE
SUBSTR_ILIKE IN — combined with AND / OR / NOT and parentheses.
[!WARNING] Discovery's
operatorscolumn over-advertises.HAS_TOKEN,HAS_ALL,HAS_ANY,LIKE_AND,ILIKE_LOG,IS_IP_ADDRESS,EXISTappear in the legend but have no WHERE-string syntax.body HAS_TOKEN oomis a parse error.exist(@field)is broken on both signals (it emits a value-bearingEXISTthe validator rejects) — there is no working existence check.
Quoting. Bare values may contain only letters, digits, _, ., -. Anything with a
slash, colon, %, or space must be double-quoted:
resource = "/api/v1/checkout" ✓
timestamp > "2026-07-30T10:00:00Z" ✓
resource = /api/v1/checkout ✗ parse error
NOT IN is not a leaf operator. Write NOT (namespace IN (kube-system, default)).
Attributes carry @ in WHERE — but only on logs. On traces, @attr filters are
rejected as unknown fields (a server-side defect; the tool descriptions claim otherwise).
Trace attributes still work in group_by_fields / fields / required_fields /
sort_by via {"field": "db.system", "is_attribute": true} — no @ there, for either
signal.
duration is millisecond-in, nanosecond-out — see
kubesense-traces.
Choosing a Tool
| Question | Tool |
|---|---|
| "show me recent errors" | search-logs |
| "how many errors in the last hour" | analyze-logs, row_count |
| "p99 latency for checkout" | analyze-traces, p99 over duration |
| "why did this request fail" | get-distributed-trace |
| "CPU usage" | get-available-metrics → analyze-metrics |
| "error rate as a percentage" | analyze-telemetry with a formula |
| "what's running / what's broken / what changed" | inventory tools — one call for what would otherwise be several queries |
Aggregate with analyze-*, don't paginate search-*. Reading raw rows to compute a
count is slower, costs far more tokens, and — because paged scans are ordered
hour-bucket-first across clusters — is not even guaranteed complete.
query_type on every analyze-*: range = time series (trends), instant = one value
per series (totals, current state, top-N).
Time ranges are RFC3339 (2026-07-30T10:00:00Z). Start narrow (15–30 min) and widen.
Discovery tools default to the last hour; inventory tools too.
Multi-Datasource Queries and Formulas
analyze-telemetry runs several queries in one call and composes them. This is the only
way to get a ratio, error rate, or percentage.
{
"from_time": "2026-07-30T10:00:00Z",
"to_time": "2026-07-30T11:00:00Z",
"query_type": "instant",
"queries": {
"A": { "selectedMode": "traces", "value_operation": "row_count" },
"B": { "selectedMode": "traces", "where": "status = error", "value_operation": "row_count" },
"C": { "query_type": "formula", "expression": "(B/A)*100" }
}
}
Queries are keyed by single-letter labels. Formula entries use
"query_type": "formula" with an expression referencing other labels. See
references/multi-query.md for cross-signal examples.
Reading the Output
Three shapes, all designed to save tokens:
TSV (search-*, all inventory and alert list tools) — a # key=value metadata line,
then a header row, then one row per record. Empty cell = field absent. A ## name line
starts a labelled sub-table (e.g. ## services, ## silences).
Columnar JSON (all analyze-*) — results keyed by query label:
range: one sharedtimestampsarray (Unix seconds, emitted once), and each series carries a barevaluesarray aligned positionally —values[i]is the value attimestamps[i],null= no data in that bucket.instant: notimestamps; each series carries a single scalarvalue.total_series/truncatedreport how many non-zero series existed before the top-N cap.
Record with sub-tables (get-*-detail) — field<TAB>value lines for scalars, ## name
TSV blocks for nested arrays, compact JSON for maps. Empty fields omitted.
Timestamps are always RFC3339 UTC at millisecond precision — exactly the format
from_time/to_time accept, so a cell can be copied straight back as a cursor.
Pagination
search-logs / search-traces have no page or offset. You page by narrowing
to_time; the window is half-open so the cursor row isn't repeated.
Take the minimum timestamp from the page (timestamp for logs, start_timestamp for
traces) and pass it verbatim as the next to_time. Copy the millisecond precision exactly.
Inventory and alert tools do paginate conventionally, with page/page_size (default
50) or limit/offset.
See the per-signal skills for the ordering caveats that make exhaustive raw scans unreliable — and prefer aggregation whenever the answer is a number.
Rules
- Discover before querying — fields, metrics, clusters. Never guess.
- Pass the same window to
get-trace-or-log-fieldsthat you'll query. - Catalog labels only. On a field error, read the suggested label; don't retry the same name.
- Never carry a field name across signals (
nodevsnode_name;typevsstatus;serviceis traces-only). - Enum casing from
enum_values, notexample. - WHERE operators:
= != < > <= >= LIKE ILIKE SUBSTR_ILIKE INonly. Quote values with/,:,%, or spaces.NOT (x IN (...)), neverx NOT IN (...). value_operationisrow_count— nevercount.analyze-*for numbers,search-*only to read records.analyze-telemetry+ formula for ratios and percentages.- An empty result is not zero — verify names and widen the window before reporting a value.
create-alertmutates state: say what you're creating, and calllist-notification-channelsfirst or it will notify nobody.
How can the creator link this skill?
Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.
<a href="https://skillzs.dev/skills/kubesense-ai/kubesense-mcp-skills/kubesense-mcp">View kubesense-mcp on skillZs</a>