news 2026/9/16 21:52:43

PostHog MCP Analytics 探索指南:用 `exploring-mcp-tool-usage` 技能从问题路由到 MCP 工具调用数据

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PostHog MCP Analytics 探索指南:用 `exploring-mcp-tool-usage` 技能从问题路由到 MCP 工具调用数据

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-neighborsneighborDirection: 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.pyMCPToolFailuresQueryRunner的注释也印证了迁移:"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-usersposthog:query-mcp-tool-neighbors

这些工具在源码中的 runner 集中在 products/mcp_analytics/backend/hogql_queries/tool_tables.py,每个 runner 都先构建"单工具 + 时间窗"的 WHERE(_tool_call_where),再拼出各自的聚合形状:

工具对应 QueryRunner核心聚合口径(源码依据)
query-mcp-tool-statsMCPToolStatsQueryRunnercount()调用数、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-statsMCPToolDailyStatsQueryRunnerdateTrunc(interval, timestamp)分桶,逐日/逐小时调用、错误、p50/p95、用户、会话
query-mcp-tool-failuresMCPToolFailuresQueryRunner$mcp_error_type+ HTTP$mcp_error_status分组,unknown兜底,输出组成 "type (HTTP status)" 标签与 harness 列表
query-mcp-tool-failure-occurrencesMCPToolFailureOccurrencesQueryRunner单个失败桶内的逐条错误调用(最多 50 条):时间、distinct_id、会话、harness、intent、$mcp_error_message(截断到 2048 字符)
query-mcp-tool-top-usersMCPToolTopUsersQueryRunnerdistinct_id分组:调用数、错误率、harness 标签、最后活跃时间,以及 person 邮箱/姓名(只返回渲染所需字段,不泄整个 person blob)
query-mcp-tool-neighborsMCPToolNeighborsQueryRunnerlagInFrame/leadInFrame窗口函数按会话取前/后一个工具,统计共现次数
query-mcp-tool-sample-intentsMCPToolSampleIntentsQueryRunner最近 5 条非空$mcp_intent,附$mcp_intent_source与 harness
query-mcp-tool-descriptionsMCPToolDescriptionsQueryRunner去重后的 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.pyHARNESS_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_mstoFloat(...)强转(这两个属性在事件里是字符串);并且永远设置时间范围,否则这些查询会全表扫描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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 21:52:12

CentOS 7 VNC启动失败排查:systemd与服务配置全解析

我先把话说在前面&#xff1a;CentOS 7 上出现Failed to start Remote desktop service (VNC)&#xff0c;十有八九不是 VNC 本身坏了&#xff0c;而是 systemd 服务单元、权限、端口占用或者图形组件这几层里有某个环节掉了链子。这个报错我第一次踩到的时候也懵了一下&#x…

作者头像 李华
网站建设 2026/9/16 21:51:51

Django毕业设计:从网易云数据清洗到可视化大屏全链路实践

简介&#xff1a;面向计算机相关专业毕业设计和课程设计的Django网易云音乐数据分析可视化大屏项目&#xff0c;基于Scrapy抓取网易云音乐数据&#xff0c;经Django后端处理&#xff0c;通过ECharts构建可视化大屏页面&#xff0c;可帮助学习者掌握从数据采集、存储建模到前端展…

作者头像 李华
网站建设 2026/9/16 21:51:35

同一首歌换6副耳机听感差异有多大?五维实测解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/16 21:47:11

浏览器里的AI视频剪辑管线:WebAssembly+WebGPU端侧推理实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华