Mastra Agent Builder Skill Registry 全流程实战:skills.sh 安装与 Library Copy 溯源机制
【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra
本篇技术指南围绕 Mastra 仓库中 Agent Builder 的 Skill Registry(技能注册表)机制展开,完整讲解两条获取外部技能的路径:skills.sh 外部注册表的浏览/搜索/预览/安装,以及Library 公共技能库的 Copy(复制)流程。读者将掌握builder.registries.skillsSh.enabled配置开关的生效规则、全部 registry API 的请求/响应契约(含搜索分页、预览 frontmatter 剥离、安装冲突 409)、metadata.origin溯源 schema 的设计,以及如何通过seed-multi-user.sh在没有第二个账户的前提下验证多用户 Copy 流程。
本文以仓库中的 registry.md 为骨架,结合 builder-registry.ts、stored-skills.ts 等服务端源码进行纵深印证,是 Agent Builder 分支 QA 与二次开发的第一手资料。
一、Skill Registry 机制总览:两条外部技能获取路径
在 Mastra Agent Builder 中,除了自己创作技能,用户可以通过两条路径获取不属于自己创作集合(outside your own authored set)的技能:
- 外部注册表(External registry)——当前仅支持
skills.sh,通过builder.registries.skillsSh.enabled开关选择性开启。开启后可浏览、搜索 skills.sh 上的技能并安装为存储技能(stored skill),安装产物带metadata.origin = { type: 'skills-sh', ... }溯源标记。 - Library Copy(公共库复制)——任何已认证用户都可以复制一条自己不拥有、但为 public 的存储技能。复制结果是一条全新的私有存储技能,带
metadata.origin = { type: 'library-copy', sourceSkillId, sourceAuthorId, copiedAt }溯源标记。
两条路径的共同点是:所有产物最终都落到存储技能(stored skills)体系,并统一通过metadata.origin记录"从哪来",前端据此渲染来源徽标(origin badge)、提供"Open existing"等交互。溯源 schema 定义在 stored-skills.ts 的skillOriginSchema(discriminated union,按type字段区分skills-sh与library-copy)。
二、核心数据契约与配置开关
2.1 服务端路由清单(Source of truth)
Registry 功能对应的路由全部挂在/editor/builder/registries前缀下:
| 方法 | 路径 | 作用 |
|---|---|---|
| GET | /editor/builder/registries | 列出所有已知注册表及其启用状态 |
| GET | /editor/builder/registries/:id/search?q=... | 代理搜索请求到注册表 |
| GET | /editor/builder/registries/:id/popular | 获取注册表热门技能列表 |
| GET | /editor/builder/registries/:id/preview?owner=...&repo=...&path=... | 预览单个技能的 SKILL.md 渲染内容 |
| POST | /editor/builder/registries/:id/install | 从注册表拉取技能文件树并持久化为新的存储技能 |
对应的权限要求为:列表/搜索/热门/预览需要stored-skills:read,安装需要stored-skills:write(见 builder-registry.ts 中各路由的requiresPermission声明)。
2.2 配置开关与默认行为
- 启用开关:
builder.registries.skillsSh.enabled,默认false。 - 脚手架项目的默认状态:scaffolded 项目默认
registries未设置(skills.sh 默认禁用)。在此配置下,UI 中的注册表入口被隐藏,且/editor/builder/registries/skills-sh/*所有子路由一律返回404。 - 服务端硬门控:handler 中的
requireEnabledRegistry会在注册表未知或未启用时抛出404 {"message": "Registry not found"}——这是刻意设计,未开启的注册表面向调用方完全不可探测("no surface leak for OFF registries",见 builder-registry.ts 顶部注释与requireEnabledRegistry实现)。 - 列表的语义:即使禁用,
GET /editor/builder/registries仍会返回该注册表条目,只是enabled: false(例如[{ id: 'skills-sh', enabled: false }]),目的是让前端能渲染"已注册但不可用"的空状态。
resolveRegistries的实现揭示了启用的判断路径:通过mastra.getEditor()解析 Builder,再调用builder.getRegistries?.(),最终判定registries?.skillsSh?.enabled === true;若 Builder 缺失或无法解析,则兜底返回enabled: false。
2.3 metadata.origin 溯源 Schema
schema定义在 stored-skills.ts(skillOriginSchema),以type为判别字段:
// type: 'skills-sh' { type: 'skills-sh', owner, repo, skillName, installedAt /* ISO-8601 */ } // type: 'library-copy' { type: 'library-copy', sourceSkillId, sourceSkillName, sourceAuthorId?, copiedAt /* ISO-8601 */ }持久化时挂在metadata.origin键下(源码常量SKILL_ORIGIN_METADATA_KEY = 'origin')。配套提供readSkillOrigin(metadata)(校验并读取,直接创作的技能返回null)与buildOriginMetadata(origin)(生成 create body 用的 metadata 补丁)。
三、skills.sh 外部注册表:从列表到安装的完整 API 流程
以下步骤假设skillsSh.enabled = true(即在src/mastra/index.ts中显式配置并重启服务后)。若未启用,先运行步骤 1 确认禁用路径行为,再把步骤 2–5 标记为⏭️ skills.sh disabled继续——只有显式启用后才执行完整注册表走查。所有 curl 基于$BASE(通常为http://localhost:4111/api)与jq输出。
步骤 1:注册表列表(Registries list)
curl -s "$BASE/editor/builder/registries" | jq .- 启用时预期:
[{ id: 'skills-sh', enabled: true, label: '...' }]。 - 禁用时预期:条目仍在但为
[{ id: 'skills-sh', enabled: false }]——注册表被列出但不可调用;对其执行 search/popular/preview/install 一律返回404(Registry not found)。
schema层面(builder-registry.ts):builderRegistryEntrySchema固定id: z.literal('skills-sh'),含enabled: boolean与label: string;响应外壳为{ registries: [...] }。
步骤 2:搜索(Search)
curl -s "$BASE/editor/builder/registries/skills-sh/search?q=react" | jq '.skills | length'- 常见关键词应返回 200 且至少一条结果。
- 每条结果包含
id、name、installs、topSource四个字段(对应服务端SkillsShSkillSummary类型,见 skills-sh-shared.ts)。
搜索还支持limit查询参数:builderRegistrySearchQuerySchema规定其为整数、范围 1–100、默认 10。服务端通过AbortController设置10 秒超时(SEARCH_TIMEOUT_MS),上游请求为GET {SKILLS_SH_API_URL}/api/skills?query=...&pageSize=...;上游非 2xx 时抛 502。
步骤 3:热门(Popular)
curl -s "$BASE/editor/builder/registries/skills-sh/popular" | jq '.skills | length'- 应返回 200 与热门列表。
- 分页参数:
limit(1–100,默认 10)与offset(默认 0),且 schema 用.refine强制offset必须是limit的倍数,否则 400(上游按limit分页,见builderRegistryPopularQuerySchema)。服务端将 offset 换算为page = floor(offset/limit) + 1请求/api/skills/top。
步骤 4:预览(Preview)
从搜索/热门结果中挑一个技能。预览以 GitHub 坐标作为查询参数:owner、repo、path(path即仓库内的技能名),schema 为builderRegistryPreviewQuerySchema(见 builder-registry.ts)。
curl -s "$BASE/editor/builder/registries/skills-sh/preview?owner=OWNER&repo=REPO&path=SKILLNAME" | jq .- 预期返回
name、description、instructions(frontmatter 已被剥离)与files树。 - 校验点:
instructions不以---开头(证明 YAML frontmatter 已去除)。
实现层面,预览由previewSkillsSh代理到上游/api/skills/{owner}/{repo}/{skillName}/content,取响应中的instructions或raw字段;上游 404 转本地 404,其余错误转 502,超时 10 秒。
步骤 5:安装(Install)
安装请求体为{ owner, repo, skillName, visibility? },schema 为builderRegistryInstallBodySchema。visibility可选(private/public),行为与标准存储技能创建流程一致:调用方已认证时默认private。
curl -s -X POST "$BASE/editor/builder/registries/skills-sh/install" \ -H 'Content-Type: application/json' \ -d '{ "owner": "OWNER", "repo": "REPO", "skillName": "SKILLNAME" }' | jq .- 预期返回 200/201 与
{ storedSkillId, name, filesWritten }。 - 随后
GET /stored/skills/<storedSkillId>应显示metadata.origin.type = "skills-sh"。 metadata.origin.owner、repo、skillName、installedAt均在。instructions不以---开头。
务必记录INSTALLED_SKILL_ID = storedSkillId,供后续清理使用。
安装链路的源码细节(builder-registry.ts):
fetchSkillFiles(owner, repo, skillName)拉取完整文件树(30 秒超时),找不到技能返回 404Could not find skill ... in owner/repo;assertSafeSkillName校验技能名(仅允许字母数字、连字符、下划线且以字母数字开头),buildFileTree将扁平文件列表转为存储技能所需的树结构,每个路径先经assertSafeFilePath防路径穿越校验(拒绝绝对路径与./..段,见 skills-sh-shared.ts);parseSkillSnapshot本地解析SKILL.md的 frontmatter,抽取name/description,正文(第二个---之后)作为instructions——避免把 YAML 元数据塞进 Agent 提示词;无 frontmatter 时回退name = skillId、description = "Imported from {owner}/{repo}";id = toSlug(resolvedName),随后用skillStore.getById(id)做碰撞检测;- 作者与可见性决策:
authorId = getCallerAuthorId(requestContext),有调用方 → 默认 private,无调用方(auth off)→ 强制 public; - 落库
skillStore.create,metadata.origin写入{ type: 'skills-sh', owner, repo, skillName }(installedAt由创建流程补充)。
步骤 6:冲突(Collision)
对同一技能重复执行安装:
curl -s -o /tmp/install-err.json -w '%{http_code}\n' \ -X POST "$BASE/editor/builder/registries/skills-sh/install" \ -H 'Content-Type: application/json' \ -d '{ "owner": "OWNER", "repo": "REPO", "skillName": "SKILLNAME" }' cat /tmp/install-err.json | jq .- 预期409 Conflict。
- 错误载荷中包含
existingSkillId(实际为cause: { storedSkillId: id }),UI 用它实现"Open existing"跳转,而不是静默覆盖已有技能。
步骤 7:UI 浏览对话框(Browse dialog)
导航到/agent-builder/skills:
- "Browse registry" 按钮仅当注册表启用时可见(由
useBuilderRegistries前端 hook 门控); - 点击后打开包含 search + popular 两个标签页的对话框;
- 选中技能后显示预览面板(markdown 渲染);
- 点击 "Install" 创建新存储技能,对话框关闭、列表刷新;
- 发生冲突时 toast 提供 "Open existing",点击跳转到已存在的存储技能。
步骤 8:技能列表上的来源徽标(Origin badge)
在/agent-builder/skills:
- 已安装技能显示来源徽标(skills.sh 图标或 "skills.sh" 字样);
- 悬停/点击徽标可跳转到来源。
四、Library Copy 流程:客户端复制的多用户闭环
核心设计:没有专用的 copy 端点。Library 的 "Copy" 是前端行为:UI 读取源技能字段,以常规
POST /stored/skills创建新记录,并在metadata.origin中写入{ type: 'library-copy', sourceSkillId, sourceAuthorId, copiedAt }。不要去服务端搜索/copy路由——验证方式是检查结果记录的 origin 元数据。该动作走标准创建路径,受stored-skills:write权限门控;查看页的 Copy 按钮使用同一检查(canCopy = !rbacEnabled || hasPermission('stored-skills:write'))。脚手架项目授予 member 该权限,因此 admin 与 member 都能看到并执行 Copy;viewer 看不到按钮(在 viewer 角色下将 Copy 步骤标记为n/a — role lacks stored-skills:write)。
4.1 前置条件:多用户数据(Setup note)
Library Copy 要求存在至少一条由其他用户拥有的 public 技能供当前用户复制。全新脚手架没有这种数据,三种方案:
- 推荐:服务端至少启动过一次后运行
bash .claude/skills/builder-smoke-test/scripts/seed-multi-user.sh。脚本向 libsql 写入由伪用户user_seed_other拥有的smoke-seed-public-skill(public)与smoke-seed-private-skill(private),使下方所有检查项无需第二个 WorkOS 账户即可执行。 - 第二账户:以另一个 WorkOS 用户登录、发布 public 技能,再切回测试用户执行 Copy。
- 跳过:
--auth off模式下所有 API 创建技能都归属同一个null作者,Copy 入口不会自然出现;未 seed 且无第二账户时,将相关步骤标记为n/a — multi-user data not available,不要标记为失败。
seed-multi-user.sh的实现要点(seed-multi-user.sh):
- 直接调用
sqlite3CLI 操作磁盘上的src/mastra/public/mastra.db(libsql 运行时 DB 路径),无 Node 依赖; - 幂等:重跑时先
PRAGMA foreign_keys = OFF删除既有 seed 行再重插(因 schema 中mastra_skills.activeVersionId与mastra_skill_versions.id存在自引用外键); - 每条技能插入一行 version 记录,保证 UI 有内容可渲染;
- 支持
--dir <path>参数与BUILDER_SMOKE_TEST_DIR环境变量定位项目目录;运行前需mastra_skills表已存在(即服务端至少启动过一次)。
步骤 9:Library 页面列出公共技能
导航到/agent-builder/library:
- 至少展示一条由当前用户以外作者创作的 public 技能(无则按上方 setup note 备注跳过);
- 页面上所有技能均为 public 且作者非当前用户。
步骤 10:复制一条公共技能
点击非自己创作的技能:
- 详情对话框以只读模式打开;
- "Copy" 按钮可见(对非 owner 的 public 技能,它取代了 Edit);
- 点击 "Copy" 弹出命名对话框;
- 默认名称为
<source-name>-copy; - 提交后创建一条新的私有存储技能;
- toast 确认,点击它跳转到新技能。
步骤 11:通过 API 验证 origin 元数据
curl -s "$BASE/stored/skills/<copiedSkillId>" | jq '.metadata.origin'type为"library-copy";sourceSkillId与源技能一致;sourceAuthorId与源作者一致;copiedAt是 ISO 时间戳。
步骤 12:副本的来源徽标
在/agent-builder/skills:
- 复制出的技能显示 "copied" 徽标,tooltip 为 "Copied from "。
步骤 13:名称冲突
不重命名地再次复制同一源技能:
- 同名的第二次复制返回409;
- UI 提示用户另取新名。
五、清理(Cleanup)
curl -s -X DELETE "$BASE/stored/skills/$INSTALLED_SKILL_ID" | jq . # 删除上面创建的任何 library 副本删除通过标准存储技能 DELETE 路由执行;脚手架整体是自包含的一次性目录,重建 scaffold 即可回到干净状态。
六、完整检查清单(Checklist)
skills.sh 注册表
- Registries list 反映启用开关(enabled flag)
- Search 返回结果
- Popular 返回结果
- Preview 剥离 frontmatter
- Install 以
metadata.origin.type = 'skills-sh'持久化 - 重复安装返回 409 且带
existingSkillId - UI Browse 按钮由注册表启用状态门控
- 已安装技能渲染来源徽标
Library Copy
- Library 页面展示非本人拥有的 public 技能
- 非 owner 可见 Copy 按钮
- Copy 产生带
library-copyorigin 的私有技能 - 复制技能渲染 origin 徽标
- 名称冲突返回 409
七、边界情况与故障定位速查
| 现象 | 原因与定位 |
|---|---|
404 Registry not found | 注册表未启用或 ID 未知。检查builder.registries.skillsSh.enabled是否在src/mastra/index.ts中显式配置并重启服务;resolveRegistries仅认enabled === true |
列表显示enabled: false | 注册表被列出但不可调用,属预期行为,UI 隐藏入口 |
| 安装返回 409 | 技能 ID(由name经toSlug派生)已存在,cause.storedSkillId供前端深链 |
| 搜索/热门 502 | skills.sh 上游非 2xx,10 秒超时触发AbortControllerabort |
| Copy 按钮不出现 | 当前角色缺少stored-skills:write(viewer),或库中没有非本人的 public 技能(auth off 的null作者场景) |
| 安装返回 400 | 技能名不合法(assertSafeSkillName)或文件路径穿越(assertSafeFilePath) |
八、关联文档与源码索引
- 本文骨架:registry.md
- 上层技能说明与执行流程:SKILL.md(
--test registry、--scope skills等参数用法见其中参数表) - 注册表路由实现:builder-registry.ts
- 注册表请求/响应 schema:builder-registry.ts
- origin 溯源 schema 与存储技能 schema:stored-skills.ts
- skills.sh 上游代理与安全校验(超时、路径穿越防护):skills-sh-shared.ts
- 多用户 seed 脚本:seed-multi-user.sh
【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考