Penpot MCP types-generator 全解析:如何从插件 API 文档自动生成 api_types.yml
2026/9/8 20:27:04 网站建设 项目流程

Penpot MCP types-generator 全解析:如何从插件 API 文档自动生成 api_types.yml

【免费下载链接】penpotPenpot: The open-source design platform for Product teams that need scalable collaboration.项目地址: https://gitcode.com/GitHub_Trending/pe/penpot

导读

mcp/types-generator是 Penpot MCP 服务器开发链路中的一组辅助脚本:它负责抓取 Penpot 插件(Plugin)API 的类型文档站点(TypeDoc 生成的静态页面),并将其清洗、汇总为一个结构化的api_types.yml文件。该 YAML 随后会被 MCP 服务器中的ApiDocs组件按需加载,作为"按需提供给 LLM 的 API 文档"数据源。阅读本文后,你将掌握整个类型的生成流程、两种数据来源(远程生产文档 vs 仓库内本地文档)、核心清理规则,以及 pixi/Caddy 等工具链的完整用法。

一、子项目定位:服务于 MCP 服务器的"文档工厂"

从 mcp/types-generator/README.md 的定义看,本目录是 Penpot MCP 服务器开发中的辅助脚本集,专门负责生成一份包含 Penpot 插件 API 类型及其文档说明的 YAML 文件。它自身不提供任何 MCP 能力,而是为 MCP 服务器供给数据资产。

从目录结构看,该子项目仅由 4 个文件组成(见 mcp/types-generator):

文件作用
prepare_api_docs.py核心抓取与清洗脚本(Python)
pixi.toml/pixi.lockpixi 环境声明与锁文件,声明 Python 依赖
build一键构建脚本:先pixi install再执行脚本
README.md使用说明

而产出的数据会落入 mcp/packages/server/data/api_types.yml(仓库当前已提交,约 23000 行),被 MCP 服务器消费。在 ApiDocs.ts 中,该文件在运行时被解析加载,并通过PenpotApiInfoTool(见 PenpotApiInfoTool.ts)暴露给 LLM 按需查询。也就是说:本文讨论的是这份 "LLM 可查询的 API 类型字典" 是如何被生产出来的

二、环境搭建:基于 pixi 的依赖管理

项目使用 pixi 管理 Python 运行环境(该工具已包含在 Penpot 的 devenv 中),因此无需手工创建 virtualenv。执行环境安装:

pixi install

按 README 说明,此步骤是可选的——后面的build脚本会代为执行pixi install

依赖清单定义在 pixi.toml 中,从源码可以确认实际用到的核心库与版本约束:

[dependencies] python = "3.11.*" beautifulsoup4 = ">=4.13.5,<5" markdownify = ">=1.1.0,<2" requests = ">=2.32.5,<3" "ruamel.yaml" = ">=0.18.15,<0.19" [pypi-dependencies] sensai-utils = ">=1.5.0, <2"

这些依赖与 prepare_api_docs.py 的 import 一一对应:

  • requests:抓取远程 HTML 页面(_fetch方法中调用requests.get);
  • beautifulsoup4:解析 TypeDoc 生成的 HTML DOM,定位.col-content.tsd-signature.tsd-member等节点;
  • markdownify:把清洗后的 HTML 片段转成 Markdown(子类化MarkdownConverter定制转换行为);
  • ruamel.yaml:以 YAML 块状字面量(block literal)风格写出结果,保留|-语义以保持长文档可读;
  • sensai-utils:提供logging.run_main包装入口与日志器。

另外值得注意pixi.toml中声明了platforms = ["win-64","linux-64"],即该工具链在 Windows 与 Linux 下均可安装使用。

三、直接抓取线上文档:prepare_api_docs.py <url>

3.1 脚本行为

prepare_api_docs.py从给定的 Web URL 读取 Penpot 插件 API 文档,将其汇总进单个 YAML 文件。成功执行后会在父级目录的packages/server/data下生成api_types.yml。脚本主入口(见 prepare_api_docs.py 中main()函数)把目标目录固定解析为:

target_dir = Path(__file__).parent.parent / "packages" / "server" / "data"

即无论从哪个目录调用,输出总是落到 mcp/packages/server/data/api_types.yml。

脚本还预置了两个常量(对应 README 中的两条路径):

