MCP 官方 Registry 发布附加验证规则:server.json 在 registry 源码中的落地实现
【免费下载链接】registryA community driven registry service for Model Context Protocol (MCP) servers.项目地址: https://gitcode.com/GitHub_Trending/registry43/registry
本文基于官方 Registry 的server.json附加要求文档,完整讲解发布到官方 MCP Registry(registry.modelcontextprotocol.io)时,在通用server.json规范之上额外施加的四类验证:命名空间鉴权、包归属验证、受限的 Registry Base URL 白名单,以及_meta命名空间限制。同时结合 registry 仓库源码,说明每条规则在验证器与鉴权处理函数中的具体实现位置与错误语义,帮助你在发布前预检问题、准确理解发布失败信息。
总览:官方 Registry 在通用规范之上做了什么
通用server.json格式(见 server.json 格式规范)定义了基础字段与结构校验,而官方 Registry 在此基础上追加了四类强制约束,目的是保证:
- 命名空间鉴权(Namespace authentication):Server 只能发布到发布者实际拥有的命名空间之下;
- 包归属验证(Package ownership verification):发布者必须能证明其确实拥有所引用的包,防止冒名;
- 受限的 Registry Base URL:包必须来自受信任的公共 Registry,私有源与第三方镜像不被允许;
_meta命名空间限制:_meta对象中仅publisher相关键的数据会被保留,其余键在发布时被静默丢弃。
从源码结构看,这四类约束分别落在不同模块:server 名称格式由 parseServerName 强制;包归属与 Base URL 白名单由internal/validators/registries/下的各验证器执行;_meta大小限制由 validatePublisherExtensions 执行;命名空间所有权证明由internal/api/handlers/v0/auth/下的各类鉴权端点完成。
命名空间鉴权:证明你拥有该命名空间
发布者必须证明其拥有对应命名空间。例如要发布到com.example/server,发布者必须证明其拥有example.com域名。
从源码可以确认 server 名称的硬性格式:name字段必须形如dns-namespace/name,恰好包含一个/,且匹配正则^[a-zA-Z0-9.-]+/[a-zA-Z0-9._-]+$(长度 3~200),定义在 ServerJSON 类型的 JSON Schema 标签与 parseServerName 中。命名空间部分只能以字母数字开头和结尾、中间可含点与连字符;name 部分额外允许下划线。格式不合法时,验证器会给出精确到"是 namespace 还是 name 部分非法"的错误提示。
Registry 提供了多种命名空间所有权证明途径,对应 鉴权实现目录 下的不同端点:
- DNS 命名空间:通过解析域名 TXT 记录完成挑战验证。DNSAuthHandler 通过
DNSResolver.LookupTXT查询 DNS TXT 记录,注册了exchange-dns-token令牌交换端点; - GitHub 命名空间(如
io.github.<user>):通过 GitHub App 访问令牌或 OIDC 流程证明仓库/组织归属,对应 github_at.go 与 github_oidc.go; - 此外还有基于 HTTP 挑战(http.go)、OIDC(oidc.go)与无鉴权测试模式(none.go)等实现,便于自建 Registry 时按环境选择鉴权后端。
具体的 GitHub 与域名命名空间的逐步认证操作,见 发布指南。
包归属验证:每种包类型都有明确的"所有权凭证"
所有包都必须携带能证明发布者拥有它的元数据。这一机制防止有人把别人的包绑定到自己控制的 server 记录上。上游项目对此的安全动机在 registry 项目 issue #96 中有专门讨论。
各 Registry 类型的具体验证要求详见 包类型文档(每种 registry 都有 "Ownership Verification" 一节)。从本仓库源码可以印证各类型的验证手段:
| Registry 类型 | 所有权凭证 | 源码位置 |
|---|---|---|
| NPM | 包的package.json元数据中必须有mcpName字段,且取值与 server name 完全一致 | ValidateNPM |
| PyPI | 包的 description(README)中必须包含mcp-name: <server-name>令牌 | pypi.go |
| NuGet | 包的 README 中必须包含mcp-name: <server-name>令牌 | nuget.go |
| Cargo | crates.io 上该版本的渲染 README 中必须包含mcp-name: <server-name>令牌 | cargo.go |
| OCI/Docker | 镜像标签(label)io.modelcontextprotocol.server.name必须等于 server name | ValidateOCI |
| MCPB | 仅允许 GitHub / GitLab Releases 下载链接 | mcpb.go |
几个源码层面的实用细节:
- NPM 的
mcpName必须精确匹配。validateNPMPackage 会请求{baseURL}/{identifier}/{version}元数据端点;缺失mcpName时,错误信息会直接告诉你该往package.json加什么:"NPM package '%s' is missing required 'mcpName' field. Add this to your package.json: \"mcpName\": \"%s\""。取值不匹配时则报ownership validation failed. Expected mcpName ... got ...。 - NPM 对 404 做了精细区分。当版本元数据返回 404 时,npmVersion404Error 会用 HEAD 请求探测包级端点,区分"包不存在"、"包存在但版本尚未同步"(新发布可能有传播延迟)与"上游瞬时故障(429/5xx)"三种情况,给出可操作的重试建议,而不是笼统地报"未找到"。
- README 令牌采用边界锚定匹配。PyPI/NuGet/Cargo 的
mcp-name:令牌必须后接空格、换行或 HTML 标签等边界;如果令牌后面紧贴了其他字符(比如mcp-name: foo与你要的mcp-name: food混淆),错误信息会明确指出"请放到单独一行并重新发布"(见 nuget.go、cargo.go)。 - OCI 验证失败是"失败关闭"(fail closed)。由于
io.modelcontextprotocol.server.name标签是官方 Registry 对 OCI 包唯一的归属凭证,ValidateOCI 在遇到 429 限流时不会放行,而是返回可重试错误;私有镜像(401/403)会明确提示"仅支持公共镜像"。 - OCI 包的字段约束:
identifier必须是规范引用(如docker.io/owner/image:1.0.0),不允许再带registryBaseUrl、version或fileSha256字段,版本信息包含在identifier中(tag 或@sha256:digest 均可)。 - 包级验证在
EnableRegistryValidation配置开启时执行,入口是 ValidatePublishRequest:它先做_meta扩展校验,再对packages数组逐包调用ValidatePackage,任一包失败即整体失败,错误信息会带上包序号与 identifier。
受限的 Registry Base URL:只允许受信任的公共源
官方 Registry 只接受受信任的公共 Registry,私有 Registry 与替代镜像一律拒绝。支持的清单如下:
| Registry 类型 | 允许值 |
|---|---|
| NPM | 仅https://registry.npmjs.org |
| PyPI | 仅https://pypi.org |
| NuGet | 仅https://api.nuget.org/v3/index.json |
| Cargo | 仅https://crates.io |
| Docker/OCI | Docker Hub(docker.io)、GitHub Container Registry(ghcr.io)、Quay.io(quay.io)、Google Artifact Registry(*.pkg.dev)、Azure Container Registry(*.azurecr.io)、Microsoft Container Registry(mcr.microsoft.com) |
| MCPB | 仅https://github.com与https://gitlab.com的 Releases 下载链接 |
源码层面的印证:
- 固定的 Base URL 常量集中定义在 pkg/model/constants.go(
RegistryURLNPM、RegistryURLPyPI、RegistryURLNuGet、RegistryURLCrates、RegistryURLGitHub、RegistryURLGitLab等)。以 NPM 为例,ValidateNPM 要求registryBaseUrl与https://registry.npmjs.org完全相等,否则返回"registry type and base URL do not match"错误(该错误定义于 constants.go)。 - OCI 的白名单是唯一的"域名集合"型规则,实现在 allowedOCIRegistries 与 isAllowedRegistry:精确匹配
docker.io、registry-1.docker.io、index.docker.io(Docker Hub 的 API 端点别名)、ghcr.io、quay.io、mcr.microsoft.com,外加两个通配后缀*.pkg.dev(Google Artifact Registry)与*.azurecr.io(Azure Container Registry)。单元测试 oci_test.go 覆盖了通配主机(如myregistry.azurecr.io、us-west1-docker.pkg.dev/...)与固定主机的放行行为。不在白名单内的 registry 会返回unsupported OCI registry错误。
_meta命名空间限制:只保留 publisher-provided 数据
server.json中的_meta字段允许发布者携带自定义元数据,但发布到官方 Registry 时有严格限制:只有io.modelcontextprotocol.registry/publisher-provided键之下的数据会被保留,_meta对象中的任何其他键都会在发布时被静默丢弃——既不存储也不会在 API 中返回。
对应地,ServerMeta 类型 中_meta只有一个PublisherProvided字段(JSON 标签即为io.modelcontextprotocol.registry/publisher-provided),这从数据结构层面保证了其他键根本无法进入存储。
示例:
{ "_meta": { "io.modelcontextprotocol.registry/publisher-provided": { "tool": "ci-publisher", "version": "1.0.0", "custom_data": "your data here" }, "some.other.key": { // 该键会被丢弃,不会被保留 } } }大小限制:publisher-provided 扩展的序列化 JSON 上限为4KB(4096 字节),超限会导致发布失败,错误信息中会给出实际字节数。该限制由 validatePublisherExtensions 实现,错误消息格式为:
_meta.io.modelcontextprotocol.registry/publisher-provided extension exceeds 4KB limit (%d bytes)推荐做法:按反向域名做子命名空间
当publisher-provided元数据变大、或来自多个来源(例如 GitHub 专属提示加上自家 CI 工具的元数据)时,按反向 DNS 子键分组是避免键冲突的实用惯例:
{ "_meta": { "io.modelcontextprotocol.registry/publisher-provided": { "com.github": { "serverDisplayName": "My Server" }, "io.example.ci-publisher": { "tool": "ci-publisher", "version": "1.0.0" } } } }需要说明的是:该惯例不被强制。简单场景下扁平键完全可以,仓库内的既有示例也使用扁平形式;只有当元数据来自多个来源时才建议改用命名空间子键形式。
注意区分:server.json的_meta与 API 响应中的_meta
server.json里的_meta与 Registry API 响应返回的_meta是两个不同的东西:
server.json中:_meta包含发布者自定义元数据,位于io.modelcontextprotocol.registry/publisher-provided之下;- API 响应中:
_meta是响应级别的独立属性(不在server.json内部),承载 Registry 托管的元数据,包括status(生命周期状态:active、deprecated、deleted)、publishedAt(首次发布时间)、updatedAt(最近更新时间)、isLatest(是否为最新版本)。
你发布的内容(server.json):
{ "name": "io.github.example/my-server", "version": "1.0.0", "description": "My MCP server", // ... 其他字段 ... "_meta": { "io.modelcontextprotocol.registry/publisher-provided": { "tool": "ci-publisher", "version": "2.0.0" } } }Registry API 返回的内容:
{ "server": { "name": "io.github.example/my-server", "version": "1.0.0", "description": "My MCP server", // ... 其他字段 ... "_meta": { "io.modelcontextprotocol.registry/publisher-provided": { "tool": "ci-publisher", "version": "2.0.0" } } }, "_meta": { // 响应级别的 Registry 托管元数据 "status": "active", "publishedAt": "2024-01-15T10:30:00Z", "updatedAt": "2024-01-15T10:30:00Z", "isLatest": true } }可以看到 API 响应中存在两个_meta:一个在server对象内部(从 server.json 原样保留的发布者数据),另一个在响应级别(Registry 自动添加的托管元数据)。Registry 托管元数据无法被发布者设置或覆盖。从源码可以确认这一点:ServerResponse 的结构正是server(内含 ServerMeta)加上响应级Meta,而后者实际包装的是 RegistryExtensions(status/statusChangedAt/statusMessage/publishedAt/updatedAt/isLatest),JSON 键为io.modelcontextprotocol.registry/official。完整的响应结构参见 官方 Registry API 文档 与 OpenAPI 定义。
如何在发布前自查
结合上述规则,发布前可以用仓库自带工具链自查:
- 用 server.schema.json(当前版本日期为
2025-12-11,见 constants.go)对server.json做结构校验;仓库提供了 validate-schemas.sh 等脚本用于校验 schema 本身。 - 检查各包的所有权凭证是否就位:NPM 包在
package.json中加mcpName;PyPI/NuGet/Cargo 包在 README 中加独立一行的mcp-name: <server-name>令牌;OCI 镜像在 Dockerfile 中加LABEL io.modelcontextprotocol.server.name="<server-name>"(验证器在缺失时会在错误信息中直接给出对应的 LABEL 写法)。 - 确认
registryBaseUrl在上表白名单内、OCI 包未误用registryBaseUrl/version字段。 - 控制
_meta中publisher-provided的序列化体积在 4096 字节以内。
发布流程的命令行操作(validate/publish等子命令)见 发布指南 与 CLI 参考;集成测试 tests/integration/main.go 中对publisher-provided元数据的逐键比对,可作为"发布后数据是否原样保留"的验收参考。
小结
官方 Registry 的附加验证可以概括为:命名空间靠 DNS/GitHub 等所有权证明把关,包靠各 Registry 的原生元数据凭证(mcpName、README 中的mcp-name:令牌、OCI label)绑定归属,包源靠固定白名单收敛到受信任的公共 Registry,_meta靠单一保留键加 4KB 上限控制体积。这些规则在 validators、registries 验证器 与 auth 处理函数 中均有可核对的实现,遇到发布失败时,对照错误信息中给出的具体字段与期望值(如期望的mcpName、期望的 LABEL 写法)修正后重新发布即可。
【免费下载链接】registryA community driven registry service for Model Context Protocol (MCP) servers.项目地址: https://gitcode.com/GitHub_Trending/registry43/registry
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考