qwen-code MCP 模型可见 Payload 过滤:可逆别名机制与组件边界解析
【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code
导读
在基于 MCP(Model Context Protocol)的 AI Coding Agent 架构中,模型 API 路由有时会拒绝文本对话历史中出现已知厂商术语的请求。本文聚焦 qwen-code 仓库中的MCP_MODEL_PAYLOAD_FILTER设计(设计文档见 docs/design/mcp-payload-filter.md),深入讲解其目标、术语清单、无状态十六进制 Token 编码机制,以及它在cua-driver(Rust)与mobile-mcp(TypeScript)两个组件中的落地边界。读完本文,你将掌握该过滤开关的配置方式、可逆别名的工作原理、哪些数据会被转换而哪些会被原样保留,以及它为何不会影响应用、窗口、设备、包的真实操作能力。
背景:为什么需要"模型可见"的文本过滤
cua-driver(桌面端计算机使用驱动)和 mobile-mcp(移动端设备操作服务)在运行时会向模型返回大量文本化 MCP Payload,例如:
- 已安装应用的名称(如
com.alibaba.qwen); - 窗口标题、进程名、包名、路径(如
/Applications/Alibaba Cloud/Qwen.app); - 设备、模拟器与应用的列表、权限描述、健康状态信息。
部分模型 API 路由会拒绝对话历史中出现这些已知厂商术语的请求,导致 Agent 无法继续工作。然而这些本机值又是操作应用、窗口、设备、包所必需的——不能简单删除或模糊化。
该设计的目标由此明确:防止 cua-driver 和 mobile-mcp 在文本化 MCP Payload 中返回已知厂商术语,同时保留操作应用、窗口、设备、包所需的真实本机值。实现方式不是删改数据,而是对匹配文本进行"可逆的别名化":模型侧看到的是安全 Token,将 Token 原样作为工具参数返回给同一个 MCP 服务器后,服务器在工具校验与执行之前将其解码回真实值。
开关与作用域:默认关闭的 Opt-in 特性
过滤是显式开启的,默认关闭。在 MCP 服务器环境中设置:
MCP_MODEL_PAYLOAD_FILTER=1- 对于拒绝这些术语的 API 路由,开启后文本 Payload 会被过滤;
- 其他路由上的用户保留原始 Payload,不受任何影响。
开关判定非常严格。在 cua-driver 的 Rust 实现中,model_payload.rs 通过std::env::var_os("MCP_MODEL_PAYLOAD_FILTER")读取,并仅当值严格等于字符串"1"时才启用;测试filter_opt_in_requires_exact_one验证了""、"0"、"01"、"true"、"TRUE"、"yes"、" 1"等值均不启用过滤。
在 mobile-mcp 侧,server.ts 中的PayloadFilteredMcpServer子类同样用process.env.MCP_MODEL_PAYLOAD_FILTER === '1'判断,只有命中时才用包装 Transport 替换原始 Transport,否则原样连接。
过滤术语表:ASCII、中文与分隔符变体
设计文档与两侧实现(Rust 常量表与 TypeScript 正则源)共同定义了同一套术语清单。
ASCII 术语(不区分大小写)
cua-driver 的 ASCII_TERMS 与 mobile-mcp 的FILTERED_TERM_SOURCES完全对应:
| 类别 | 术语 |
|---|---|
| 模型/服务品牌 | qwen、qianwen、tongyi、bailian、modelscope、damo、wanx、maxcompute、qoder、lingma |
| 云厂商品牌 | alibaba、aliyun、aliyuncs、alicloud、dashscope、alipay、antfin、antgroup |
| 协作/通讯应用 | yuque、dingtalk、taobao、tmall |
共 21 个 ASCII 术语,匹配时使用 ASCII 大小写不敏感比较(Rust 侧eq_ignore_ascii_case,TS 侧正则gi标志)。
中文术语(精确匹配)
共 14 个,见 CHINESE_TERMS:通义、千问、阿里、百炼、魔搭、达摩、灵码、万相、支付宝、蚂蚁、语雀、钉钉、淘宝、天猫。
分隔符变体(多词名称)
对于由两部分拼成的名称,还匹配带有分隔符的变体,例如q-wen、dash_scope、ali cloud、qian-wen、ant_group。Rust 侧由 SEPARATOR_PATTERNS 定义 8 组二元模式:q/wen、dash/scope、ali/baba、ali/yun、ali/cloud、tong/yi、qian/wen、ant/group,允许中间出现-、_、空格或无分隔符。mobile-mcp 侧则在正则中直接用[-_ ]?表达同样语义,并在匹配时使用最长匹配策略,避免ali与alibaba等重叠词互相干扰。
编码机制:无状态 Token 与 UTF-8 十六进制往返
这是整个设计的核心:每个被匹配到的子串被替换为一个无状态 Token,Token 内嵌该子串的 UTF-8 十六进制字节。两个组件使用各自独立的前缀(实现上不互通,但机制一致):
| 组件 | Token 前缀 | Token 形态示例(qwen→71 77 65 6E) |
|---|---|---|
| cua-driver(Rust) | __cuaf_ | __cuaf_7177656E__ |
| mobile-mcp(TypeScript) | __mcp_ref_ | __mcp_ref_7177656E__ |
以 cua-driver 的 encode_text / decode_text 为例:
- 编码:从左到右扫描输入;命中过滤术语(或已存在的 Token 前缀)时,将该子串的原始 UTF-8 字节逐个转为大写十六进制,写入
前缀 + hex + __;其余字符原样复制。push_token使用0123456789ABCDEF十六进制表。 - 解码:查找
TOKEN_PREFIX,提取其后到__之间的十六进制串,按两字节一组还原为 UTF-8 字节;无效 Token 保持字面原文(测试leaves_invalid_tokens_literal验证__cuaf___、__cuaf_0__、__cuaf_ZZ__、__cuaf_FF__均原样返回)。解码是单遍的:一个字面量 Token 前缀被转义后作为数据恢复,不会被二次解释(测试escapes_a_literal_token_prefix)。
选择这种设计而非"会话内映射表"的关键收益:
- 无状态:不维护 session map,进程重启后 App/包/路径的往返依然成立;
- 可读性:被过滤的 App 名周围文本保持可读,Token 只占匹配部分;
- 可恢复:模型把该值原样返回给同一 MCP 服务器时,工具校验与执行前就能还原出精确的原始子串。
组件边界:两个方向的接入点
设计文档明确了两侧"模型可见边界"的接入点,仓库源码与之完全对应。
cua-driver(Rust):Response 与 Request 双向接入
在 protocol.rs 中:
- 出站方向:
Response::ok与Response::error是直接 stdio、HTTP、daemon-proxy MCP 响应共用的模型可见边界。ok_with_filter在启用时对整个 result 调用model_payload::encode_value(protocol.rs);error_with_filter对错误 message 调用encode_text(protocol.rs)。 - 入站方向:
Request::tool_call在派发工具之前解码工具名与参数——先对arguments整体执行decode_value,再对工具name执行decode_text(protocol.rs)。 - 两个方向都只在
MCP_MODEL_PAYLOAD_FILTER=1时应用转换,is_enabled()每次调用时实时读取环境变量。
mobile-mcp(TypeScript):Transport 包装层
在 payload-filter.ts 中,PayloadFilteringTransport是一个包装 Transport:
- 出站:
send()对每条消息调用encodePayloadMessage,只转换params、result、error三个字段(transformMessage); - 入站:
onmessage处理器先decodePayloadMessage再交给 SDK 进行 schema 校验; - 键冲突防护:若解码导致对象键折叠(恶意 Payload 可制造别名让两个键塌缩成一个),
PayloadKeyCollisionError会被捕获;若消息带id,则向客户端回一个-32602(Invalid params)JSON-RPC 错误,否则交给onerror处理。
而 server.ts 中的PayloadFilteredMcpServer在connect()时根据环境变量决定:启用则new PayloadFilteringTransport(transport),否则直接使用原始 Transport。因此 stdio、SSE、内存测试及未来新增 Transport 全部自动获得同样的过滤能力。
遍历规则与边界保护
过滤以递归方式作用于 JSON 结构,具体规则(Rust 侧 transform_value,TS 侧transformPayload):
- 字符串:编码/解码其文本;
- 数组:递归处理每个元素;
- 对象:键和值都递归转换;
type为image或audio的对象:其data字段被标记为不透明,逐字节原样保留,不参与文本过滤(Rust 与 TS 两侧实现一致);但该对象的其他字段(如annotations、文本描述)仍会转换;null、布尔、数字:不处理。
两条硬性边界:
- JSON-RPC
id与method永不转换——它们不参与往返,转换会破坏协议寻址; - 键冲突即报错:解码过程中若两个不同的键被还原为同一个键(
"Qwen": 1与"__cuaf_5177656E__": 2解码后都变成Qwen),Rust 侧decode_value返回错误,TS 侧抛PayloadKeyCollisionError——拒绝这类输入而不是静默丢弃其中一个值。
非目标与适用限制
设计文档明确划定了过滤的边界,这些限制在实现中同样成立:
- 不做重命名:不会重命名已安装的应用、进程、bundle、npm 包、签名身份、仓库或分发 URL;
- 不碰非模型可见输出:stderr、遥测(telemetry)、构建日志不会被转换;
- 不保证 OCR 场景:图片字节被保留,因此基于 OCR 的过滤不在"文本 Payload"保证范围内;
- 别名只在"回家"时解码:别名只有在返回给同一个 MCP 组件时才会被还原为本地值;把别名传给 shell 或另一个 MCP 服务器,不会恢复本地值(mobile-mcp README 也做了同样说明)。
配置示例:在移动端 MCP 服务器上开启过滤
以 mobile-mcp 为例,README(packages/mobile-mcp/README.md)给出在 MCP 客户端配置中针对特定路由开启过滤的方式:
{ "mcpServers": { "mobile-mcp": { "command": "npx", "args": ["@qwen-code/mobile-mcp"], "env": { "MCP_MODEL_PAYLOAD_FILTER": "1" } } } }对于 cua-driver,在服务器进程环境中导出MCP_MODEL_PAYLOAD_FILTER=1即可(见 packages/cua-driver/README.md),文档明确该开关默认关闭、不会改变直接 SDK 契约。只有在模型 API 路由会拒绝这些术语时再开启;其他场景保持默认即可获得原始 Payload。
验证与测试:从单元到真实协议交互
设计文档列出的验证清单,在仓库中均有对应实现。
cua-driver:Rust 单元测试
model_payload.rs 内置了完整测试套件:
filters_every_canonical_term_case_insensitively:每个 ASCII 术语(含全大写变体)与每个中文术语都做编码→解码往返断言;filters_supported_separator_variants:对 8 组模式覆盖空分隔符、-、_、空格四种变体,以及DaSh_ScOpE、QIAN-WEN、Ant Group混合写法;preserves_surrounding_text_and_multiple_matches:验证Open Q-Wen in 阿里 Cloud, then inspect Dash_Scope.编码后首尾文本保留、过滤词消失、解码后完全还原;recursively_transforms_keys_and_values_but_not_media_data:验证对象键被转换、image/audio的data字节保留、注解字段仍被过滤;decoding_rejects_object_key_collisions:验证键冲突返回错误;filters_driver_identity_from_observed_windows_apps_and_tree_shapes:用真实观察到的qwen-cua-driver.exe形态(text content、structuredContent 中的 windows/apps/processes)验证编码后序列化结果中不再包含qwen,且解码后与原文一致;safe_text_is_borrowed_and_unchanged:不包含过滤词的文本走零拷贝Cow::Borrowed路径,性能不受影响。
mobile-mcp:端到端协议测试
test/server-payload-filter.test.ts 使用InMemoryTransport+ 真实 MCP Client 验证两条主路径:
- 禁用时:
payload_unfiltered_probe的描述与结果原样保留,证明默认边界不被改变; - 启用时:覆盖
initialize、tools/list、成功响应(text 与 structuredContent)、错误响应、schema 校验失败、图片 data 保留、mobile_list_apps/mobile_launch_app真实工具链路的完整往返——尤其验证了客户端用encodeFilteredText('Q-Wen')传入参数时,服务端在 schema 校验(superRefine)之前已将其解码为Q-Wen原始值,即"先解码、后校验、再执行"的顺序成立。
小结
MCP 模型可见 Payload 过滤是 qwen-code 为适配对厂商术语敏感的模型 API 路由而设计的一套可逆、无状态、默认关闭的文本净化方案。它通过MCP_MODEL_PAYLOAD_FILTER=1一键启用,用内嵌 UTF-8 十六进制字节的 Token 替换 21 个 ASCII 术语、14 个中文术语及 8 组多词分隔符变体,递归作用于对象键与文本值,同时逐字节保留图片/音频data、永不触碰 JSON-RPCid与method。在 cua-driver 侧,Response::ok/error与Request::tool_call构成双向过滤边界;在 mobile-mcp 侧,PayloadFilteringTransport统一包装 stdio、SSE 与内存传输。理解这一机制,就能在需要时安全地为指定路由开启过滤,同时确信应用、包名、路径等真实操作值始终能够无损往返。
【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考