LOCAL_API_DOCS_URL = "http://localhost:9090" PROD_API_DOCS_URL = "https://doc.plugins.penpot.app" DEFAULT_API_DOCS_URL = LOCAL_API_DOCS_URL # 不传 URL 时默认抓本地

3.2 运行方式

手动运行(需已执行过pixi install):

pixi run python prepare_api_docs.py <url>

也可以直接调用封装脚本 build,它会额外自动完成 pixi 环境安装(pixi install+pixi run python prepare_api_docs.py $URL),默认 URL 同样是http://localhost:9090

./build <url>

例如,基于当前生产环境的 Penpot 插件 API 文档生成(即 README 示例):

./build https://doc.plugins.penpot.app

3.3 输出文件形态

生成的api_types.yml类型名 →TypeInfo的组织方式存放。TypeInfo数据类(见源码第 106–123 行)包含两个字段:

  • overview:类型主文档,包含全部声明/签名但不含成员详情;
  • members:按成员分组(如 "Properties"、"Methods")映射到"成员名 → Markdown 描述"。

此外每个类型会在overview末尾追加一段 "Referenced by: ...",列出所有引用它的其他类型——这是脚本在抓取每个页面时,通过解析.tsd-signaturea.tsd-signature-type链接收集到的反向引用关系(见process_pageadd_referencing_types)。仓库中已生成的样例(节选)如下:

Penpot: overview: |- Interface Penpot ================ These are methods and properties available on the `penpot` global object. ``` interface Penpot { ui: { open: ( name: string, url: string, options?: { width: number; height: number; hidden?: boolean }, ) => void; ... ``` ## 四、基于仓库内最新文档生成:`pnpm run build:types` ### 4.1 前提:需要 Caddy 这种模式下数据源是**仓库内当前源码**实时构建出的文档页面,因此必须先起一个静态文件服务器。README 明确要求 Caddy 已安装并在系统 PATH 中(Caddy 同时被下文 `build:types` 脚本内部调用)。 ### 4.2 一键流程 在 [mcp](https://link.gitcode.com/i/61b88d5bac6dc6c6136964e2ec89c22e) 目录(`types-generator` 的父级)执行: ```bash pnpm run build:types

该命令对应 mcp/package.json 中的脚本定义"build:types": "bash ./scripts/build-types",实际执行 build-types:

# 1. 默认 URL 指向本地 9090,因此先构建本地文档 if [[ "$URL" = "http://localhost:9090" ]]; then pushd ../../plugins pnpm install pnpm run build:doc popd fi # 2. 用 Caddy 起 9090 静态服务,并同时执行抓取脚本 pnpx concurrently --kill-others-on-fail -s last -k \ "caddy file-server --root ../../plugins/dist/doc/ --listen :9090" \ "bash ../types-generator/build $URL"

结合 plugins/package.json 中"build:doc": "typedoc ... --out dist/doc"可知完整链路为:

  1. 在 plugins 目录执行pnpm run build:doc,用 TypeDoc 把插件运行时类型定义生成静态站点到plugins/dist/doc/
  2. caddy file-server将该目录以127.0.0.1:9090暴露(build:types的监听地址为:9090,README 中单独起服务示例为127.0.0.1:9090);
  3. ../types-generator/build(即前一节的封装脚本)随后调用prepare_api_docs.py http://localhost:9090完成抓取与转换。

由于concurrently配合--kill-others-on-fail -s last -k,只要抓取脚本失败,Caddy 进程也会被一并终止,避免遗留后台服务。

4.3 仅启动文档服务器

若只想在本地查看文档而暂不执行抓取,可跳过脚本、只启动静态服务器(在mcp目录下):

caddy file-server --root ../plugins/dist/doc/ --listen 127.0.0.1:9090

对应仓库路径即 plugins/dist/doc(需先构建产生)。同时build-types脚本也支持通过环境变量PENPOT_PLUGINS_API_DOC_URL覆盖目标 URL,例如把它指向上游文档地址即可跳过本地构建、直接抓取远程文档。

五、深入:脚本内部如何把 HTML 清洗成"LLM 友好"的 Markdown

prepare_api_docs.py最见功力的部分在于对 TypeDoc 页面做了大量定制清洗,这正是保证最终 YAML 体积可控、语义干净的关键。理解这些规则有助于你判断何时需要重新生成、以及生成结果的形态。

