文章

mcp2cli 架构笔记:运行时 CLI 映射,而不是代码生成

基于 mcp2cli 的 PyPI、GitHub README 与公开文档,解释它如何在运行时把 MCP、OpenAPI 和 GraphQL 翻译成可执行命令,以及这种设计在 agent 调用链里意味着什么。

mcp2cli 架构笔记:运行时 CLI 映射,而不是代码生成

MCP 的讨论这两年越来越多,焦点大多落在两件事上:让模型看到更多工具,以及让工具调用在代理工作流里足够稳定。但只要真正把 MCP、OpenAPI 或 GraphQL 接进一个 AI agent,另一个更现实的问题就会浮出来:工具本身不难接,难的是怎么把它们变成一种低摩擦、低上下文开销、又适合终端的调用方式。

mcp2cli 没有试图重新发明一套代理协议,而是换了一个更务实的切入点:在运行时把 MCP server、OpenAPI spec 或 GraphQL endpoint 直接映射成一组 CLI 命令,不生成任何代码。

核心工作流

不管面对哪种来源,mcp2cli 的使用路径都是固定四步:

  1. 连接来源(MCP HTTP/SSE、MCP stdio、OpenAPI spec、GraphQL)
  2. --list 发现可用命令
  3. <command> --help 查看参数
  4. 带 flags 执行调用

这套”连接 → 发现 → 查看 → 执行”的流程看起来朴素,但它把”接新接口”的前期准备压缩成了一个很短的回路。你不需要先读完整文档,不需要生成客户端代码,也不需要先配好一套正式封装,就可以直接对着接口试。

为什么不是代码生成

很多开发者看到”把 API 变成 CLI”时,第一反应是代码生成:读一份 schema,输出一组本地命令封装。codegen 的好处很明显,生成物稳定、接口清晰、可以被二次开发。如果你长期维护一个固定的内部 API,codegen 往往是合理路线。

但 codegen 有一个前提:你已经确定值得为这个接口投入工程成本。现实中你经常面对的是另一种情况:

  • 临时接一个第三方 OpenAPI 服务做验证
  • 让 agent 先探索某个 MCP server 再决定是否正式集成
  • 快速调通一个接口,然后再判断是否值得写 SDK

这时如果还要先生成代码、再组织命令、再处理认证和缓存,链路就太长了。

mcp2cli 的选择是把 schema 或 tool definition 当成运行时输入,而不是构建时输入。CLI 不是提前编译好的,而是根据当前传入的 --mcp--spec--mcp-stdio 动态展开的。用户操作的不是固定命令集,而是一套被即时解析出来的命令界面。

放弃静态生成带来的确定性,换来更低的接入门槛和更短的探索路径。在 agent 工作流里这个取舍通常合理,因为 agent 最常见的需求不是长期维护某个 API SDK,而是”现在就把这个工具接进来用一下”。

三类接入面

MCP

mcp2cli 同时支持 HTTP/SSE 形式的远程服务(--mcp)和通过本地进程启动 server 的 stdio 方式(--mcp-stdio)。

# MCP over HTTP/SSE
mcp2cli --mcp https://mcp.example.com/sse --list

# MCP over stdio(本地进程)
mcp2cli --mcp-stdio "npx @modelcontextprotocol/server-filesystem /tmp" --list

MCP 生态里的 server 并不是单一形态,有些天然就是 HTTP 服务,有些更适合 stdio 启动。mcp2cli 没有要求来源统一成某种 transport,而是在 CLI 层把两种模式都兜住了。按照公开 README 的描述,对于 HTTP MCP,它会先尝试 streamable HTTP,再在需要时回落到 SSE,也可以通过 --transport 手动指定。

OpenAPI

只要服务公开了 OpenAPI spec,就已经具备足够的结构信息。mcp2cli 允许你直接通过 --spec 指向远程或本地 spec(JSON 或 YAML 都支持),再用统一 CLI 调用接口。

mcp2cli --spec https://petstore3.swagger.io/api/v3/openapi.json --list
mcp2cli --spec ./openapi.json --base-url https://api.example.com list-pets --status available

这件事的价值在于,它把”已经有 OpenAPI 的传统 API 服务”纳入 agent 工具链,而不要求服务提供方先为 MCP 生态做一层专门改造。

GraphQL

GraphQL 支持让 mcp2cli 的接入面再往外扩了一层。根据公开 README 的描述,它会通过 introspection 读取 schema,把 query 和 mutation 映射为可调用命令,并允许用户通过 --fields 一类参数覆盖 selection set。这意味着它不只在处理 REST 风格的接口,而是在尝试建立一个更宽的统一调用层。

认证与 OAuth

认证是工具接入里最容易拖慢节奏的环节。公开文档里可以看到,mcp2cli 至少支持通过 --auth-header 传递 HTTP 认证信息,也单独支持 OAuth 相关参数:

