MCP Registry — 架构与原理
30 秒导读: MCP Registry 是 MCP 服务器的「应用商店」的后端目录。它本身不存任何代码或二进制,只存一份描述「某个 MCP 服务器叫什么、去哪下载、怎么跑」的元数据(
server.json)。它最核心、也最难的一件事是:怎么保证「io.github.alice/weather」这个名字只能被真正的 alice 发布——答案是一整套「证明你拥有这个命名空间」的密码学握手。
1. 这是什么(零基础也能懂)
一句话定义: MCP Registry 是一个 Go 写的 HTTP 服务 + PostgreSQL,维护一份公开、带版本、可增量同步的 MCP 服务器清单。
先理解背景:MCP 是什么、为什么需要一个注册表。 MCP(Model Context Protocol)是让 AI 客户端(如 Claude Desktop、各类 IDE 助手)接入「工具/数据源服务器」的协议。社区里有成千上万个这样的 MCP 服务器(查天气的、连数据库的、读 GitHub 的……)。问题来了:客户端怎么发现它们?怎么知道每个该怎么装、怎么跑? 这就是注册表要解决的。
关键定位:它是 metaregistry(元注册表),不是包仓库。 这是理解整个项目的第一把钥匙。
| 存什么 | 例子 | |
|---|---|---|
| 包仓库(npm / PyPI / Docker Hub) | 真实代码 / 二进制 | weather-mcp 的实际 tar 包 |
| MCP Registry(本项目) | 指向包的元数据 | 「weather-server v1.2.0 在 npm 上叫 weather-mcp」 |
这个区分写在设计文档里(docs/design/ecosystem-vision.md「Registry vs Package Registry」一节):registry 只说「东西在哪」,真正的代码仍住在 npm/PyPI/Docker。
给谁用:
- 服务器作者——用一个叫
mcp-publisher的 CLI 把自己的server.json发布上来。 - 客户端 / 子注册表(subregistry)——通过只读 API 拉清单。Smithery、PulseMCP 这类「子注册表」会定期 ETL 官方 registry,再加自己的评分/策展。
用起来什么样(作者视角的最小流程):
# 1. 生成一份 server.json 模板
mcp-publisher init
# 2. 登录(走 GitHub OAuth,证明你是 github.com/alice)
mcp-publisher login github
# 3. 发布
mcp-publisher publish
# → Publishing to https://registry.modelcontextprotocol.io...
# → ✓ Successfully published
# → ✓ Server io.github.alice/weather version 1.0.0
一句话 直觉/类比: 把它想成 npm registry 的「元数据层」+ DNS 的「域名所有权证明」的结合体。名字用反向 DNS(io.github.alice/weather),而「你凭什么能用这个名字」靠的是和 DNS-01 证书挑战同源的思路:你得证明你控制那个域名/那个 GitHub 账号。
本节到此——你现在应该能对别人讲清楚「MCP Registry 是个只存元数据、用域名所有权防冒名的服务器目录」。
2. 顶层全景(它大概怎么转)
这一节给你「大盘」:有哪些部件、一次发布怎么流过它们。
2.1 一张图:发布主线
作者机器 Registry 服务 (Go + Postgres)
┌──────────┐ ┌───────────────────────────────────────┐
│mcp- │ ① 换 token │ auth handlers (github/dns/http/oidc) │
│publisher │ ───────────────▶ │ 「证明你拥有命名空间」→ 签发 5 分钟 JWT │
│ (CLI) │ ◀─────────────── │ JWT 里带 permissions: io.github.alice/*│
└────┬─────┘ Registry JWT └───────────────────────────────────────┘
│
│ ② POST /v0/publish (Bearer JWT + server.json)
▼
┌───────────────────────────────────────────────────────────────────────┐
│ publish handler → registry service (一个大事务) │
│ │
│ (a) 验 JWT (b) 权限匹配 (c) schema 校验 │
│ jwtManager HasPermission ValidateServerJSON │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ (d) 包归属校验 → (e) 取锁 → (f) 版本检查 → (g) 算 latest → 落库 │
│ 去 npm/OCI advisory 重复? 上限? 比版本号 │
│ 反查 mcpName lock CompareVersions │
└───────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────┐ 只读 + CDN 缓存
│ Postgres │ ◀───────────────── 客户端 / 子注册表 GET /v0/servers
└──────────┘
怎么读这张图: 上半是「拿令牌」(第 1 章),下半是「带令牌发布」(第 2 章)。(d) 包归属校验是第 3 章,(f)(g) 版本逻辑是第 4 章。
2.2 部件一句话职责
| 部件 | 干什么 | 在哪个文件 |
|---|---|---|
| publisher CLI | 作者侧:init / login / publish | cmd/publisher/ |
| auth handlers | 把外部身份(GitHub/DNS/HTTP)换成 Registry JWT | internal/api/handlers/v0/auth/ |
| JWTManager | 用 Ed25519 签发/校验 5 分钟短令牌,做权限匹配 | internal/auth/jwt.go |
| publish/edit handlers | HTTP 层:验令牌、查权限、调 service | internal/api/handlers/v0/publish.go |
| registry service | 核心业务:事务、版本检查、latest 重算 | internal/service/registry_service.go |
| validators | schema 校验 + 「包归属」反查 npm/PyPI/OCI… | internal/validators/ |
| Database (Postgres) | 版本化存储、advisory lock、游标分页 | internal/database/postgres.go |
| model / api types | server.json 的数据结构 | pkg/model/、pkg/api/v0/ |