Skip to content

v0.9.2

Choose a tag to compare

@github-actions github-actions released this 18 Sep 08:02

为什么会有 0.9.2:0.9.1 是在 Python 版进仓库之前发出去的 —— 它只包含扩展与 npm 包,
npm 上的 0.9.1 里没有下面这些改动。本版把 Python 版 MCP 一并发布到 PyPI,
并让两个实现的行为与返回文本完全对齐(两个包共用同一版本号,是「同一次构建产出、
内容一致」的前提)。

新增

  • Python 版 MCP 工具服务 gitea-toolkit-mcp(PyPI) —— 补齐到 35 个工具
    与 TypeScript 版同名、同参数、同返回文本、同错误措辞,两个实现可互换:
    同一份提示词与客户端配置在任一实现下都能工作。

    工具名与参数名是按 TS 版的 inputShape 逐字对齐的(含 autoInit / filePath /
    deleteBranchAfterMerge 这类 camelCase,以及 Actions 域刻意保留的 run_id 等 snake_case),
    并有测试逐个断言 —— 两边的漂移会让「可互换」变成空话。

    实现中按实例的 /swagger.v1.json 核对出三处上游细节,值得记下来:

    • EditIssueOption 根本没有 labels 字段(10 个字段里确实没有)。也就是说
      把 labels 塞进 PATCH /issues/{index} 会被服务端静默忽略 —— 调用方以为改了标签、
      实际没改,而且不会报任何错。标签有专门的替换端点 PUT /issues/{index}/labels
      两个实现都改走它(TS 侧原先也有这个问题,见「修复」)。
    • MergePullRequestOption 的字段全是小写蛇形do / merge_title_field /
      merge_message_field),按 Go 结构体写成 Do 会因缺 do 被拒(服务端把 do 标为 required)。
    • /repos/issues/search/notifications 的数组参数要展开成同名重复参数
      status-types=unread&status-types=pinned),逗号拼接服务端不认。
  • 两个 MCP 都会检查服务端 Gitea 版本,并把结论交给 AI 转达用户。

    MCP 里没有弹窗,唯一能到达模型的通道是工具返回。所以首次调用工具时自动读一次
    /version,判定不兼容就在返回开头插一段提示(开头写明「请转达给用户」,
    否则模型很容易只当背景信息),同一进程内只插一次;兼容时完全不插。
    两个实现都支持「仅本地可用」的用法,因此探测失败(网络不通、反代拦了 /version
    只写 stderr 日志、绝不让工具调用失败

    判定沿用扩展已有的那套(src/core/version.ts:已核对 1.26.4、最低支持 1.21.0
    只比较主次版本,分 ok / newer / older / unsupported / unknown 五档),
    Python 侧按同一口径移植到 version.py,并有测试直接读 TS 源文件比对常量与措辞 ——
    同一台服务端换个实现就该得到同样的结论、同样的话。

    顺带:gitea_get_current_user(本就是「令牌 / 连通性自查」工具)的返回里始终带一行
    「服务端版本|兼容性」,用户随时能复查。

  • MCP 协议合规性修正(Python 版,逐条都有测试钉住):

    • 必填参数真正进了 inputSchema.required。此前 35 个工具里只有 1 个声明了必填 ——
      因为参数写成 index: int = 0 再用运行时 raise 兜底,schema 于是宣称「什么都可以不传」,
      模型只能靠猜,且要等一次往返之后才拿得到报错。现在必填参数排在签名前面、不带默认值,
      与 TS 版的 zod 声明(没写 .optional() 就是必填)一致。
    • 工具报错终于能到达模型。MCP Python SDK v2 会把「非 ToolError 的异常」统一替换成
      Error executing tool <名字>(原始文案刻意不外泄)—— 结果是中文报错一个字都传不出去
      现在统一在注册装饰器里包成 ToolError
    • 每个工具都带 title(取自 TS 版 displayName,如「获取 Issue 详情」),
      tool.titleannotations.title 两处都写,与 TS 版一致。
    • destructiveHint 如实反映行为:原本所有写工具一律标破坏性,于是「创建 Issue」
      也会触发客户端的确认框 —— 用户很快会被训练成无脑点「同意」,真正危险的操作反而失去警示。
      现在分四档:只读 / 增量写(建 Issue、评论、提 PR、评审、触发与重跑工作流)/
      幂等写(标记通知已读)/ 破坏写(更新 Issue、覆盖文件、合并 PR、改工作流开关)。
    • 跨仓库检索的结果带上了仓库名/repos/issues/search 的返回带 repository 字段,
      原先没利用 —— 「分配给我的 Issue」会返回一堆看不出属于哪个仓库的条目,模型无法跟进。

修复

  • 修掉一个会让 Python 版完全连不上部分实例的坑:默认 User-Agent 从
    gitea-toolkit-mcp-python 改为 gitea-toolkit-mcp(与 TS 版一致)。
    起因是实测某实例的前置 nginx 按 UA 关键字拦截 —— UA 里只要出现 python
    (或干脆不发 UA)就直接 403 Forbidden,而那个 403 长得像权限问题,
    排查时极易被带偏。已加测试防止回退。

  • 修掉两处「两个实现返回文本不一致」(都属于「可互换」承诺的破口):

    • gitea_get_current_user:TS 版有「主页 / 注册时间」、Python 版有「实例」且用昵称当标题 ——
      同一份数据在两处输出成两个样子。现在统一为同一组字段(并为 TS 侧补上了实例地址,
      GiteaToolContext 新增 serverUrl)。
    • 相对时间:Python 版超过 30 天会退化成绝对日期、单位换算向下取整(90 秒说成「1 分钟前」)、
      未来时间说成「刚刚」、空值返回空串 —— TS 版则是永远相对表述(3 个月前)、
      四舍五入、未来用「后」、空值返回 -。已逐条对齐(含把 Python 的银行家舍入换成
      JS Math.round 的口径)。这两处都是跨语言比对测试抓出来的,不是看代码看出来的。
  • gitea_update_issue 的「整体替换标签」其实一直没生效。 它把标签 ID 放进
    PATCH /repos/{o}/{r}/issues/{index} 的 body,但按实例 swagger 核对,那个接口的
    EditIssueOption 根本没有 labels 字段 —— 服务端静默忽略,既不报错也不生效。
    改为走专用端点 PUT /issues/{index}/labels(与 Python 版同一处理),并放在 PATCH
    之前发出,这样 PATCH 返回的实体里带的就是更新后的标签。
    只有 AI 工具这条路会传 labels(扩展自己的命令只传 {state}),所以其余行为不变。

  • gitea_get_repo 在权限信息为空时会显示一个空荡荡的「- 权限:」。
    原代码是 f"- 权限:" + join(...) or "- 权限:(未知)" —— + 的优先级高于 or
    而左边永远是非空字符串(至少含「- 权限:」),所以 or 那半边是死代码
    兜底永远不会触发。

文档

  • 根 README 补上「服务介绍」与「服务配置」两节(标题名就是平台提取用的字段名)。
    把本项目提交到 MCP 广场(魔搭 ModelScope 等)时,「从 GitHub 仓库快速创建」会从仓库根
    README 里按名字提取这两段,并把它们当强制校验字段 —— 解析不到就直接中断。
    README 里原本虽有 mcpServers 的 JSON,却没有任何标注,能否被提取完全看提取器的理解。
    新增 npm run check:mcp-manifest(已接进 npm run ci)守住:两节存在且非空、
    服务配置是合法 STDIO(command 只能是 npx / uvx、args 里要有包名且与真实包名一致、
    不得有本地绝对路径、JSON 不得带注释)、env 里有 GITEA_TOKEN
    已用正反 8 个场景验证:正向通过;标题改名 / command 非法 / JSON 带注释 / 包名写错 /
    缺 env / 缺 json 代码块 / 塞本地绝对路径,各自都能拦住。

为什么这些文档修正值得单独发一个版本:npm 页面上的 README 与终端里的 --help
都只随发布更新
—— 0.9.0 的包页与已下载的 tarball 里仍是上述过时内容。


完整变更对比v0.9.1...v0.9.2


发布通道

通道 本次结果
Open VSX 已发布 ✅
VS Code Marketplace 已发布 ✅
npm · npx @echo-note/gitea-toolkit-mcp 已发布 ✅