Claude Code 的 plugin 系統可以把 skills、MCP server、hooks、slash commands 打包成一個可安裝的單位,再透過 marketplace 發佈。這篇記錄我把自家的 WordPress 發文 skill、Dokploy/MLflow 兩個 MCP server、還有整份機器知識庫打包成 plugin、放上 GitHub 私人 marketplace 的完整過程——以及一路踩到的六個坑。
官方文件:
為什麼要自建 Marketplace?
我遇到的痛點:換一個專案(或換一台機器),skills 要重新複製、MCP server 要重新設定、CLAUDE.md 裡的環境知識要重新貼。自建一個私人 marketplace 之後,任何專案只要兩行指令就全部到位:
/plugin marketplace add youruser/my-plugins # 每台機器一次
/plugin install homelab@my-plugins
Code language: PHP (php)Marketplace 的最小結構
一個 marketplace 就是一個 git repo,結構如下:
my-plugins/
├── .claude-plugin/
│ └── marketplace.json # marketplace 本體
└── plugins/
└── homelab/
├── .claude-plugin/
│ └── plugin.json # plugin manifest
├── .mcp.json # (可選)plugin 內建的 MCP servers
└── skills/
└── my-skill/
├── SKILL.md
└── references/
Code language: PHP (php)marketplace.json:
{
"name": "my-plugins",
"owner": { "name": "youruser" },
"metadata": {
"description": "私人 plugin marketplace",
"version": "1.0.0"
},
"plugins": [
{
"name": "homelab",
"source": "./plugins/homelab",
"description": "自家機器管理套件:MCP + 知識庫"
}
]
}
Code language: JSON / JSON with Comments (json)plugin.json:
{
"name": "homelab",
"version": "1.0.0",
"description": "……",
"author": { "name": "youruser" }
}
Code language: JSON / JSON with Comments (json)改完隨時用官方驗證器檢查:
claude plugin validate <repo根目錄>
Code language: HTML, XML (xml)坑 1:marketplace 名字不能叫 claude-*
我第一版把 marketplace 取名 claude-plugins,claude plugin validate 直接報錯:
✘ name: Marketplace name impersonates an official Anthropic/Claude marketplace
marketplace 的 name 欄位不能用 claude 開頭(會被判定冒用官方名稱),但 GitHub repo 名不受限制。取名前先跑一次 validate 可以省掉 push 完才發現的尷尬。
坑 2:Skill 路徑一定要用 ${CLAUDE_PLUGIN_ROOT}
原本我的 SKILL.md 裡寫的是專案相對路徑:
cd .claude/skills/my-skill/scripts && uv run python foo.py
這在 plugin 裡必壞——plugin 安裝後住在 ~/.claude/plugins/ 底下的快取目錄,跟專案目錄無關。Claude Code 提供了 ${CLAUDE_PLUGIN_ROOT} 環境變數指向 plugin 安裝位置,SKILL.md 裡的路徑要改成:
cd "${CLAUDE_PLUGIN_ROOT}/skills/my-skill/scripts" && uv run python foo.py
Code language: JavaScript (javascript)另外如果 skill 的腳本自己讀設定檔(例如 .env),用「相對於腳本檔案」的方式找就不用改:
_env_path = Path(__file__).resolve().parent.parent / ".env"
load_dotenv(_env_path)
Code language: PHP (php)技巧:把 CLAUDE.md 知識庫搬進 plugin skill
我的 CLAUDE.md 原本塞了整份機器清單、服務拓撲、維運 SOP(約 10KB),每個 session 都全量載入。搬進 plugin 之後改成這樣:
- plugin skill 的
references/infra.md收完整知識庫(正本) - SKILL.md 只放速查表 + 觸發條件寫進 description(提到某些主機、服務、網域就載入)
- CLAUDE.md 縮成三行 stub:「動 infra 前先讀 homelab-infra skill」
好處是 progressive disclosure——知識只在需要時載入 context,而且任何專案都帶著走。要注意單一事實來源只能有一個:搬過去之後就直接改 plugin 裡的檔案,別維護兩份。
Plugin 內建 MCP Server
plugin 根目錄放一個 .mcp.json(跟專案級格式相同),安裝 plugin 時 MCP server 會自動註冊,設定跟著 plugin 走:
{
"mcpServers": {
"dokploy-mcp": {
"command": "npx",
"args": ["-y", "@dokploy/mcp"],
"env": {
"DOKPLOY_URL": "http://<dokploy主機>:3000/",
"DOKPLOY_API_KEY": "<你的 API key>"
}
},
"mlflow-mcp": {
"command": "uv",
"args": ["run", "--with", "mlflow[mcp]>=3.5.1", "mlflow", "mcp", "run"],
"env": {
"MLFLOW_TRACKING_URI": "http://<mlflow主機>:5000"
}
}
}
}
Code language: JSON / JSON with Comments (json)MLflow 官方 MCP 的設定來自官方文件,需要 MLflow 3.5.1 以上,提供 search_traces、get_trace、evaluate_traces 等 26 個工具。
兩個實務提醒:
MLFLOW_TRACKING_URI要用 MCP 連得上的端點。如果你的 MLflow 對外網域掛了 SSO / Cloudflare Access 之類的登入保護,MCP 這種非互動連線會被擋在門外——要嘛用內網直連位址,要嘛給它一條免登入(token 驗證)的路。- 寫進 plugin 前先在本機驗證 MCP server 起得來。最簡單的煙霧測試是直接跑那條 command 看會不會掛,更完整的是用 stdio 塞一個
initializeJSON-RPC 請求確認握手成功、tools/list回得出工具清單。debug 設定檔比 debug「為什麼 Claude Code 裡看不到工具」快得多。
安裝位置與 Scope
檔案一律落在 ~/.claude/plugins/(marketplace clone + plugin 快取),不會進專案目錄。能選的是啟用範圍:
claude plugin install homelab@my-plugins -s user # 預設:這台機器所有專案
claude plugin install homelab@my-plugins -s project # 寫進專案 .claude/settings.json(會進 git)
claude plugin install homelab@my-plugins -s local # 只有你、只在這個專案
Code language: PHP (php)如果 plugin 裡有私有設定,建議用 user scope:project scope 會把「我用了哪個 marketplace」宣告寫進專案 repo,等於向所有 clone 的人暴露你私人 marketplace 的存在。
坑 3:內容更新了,卻永遠拉不到新版
改了 skill 內容、push 上去、跑 claude plugin update,結果:
✔ homelab is already at the latest version (1.0.0).
Code language: CSS (css)claude plugin update 只比對 plugin.json 的 version 欄位,不比 git 內容。 忘了 bump 版號,安裝端永遠拿不到新內容。正確的更新流程是:
# 1. 改內容
# 2. bump plugin.json 的 version(必要!)
# 3. commit + push
# 4. 安裝端:
claude plugin marketplace update my-plugins && claude plugin update homelab@my-plugins
# 5. 重啟 session 生效
Code language: PHP (php)引用外部 Plugin:理想與現實
marketplace 除了裝自己的 plugin,entry 的 source 還能指向別人的 GitHub repo——不用複製任何檔案,安裝時直接抓 upstream:
{
"name": "mlflow",
"source": { "source": "github", "repo": "mlflow/skills" },
"description": "MLflow 官方 skills"
}
Code language: JSON / JSON with Comments (json)理想很美好,實際連踩三個坑:
坑 4:source 格式支援度比文件想像的窄
我這版 Claude Code(2.1.207)只接受 {"source": "github", "repo": "owner/repo"} 物件格式;{"source": "git", "url": "https://…"} 和純 URL 字串都會被 validator 拒絕(plugins.N.source: Invalid input)。不確定就逐一 validate 測。
坑 5:外部 source 走 SSH clone,機器沒 GitHub SSH key 就炸
github 型 source 在 clone 時偏好 SSH([email protected]:),如果機器只設定了 HTTPS + gh CLI 認證、沒有 GitHub SSH key,會直接死在 host key verification。解法是 git 全域改寫,把 SSH URL 轉回 HTTPS:
git config --global url."https://github.com/".insteadOf "[email protected]:"
Code language: PHP (php)對本來就走 HTTPS 的工作流零影響,private repo 照樣吃 gh 的 credential helper。
坑 6:外部 repo 的 plugin.json 過不了 schema,strict:false 也救不了
clone 成功後又卡一關:mlflow/skills 的 plugin.json 用了跟 Claude Code plugin schema 不相容的欄位格式(author 是字串、hooks/skills 欄位格式不合),install 時驗證直接失敗。在 marketplace entry 加 "strict": false 也繞不過——install 階段照樣會驗 clone 下來的 manifest。
追根究柢:人家 repo 主要支援的是 skills CLI 這條安裝路徑,不是 Claude Code 的 plugin 系統。與其硬掰(我試過 git clone + 本地 patch commit,能動但醜),不如直接用它官方支援的裝法:
# 安裝到 user scope(全專案可用)
npx -y skills add mlflow/skills -g -a claude-code -s '*' -y
# 跟著 upstream 更新
npx -y skills update -g -y
Code language: PHP (php)教訓:引用外部 skills 前,先看它 README 寫的安裝方式是什麼。 是 Claude Code plugin 就走 marketplace 外部 source;是 skills CLI 生態的就直接用 npx skills,別硬套。
機敏資料要不要進 plugin?
私人 plugin 的一大用途是把 .env、API key 這類設定直接打包,裝好即用。幾個原則:
- 只能放 private repo,且要有「進了 git 歷史就刪不乾淨」的覺悟——repo 從此絕不可轉 public
- 安全邊界等於你 GitHub 帳號的安全性(開 2FA)
- 洩漏時的止血方式是去源頭 revoke 憑證(WordPress Application Password、面板 API key 都能隨時作廢重發),不是刪 git 歷史
- 公開範例、部落格文章裡一律用 placeholder(本文所有 key 與內網位址皆已替換)
總結 Checklist
自建私人 marketplace 的完整流程:
- 建 repo:
.claude-plugin/marketplace.json+plugins/<name>/(名字別用 claude 開頭) - skill 路徑全部改
${CLAUDE_PLUGIN_ROOT} - MCP 設定放 plugin 根目錄
.mcp.json,寫進去前先在本機驗證能起來 claude plugin validate .通過再 push- GitHub private repo +
claude plugin marketplace add owner/repo - 安裝用 user scope,避免 project scope 暴露私人 marketplace
- 每次改內容都要 bump
plugin.json版號,否則安裝端永遠更新不到 - 外部 skills 先確認它支援哪種安裝路徑,Claude Code plugin 跟 skills CLI 是兩個生態