PostHog MCP Analytics 探索指南:用exploring-mcp-tool-usage技能从问题路由到 MCP 工具调用数据
【免费下载链接】posthog: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.项目地址: https://gitcode.com/GitHub_Trending/po/posthog
导读
任何用@posthog/mcpSDK 埋点的 MCP 服务器,都会在 Agent 每次调用工具时发出一个$mcp_tool_call事件,PostHog 的 MCP Analytics 能力(仪表盘、工具质量、工具详情、会话、意图聚类)全部建立在这单一事件之上。本文围绕 PostHog 仓库中exploring-mcp-tool-usage这个"入口技能"展开:当用户只想知道"我的 MCP 用得怎么样"却还没选定具体问题时,如何先调用治理指标得到权威失败率,再通过建议问题菜单路由到对应的 typed tool(或专用技能),以及何时该降级手写 HogQL。读完你就能掌握一套从"宽泛提问"到"单工具深度下钻"的完整探索路径。
一次 Agent 调用,一个$mcp_tool_call事件
先建立数据模型:MCP 服务器只要用 PostHog 的 MCP analytics SDK 埋点,Agent 每次调用工具都会在共享的events表上产生一个$mcp_tool_call事件。没有专门的 ClickHouse 表——所有字段都以$mcp_*前缀的属性挂在events上,MCP Analytics 仪表盘、tool-quality、tool-detail 等屏幕上的每个指标,都是对这个事件做聚合得到的,且都可以用 HogQL 复现。
仓库中products/mcp_analytics/backend/constants.py明确了事件名约束:
# Key on this canonical event only — also matching the legacy `mcp_tool_call` alias # (frozen, pre-2026-06-16 history) would double-count every call. MCP_TOOL_CALL_EVENT = "$mcp_tool_call"注意这里的细节:PostHog 自托管的 MCP 服务器在过渡期会双发无前缀的mcp_tool_call别名事件,因此查询时只能匹配规范的$mcp_tool_call,写event IN ('mcp_tool_call', '$mcp_tool_call')会对 PostHog 自己的服务器重复计数。这是理解本文所有工具与 SQL 配方的前提。
本技能的角色:MCP 工具使用探索的"大门"
exploring-mcp-tool-usage(位于 products/mcp_analytics/skills/exploring-mcp-tool-usage/SKILL.md)定位是front door:用户知道想查看 MCP 工具使用情况,但还没选定具体问题。它的做法是先向用户展示一份"建议问题菜单",每个问题背后都有一个对应的查询工具,然后把用户选中的问题路由到该工具——或路由到更聚焦的专用技能(如 tool-quality、sessions、intent-clusters)。
它的触发场景包括:"我的 MCP 怎么样了?"、"我该看什么?"、"探索我的工具调用"、"谁在用我的 MCP 工具?"、"Agent 们用 MCP 在做什么?",或用户贴了一个 MCP Analytics 的 URL 却没有带具体问题。
权威指标优先(Governed metric first)
技能的第一条纪律是:当问题涉及 MCP 失败率或百分比时,先走治理指标,再碰 typed tool 或 SQL 配方。
- 先调用
posthog:metric-list,在结果中查找mcp_tool_call_fail_pct; - 若存在一个已批准(approved)且未漂移(non-drifted)的匹配项,用
posthog:data-catalog-metric-run运行它,把结果作为权威 headline汇报; - 若用户还追问"是哪个工具/哪个 harness 导致了失败",先回答 headline,再用下面的 per-tool 工作流做**非权威(noncanonical)**分解;
- 若治理指标目录里没有匹配项,明确说明"catalog 无匹配",并把自行推导的失败率标注为非权威。
这条规则的用意是:MCP 失败率的"官方数字"必须来自被治理的指标定义,任何临时 SQL 算出的百分比只能作为补充性下钻,不能冒充权威口径。
建议问题菜单:12 个问题路由到正确工具
当用户不确定该问什么时,直接抛出下面这份菜单(原技能核心内容,完整保留):
| 问用户… | 由谁来回答 |
|---|---|
| "哪些工具失败最多,或最慢?" | exploring-mcp-tool-quality(先对所有工具排序),再用posthog:query-mcp-tool-stats下钻 |
| "工具 X 整体表现如何?" | posthog:query-mcp-tool-stats— 调用数、错误数、p50/p95、用户、会话、意图 |
| "工具 X 的趋势如何?" | posthog:query-mcp-tool-daily-stats— 逐日序列 |
| "工具 X 为什么失败?" | posthog:query-mcp-tool-failures— 按 harness 分组的 top 错误信息 |
| "谁最常用工具 X?" | posthog:query-mcp-tool-top-users— top 调用者(含 person 邮箱/姓名) |
| "工具 X 前后通常被什么调用?" | posthog:query-mcp-tool-neighbors(neighborDirection: before/after) |
| "Agent 用工具 X 想做什么?" | posthog:query-mcp-tool-sample-intents— 最近的 Agent 意图 |
| "工具 X 注册时带什么描述?" | posthog:query-mcp-tool-descriptions— 观察到的不同描述 |
| "哪些 harness 在用我的 MCP,可靠性如何?" | posthog:query-mcp-harness-breakdown— 每个客户端的调用/错误/会话 |
| "跨所有工具,Agent 们在做什么?" | exploring-mcp-intent-clusters— 语义目标聚类 |
| "谁在连接,活跃度如何?" | posthog:mcp-analytics-sessions-list— 每个会话一行,含客户端与 person |
| "这个会话具体做了什么?" | exploring-mcp-sessions— 单次 Agent 运行的工具序列 |
关键前提:所有 per-tool 工具都受mcp-analyticsflag 门控
技能文档强调,上述每个 per-tool 工具都:
- 被
mcp-analytics特性开关门控——项目的 flag 未开启时,这些工具根本不会出现在工具列表里; - 接受
toolName(服务端解析后的有效工具名,即 Agent 实际调用的那个名字)加dateRange; - 运行与tool-detail UI 完全相同的查询 runner,因此结果与界面一致,你无需手写 HogQL。
这个"门控"在源码里有明确落点。products/mcp_analytics/backend/hogql_queries/base.py定义了 flag 常量与访问校验:
MCP_ANALYTICS_FEATURE_FLAG = "mcp-analytics" def validate_mcp_analytics_access(team, user): ... enabled = posthoganalytics.feature_enabled( MCP_ANALYTICS_FEATURE_FLAG, ... ) if not enabled: raise UserAccessControlError("mcp_analytics", "viewer") return UserAccessControl(user=user, team=team).assert_access_level_for_resource("mcp_analytics", "viewer")也就是说,这些查询 runner 与产品的 DRF 端点共用同一个 flag + RBAC 资源检查,/query/通用端点无法绕过。工具级定义(enabled、scopes、annotations、feature_flag: mcp-analytics)则集中在 products/mcp_analytics/mcp/tools.yaml,例如:
query-mcp-tool-stats: schema_ref: MCPToolStatsQuery enabled: true scopes: - query:read - mcp_analytics:read annotations: readOnly: true destructive: false idempotent: true feature_flag: mcp-analytics找到正确的toolName:有效工具名(effective tool name)
per-tool 工具都需要一个toolName,取值规则:
- 用户直接点名了某个工具 → 直接传它;
- 用户问的是宽泛的"哪个工具……" → 先跑
exploring-mcp-tool-quality对所有工具排序,选出最突出的那个,再用 per-tool 工具下钻。
关键概念是effective tool name(有效工具名):新版 SDK 的事件把真实工具包在单次 exec 调用(single-exec wrapper)里,如果直接按原始$mcp_tool_name分组,所有调用都会塌缩到 wrapper 名下。因此要传服务端解析出的"内层真实工具名"——这正是 tool-quality 排序返回的字符串。
源码层面,这个表达式在 products/mcp_analytics/backend/hogql_queries/base.py 中定义为共享常量EFFECTIVE_TOOL_SQL:
EFFECTIVE_TOOL_SQL = ( "coalesce(nullIf(toString(properties.$mcp_exec_tool_call_name), ''), toString(properties.$mcp_tool_name))" )它被所有按工具收敛的 runner 复用(见tool_scope_exprs,且工具名以ast.Constant绑定、绝不字符串插值)。同样的口径也给出了 effective description:
EFFECTIVE_DESCRIPTION_SQL = ( "coalesce(nullIf(toString(properties.$mcp_exec_tool_call_description), ''), " "toString(properties.$mcp_tool_description))" )一处值得注意的口径差异
本技能 SKILL.md 中提到一个例外:posthog:query-mcp-tool-failures匹配$exception事件、因此要传原始注册名$mcp_tool_name。但从当前仓库源码看,这一说法已经过时:
- products/mcp_analytics/mcp/tools.yaml 中
query-mcp-tool-failures的描述明确写着 "Failures are the errored$mcp_tool_callevents ($mcp_is_error = true)… Pass toolName (effective tool name, resolved server-side, matching the other tool-detail tools)"; products/mcp_analytics/backend/hogql_queries/tool_tables.py中MCPToolFailuresQueryRunner的注释也印证了迁移:"Previously this read$exceptionevents, which don't carry MCP tool markers, so the table was always empty while the error rate showed failures"——现在它与其他工具详情表共享同一数据源(errored$mcp_tool_call+ effective tool name),两者不可能互相矛盾。
因此实际使用时,query-mcp-tool-failures与其余 per-tool 工具一样传 effective tool name 即可,以 models-mcp.md 和 tools.yaml 为准。
如何调用一个 per-tool 工具
以某工具的 headline 数字为例(技能原文示例):
posthog:query-mcp-tool-stats { "toolName": "<tool>", "dateRange": { "date_from": "-7d" } }拿到结果后,根据菜单自然跟进:若posthog:query-mcp-tool-stats显示错误率偏高 → 接posthog:query-mcp-tool-failures;若显示覆盖面广 → 接posthog:query-mcp-tool-top-users或posthog:query-mcp-tool-neighbors。
这些工具在源码中的 runner 集中在 products/mcp_analytics/backend/hogql_queries/tool_tables.py,每个 runner 都先构建"单工具 + 时间窗"的 WHERE(_tool_call_where),再拼出各自的聚合形状:
| 工具 | 对应 QueryRunner | 核心聚合口径(源码依据) |
|---|---|---|
query-mcp-tool-stats | MCPToolStatsQueryRunner | count()调用数、countIf(toBool($mcp_is_error))错误数、quantile(0.5)/(0.95)(toFloat($mcp_duration_ms))p50/p95、uniq(distinct_id)用户数、uniq(conversation_id)会话数、带 intent 的调用数 |
query-mcp-tool-daily-stats | MCPToolDailyStatsQueryRunner | dateTrunc(interval, timestamp)分桶,逐日/逐小时调用、错误、p50/p95、用户、会话 |
query-mcp-tool-failures | MCPToolFailuresQueryRunner | 按$mcp_error_type+ HTTP$mcp_error_status分组,unknown兜底,输出组成 "type (HTTP status)" 标签与 harness 列表 |
query-mcp-tool-failure-occurrences | MCPToolFailureOccurrencesQueryRunner | 单个失败桶内的逐条错误调用(最多 50 条):时间、distinct_id、会话、harness、intent、$mcp_error_message(截断到 2048 字符) |
query-mcp-tool-top-users | MCPToolTopUsersQueryRunner | 按distinct_id分组:调用数、错误率、harness 标签、最后活跃时间,以及 person 邮箱/姓名(只返回渲染所需字段,不泄整个 person blob) |
query-mcp-tool-neighbors | MCPToolNeighborsQueryRunner | lagInFrame/leadInFrame窗口函数按会话取前/后一个工具,统计共现次数 |
query-mcp-tool-sample-intents | MCPToolSampleIntentsQueryRunner | 最近 5 条非空$mcp_intent,附$mcp_intent_source与 harness |
query-mcp-tool-descriptions | MCPToolDescriptionsQueryRunner | 去重后的 effective description 列表(notEmpty过滤),按最后出现时间排序 |
一个值得说明的实现细节:错误类型/状态是事件自带的、无界字符串,源码在分组前先用substring(..., 1, 200)/substring(..., 1, 20)截断(_RAW_ERROR_TYPE/_RAW_ERROR_STATUS),防止恶意事件用海量唯一值撑爆分组键;错误消息同理截断到 2048 字符(_ERROR_MESSAGE)。toolName全程以常量绑定,杜绝注入。
harness 标签的解析同样在服务端完成——products/mcp_analytics/backend/mcp_harness.py(HARNESS_TOKEN_SQL/harness_label_sql)是唯一权威,前端只负责把解析出的标签映射成 Logo。如果手写 SQL 与界面不一致,说明你的分桶逻辑偏离了mcp_harness.py,此时应优先用 typed tool(posthog:query-mcp-harness-breakdown)而不是继续手工推导。
何时降级到 SQL
技能给出了一条清晰的边界,避免在已有 typed tool 的场景下手写 SQL:
已被 typed tool 覆盖——不要手写 SQL:
| 问题 | 工具 |
|---|---|
| 单个工具的 headline 数字 | posthog:query-mcp-tool-stats |
| 单个工具的逐日趋势 | posthog:query-mcp-tool-daily-stats |
| 单个工具的 top 错误 | posthog:query-mcp-tool-failures |
| 单个工具的 top 调用者 | posthog:query-mcp-tool-top-users |
| 某个工具前后调用的工具 | posthog:query-mcp-tool-neighbors |
| 单个工具最近的 Agent 意图 | posthog:query-mcp-tool-sample-intents |
| 单个工具的注册描述 | posthog:query-mcp-tool-descriptions |
| 按客户端 harness 拆分使用量 | posthog:query-mcp-harness-breakdown |
| 列出会话 | posthog:mcp-analytics-sessions-list |
| 单个会话的工具调用 | posthog:mcp-analytics-sessions-tool-calls |
未被覆盖——使用posthog:execute-sql:
- 跨工具排名(tool-quality 矩阵,如"哪个工具错误最多");
- 错误会话过滤(会话列表没有错误过滤器或错误计数);
- 会话内的有效工具名(
posthog:mcp-analytics-sessions-tool-calls返回的是原始$mcp_tool_name,不是 wrapper 调用的内层工具); - 任意自定义分解。
posthog:execute-sql同时也是mcp-analyticsflag 关闭时的兜底路径——上表所有工具都被该 flag 门控,而execute-sql不受门控。此时直接查询$mcp_tool_call即可;完整的$mcp_*属性 schema 与现成查询配方(tool-quality 矩阵、harness 分桶、工具共现等)都在共享参考文档 products/posthog_ai/skills/querying-posthog-data/references/models-mcp.md 中,查询前建议先读它,不要重新推导。
作为示例,跨工具的"哪个工具错误率最高"(非权威分解)在exploring-mcp-tool-quality技能中给出了完整配方,核心是 effective tool name + 小样本防护:
posthog:execute-sql SELECT coalesce(nullIf(toString(properties.$mcp_exec_tool_call_name), ''), toString(properties.$mcp_tool_name)) AS tool, count() AS total_calls, countIf(toBool(properties.$mcp_is_error)) AS errors, round(countIf(toBool(properties.$mcp_is_error)) * 100.0 / count(), 1) AS error_rate_pct FROM events WHERE event = '$mcp_tool_call' AND coalesce(nullIf(toString(properties.$mcp_exec_tool_call_name), ''), toString(properties.$mcp_tool_name)) != '' AND timestamp >= now() - INTERVAL 30 DAY GROUP BY tool HAVING total_calls >= 20 ORDER BY error_rate_pct DESC, total_calls DESC LIMIT 20汇报时同时给出错误率与调用量:3 次调用 100% 错误率通常不是真相,50,000 次调用 12% 才是。技能还提醒两条铁律:$mcp_is_error永远用toBool(...)读取、$mcp_duration_ms用toFloat(...)强转(这两个属性在事件里是字符串);并且永远设置时间范围,否则这些查询会全表扫描events。
相关技能:从大门走向纵深
exploring-mcp-tool-usage是路由入口,选好方向后由三个专用技能接力:
- exploring-mcp-tool-quality — 按错误率/延迟/覆盖面给所有工具排序,再下钻到单个工具;
- exploring-mcp-sessions — 单次 Agent 运行及其工具调用序列(注意其两个详情工具默认 7 天回看窗口,老会话要携带
session_start作为date_from); - exploring-mcp-intent-clusters — 按语义相似度聚类的 Agent 目标,回答"Agent 们想做什么、是否如愿"。
它们共同构成一套完整的分层:先治理指标定权威口径,再按问题路由到 typed tool,最后在 typed tool 覆盖不到的地方用 HogQL 兜底。掌握了这条链路,任何"我的 MCP 用得怎么样"的宽泛提问,都能一步步收敛为对单个工具、单个 harness、甚至单次会话的确凿数据。
【免费下载链接】posthog: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.项目地址: https://gitcode.com/GitHub_Trending/po/posthog
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考