CC Switch 的搜索为什么把凭据字段排除在外

2026-08-31

列表页上的搜索框大概是一个桌面工具里最不起眼的功能。但只要一条记录里既有”是什么”的结构信息、又有”怎么接进去”的凭据信息,这个搜索框就会被迫回答一个不轻松的问题:把这条记录的哪些字段,拼成那串用来做子串匹配的文本?

MCP 服务器条目正是这样一条记录。stdio 型要有 command,http 与 sse 型要有 url,除此之外还有 env 和 headers 两组完全由使用者填写的键值对。答错这道题的后果不是搜不到东西,而是把一段本该只躺在配置里的凭据,变成一个能被子串命中、能被后续逻辑顺手复用的普通字符串。

CC Switch 在这一步做了一个明确的取舍,而同一个仓库里另一个列表页做了相反的取舍。两段代码就在同一个 src/components/ 下的两个目录里,是一组不用自己去造的对照样本。

这篇依据的是什么

本文对应 cc-switch 仓库的 v3.20.1 快照(commit 3217f725),核对日 2026-08-31。所有结论都来自静态阅读 src/ 下的源码与 docs/ 下的用户手册:没有编译,没有运行,也没有安装过这个桌面应用。因此下面不会出现任何关于界面长相、按钮位置或搜索响应快慢的描述——那些我们没有依据说。

白名单写在函数体里,不是写在注释里

关键那段在 src/components/mcp/UnifiedMcpPanel.tsx 的 27 到 51 行,函数名 getMcpSearchText

它不是把整条记录一股脑丢进搜索,而是显式列出一份可搜字段清单,只有清单上的字段才会被拼进那串用来做匹配的搜索文本:

大致归类(便于理解,不是代码里的分组)字段
这条记录是谁idnamedescriptiontags
怎么把它跑起来typecommandargscwdurl
出处与文档homepagedocssource

这是 v3.20.1 的这一版清单,字段增删都会让它变,别当成一份长期有效的定义。要紧的不是这十来个名字本身,而是清单之外的一律不进搜索文本——envheaders 正是被留在清单之外的那两组。

这个清单上方压着两行英文注释,大意是:这里要保持一份显式的允许清单,尤其是 env 和 headers 可能含有凭据,绝不能成为可搜索文本的一部分——原文里那半句是 “must never become part of the searchable text”。

注意它的措辞是”可能含有”,不是”含有”。这个区别决定了处置方式:因为工具无法预判使用者往这两组键值对里塞了什么,所以只能整组排除,而不是去做什么内容识别。

白名单与黑名单,差别在于新字段的默认待遇

同一件事有两种写法。写成排除式——“除了 env 和 headers,其余都可搜”——今天的效果和现在这份代码完全一样。差别在明天:当 McpServer 上再加一个字段时,排除式写法会让这个新字段默认进入搜索文本,除非有人想起来去补一条排除;显式列举则相反,新字段默认在清单之外,想让它可搜必须有人主动加一行。

这就是”默认拒绝”。它的代价是真实存在的:确实该被搜到的新字段可能因为没人补那一行而搜不到。但两种失误的代价不对称——一边是”搜不到”,一边是”不该出现的东西出现了”。这段代码把可以承受的那种失误留给了自己。

为什么 command、args、url 反而在清单里

乍看之下 command 和 url 也不像”公开信息”。它们进清单的理由,从校验规则那一侧能看得更清楚。

src/components/mcp/useMcpValidation.ts 里 TOML 与 JSON 两条校验路径的必填规则是对称的(分别在 41 到 49 行与 76 到 81 行):type === "stdio" 必须有 commandtypehttpsse 必须有 url。也就是说,这几个字段是判定”这条 MCP 服务器是什么”的结构性信息,缺了它连保存都过不去。

env 和 headers 则完全不同,它们是自由文本解析出来的键值对。数据入口在 src/components/mcp/McpWizardModal.tsx 里两个手写的纯文本解析器:parseEnvText(27 到 42 行)按行切,只认每行第一个 =parseHeadersText(47 到 70 行)KEY: VALUEKEY=VALUE 都吃,哪个分隔符的下标更靠前就用哪个。解析器不关心内容语义,键名与值都由填写者决定。

一边是结构,一边是任意内容——这条线划在哪儿,代码里是有据可循的。

对照组:提示词列表把正文也纳入了搜索

src/components/prompts/PromptLibrary.tsx 的 42 到 52 行是另一种做法:这个列表页的搜索把提示词正文 content 也纳入了搜索范围。

content 就是用户写在一条提示词里的那一整段内容。也就是说,正文里出现过某个词的条目同样会被留下,哪怕它的名字里根本没有这个词。对一个提示词库来说这很实用——你往往只记得自己在里面写过某句话,记不得当初给它起了什么名。

两处的取舍方向正好相反:MCP 那侧把一类字段挡在搜索之外,提示词这侧把体量最大的正文字段主动放了进来。同一个仓库、同一类交互,选字段的策略不一样——这是我们实读到的状态,到此为止。哪一种更该被推广、为什么没统一,这些我们没有依据,也不打算猜。可以说的只是:两处面对的字段语义确实不是一回事,一边是使用者要找的正文本身,另一边是接入用的凭据槽位。