5.1 页面定位与成员拆分

PenpotAPIDocsProcessor.run()先抓取modules.html,在.col-content中收集所有指向interfaces/types/的链接作为待处理类型列表,再逐页处理。处理单个类型页时(process_page):

  1. 遍历.col-content下带tsd-member-group的分组标签,按h2文本(如 Properties / Methods)归类,把组内每个tsd-member拆成"成员名 → Markdown 描述";
  2. 成员名的提取优先取a.tsd-anchorid,否则取h3 > span的文本(无法确定时会触发断言);
  3. h3标题中提取的tsd-tag(如Readonly)会被重新插回签名区,标题本身随后被移除(因为它是冗余信息);
  4. 成员分组从 DOM 中删除后,剩余内容作为该类型的overview

5.2 定制 Markdown 转换规则

PenpotAPIContentMarkdownConverter继承自 markdownify,通过重写process_tag实现了一套针对 Penpot API 文档的规则,主要可归纳为:

  • 丢弃导航噪音:面包屑(tsd-breadcrumb)、索引区(tsd-index-content)、按钮(如 "Copy")、"Defined in" 列表项、"Inherited from" 段落一律置空;
  • 代码块化签名tsd-signature中的<br>转成换行,仅保留readonly标记(optional因已通过?表达而删除),整体包裹进```代码块;<pre>块也会去掉内部按钮后转成代码块;
  • 去冗余列表:仅含单个<li>tsd-signatures列表会被"拆包"为普通<div>递归处理,避免出现只有一条且带缩进的项目符号;
  • 链接取纯文本<a>链接只保留文字,减少无关超链接对 LLM 上下文的干扰。

5.3 YAML 写出:全程块状字面量

YamlConverter使用ruamel.yaml并开启preserve_quotes、把宽度放宽到 4096 以阻止自动折行,再将所有字符串统一递归转换为LiteralScalarString,使生成的 YAML 采用|-块状风格。这样长 Markdown 描述不会被折叠成冗长单行,便于人读与增量 diff。

5.4 调试辅助函数

源码中还带有一个debug_type_conversion函数(仅用于调试,默认被注释),可单独处理某个类型页并把转换结果打印到控制台,例如将注释行替换为:

debug_type_conversion("interfaces/Path.html", LOCAL_API_DOCS_URL)

在扩展转换规则、排查某个类型为何转换异常时非常有用。

六、产出如何被消费:从 api_types.yml 到 MCP 工具

为了让"生成 YAML"这件事的上下文更完整,这里补充其在运行时一侧的消费链路(均位于 mcp/packages/server/src):

  • ApiDocs.ts:ApiDocs类在初始化时从data/api_types.yml加载全部类型数据(运行时默认路径为当前工作目录下的data/api_types.yml);
  • PenpotApiInfoTool.ts:作为 MCP 工具暴露 API 文档查询能力,其构造器接收ApiDocs实例;
  • PenpotMcpServer.ts:服务器启动时实例化ApiDocs并注入各工具。

也就是说,当你修改了插件 API 类型或文档注释后,应当重新执行本 README 描述的类型生成流程(推荐走pnpm run build:types的本地模式),使api_types.yml与最新源码保持同步,MCP 服务器提供的 API 文档能力才会随之更新。

七、两种生成模式的取舍小结

模式命令数据源前置依赖适用场景
生产文档./build https://doc.plugins.penpot.app线上发布版 API 文档pixi对齐线上公开 API、无需本地构建
本地文档pnpm run build:types仓库内当前源码经 TypeDoc 构建的 plugins/dist/docCaddy、pnpm修改 API 类型/注释后同步生成最新 YAML

需要说明的边界:TypeDoc 本地构建依赖 plugins 子工作区中 plugins/package.json 声明的typedoc等开发依赖及其配置;而脚本抓取的是 TypeDoc 输出中的特定 CSS 类结构(tsd-*系列),因此若上游 TypeDoc 的页面结构发生大版本变化,可能需要同步修订prepare_api_docs.py中的选择器规则——这正是上文保留debug_type_conversion这类调试入口的原因。

【免费下载链接】penpotPenpot: The open-source design platform for Product teams that need scalable collaboration.项目地址: https://gitcode.com/GitHub_Trending/pe/penpot

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询