FastMCP(27k stars)的
Context.elicit 方法声明了六个 @overload 存根,但每个存根的函数体只有 ...,说明文字被放在了函数体之后。这导致这些字符串不是真正的 docstring,而是类体中的裸表达式语句,会中断 mypy 的 overload 链。mypy 只能识别六个重载中的第一个——恰好是库自身标记为弃用的
response_type: None 形式。pyright 和 ty 不受影响,而 FastMCP 自己的 CI 用的正是 ty,所以问题被顺利发布。作者尝试了五种调用写法,在 mypy 下全部失败,pyright 下全部通过。更严重的是,response_type=None会编译成空 JSON schema,意味着自动接受的客户端产生的数据与人工点击批准在网络上无法区分。作者此前已用真实探测证明,一个删除工具在confirm=False时能走完完整删除路径。修复方案是要求明确的确认值(如["cancel", "confirm"]),而不是把"已接受"当作信号。
修复本身很小:把每个字面量移入存根函数体,变成真正的 docstring,+15/-24 行,无运行时和 API 变化。已合并到 4.x 主线,但尚未进入任何 release。v3.4.6(08-05 发布)仍带此问题,且仓库没有 3.x 维护分支,固定fastmcp>=3.4.5,<4的用户会一直用到有问题的版本。
作者还记录了一个插曲:PR 被机器人以"缺少 issue 链接"为由 15 秒内自动关闭,又被另一个机器人贴上 too-long 标签,最终未经任何修改直接合并,标签至今还在。项目负责人随后在 CLAUDE.md 中加了一行:"Review closed contributor PRs. External PRs may be closed as part of the issue-link workflow, so closure alone is not a negative signal."
核心教训:绿色 CI 只证明你运行的那个检查器同意你的代码,不证明代码正确。三个类型检查器看同一份文件,只有一个发现了问题。维护类型化 Python 库的开发者,值得在非阻塞任务里跑第二个检查器——不必修复它发现的所有问题,但至少得能看见。
GitHub
GitHub
#GitHub #开源 #FastMCP #mypy #pyright #Python #类型检查 #MCP
@GitHubTrendingHub