共享的是搜索框,不是”什么可搜”

值得留意的是,这两个列表页用的输入框是同一个组件:src/components/common/ManagementListSearch.tsx。它在 skills、prompts、mcp 三个面板里都被引用,proxy 不引用它,universal 干脆是几个功能面板里唯一没有搜索框的那个。

所以共享层止步于”那个带图标的输入框”,而”哪些字段可搜”这条策略被留在了各自的面板文件里。这个切法有它的道理:真要把取文本也做成一个通用函数,那份白名单就得跟着通用化,而字段语义恰恰是逐个实体各不相同的。至于这是不是当初的考量,代码里没写,我们也就不替它说。

顺带澄清一个容易混淆的点:skills 的发现页里那个搜索不是同一类东西。它走的是后端命令 search_skills_shsrc/lib/api/skills.ts 226 行),查的是外部技能注册表,不是本地数组过滤;hook 层还设了 enabled: query.length >= 2src/hooks/useSkills.ts:374),少于两个字符不发起查询——这个门槛写死在 v3.20.1 的源码里,会随版本变。本地过滤和”把查询词交给外部服务”,安全考量完全不在一个层面上。

搜索改变的是可见集合,不是批量动作的作用集合

还有一处和搜索直接相关、但方向相反的设计,在 UnifiedMcpPanel.tsx 的 176 到 210 行,handleToggleAll

这个批量开关用的是 serverEntries 而不是 filteredServerEntries。代码注释给了理由:统计条汇总的是完整集合,那么挂在它上面的批量动作也必须作用于完整集合,哪怕当前正开着搜索过滤。同一段里还有一层收敛(182 到 184 行):只挑当前状态与目标状态不一致的条目,如果全都一致就直接 endWrite() 返回,不发起任何写入。

搜索框在这里被明确定位成”看的范围”,而不是”改的范围”。这条从代码上是清楚的。

版本口径与我们没核到的部分

  • v3.19.2 到 v3.20.1 之间,UnifiedMcpPanel.tsx 的差异只有两处运行时守卫:handleToggleApp(165 行)和 handleToggleAll(177 行)各加了一句 if (!isMcpAppId(app)) return;。上面讲的这个白名单函数不在这两处改动里。
  • 提示词那一侧有搬家:v3.19.2 时列表的计数、搜索框与过滤逻辑还内联在 PromptPanel.tsx 里,v3.20.1 已经整块挪进了新增的 PromptLibrary.tsx。我们只核到了搬家这件事本身,没有逐字段对比搬家前后的清单,所以不下”字段没变过”这种断言。
  • 最重要的一条:这份白名单只管这一个列表页的本地过滤。env 与 headers 在保存、写盘、导出、日志等其它路径上分别怎么处理,不在我们这次核到的范围内,不能由这一行代码推出”凭据在别处也不会露面”。这个工具会在本机保存 API Key 并读写真实的 CLI 配置文件,那是实打实的本机敏感数据,任何”这样就安全了”的结论都不成立。

你自己去核这两处

在你 clone 下来的仓库根目录执行,Linux、macOS 或 Windows 上的 Git Bash:

grep -n "explicit allow-list" src/components/mcp/UnifiedMcpPanel.tsx
grep -n "prompt.content" src/components/prompts/PromptLibrary.tsx

Windows PowerShell 下换成:

Select-String -Path src\components\mcp\UnifiedMcpPanel.tsx -Pattern "explicit allow-list"
Select-String -Path src\components\prompts\PromptLibrary.tsx -Pattern "prompt.content"

以上为按仓库中的文件路径组合的检索命令示例,未经实测,实际行号以你本机拉到的版本为准。这个仓库迭代很快,行号漂移是常态,函数名和注释里那半句英文比行号耐用得多。


本文依据 CC Switch 官方仓库(github.com/farion1231/cc-switch)的 README、docs/ 下的用户手册、路由指南与发布说明,以及 src/src-tauri/tests/ 的源码整理,核对日 2026-08-31,对应仓库快照 3217f725(仓库内版本号 3.20.1)。本文内容为仓库源码与文档口径,我们没有安装或运行过这个桌面应用,因此不涉及界面外观、操作手感与切换速度的任何描述。文中出现的阈值与默认值均为源码中的默认配置,不构成对实际运行结果的保证。该项目仍在快速迭代,版本与默认值随时可能变动,请以仓库最新内容为准。安全相关做法请结合自身环境评估,本文不构成安全方案建议。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。

留言讨论

评论发布后会被人工复核,违规内容将被删除。

    还没有人评论,来说说你的看法

    如果发表没有反应,可以前往联系我们告诉我们。

    在 CC Switch 里加一个国内直连的供应商

    力达云网关,注册送 ¥5 额度,一期提供 DeepSeek。

    去添加

    这个页面有问题?

    提交时会附带当前页面地址和浏览器信息,帮助我们定位问题。不填联系方式即为匿名。