# 明文 API key
mcp2cli --spec ./spec.json --auth-header "Authorization:Bearer tok_..." list-items

# 从环境变量读取(避免密钥出现在进程列表里)
mcp2cli --mcp https://mcp.example.com/sse \
  --auth-header "Authorization:env:API_TOKEN" search --query "test"

# 从文件读取
mcp2cli --mcp https://mcp.example.com/sse \
  --auth-header "x-api-key:file:/run/secrets/api_key" search --query "test"

对于 MCP HTTP 等来源,公开文档还列出了 OAuth 支持,包括 authorization code + PKCE,以及 client credentials 这类机器对机器模式。按照项目文档说明,OAuth token 会缓存到 ~/.cache/mcp2cli/oauth/,并尽量复用已有授权结果。

缓存

按照公开文档说明,远程 spec 和 MCP tool list 会缓存到 ~/.cache/mcp2cli/,默认 TTL 为 1 小时;本地文件不会被缓存。需要刷新时用 --refresh,需要更长缓存周期时用 --cache-ttl(单位秒):

mcp2cli --spec https://api.example.com/spec.json --refresh --list       # 强制刷新
mcp2cli --spec https://api.example.com/spec.json --cache-ttl 86400 --list  # 24 小时缓存

缓存的意义不只是减少网络请求,在 agent 场景里它还避免了每次调用都重新做 discovery,对调用频率高的工作流很有帮助。

Token 成本和 TOON 输出

PyPI 和 README 都强调减少工具 schema 在每轮上下文里的 token 消耗。这个出发点合理:agent 使用成本里,真正吃掉预算的往往不是回答本身,而是上下文里反复携带的工具描述、参数结构和协议元信息。

mcp2cli 的策略是做一次表示层切换:不让模型每轮都重新消化整套 schema,而是把这些结构压缩成终端可调用的命令语义。这样模型面对的不是一大段 JSON,而是一条相对简短的命令。

对于输出侧,它还提供了 --toon 标志,以 TOON 格式输出响应。根据公开文档的描述,这种格式在大型均匀数组场景下可以比 JSON 少 40-60% 的 token。这个数字属于项目方给出的性能主张,不是独立验证的数据,但方向本身可信:对 agent 来说,减少协议噪音、把模型预算留给任务推理,通常比追求输出格式漂亮更实用。

Skill 生成与复用工作流

mcp2cli 的公开技能文档里描述了另一种实用的工作流:从 API 反向整理出 skill。具体做法是先用 --list 发现全部命令,再用 --help 逐一检查参数,最后把这些信息写成一份 SKILL.md,教给另一个 AI agent 如何通过 mcp2cli 命令调用这个 API。

这个流程让 mcp2cli 本身也可以作为”把外部 API 沉淀成可复用知识”的工具。agent 执行时跑的是 uvx mcp2cli ... 命令,而不是直接发 HTTP/MCP 请求,接入层仍然是 CLI,不引入额外依赖。

适配层,而不是平台

它不像 OpenCode、Claude Code 这类系统那样承担完整的代理编排、上下文管理或 UI 层责任。如果把一个 agent 系统粗略拆成几层,上层是交互与规划,中层是上下文和权限管理,下层是工具和外部服务接入,那么 mcp2cli 更接近最下面这一层。它不负责规划,不负责长期状态,也不做对话界面。

它负责的是:把外部能力翻译成一种足够轻、足够统一、适合终端和 agent 的执行入口。

边界这么清晰,它才容易被嵌进各种不同的系统里。不是一个”包打天下”的大框架,而是一块足够薄、足够专注的适配层。

适合放在哪里

从公开能力来看,mcp2cli 最适合三种位置:

API 探索阶段。 刚接触一个新的 MCP server 或 OpenAPI 服务,还不确定后续要不要正式接入时,先用它跑通一遍,成本最低。

Agent 执行层。 已有 agent 但不想每轮都背着大量 tool schema 时,可以把 mcp2cli 作为中间层,让 agent 面对的是 CLI,而不是完整的协议描述。

团队内部桥接。 并非每个内部服务都值得维护正式 SDK 或插件。用 mcp2cli 做一层轻量 CLI 入口,比各处写零散脚本更统一,也更容易被其他 agent 复用。

三类场景的共同点是:先连上、先可用、先降低摩擦,而不是一开始就做最重的工程化投入。

小结

mcp2cli 解决的问题很具体:工具生态越来越丰富,但每接一个新接口都要先过一段”文档在哪、命令怎么组织、认证怎么传、值不值得写封装”的摩擦期。它用运行时 CLI 映射把这段成本压缩了。

支持了多少种协议不是重点,重点是它对工具接入的判断:协议层能力已经足够可描述,真正缺的是一个足够轻、足够直接、能让人和 agent 都立刻开始用的执行入口。

讨论

留下你的想法

欢迎补充观点、指出问题,或分享与你类似的实践经验。

💬 留言评论

欢迎交流讨论,提问或分享你的想法。