/exploring-mcp-sessions
Investigate individual PostHog MCP sessions — the sequence of tool calls a single agent made in one run, what it was trying to do, and where it went wrong. Use when the user asks "what did this MCP session do?", "show me the tool calls for session X", "what was the agent's
$ npx -y skills add posthog/posthog --skill exploring-mcp-sessions --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/exploring-mcp-sessions
Context preview
The summary Claude sees to decide when to auto-load this skill.
Investigate individual PostHog MCP sessions — the sequence of tool calls a single agent made in one run, what it was trying to do, and where it went wrong. Use when the user asks "what did this MCP session do?", "show me the tool calls for session X", "what was the agent's
SKILL.md
exploring-mcp-sessions.SKILL.mdname: exploring-mcp-sessions
description: >
Investigate individual PostHog MCP sessions — the sequence of tool calls a
single agent made in one run, what it was trying to do, and where it went
wrong. Use when the user asks "what did this MCP session do?", "show me the
tool calls for session X", "what was the agent's goal?", "which sessions had
errors?", "who is connecting to my MCP?", or pastes an MCP analytics sessions
URL.
Exploring MCP sessions
An MCP session is one agent run: the set of `$mcp_tool_call` events sharing a `$session_id`, ordered by `timestamp`.
Listing sessions, reading a session's tool calls, and summarising its goal each have a **typed tool** — reach for those first. Drop to HogQL only for the three things the typed tools genuinely can't do (see [When to drop to SQL](#when-to-drop-to-sql)). The full `$mcp_*` property schema and query recipes live in the shared reference: [`models-mcp.md`](../../../posthog_ai/skills/querying-posthog-data/references/models-mcp.md).
Tools
| Tool | Purpose | | ------------------------------------------------ | ---------------------------------------------------------- | | `posthog:mcp-analytics-sessions-list` | List sessions — one row per session, newest first | | `posthog:mcp-analytics-sessions-tool-calls` | One session's tool calls, chronological | | `posthog:mcp-analytics-sessions-generate-intent` | LLM summary of a session's goal (cached after first call) | | `posthog:execute-sql` | Errored sessions, effective tool names, cross-session cuts |
The three `mcp-analytics-*` tools are gated behind the `mcp-analytics` flag and run the same code as the sessions UI, so results match the screen. If they aren't in your tool list, the project doesn't have the flag — fall back to `posthog:execute-sql`, which is ungated.
The date-window trap — read this first
The two detail tools default to a **7-day lookback**. A session you found in a list that reaches further back will come back **empty** unless you pass its `session_start` as `date_from`:
- `posthog:mcp-analytics-sessions-tool-calls` — `date_from` is an absolute ISO
timestamp; pass the `session_start` you got from `posthog:mcp-analytics-sessions-list`.
- `posthog:mcp-analytics-sessions-generate-intent` — same `date_from` query
param, same reason.
Empty tool calls for a session that visibly exists is almost always this, not a data problem. Carry `session_start` forward from the list row.
Workflow: list recent sessions
posthog:mcp-analytics-sessions-list
{ "date_from": "-7d", "order_by": "-session_start", "limit": 100 }Each row: `session_id`, `tool_calls`, `session_start`, `session_end`, `tools_used`, `mcp_client_name`, `distinct_id` (+ resolved `person_email` / `person_name`), and `intent` (empty until generated). Response is `{ results, has_next }` — page with `limit` / `offset`.
Three sharp edges:
- **`order_by` takes column names, not response field names.** Sort call volume
as `tool_call_count` (not `tool_calls`). `duration_seconds` sorts fine even though it isn't returned. An unrecognised key **silently** falls back to newest-first — so verify the order you got is the order you asked for. Valid: `session_id`, `session_start`, `session_end`, `duration_seconds`, `tool_call_count`, `mcp_client_name`, `distinct_id`; prefix `-` to descend.
- **There is no error filter and no error count on a session row.** "Which
sessions had errors?" is a SQL question — see below.
- **`distinct_id_count` is always `0`.** The field is in the response but the
backend never populates it, so don't read it as "one distinct id per session" — it says nothing. To count distinct ids in a session, use SQL.
`search` does a case-insensitive substring match across `session_id`, `distinct_id`, `mcp_client_name`, and `tools_used`.
Workflow: read one session's tool calls
posthog:mcp-analytics-sessions-tool-calls
{ "id": "<session_id>", "date_from": "<session_start>", "limit": 500 }Chronological `tool_name`, `intent`, `timestamp`, `duration_ms`, `is_error`, `error_message` — read top to bottom to reconstruct the run. `limit` defaults to 500 (also the max), which is the whole page for almost every session; `has_next` tells you if more remain.
**Caveat: `tool_name` here is the raw `$mcp_tool_name`.** Unlike the tool-quality and tool-detail tools, this endpoint does not resolve the inner tool of a single-exec wrapper call, so wrapper calls show the wrapper. When the inner tool is what matters (comparing against a tool-quality ranking, tracing a specific tool through a run), use the SQL recipe below instead. The same applies to `tools_used` on the session list.
Workflow: summarise the agent's goal
posthog:mcp-analytics-sessions-generate-intent
{ "id": "<session_id>", "date_from": "<session_start>" }Summarises the session's recorded `$mcp_intent` values via an LLM and persists the result; later calls return the cached summary. Returns `{ session_id, intent }`. A 503 means LLM summarisation isn't configured — fall back to reading the raw `$mcp_intent` values from the tool-call list.
When to drop to SQL
Four cases, all via `posthog:execute-sql`, which — unlike the typed tools above — is **not** gated behind the `mcp-analytics` flag.
**1. The project doesn't have the `mcp-analytics` flag.** The typed tools simply won't be in your tool list. Everything below still works; this query is the plain session listing:
SELECT
$session_id AS session_id,
min(timestamp) AS session_start,
max(timestamp) AS session_end,
dateDiff('second', min(timestamp), max(timestamp)) AS duration_seconds,
count() AS tool_calls,
countIf(toBool(properties.$mcp_is_error)) AS errors,
any(properties.$mcp_client_name) AS client
FROM events
WHERERead more
name: exploring-mcp-sessions description: > Investigate individual PostHog MCP sessions — the sequence of tool calls a single agent made in one run, what it was trying to do, and where it went wrong. Use when the user asks "what did this MCP session do?", "show me the tool calls for session X", "what was the agent's goal?", "which sessions had errors?", "who is connecting to my MCP?", or pastes an MCP analytics sessions URL.
Exploring MCP sessions
An MCP session is one agent run: the set of `$mcp_tool_call` events sharing a `$session_id`, ordered by `timestamp`.
Listing sessions, reading a session's tool calls, and summarising its goal each have a **typed tool** — reach for those first. Drop to HogQL only for the three things the typed tools genuinely can't do (see [When to drop to SQL](#when-to-drop-to-sql)). The full `$mcp_*` property schema and query recipes live in the shared reference: [`models-mcp.md`](../../../posthog_ai/skills/querying-posthog-data/references/models-mcp.md).
Tools
| Tool | Purpose | | ------------------------------------------------ | ---------------------------------------------------------- | | `posthog:mcp-analytics-sessions-list` | List sessions — one row per session, newest first | | `posthog:mcp-analytics-sessions-tool-calls` | One session's tool calls, chronological | | `posthog:mcp-analytics-sessions-generate-intent` | LLM summary of a session's goal (cached after first call) | | `posthog:execute-sql` | Errored sessions, effective tool names, cross-session cuts |
The three `mcp-analytics-*` tools are gated behind the `mcp-analytics` flag and run the same code as the sessions UI, so results match the screen. If they aren't in your tool list, the project doesn't have the flag — fall back to `posthog:execute-sql`, which is ungated.
The date-window trap — read this first
The two detail tools default to a **7-day lookback**. A session you found in a list that reaches further back will come back **empty** unless you pass its `session_start` as `date_from`:
- `posthog:mcp-analytics-sessions-tool-calls` — `date_from` is an absolute ISO
timestamp; pass the `session_start` you got from `posthog:mcp-analytics-sessions-list`.
- `posthog:mcp-analytics-sessions-generate-intent` — same `date_from` query
param, same reason.
Empty tool calls for a session that visibly exists is almost always this, not a data problem. Carry `session_start` forward from the list row.
Workflow: list recent sessions
posthog:mcp-analytics-sessions-list
{ "date_from": "-7d", "order_by": "-session_start", "limit": 100 }Each row: `session_id`, `tool_calls`, `session_start`, `session_end`, `tools_used`, `mcp_client_name`, `distinct_id` (+ resolved `person_email` / `person_name`), and `intent` (empty until generated). Response is `{ results, has_next }` — page with `limit` / `offset`.
Three sharp edges:
- **`order_by` takes column names, not response field names.** Sort call volume
as `tool_call_count` (not `tool_calls`). `duration_seconds` sorts fine even though it isn't returned. An unrecognised key **silently** falls back to newest-first — so verify the order you got is the order you asked for. Valid: `session_id`, `session_start`, `session_end`, `duration_seconds`, `tool_call_count`, `mcp_client_name`, `distinct_id`; prefix `-` to descend.
- **There is no error filter and no error count on a session row.** "Which
sessions had errors?" is a SQL question — see below.
- **`distinct_id_count` is always `0`.** The field is in the response but the
backend never populates it, so don't read it as "one distinct id per session" — it says nothing. To count distinct ids in a session, use SQL.
`search` does a case-insensitive substring match across `session_id`, `distinct_id`, `mcp_client_name`, and `tools_used`.
Workflow: read one session's tool calls
posthog:mcp-analytics-sessions-tool-calls
{ "id": "<session_id>", "date_from": "<session_start>", "limit": 500 }Chronological `tool_name`, `intent`, `timestamp`, `duration_ms`, `is_error`, `error_message` — read top to bottom to reconstruct the run. `limit` defaults to 500 (also the max), which is the whole page for almost every session; `has_next` tells you if more remain.
**Caveat: `tool_name` here is the raw `$mcp_tool_name`.** Unlike the tool-quality and tool-detail tools, this endpoint does not resolve the inner tool of a single-exec wrapper call, so wrapper calls show the wrapper. When the inner tool is what matters (comparing against a tool-quality ranking, tracing a specific tool through a run), use the SQL recipe below instead. The same applies to `tools_used` on the session list.
Workflow: summarise the agent's goal
posthog:mcp-analytics-sessions-generate-intent
{ "id": "<session_id>", "date_from": "<session_start>" }Summarises the session's recorded `$mcp_intent` values via an LLM and persists the result; later calls return the cached summary. Returns `{ session_id, intent }`. A 503 means LLM summarisation isn't configured — fall back to reading the raw `$mcp_intent` values from the tool-call list.
When to drop to SQL
Four cases, all via `posthog:execute-sql`, which — unlike the typed tools above — is **not** gated behind the `mcp-analytics` flag.
**1. The project doesn't have the `mcp-analytics` flag.** The typed tools simply won't be in your tool list. Everything below still works; this query is the plain session listing:
SELECT
$session_id AS session_id,
min(timestamp) AS session_start,
max(timestamp) AS session_end,
dateDiff('second', min(timestamp), max(timestamp)) AS duration_seconds,
count() AS tool_calls,
countIf(toBool(properties.$mcp_is_error)) AS errors,
any(properties.$mcp_client_name) AS client
FROM events
WHERE:hedgehog: PostHog is the leading platform for building self-driving products. Our developer tools – AI observability, analytics, session replay, flags, experiments, error tracking, logs, and more – capture all the context agents need to diagnose problems, uncover opportunities, and ship fixes. Steer it all from Slack, web, desktop, or the MCP.
Repo: posthog/posthog
Other skills on posthog.
- /analyzing-expensive-users
Analyze the most expensive users in AI observability and explain why they cost so much. Use when the user asks about top spenders, expensive users, per-user LLM cost, user-level cost drivers, or patterns behind high AI observability spend.
Open skill - /creating-online-evaluations
Author continuously-running online evaluations in PostHog AI observability, grounded in real failure modes you've identified. Use when the user wants evaluations that automatically score new generations or whole traces going forward — "create an eval to catch X", "continuously
Open skill - /exploring-ai-failures
Find where an AI/LLM application is failing in production and surface the failure patterns, working from real traces. Use when someone wants to understand what's going wrong with an AI feature, find and categorize failure modes, triage errors, or investigate quality issues
Open skill - /exploring-llm-clusters
Investigate AI observability clusters — understand usage patterns in AI/LLM traffic, compare cluster behavior, compute cost/latency metrics, and drill into individual traces within clusters.
Open skill - /exploring-llm-costs
Investigate LLM spend in PostHog — total cost over time, cost by model, provider, user, trace, or custom dimension, token and cache-hit economics, and cost regressions. Use when the user asks "how much are we spending on LLMs?", "which model / user / feature is most expensive?",
Open skill - /exploring-llm-evaluations
Investigate AI observability evaluations — `hog` (deterministic code-based), `llm_judge` (LLM-prompt-based), and `sentiment` (user-message sentiment). Find existing evaluations, inspect their configuration, run them against specific generations, query individual results, and
Open skill

