跨团队排查指南:managed-migrations-support-list 工具全解析)
基于 MCP 的 PostHog 托管迁移Batch Import跨团队排查指南managed-migrations-support-list 工具全解析【免费下载链接】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本文以 products/managed_migrations/mcp/prompts/managed-migrations-support-list.md 为核心骨架结合products/managed_migrations产品目录下的 API、模型与 Django Admin 源码展开讲解 PostHog 员工staff如何通过 MCP 工具跨团队列出并诊断客户托管迁移managed migration / batch import任务。一、工具定位为什么需要跨团队的只读排查通道PostHog 的托管迁移managed migration本质上是一条batch import批量导入任务后台 worker 从客户的数据源S3、Mixpanel、Amplitude、URL 列表、文件夹等拉取历史事件解析后以设定速率写入 PostHog 的 capture 管道。当客户迁移卡住、报错或进度异常时Support 员工需要跨团队查看任务状态——而普通的BatchImportViewSet是绑定团队team成员关系的员工无法查看不属于自己的团队的任务。managed-migrations-support-list正是为此设计的staff-only、只读、跨团队的 MCP 工具跨所有PostHog 团队列出 batch import 任务用于排查客户迁移支持按team_id客户的项目 ID、statusrunning/paused/failed/completed过滤或按search全文检索匹配开发者状态消息与团队名称结果**按创建时间倒序newest-first**排列便于先看到最新任务。与它配套的是 managed-migrations-support-get按任务 UUID 获取单个任务的原始 workerstate与import_config数据块。两个工具的 MCP 定义都在 tools.yaml 中managed-migrations-support-list的 operation 是managed_migrations_support_listmanaged-migrations-support-get是managed_migrations_support_retrieve两者都声明了batch_import_support:readscope并带有readOnly: true、destructive: false、idempotent: true注解工具的提示词描述description_file正是这两个 prompt 文档本身。关键边界两个工具都只读。所有变更操作resume、pause、in-flight part reset目前仍保留在 Django Admin 中见 admin/batch_imports.py。二、访问控制模型什么样的凭证才能调用工具 prompt 开篇就划定了严格的访问边界这一点在源码中得到完整印证。后端 support_batch_imports.py 中BatchImportSupportViewSet的声明如下scope_object INTERNAL required_scopes [batch_import_support:read] permission_classes [IsAuthenticated, IsStaffUser, APIScopePermission, UnscopedPersonalAPIKeyPermission] authentication_classes [SessionAuthentication, PersonalAPIKeyAuthentication]访问需要同时满足staff 用户is_staff True会话认证与个人 API Key 认证都会被强制校验因为PersonalAPIKeyAuthentication会以该 Key 对应的真实用户身份认证个人 API Key 必须显式携带batch_import_support:readscopescope_object INTERNAL会拒绝全权限*Key——通配符 Key 在这个端点上一律失效必须显式列出隐藏 scope该 scope 对 OAuth 隐藏属于OAUTH_SCOPES_HIDDENOAuth 登录流程永远无法授予它Key 必须无团队/组织限定无scoped_teams/scoped_organizations这是根级root-level跨团队端点UnscopedPersonalAPIKeyPermissionL219-L237专门拦下带组织限定的 Key防止其绕过组织上限。2.1 MCP 侧的过滤逻辑表现层非安全边界MCP 服务端通过 services/mcp/src/lib/staff-only-tools.ts 决定是否把工具暴露给某个 KeyisStaffOnlyTool凡是要求 OAuth 隐藏 scope 的工具都被视为 staff-only 表面L9-L10keyExplicitlyGrantsHiddenScopes隐藏 scope必须被显式列出*通配 Key 不算数与后端 INTERNAL scope 拒绝通配符的行为保持一致L17-L18filterStaffOnlyToolstenant-scoped Key 直接过滤掉所有 staff-only 工具对普通 Key 则会调用/api/users/me/确认用户是 staff任何一步失败都 fail-closed默认隐藏工具L32-L55。这段过滤是表现层优化——它只是不让 staff-only 工具污染客户工具列表真正的安全边界是 Django 端点本身。2.2 如何铸造 Key由于batch_import_support:read在 Key 创建 UI 中隐藏、且无法通过 OAuth 授予铸造需要两步完整指南见 docs/support-mcp-tools.md在对应区域的 PostHog UI 中Settings → Personal API keys创建带User: Readscope、且无组织/项目限定的 Key在 Django Admin/admin/posthog/personalapikey/中给该 Key 的scopes列表手动追加batch_import_support:read。几个容易踩坑的规则MCP 使用需要双 scopebatch_import_support:read授权端点user:read让 MCP 工具发现机制能通过/api/users/me/确认 staff 身份——没有user:read工具会静默不出现fail-closed*全权限 Key 不工作每个区域一把 KeyUS 与 EU 是独立部署、独立用户与 Key且user:read还参与区域路由——MCP 服务器会用你的 token 探测两个区域Key 缺user:read会导致路由错乱默认落到 US。三、请求参数与过滤能力工具支持三类过滤对应后端filterset_fields [status, team_id]、search_fields [status_message, team__name]见 support_batch_imports.py#L284-L290参数取值/语义源码依据team_id客户的 PostHog 项目teamID精确匹配filterset_fieldsstatusrunning、paused、failed、completed之一filterset_fields 模型Statuschoicesmodels/batch_imports.py#L40-L44search自由文本同时匹配开发者状态消息status_message与团队名称team__namesearch_fields结果默认按-created_at创建时间倒序即newest-first同时支持created_at、updated_at、status的显式排序。需要特别说明的是search不匹配任务 UUID——由于 Postgres 原生 uuid 列不支持icontains精确的 id 查询交给managed-migrations-support-get的retrieve动作完成support_batch_imports.py#L285-L288 注释明确exact id lookups are whatretrieveis for。四、返回字段全览一份紧凑的跨团队诊断视图列表序列化器BatchImportSupportListSerializersupport_batch_imports.py#L47-L183刻意排除了原始state/import_config大块数据那是 detail 序列化器的职责也永远不暴露加密的secrets列——注释明确写道显式字段清单是加密凭证列永远不会序列化进 support 响应的结构性保证。核心字段字段含义id任务 UUIDteam_id/team_name所属团队项目ID 与名称status持久化的原始状态display_status有效状态任务为running但lease_id为 null 时显示waiting_to_start否则等于原始状态status_message开发者看到的调试信号客户不可见内嵌 URL 的查询串与 userinfo 会被脱敏display_status_message客户在 PostHog UI 中看到的状态消息同样做 URL 脱敏parts_progressworker 分片进度摘要done/total/inflight_key/inflight_offset/inflight_total_sizesource_type/content_type数据源类型s3、mixpanel、amplitude、urls、folder…与事件格式mixpanel、amplitude、captured…source_start_date/source_end_date日期范围型数据源Mixpanel/Amplitude的起止日期sink_type/sink_send_rate写入目标通常为capture内部用kafka/noop与发送速率事件/秒lease_id/leased_until/lease_expiredworker 租约 token、租约到期时间、租约是否已过期backoff_attempt/backoff_until连续瞬时失败重试次数与下次重试时间created_by_id/created_at/updated_at创建人、创建时间、最近写入时间worker 处理中会持续心跳更新它五、状态机深度解读如何判断一个任务真的卡住了这是工具提示词的核心价值所在——不是所有非completed状态都代表故障。逐条拆解。5.1waiting_to_start≠ 卡住只是还没被认领display_status: waiting_to_start表示任务状态是running但还没有任何 worker 认领它lease_id为 null。它只是排队中不是卡住。源码中这是由get_display_status动态计算的support_batch_imports.py#L142-L146def get_display_status(self, obj: BatchImport) - str: if obj.status BatchImport.Status.RUNNING and obj.lease_id is None: return waiting_to_start return obj.status5.2 租约lease机制30 分钟认领 5 分钟心跳worker 对每个任务持有一把租约首次认领时租约 30 分钟lease_id与leased_until被写入每次心跳续租 5 分钟updated_at持续刷新lease_expired: true且任务状态为running意味着worker 已死亡或该行即将被下一次 worker 轮询重新认领。注意lease_expired的判定逻辑support_batch_imports.py#L159-L160lease_id非空且leased_until已过当前时间才为 true——从未被认领的任务不会误报。5.3paused任务会保留租约恢复必须清租约paused任务会继续持有worker 租约。这意味着仅仅把状态改回running是不够的——不清租约的话未来最多 30 分钟内没有任何 worker 能重新认领该行。恢复操作需要清空租约这正是 Django Admin 的动作本 API 只读无法代劳。模型层的_flip_to_runningmodels/batch_imports.py#L156-L176会一并清空lease_id、leased_until、backoff_attempt、backoff_until并写入恢复原因到status_message。Django Admin 中提供两个恢复动作admin/batch_imports.py#L159-L198Resume (keep progress)从已保存的字节偏移继续——适用于瞬时暂停、或源数据未变的情形模型方法resume_after_pausemodels/batch_imports.py#L108-L121Resume re-import in-flight part先把 in-flight 分片重置为 offset 0 再恢复——适用于源字节在已提交偏移之下发生变化的情形模型方法resume_with_inflight_part_resetmodels/batch_imports.py#L123-L154。5.4 中毒字节偏移poisoned byte offsetpaused 非法 JSON 的典型场景一个很有价值的排查信号paused状态且status_message提到恢复点处 JSON 语法非法invalid JSON syntax通常意味着中毒的字节偏移——源字节在已提交的偏移之下发生了变化典型触发非确定性导出被重新下载每次下载解压后字节流都可能不同源文件在数据修复后被替换。已提交的偏移只对测量它所基于的那条精确字节流有意义当 worker 重新下载分片pod 替换而未走临时 bucket 暂存或数据错误暂停后源文件被替换保存的偏移落在新字节流的中段解析便以恢复点处的解析错误而暂停。修复手段就是上面提到的 Django Admin 动作Resume re-import in-flight part将 in-flight 分片重置为 offset 0。它对Mixpanel / Amplitude 是安全的——这两类源按确定性事件 UUIDMixpanel$insert_id、Amplitudeuuid去重重导入的重叠部分会被去重掉不会产生重复事件。模型实现中重置时不仅把current_offset归零还把total_size置为 nullmodels/batch_imports.py#L145-L148因为非确定性导出每次下载的解压后大小也可能不同陈旧的 total 同样是错的。5.5backoff_until在未来 ≠ 卡住backoff_until在未来表示任务处于瞬时失败重试循环中backoff_attempt记录连续重试次数0 表示健康并非卡死。这是 Rust worker 使用的指数退避状态模型注释明确Exponential backoff state (used by rust worker). Mirrors columns used by the workermodels/batch_imports.py#L61-L63。遇到它时应等待退避窗口结束再看而不是立即判定故障。5.6parts_progress读懂 worker 的工作单元parts_progress是 worker 工作单元的进度摘要语义源自模型层的parts_progress()models/batch_imports.py#L89-L106一个 part完成的标志其已提交字节偏移current_offset达到其总大小total_sizepart 按顺序处理因此in-flight 的 part 就是第一个未完成的 part摘要字段done完成数、total计划总数、inflight_key第一个未完成 part 的 key全部完成或未开始时为 null、inflight_offsetin-flight part 内已提交的解压后字节偏移、inflight_total_sizein-flight part 的解压后总字节数worker 尚未测量时为 null。URL 类数据源url_list的 part key 是完整源 URL其查询串与 userinfo 可能携带预签名 token 或凭证因此inflight_key返回前会被脱敏redact_part_key。5.7 两条状态消息的分工status_message开发者面向的调试信号——worker 或操作者写入的原始消息客户不可见display_status_message客户在 PostHog UI 中看到的展示消息。两者在返回前都会对 URL 做查询串/userinfo 脱敏redact_urls_in_json/redact_urls_in_text见 support_batch_imports.py#L134-L140。六、与 managed-migrations-support-get 的分工列表工具给出的是紧凑诊断视图当需要深挖单个任务时用managed-migrations-support-get 任务 UUID。它在列表字段之上追加三个字段BatchImportSupportDetailSerializersupport_batch_imports.py#L186-L216stateworker 拥有的进度 blob形如{parts: [{key, current_offset, total_size}]}。part 在current_offset total_size时完成part 按序处理第一个未完成的就是 in-flight 的。current_offset是解压后part 内的字节偏移——只对测量它时的那条精确字节流有意义import_config任务的数据源 / 数据格式 / sink 配置。它只按密钥名引用凭证secret key NAME凭证值存放在加密列中任何 API 都不会返回created_by_email启动迁移的用户邮箱已知时。七、安全设计这组工具如何保护客户数据从源码可以归纳出五层安全设计凭证永不返回secrets使用EncryptedJSONStringField加密存储models/batch_imports.py#L60support 序列化器通过显式字段清单从结构上排除它URL 脱敏state、import_config、status_message、display_status_message、part key 中的 URL 均做查询串/userinfo 脱敏防止预签名 token 与凭证外泄拒绝查询串 Keyinitial()support_batch_imports.py#L292-L305拒绝?personal_api_key查询串传参——否则分页器会把明文 staff Key 反射进 next/previous 响应链接。且该拦截在认证之前执行覆盖所有认证路径含浏览器会话中误带 Key 的情况审计日志finalize_responsesupport_batch_imports.py#L318-L337对每次 staff 读取记审计日志且 query_params 采用白名单LOGGED_QUERY_PARAMS仅允许status、team_id、search、ordering、limit、offset——因为search匹配的是未脱敏的status_message列staff 搜索完整消息本身就可能携带含凭证的 URL日志前同样先脱敏fail-closed 风格未来任何新增的含密参数也不会被日志记录双端点防线MCP 侧过滤表现层 Django 端点强制校验安全边界docs/support-mcp-tools.md 与 staff-only-tools.ts 的注释均明确这一分工。后端测试覆盖在 backend/api/test/test_support_batch_imports.py 中可对照验证上述行为。八、使用前置条件与快速排障最小前置条件详见 docs/support-mcp-tools.md你的 PostHog 云用户是 staffis_staff True非 staff 用户一切 fail-closed工具不出现在 MCP 客户端API 即使有正确 scope 也返回 403铸造一把显式携带batch_import_support:readuser:read的无限定个人 API Key用 Bearer Header 连接 PostHog MCP 服务器Authorization: Bearer key不要走 OAuth 浏览器登录流——OAuth token 结构上无法携带隐藏 scope每个支持的区域各铸一把 KeyUS/EU 独立。工具不出现时的排查顺序由 docs/support-mcp-tools.md 给出Key 是否显式而非通过*携带batch_import_support:readKey 是否带user:read或*与显式 support scope 并存该区域你的用户是否为 staffKey 是否无限定、是否连对了区域MCP 服务器会缓存 token 的 scopes——首次使用后修改 scopes 可能读到过期结果直接铸新 Key。九、结语一条从看到任务到读懂任务的完整链路managed-migrations-support-list的价值在于把跨团队的任务清单与状态诊断浓缩成一个只读 MCP 工具team_id/status/search过滤快速定位display_status、lease_expired、backoff_until、parts_progress与双状态消息构成一套完整的是否真的卡住判读体系managed-migrations-support-get负责下钻原始 worker 状态而所有变更动作由 Django Admin 承接并附有清晰的操作语义说明见 admin/batch_imports.py 的 fieldsets 描述。对 Support 工程师而言掌握这套状态语义意味着能把看起来停了的任务精确区分为排队中、租约过期待认领、退避重试、暂停待恢复、中毒偏移需重置等不同情形从而给出正确的处置动作。【免费下载链接】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),仅供参考