自建过 MCP Server 的人都知道,写完工具只是前一半,分发给同事用才是后一半:对方要装对 Node 或 Python 版本、克隆你的仓库、npm install、再手动往 Claude Desktop 的配置文件里粘一段 JSON——任何一步出错,你的 Server 就变成了「我这能跑,你那不行」。官方的 MCPB(MCP Bundles)格式就是冲着这个来的:把本地 MCP Server 连同运行时依赖和一份 manifest.json 打进一个 zip 包(扩展名 .mcpb),同事拿到文件双击安装即可,机器上不需要预装 Node、Python 或任何工具链。这个格式此前叫 DXT(Desktop Extensions),现已全面更名为 MCPB,官方提供了 mcpb init 与 mcpb pack 两条命令完成从清单到打包的全流程。本篇从安装 CLI 到把你的 Server 塞进一个可分发的 .mcpb,按官方仓库路径完整走一遍。
先理解:MCPB 的定位与包结构
MCPB 的心智模型是「Chrome 扩展之于浏览器」:.mcpb 本质是一个 zip 压缩包,里面是完整的 MCP Server 文件加一份描述身份、入口与配置项的 manifest.json,宿主应用(Claude Desktop)读取清单、按 mcp_config 里的命令把 Server 作为 stdio 子进程拉起,之后的通信与你平时本地 stdio Server 完全一样——工具逻辑零改动,改的只是分发方式。一个典型的 Node 包长这样:
my-server.mcpb(ZIP 压缩包)
├── manifest.json # 必须:身份、入口、配置模式、兼容性
├── server/ # 你的 Server 代码
│ └── index.js # 主入口
├── node_modules/ # 打包进去的依赖(npm install --production 产物)
├── package.json # 可选
└── icon.png # 可选:包图标
先说一个定位判断,免得白打包:官方明确 MCPB 是二级分发路径,如果你的 Server 只调云端 API、不碰本地文件与本地服务,官方更推荐做成 remote MCP Server 去目录收录;MCPB 的价值场景是「必须跑在用户机器上」的 Server——读本地文件、驱动桌面应用、连 localhost 服务、调操作系统 API。
MCPB 只支持 stdio。以 HTTP 或 Streamable HTTP 形态部署的 Server(比如部署在 Cloudflare Workers 上的)不适用这套打包流程,保持 URL 分发即可。别为能用 URL 分发的东西付 MCPB 的打包成本——这是官方文档的原话逻辑。
Step 1:装 CLI 并初始化清单
# 全局安装 MCPB CLI(npm 包名:@anthropic-ai/mcpb)
npm install -g @anthropic-ai/mcpb
# 进入你的 MCP Server 项目目录,初始化
cd my-mcp-server
mcpb init
mcpb init 会以问答方式引导你填好 manifest 的各个字段:包名、版本、描述、作者、Server 类型(node / python / binary / uv)、入口文件、用户安装时要配置的项。填完自动落盘一份 manifest.json 到项目根目录。老项目注意:如果你在远古教程里见过 dxt 命令或 .dxt 文件,那是更名前的旧称,一律换成 mcpb 与 .mcpb。
Step 2:吃透 manifest 关键字段与占位符
manifest 的核心是 server.mcp_config——宿主就按它把你的 Server 当 stdio 子进程启动。一份完整的 Node 示例(官方 local-files 示例的骨架):
{
"$schema": "https://raw.githubusercontent.com/anthropics/mcpb/main/schemas/mcpb-manifest-v0.4.schema.json",
"manifest_version": "0.4",
"name": "local-files",
"version": "0.1.0",
"description": "Read, search, and watch files on the local filesystem.",
"author": { "name": "Your Name" },
"server": {
"type": "node",
"entry_point": "server/index.js",
"mcp_config": {
"command": "node",
"args": ["${__dirname}/server/index.js"],
"env": { "ROOT_DIR": "${user_config.rootDir}" }
}
},
"user_config": {
"rootDir": {
"type": "directory",
"title": "Root directory",
"description": "Directory to expose. Defaults to ~/Documents.",
"default": "${HOME}/Documents",
"required": true
}
},
"compatibility": {
"claude_desktop": ">=1.0.0",
"platforms": ["darwin", "win32", "linux"]
}
}
三个最容易踩的点:其一,${__dirname} 在安装后被替换为包解压目录,用来拼包内相对路径;其二,${user_config.xxx} 在安装时被替换为用户填的配置值,且没有自动前缀——你的代码读到的环境变量名就是你在 env 里写的名字;其三,v0.4 schema 里所有 server type 都必须有 mcp_config,漏写会导致 mcpb pack 直接失败。
directory 渲染原生文件夹选择器,string/number/boolean 渲染对应表单。给每个配置项写清楚 title 与 description,用户安装时不用猜。Step 3:处理敏感配置——让密钥进系统钥匙串
"user_config": {
"apiToken": {
"type": "string",
"title": "API Token",
"description": "Your service API token.",
"sensitive": true,
"required": true
}
}
凡是 API Key、访问令牌这类凭据,给对应配置项加 "sensitive": true:宿主会把它存进操作系统钥匙串而不是普通配置文件,界面上按密码框渲染。配套地,在 mcp_config.env 里照常声明对应的环境变量映射,Server 代码侧不需要任何改动——读的还是同一个环境变量名。这一步是对「密钥别写进 zip、别写进用户家目录的明文文件」这条军规的官方实现。
不要把密钥打进包里。打包前检查一遍代码与配置:.env 文件、硬编码的 Key、测试凭据都必须剔除——.mcpb 是 zip 包,收到的人解压就能看到全部内容。密钥只能通过 user_config 的 sensitive 项让用户安装时填入。
Step 4:打包 mcpb pack
# Node 项目:先装生产依赖,把 node_modules 一起打包
npm install --production
mcpb pack
# 产出:my-server.mcpb(当前目录)
# 复现构建建议用锁定文件
npm ci && mcpb pack
Node 是官方推荐的 Server 实现语言,理由很实在:Claude for macOS 与 Windows 自带 Node 运行时,你的包开箱即跑,用户不用额外装任何东西。依赖直接进 node_modules/ 随包分发,用 npm ci 或 yarn install --frozen-lockfile 保证可复现构建。如果依赖里有平台相关的原生编译模块,要在对应平台上分别打包测试,否则换平台可能加载失败。
"uv",附上 pyproject.toml 声明依赖即可,宿主自动管 Python 与依赖,包体积从 5-10 MB 降到约 100 KB,还能跨平台处理编译依赖。纯 binary 的 Go/Rust Server 也有对应 type,静态链接优先。Step 5:安装验收
# 安装方式(任选其一):
# 1. 双击 .mcpb 文件
# 2. 把 .mcpb 拖进 Claude Desktop 窗口
# 3. Claude Desktop -> Settings -> Extensions
# -> Advanced settings -> Install Extension
#
# 安装后确认:
# - 用户配置界面按 manifest 的 user_config 渲染
# - sensitive 项进系统钥匙串
# - 会话中 Server 正常拉起、tools/list 返回预期工具
验收时重点看三处:配置界面是否按你的 user_config 定义渲染(特别是 directory 类型的文件夹选择器);填入敏感项后 Server 能否正常启动并返回工具列表;给一个真实任务跑一遍工具调用。有问题先看 Claude Desktop 的 MCP 日志定位是清单字段问题还是运行时问题。
Step 6:版本更新与分发
# 迭代发布:改 manifest 里的 version,重新打包
# (macOS 用 sed / Windows 手改或脚本改均可)
mcpb pack
# 分发渠道:GitHub Release 附件、内网文件服务器、
# 或直接发给同事(一个文件搞定)
MCPB 宿主侧带自动更新能力:用户装过的包在新版本可用时会收到更新提示。发布节奏上建议 manifest 的 version 严格跟随你的语义化版本,同版本号不要重复打包(避免缓存与更新判定混乱)。如果同时想进官方 MCP Registry 做可发现性收录,那是另一条线(server.json + 所有权校验),与本篇的桌面分发互补而非互斥。
发布前在干净环境测一遍。你的开发机有全套工具链,很多问题只在用户机器上暴露:系统缺运行时、原生模块平台不匹配、${__dirname} 路径拼错。有条件就在一台没装过 Node 的虚拟机上走完安装与调用全流程再分发。
常见问题 FAQ
Q:MCPB 和 MCP Registry 什么关系?两条正交的分发线。Registry 解决「可发现性」(别人能搜到你的 Server),MCPB 解决「本地 Server 的一键安装」(别人能双击装上你的 Server)。只调云端 API 的 Server 优先走 Registry + 远程 URL;必须本地运行的才打包 MCPB。
Q:Python 依赖里有编译包(如 pydantic)还能打包吗?传统 python type 的限制是不能便携地打包编译依赖;官方已把 UV 运行时转正为推荐方案,由宿主跨平台处理依赖,编译包问题随之化解。
Q:用户改了配置要重装吗?不用。user_config 的值在宿主的扩展设置里随时可改,宿主会以新值重启 Server 进程。