【Claude Code 踩坑】自建私人 Plugin Marketplace:打包 Skills、MCP 與知識庫的實戰筆記

從零自建 Claude Code 私人 plugin marketplace 的完整踩坑記錄:marketplace 命名限制、${CLAUDE_PLUGIN_ROOT} 路徑、版本號更新陷阱、內建 MCP server、引用外部 plugin 的相容性問題,以及把 CLAUDE.md 知識庫搬進 skill 的技巧。

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-pluginsclaude 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 個工具。

兩個實務提醒:

  1. MLFLOW_TRACKING_URI 要用 MCP 連得上的端點。如果你的 MLflow 對外網域掛了 SSO / Cloudflare Access 之類的登入保護,MCP 這種非互動連線會被擋在門外——要嘛用內網直連位址,要嘛給它一條免登入(token 驗證)的路。
  2. 寫進 plugin 前先在本機驗證 MCP server 起得來。最簡單的煙霧測試是直接跑那條 command 看會不會掛,更完整的是用 stdio 塞一個 initialize JSON-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.jsonversion 欄位,不比 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/skillsplugin.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 的完整流程:

  1. 建 repo:.claude-plugin/marketplace.json + plugins/<name>/(名字別用 claude 開頭)
  2. skill 路徑全部改 ${CLAUDE_PLUGIN_ROOT}
  3. MCP 設定放 plugin 根目錄 .mcp.json,寫進去前先在本機驗證能起來
  4. claude plugin validate . 通過再 push
  5. GitHub private repo + claude plugin marketplace add owner/repo
  6. 安裝用 user scope,避免 project scope 暴露私人 marketplace
  7. 每次改內容都要 bump plugin.json 版號,否則安裝端永遠更新不到
  8. 外部 skills 先確認它支援哪種安裝路徑,Claude Code plugin 跟 skills CLI 是兩個生態

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *