news 2026/9/7 4:38:04

CLI与MCP并非二选一:协议化工具接入的价值与实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CLI与MCP并非二选一:协议化工具接入的价值与实践

我最近在一个技术交流群里看到一场很有意思的争论。有人提了一个观点:现在 CLI 工具已经那么成熟了,Claude CLI、Codex CLI、各种终端里的 Agent 都很能打,为什么还要专门搞 MCP,是不是多此一举?另一个人马上接话:MCP 不是给 SaaS 平台服务的吗,普通开发者接什么 MCP。两种说法乍一听都有道理,但放到同一个语境里,它们其实都把概念用错了。CLI 不能替代 MCP,MCP 也不是 SaaS 专属。这篇文章我想把这些概念的边界讲清楚,也给出一个现在就能落地的接入思路。

1. 先搞明白:CLI 和 MCP 到底是不是同一类东西

1.1 大家说“CLI 替代 MCP”时,到底在说什么

很多人会把这两者放在对立面,是因为在讨论里,CLI 和 MCP 都被当成了一种“让 AI 干活”的方式。

你可能会听到这样的说法:“我用 CLI 就能让模型读文件、跑命令、写代码,还要 MCP 干什么?”这话听起来体验感很真实,但问题在于,它把两个不同层次的东西混在一起了。

CLI 是一种命令行界面。它的核心是“人通过输入命令,让程序执行操作”。哪怕写成脚本批量执行,CLI 仍然是面向人的交互方式:命令的参数、输出格式、错误码,都是设计给有经验的使用者去理解和处理的。

MCP 不是一个工具,不是一款软件,也不是某个插件。它更接近一套协议。它解决的不是“人怎么操作程序”,而是“模型怎么调用外部工具”。在没有约定之前,每个工具暴露给模型的方式都不一样,模型客户端要接十个工具,就得写十套适配逻辑。MCP 做的事情,是把“模型对工具的请求”和“工具返回的结果”标准化。

所以,“CLI 替代 MCP”这个说法,本质上类似于说“HTTP 协议可以被一个优秀的前端页面替代”。不是谁强谁弱的问题,而是层级不同。命令行工具是一个具体的执行端,MCP 是模型与执行端之间的一种通信约定。两者根本不是二选一的关系。

1.2 CLI 解决的是“人怎么把指令给程序”,MCP 解决的是“模型怎么把请求给工具”

CLI 在设计之初,就没有考虑过“模型作为使用者”这个场景。

它假设坐在终端前面的是一个会读文档、会处理报错、会调整参数的人。所以它的一个明显特征是:信息密度很低,上下文依赖很高。

举个例子,一个人用 Git 命令操作仓库,看到git status的输出,能立刻判断哪些文件被修改了、哪些分支需要处理。但如果让模型直接执行命令行,它面临的是一大堆文本输出,而且这些输出的格式会随着工具版本、系统环境、目录状态而改变。模型需要把这段文本解析成结构化的结论,才能决定下一步动作。这个过程不是不能做,但非常脆弱。

MCP 换了一种方式。它把“命令”变成了“工具调用”。模型客户端通过统一协议,发送一个结构化的请求,里面包含工具名称和参数;MCP server 收到请求后,执行对应的操作,返回结构化的结果。这个过程里,模型不需要关心命令的拼法,也不需要解析不稳定的文本输出。它只需要知道这个 MCP server 暴露了哪些工具、每个工具接收什么参数、返回什么结构。

从实际体验上看,同样一个本地文件操作需求:

  • 用 CLI 直连,模型先要“猜”有哪些命令可用,再猜测参数,再解析输出;
  • 用 MCP server,模型只需要知道“有一个读取文件信息的工具,传入路径就能拿到结构化结果”。

后者更稳定,也更容易做安全控制。这正是 MCP 的价值所在:不是取代命令行,而是把工具能力包装成模型能直接理解的形式。

1.3 一个反直觉的点:MCP server 也可以是 CLI 的“壳”

很多人会忽略一个事实:MCP server 内部完全可以调用 CLI。

我自己在做一个本地代码库管理的小工具时,就采用了这种分层:MCP server 负责暴露get_repo_statusget_repo_diffrun_tests这类工具,但底层真正执行的其实是git statusgit diffpytest这些命令。模型不需要知道这些命令的存在,它只负责按照工具说明传入参数,剩下的执行细节全部由 MCP server 封装好了。

反过来,一个 CLI 工具不会自动变成 MCP server。它必须有人实现协议适配,把命令行参数和输出格式翻译成结构化的工具定义和返回结果。

所以正确的理解是:CLI 可以成为 MCP server 的执行后端,MCP 是模型与服务端之间的通信层。两者是配合关系,不是替代关系。

2. MCP 的价值不在接入方式,而在“协议化协作”

2.1 为什么工具接入一直是一地鸡毛

今天大家习惯了一件事:让 AI 模型调用工具。但在 MCP 这类协议成熟之前,工具接入的方式非常原始。

最简单的方法是模型直接生成代码,写一段 Python 脚本调用某种接口。这个方法对单机小任务可行,但一旦遇到认证、分页、错误处理、超时重试,代码就会迅速膨胀,而且每次换一个工具都要重新写一遍。

后来出现了 Function Calling 机制,模型可以按照平台定义的 JSON Schema 来调用函数。这个进步很大,但它仍然是“每个平台自己定义一套规则”的思路。你在一个模型平台里定义好的工具,不能直接拿到另一个客户端里用。工具和数据源越接越多,适配成本仍然居高不下。

MCP 的做法是引入一个统一的“插口”。它的模式很像 Central Hub:模型客户端通过同一个协议,去连接不同的 MCP server。每个 server 负责把自己背后的工具和数据源暴露出来。只要双方都支持这套协议,就能完成连接,不需要为每个工具单独定制一套模型侧的调用逻辑。

如果一开始接触这个概念觉得抽象,可以这样理解:CLI 是“每家银行各发一张只能在自己柜台用的存折”,MCP 则是“一套通用的跨行转账协议”。本质上是为了降低连接成本。

2.2 一次配置,多处复用

协议化带来的另一个直接收益是复用一个 MCP server,可以在多个客户端里使用。

同一个本地文件读取 MCP server,配置到支持 MCP 的桌面客户端、IDE 插件、Agent 框架里,都能工作。同一个设计稿 MCP server,可以让前端编程助手直接读到标注和样式信息,减少“边看设计稿边写代码”的切换成本。同一个数据库 MCP server,能通过自然语言查询表结构和样本数据,而不是把 SQL 片段在对话里反复粘贴。

现实中,不同客户端对 MCP 特性的支持程度并不一样,配置方式也可能有差异,比如有的用mcpServers配置块,有的通过图形界面添加。但协议层面的标准化,已经让“一套能力,多处接入”这件事变得可行了。

这也是为什么最近会有那么多“MCP server demo”、低代码平台搭建 MCP 服务器、Java 生态里也出现 MCP 集成尝试。大家的目标都是同一个:把工具能力暴露成模型可以调用的标准接口,而不是每次换客户端就重新接一遍。

2.3 为什么很多人误以为 MCP 是 SaaS 专属

有这个印象并不奇怪。因为最容易被包装成 MCP server 的工具,往往是那些已经有现成 HTTP API 的云服务。文档、项目管理、在线数据库、代码托管平台等等,这些服务本身就有清晰的接口,做成 MCP server 只是多包一层协议适配。宣传材料里也经常出现“连接你的 SaaS 应用”这类场景,看多了自然会产生联想:MCP 是不是给 SaaS 集成用的?

但 MCP 协议本身并没有这个限制。它的 client-server 模型天然支持本地场景。本地文件系统、本地数据库、本地代码仓库、本地浏览器自动化、本地设计稿工具、游戏引擎资源,这些都特别适合用 MCP 暴露给模型,原因很简单:数据本身就在本地,模型助手需要的是“能安全、结构化地访问它们”的通道。

所以更准确的说法是:SaaS 只是 MCP 应用的其中一个类别,不是前提。对普通开发者来说,MCP 在本地开发工具链里的价值,可能比放上云更直接。你不需要先有一个复杂的云服务,才有资格使用 MCP。

3. 从最近被反复搜索的场景,看 MCP 的真实适用范围

3.1 设计稿、游戏引擎和安全工具:本地不是例外,而是主要场景

最近我在梳理关于 MCP 的搜索材料时,发现了一批很有意思的关键词:蓝湖 MCP、Cursor 连接蓝湖 MCP、Unity MCP、CocosCreator MCP、Playwright MCP、Wazuh MCP、Burpsuite MCP。

这些关键词背后是几种完全不同的工具类型,但它们有一个共同点:都不属于“典型的云端 SaaS”。它们更像是一条条具体的本地开发链路。

  • 设计稿接入:蓝湖这类设计协作工具可以把标注、切图、组件信息暴露成 MCP 工具,模型就能直接理解设计稿里的尺寸、颜色和间距,不再依赖人工描述。
  • 游戏引擎接入:Unity、Cocos Creator 这类引擎资源结构比较复杂,MCP server 可以把场景树、资源列表、组件属性变成模型可调用的工具,适合做场景分析和脚本生成。
  • 浏览器自动化接入:Playwright MCP server 把浏览器操作变成工具,模型可以直接驱动浏览器做页面检查和交互验证,比让人复制截图再描述要高效。
  • 安全工具接入:Wazuh、Burpsuite 这类安全监控和测试工具,也可以被包装成 MCP server,让模型读取告警、调用扫描能力。

这个名单告诉我们一件事:MCP 的适用范围,远远超出“给 SaaS 平台接 API”的范畴。本地重度工具反而可能是更刚需的场景,因为模型能直接作用在开发环境里,效果是立竿见影的。

3.2 “Computer Use 和 MCP 有什么区别”背后的认知混乱

还有一个高频搜索问题:Computer Use 和 MCP 的区别。

这说明很多人把“让模型使用工具”当成了一件事。实际上,这两者走的是完全不同的技术路线。

Computer Use 本质上是通过视觉和鼠标键盘事件,让模型像人一样操作图形界面。模型先看截图,理解屏幕内容,再生成点击、输入、滚动等动作。它的优点是很通用,不需要接口,老旧系统也能操作;缺点是慢、不稳定、容易因为界面变化而失败,而且很难做细粒度的安全控制。

MCP 走的是相反的方向:模型不关心界面长什么样,只通过结构化协议调用已经定义好的工具。每个工具都有清晰的名称、参数和返回结构,调用关系是确定的,结果是可校验的。

在实际工程里,两者不是替代关系。如果一个场景里工具已经有 API 或 CLI,优先用 MCP 封装,速度快、可靠、可控。如果一个场景是完全没有接口的遗留 GUI 程序,Computer Use 可以作为兜底方案。常见的协作形态是:MCP 处理高频、确定性强的操作,Computer Use 处理那些没有别的入口的遗留任务。

换句直白的话说:MCP 是“给模型开一个专用后门”,Computer Use 是“让模型像人一样走正门”。后门更高效,正门更通用。成熟方案里两者常互相补充。

3.3 CLI 报错现场给我们的一个重要提醒

那些搜索热词里还有一个很具体的报错,经常出现在图形客户端启动阶段:

“unable to locate the codex cli binary”

意思是客户端在启动时,没有找到 Codex CLI 的二进制文件路径。类似的情况在 Claude CLI 接入时也会出现:客户端日志里提示找不到对应命令,或者不满足路径配置要求。

这个问题的本质是:命令行工具要被模型客户端调用时,必须先解决“二进制在哪里、路径怎么配置、环境变量对不对、沙箱权限开没开”这一连串接入问题。CLI 不是天然就能被模型客户端直接调用的,它有系统边界,需要适配。

我在本地环境也遇到过类似体验。安装好 CLI 工具后,图形端依然报找不到路径,检查之后发现是当前用户环境变量没有把安装目录暴露给客户端进程。这不是 MCP 特有的问题,但它能说明一个更底层的规律:CLI 与 AI Agent 之间,需要有一层明确的适配配置,否则就会卡在启动阶段。

MCP server 的作用之一,就是把这种适配过程标准化、可复用。server 负责启动,客户端负责通过协议连接,两边各干各的,排查起来也更清晰。

4. 如果你想现在就开始用 MCP:最小可跑通流程

4.1 先定场景,再选客户端和 server

很多人第一次接触 MCP 会犯一个错误:先把 MCP server 安装一堆,配置几十个工具,然后发现根本没用到,也不知道怎么调试。与其这样,不如先用最小流程跑起来。

第一步,明确目标:你希望模型拿到什么数据,或者操作什么工具?三个常见方向:

  • 读本地信息:文件内容、代码库状态、设计稿标注;
  • 操作外部服务:查询数据库、创建工单、发送消息;
  • 控制本地程序:浏览器自动化、测试运行、脚本执行。

第二步,判断这个工具是否已经有现成的 MCP server。社区里能找到很多现成的,设计稿、数据库、浏览器、代码托管平台都有。如果找不到,再考虑自己写一个。

第三步,选择客户端。支持 MCP 的客户端越来越多,有的在桌面应用里直接提供配置入口,有的需要在项目配置文件里写 JSON。选择时优先看自己平时用的开发工具是否已经支持,避免为了接 MCP 再引入一套新客户端。

对大部分第一次尝试的人来说,我建议先从“读取本地文件”或“查询本地数据库结构”这类只读、低风险场景开始。原因很简单:只读操作即使配置出错,也不会破坏数据。

4.2 一个最小 MCP server 示例结构

如果你决定自己写一个测试用的 MCP server,可以参考下面的结构。这里用的是 Python 生态里常见的一种写法,只用于说明整体结构,依赖版本和 API 细节要以你实际使用的官方文档为准。

# 一个最简单的 MCP server 示例骨架 from mcp.server.fastmcp import FastMCP mcp = FastMCP("demo-server") @mcp.tool() def echo_text(text: str) -> str: """原样返回输入文本,用来验证工具调用链路是否通了。""" return text @mcp.tool() def list_directory(path: str) -> list[str]: """返回指定目录下的条目名称列表。仅用于演示,生产环境建议增加路径校验。""" import os return os.listdir(path) if __name__ == "__main__": mcp.run()

然后,在支持 MCP 的客户端配置文件里,加上这个 server 的启动方式。常见写法是这样的:

{ "mcpServers": { "demo-server": { "command": "python", "args": ["path/to/your/server.py"], "env": {} } } }

不同客户端的配置键名可能会略有差异,有的还需要填urltransport或额外的环境变量。第一次配置时,先看官方示例,再对照自己的路径和 Python 环境调整。

跑起来之后,最简单的验证方式是:在对话里直接让模型调用echo_textlist_directory。如果模型能正确返回结果,说明客户端与 server 之间的链路已经通了。

4.3 本地验证的检查清单

把第一次运行 MCP server 的调试经验,整理成下面这份清单。以后无论接入新的 server,还是排查旧的工具,都可以按这个顺序走一遍。

检查项优先级说明
输入定义工具名和参数 schema 是否清晰,模型是否容易理解
输出结果返回是否结构化,是否稳定,会不会因为数据变化而报错
进程状态server 是否能正常启动,报错时先看日志
路径与环境变量二进制路径、Python 环境、系统 PATH 是否正确
权限边界只读操作是否只读,写操作是否有白名单
资源占用本地 server 是否占用过多 CPU、内存或端口
客户端兼容当前客户端对 MCP 特性的支持程度是否满足需求

这份清单也是一个排查框架。如果连接失败,我会按“先看日志,再看进程,再看路径,再看工具定义,最后看权限”的顺序处理,先确定是哪一层断了,再决定修哪里。

4.4 从单次验证到批量任务要注意什么

很多人在本地跑通一个 MCP server 后,会立刻想着批量处理几百个文件。这个跳跃通常会踩坑。

更稳妥的路径是分三步:

  1. 单条样例跑通:只处理一条输入,确认输入、执行、返回都正常;
  2. 小批量试跑:把数量控制在 3 到 5 个,观察调用是否稳定,有没有超时、报错或返回异常;
  3. 再规模化:确认稳定后,再考虑并发数、限流、重试策略和超时时间。

在这个阶段,最重要的是保持“可观察”。MCP 调用不像人敲命令那样能直观看到每一次操作,模型的调用参数、执行结果、错误信息都要记录日志。我通常会要求客户端的工具调用记录能留痕,至少能回答三个问题:调了哪个工具?传了什么参数?返回了什么结果?

还有一个容易忽略的点:当同一个 MCP server 同时被多个会话调用时,要注意它是否线程安全、是否支持并发请求。很多刚写完的 server 在单会话场景下没问题,一旦被并发调用,就会出现变量共享、端口冲突、数据库连接池耗尽一类问题。

注意:不要一上来就把批量数和并发数拉满。先用一条样例确认输入、输出和日志都正常,再逐步扩大规模,每扩大一步都观察一次调用质量和错误率。

5. 长期使用前,先补齐这几块工程化拼图

5.1 安全与权限:不是所有工具都该开放

MCP server 暴露出来的能力,就是模型的能力边界。你给模型接了一个能执行 shell 命令的 server,那它在对话里就可能尝试执行任何命令;你给模型接了一个能写文件的 server,那它就可能修改指定路径下的文件。

安全边界不是模型自己会守的,而是配置方需要控制的。更稳妥的做法是:

  • 只暴露当前场景需要的工具,不暴露全部能力;
  • 默认只读,需要写入时再单独开放;
  • 对高风险操作增加人工确认环节;
  • 对文件系统操作做路径校验,限制在指定目录内。

这一点在接入 SaaS 系统时尤其重要。热搜里有一个问题,大意是“SaaS 系统怎么确保数据安全不可篡改”,放到 MCP 语境下,答案不是靠模型自律,而是靠权限、审计和操作范围的严格控制。数据库连接只给只读账号,文件访问只允许读特定目录,工具调用记录完整留档,这些都是可以落地的工程手段。

5.2 日志、重试和可观测性

MCP 的使用模式是“模型调用工具”,这意味着很多操作是在没有人工直接参与的情况下发生的。出问题时,你往往看不到现场,只能靠日志复盘。

因此,长期使用 MCP 之前,建议补齐三块能力:

  1. 日志:记录每次工具调用的时间、输入端参数和返回状态;
  2. 重试:对偶发网络错误或超时设置合理的重试次数,但不要无限重试;
  3. 指标:至少记录调用次数、失败率、平均耗时,便于快速发现异常趋势。

这套东西听起来像正规后端服务的要求,但只要你想把一个 MCP server 放进日常工作流里,而不是只在本地演示一次,就需要尽早补齐。

5.3 版本与兼容性:MCP 还在快速演进

接触 MCP 的人越来越多,但这个协议本身还在演进中。客户端支持的枚举类型、server 声明能力的方式、某些高级资源的获取机制,不同版本之间可能存在差异。

实际落地时,最让人头疼的一类问题是:客户端说 server 连接成功,但界面里看不到任何工具。这种情况通常不是 server 写错了,而是客户端和 server 定义工具时的 schema 不一致,或者客户端版本太老,不支持 server 用的新字段。

排查这类问题,第一步永远不是重写代码,而是先确认两端版本:

  • 客户端版本是否支持你用的 MCP 特性;
  • server 框架版本是否和你写的代码一致;
  • 日志里是否明确提示tool not foundschema mismatch

5.4 适用边界:MCP 不适合什么场景

MCP 很有用,但并不是所有场景都应该用它。如果只看热度,容易误以为“什么都该接 MCP”,实际落地的判断标准不在这里。

我梳理了几个明显不适合用 MCP 的场景:

场景为什么不适合 MCP
一次性的临时脚本任务直接写一段脚本或命令更快,不需要引入协议层
完全开放、边界模糊的对话场景模型没有明确的工具调用范围,硬接 MCP 反而增加不可控因素
依赖大量视觉判断的任务MCP 适合结构化交互,视觉判断更适合 Computer Use 或人眼确认
没有明确接口的老旧系统没有东西可以暴露给模型时,MCP 无从谈起

选型时要先问一个问题:这个场景是不是“重复发生的、有明确工具边界、结果可校验”?三个条件都满足,MCP 才值得引入。如果只是临时用一次,写段脚本反而更直接。

6. 回到开头那个争论

6.1 不要为了用而用,先看工作流效率是否真的提升

CLI 和 MCP 之间不是替代关系,而是层级关系。CLI 继续是操作员入口,MCP 成为模型的工具通道。对同时用到两者的人而言,真正的收获不是“多了一个协议”,而是“重复的接入过程被标准化了”。

但也不要因为 MCP 热度高,就把所有流程都硬套上去。如果一个场景用 CLI 或普通脚本足够,就不需要为了面子接 MCP。判断标准只有一个:实际使用下来,是不是减少了你在一套重复流程上的时间。

6.2 MCP 的真正价值是让工作流可组合

MCP 给开发者带来的最大改变,不是给 AI 多接一个插件,而是让工作流变得可组合。

以前,一个完整的本地开发流程需要人手动串联多个 CLI 工具:先查代码仓库状态,再读文件,再运行测试,最后把结果反馈到聊天窗口。有了 MCP 之后,这套流程可以被拆成多个标准工具,模型在上下文里自主选择调用顺序,并在多个工具之间传递结构化数据。人可以只提一个目标,让它逐步完成。

这套路径能成立的前提,仍然是工具定义足够清晰、权限边界足够明确、日志足够完整。协议只是一层连接,质量和可控性取决于你怎么配置。

6.3 下一步最先该做什么

如果你也想尝试 MCP,我的建议不是去安装一堆 server,而是找一个每天都会用、但过程重复的小工具,把它变成 MCP server,或者接一个现成的。

从只读、低风险场景开始,跑通最小闭环后再考虑批量、权限和日志。你会发现,CLI 和 MCP 各自站在合适的位置上,真正值得长期关注的不是“谁替代谁”,而是“模型与工具之间的协作,终于有了统一语言”。

先把自己最常用的那个工具接进去,比研究十个抽象概念更有用。跑通一次真实工作流,比读完一百篇讨论更接近答案。

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

ST25R3916实战:NFC读取全流程解析与天线调谐

最近在做一款便携式 NFC 读写设备,主控选型时在 PN532、CR95HF 和 ST25R3916 之间犹豫了很久。PN532 资料多、上手快,但射频性能和高低温稳定性在工业场景下有点捉襟见肘;CR95HF 是 ST 自家的经典方案,不过协议支持相对保守。最后…

作者头像 李华
网站建设 2026/9/7 4:36:44

AJAX实战:从XMLHttpRequest到axios,蓝桥杯“组课神器”全解析

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

作者头像 李华
网站建设 2026/9/7 4:34:08

BMS接口技术需求全解析:从硬件选型到协议测试

简介:《BMS与其他系统接口技术需求.doc》是一份面向智能楼宇和智慧建筑项目的系统集成工程师、弱电设计师及BMS调试人员的技术需求文档,基于IOTOS物联网平台系统梳理了BMS与消防报警、楼宇自控、视频监控、入侵报警、出入口控制、车库管理、能源管理及一…

作者头像 李华
网站建设 2026/9/7 4:33:16

弹性容错防线全景:多维限流、双桶平滑、熔断与四级降级

弹性容错防线全景:多维限流、双桶平滑、熔断与四级降级在微服务与高可用分布式架构中,“容错与弹性(Resilience & Fault Tolerance)”是保障系统在极端网络抖动、流量洪峰与下游依赖瘫痪时依然能够屹立不倒的核心生命线。 然而…

作者头像 李华
网站建设 2026/9/7 4:33:05

《Turing Complete》2.1版:从NAND门到CPU的硬核通关与全成就指南

如果你在Steam上翻过“硬核编程”或者“逻辑”这类标签,大概率早就撞见过《Turing Complete》这个名字。它不是那种点两下鼠标就开心的休闲益智,而是一个让你从一枚最底层的NAND门开始,一步步搭出内存、ALU、寄存器、指令集,最后造…

作者头像 李华
网站建设 2026/9/7 4:32:42

国产开关机芯片替代选型:关键参数对比与实测验证指南

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

作者头像 李华