Skip to main content

elf-mcp-gateway

Elf Express MCPJungle gateway 部署與註冊規範:vendored fork 從源碼 build、docker compose(內建 Postgres + gateway + docs-mcp + 一次性 registrar,零設定可起)、 mcpjungle/servers/*.json 註冊檔格式、registrar 重試與冪等、REGISTER_LIST、Dockhand / pull 部署、nginx 反向代理(SSE)、 GHCR 發佈(GHCR_PAT fallback)、SHA 釘選的 GitHub Actions、環境變數與安全(SERVER_MODE、bearer token、dashboard 不外露)。 當任務涉及 mcpjungle/ 目錄、docker-compose*.yml、Dockerfile.registrar、registrar.sh / register.sh、servers/*.json、 MCPJungle 設定或升級、nginx.example.conf、docker-publish.yml / ci.yml,或排查「gateway 看不到工具 / 註冊失敗 / 連不上」時觸發。

معلومات المصدر

المستودع
elf-express/skillshare-source
آخر نشاط في المصدر
١١ أكتوبر ٢٠٢٦ في ١٣:٤٥
لغة SKILL.md المكتشفة
الصينية
النجوم
٠
التفرعات
٠

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

مستكشف الملفات
13 ملفات

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
elf-mcp-gateway
description
Elf Express MCPJungle gateway 部署與註冊規範:vendored fork 從源碼 build、docker compose(內建 Postgres + gateway + docs-mcp + 一次性 registrar,零設定可起)、 mcpjungle/servers/*.json 註冊檔格式、registrar 重試與冪等、REGISTER_LIST、Dockhand / pull 部署、nginx 反向代理(SSE)、 GHCR 發佈(GHCR_PAT fallback)、SHA 釘選的 GitHub Actions、環境變數與安全(SERVER_MODE、bearer token、dashboard 不外露)。 當任務涉及 mcpjungle/ 目錄、docker-compose*.yml、Dockerfile.registrar、registrar.sh / register.sh、servers/*.json、 MCPJungle 設定或升級、nginx.example.conf、docker-publish.yml / ci.yml,或排查「gateway 看不到工具 / 註冊失敗 / 連不上」時觸發。
metadata
{"version":"1.1.1","owner":"Elf Express"}
# MCPJungle Gateway 部署與註冊(Elf Express) > 參考實作:`elf-express/mcp-library` 的 `mcpjungle/`、根 `docker-compose*.yml`、`.github/workflows/`。 > 架構:**AI 工具只連一個 MCPJungle**;gateway 後面掛多個知識 MCP server(目前 `docs-mcp-server` 的 `sqlsugar-zh-tw` / `fc-zh-tw`, > 加上官方 stdio server `filesystem` / `fetch` / `time`)。 相關 skill: - **`elf-mcp-knowledge`** — 新增知識庫的主流程(本 skill 是其中「註冊 / 部署 / 驗證」三步的細節) - `elf-mcp-server` — server 本身的寫法 - `elf-cicd-docker`(映像規範)、`elf-cicd-review`(PR AI review)、`elf-cicd-versioning`(版號 / tag)、`elf-stack` --- ## 1. 何時使用 - 部署 / 重佈 gateway stack;換 DB、換 port、改網路 - 新增 / 修改 `servers/*.json`、`REGISTER_LIST`、tool group - 升級 vendored MCPJungle fork、改 `Dockerfile.fullbuild` - 對外開放(nginx / HTTPS / token) - 改 `docker-publish.yml`、`ci.yml`、`dependabot.yml` - 排查:registrar 失敗、23505 重名、stdio server 起不來、SSE 收不到回應 --- ## 2. 固定規則 ### 拓撲與 compose 1. **MUST** 以根 `docker-compose.yml`(`include: mcpjungle/docker-compose.mcpjungle.yml`)為正式入口;**四個**服務: `postgres`(`postgres:16-alpine`,container `mcpjungle-postgres`,**不對外開 port**,volume `pgdata`)、 `mcpjungle`(container `mcpjungle-server`,:18800→8080)、`docs-mcp-server`(:5690,只在內網)、`registrar`(一次性,`restart: "no"`)。 docs-mcp 的 build context 是 `../mcp/docs-mcp-server`(`fb1d578` 把 server 收進 `mcp/` 之後的路徑)。 2. **MUST** 維持「**DB 內建、零設定可起**」:stack 自帶 `postgres` service,`mcpjungle` 的 `DATABASE_URL` 預設值是 `postgres://mcpjungle:mcpjungle@postgres:5432/mcpjungle?sslmode=disable`(**容器名** `postgres`,在 `mcpjungl` 網路內解析), gateway 以 `depends_on: postgres: condition: service_healthy` 等 DB 就緒。`docker compose up -d --build` 不必先 `cp .env.example .env`。 WHY:`a39e3eb`(feat(deploy): bundle Postgres into the stack, drop shared-db)——這條**取代**了早先 `6d25d6f` 的「DB 走外部 IP、stack 不含 Postgres」決策,`shared-db/` 目錄已不存在。 2a. **MUST** 只有要接「既有的外部 DB」才在 `.env` 設 `MCPJUNGLE_DATABASE_URL` 覆寫(host 填該 DB 的 **IP**、port 填它實際監聽的 port); 內建那顆容器仍會起,可 `docker compose stop postgres` 關掉。 2b. **MUST** 改內建 DB 密碼時 **`POSTGRES_PASSWORD` 與 `MCPJUNGLE_DATABASE_URL` 兩處同步改**,否則 gateway 連不上 DB。 `pgdata` volume 的資料 `docker compose down` 不會刪,`down -v` 才會——**MUST NOT** 在正式環境用 `down -v` 當重啟手段。 2c. 網路 `mcpjungl` 由 stack **自建**(實際名 `<project>_mcpjungl`),不必 `docker network create`;自包含端到端測試用 `docker-compose.localtest.yml`(現況見待確認 8)。 3. **MUST** 分清兩個位址:`--registry http://<host>:18800`(CLI → gateway)與 `servers/*.json` 的 `http://docs-mcp-server:5690/...`(gateway → server,用**容器名**在 `mcpjungl` 內解析)。 4. **MUST** gateway image 從 vendored fork 源碼 build:`context: ./MCPJungle`、`dockerfile: Dockerfile.fullbuild`、tag `mcpjungle-fork:latest`(`75c0649`)。 5. **MUST** gateway runtime 保持 **Node 22+ 與 uv**(`Dockerfile.fullbuild` stage 3:`ghcr.io/astral-sh/uv:debian` + nodesource `setup_22.x` + `tzdata`)。 WHY:`67b8864`——Node 20 跑不動使用 Node 22 API 的 stdio server(`@smartbear/mcp` 用 `module.enableCompileCache`);`npx` / `uvx` 是 stdio server 的前提。 6. **MUST** `MCP_SERVER_INIT_REQ_TIMEOUT_SEC` 預設 **30**。WHY:`bafd8e8`——`npx -y` 首次執行要下載套件,10 秒不夠。 7. **MUST** 四個服務都設 `TZ: ${TZ:-Asia/Taipei}`(含 `postgres`),gateway image 安裝 `tzdata`(`bafd8e8`)。TZ 只影響日誌 / 顯示;DB 時間欄位依 `elf-postgresql` 以 UTC `timestamptz` 儲存。 8. **MUST** 要接「現有」gateway 時用 `mcpjungle/docker-compose.dockhand.yml`(只起 docs-mcp + registrar,加入 external 網路 `MCPJUNGLE_NETWORK`),**MUST NOT** 再起第二個 gateway。 WHY:container 名固定 `mcpjungle-server`,同機已有同名 gateway 會撞名。 ### 註冊檔 `servers/*.json` 9. **MUST** 依 MCPJungle `RegisterServerInput`(`MCPJungle/pkg/types/mcp_server.go`)撰寫,欄位: | 欄位 | 何時必填 | 說明 | |---|---|---| | `name` | 一律 | `^[a-zA-Z0-9_-]+$`、不含 `__`、不以 `_` 結尾;團隊規定小寫 kebab-case。全 gateway 唯一 | | `transport` | 一律 | `streamable_http`(自家 server)/ `stdio`(npx / uvx 官方 server)/ `sse` | | `description` | 團隊規定必填 | 列出該 server 有效的**全部**工具名(證據 E10) | | `url` | `streamable_http` | gateway → server 的容器內網址 | | `bearer_token` | server 設了 `MCP_AUTH_TOKEN` 時 | 用 `${ENV_NAME}`,CLI 註冊時展開;**MUST NOT** 寫明文 | | `headers` | 選用 | 自訂 header;`Authorization` 會蓋過 `bearer_token` | | `command` / `args` / `env` | `stdio` | `env` 值也用 `${ENV_NAME}` | | `session_mode` | 選用 | `stateless`(預設)/ `stateful` | 範本:`templates/servers/http-server.json`、`http-server-auth.json`、`stdio-server.json`、`stdio-server-env.json`。 10. **MUST** 需 token 的 server(如 github)不放進自動註冊清單,除非 token 已由 env 提供。WHY:`mcpjungle/README.md`「沒進自動註冊,以免無 token 失敗」。 11. **MUST** 知識語料採**策略 A**(每本書一個 server,`/mcp/<id>`,工具 `<id>__docs_search`);策略 B(`docs-all.json`,`docs__docs_*`)只當補充入口。 WHY:A 才能在 gateway 以 server 為單位做 tool group 與 enterprise ACL。 ### registrar 12. **MUST** registrar 以 `Dockerfile.registrar` 把 `servers/` 與 `registrar.sh` **烤進 image**(`ghcr.io/elf-express/docs-registrar`),**MUST NOT** 改回 bind mount。 WHY:`54a9c08`——Dockhand / Portainer 把 repo clone 進自己的容器,相對 bind mount 由 host daemon 解析,報 `mkdir /app: read-only file system`。 13. **MUST** 保留 `registrar.sh` 的三段邏輯:等 gateway(每 2 秒、上限 60 次=120 秒)→ 每個 server 註冊重試(3 秒、上限 20 次)→ 冪等(`list servers` 已有則略過)。 WHY:`3eaebfd`——docs-mcp 尚未監聽時註冊會 connection refused;冪等讓每次 push 後 redeploy 安全。 14. **MUST** 新 server 加進 `REGISTER_LIST`:`registrar.sh` 預設值(主 stack)與 `docker-compose.dockhand.yml` 預設值(接現有 gateway)各自維護。 15. registrar 結束 `Exited (0)` 是**正常**;`docker logs` 應見「registrar: 完成,目前 servers:」與清單。 ### 對外與安全 16. **MUST** 對外只經 nginx(`templates/nginx.example.conf`):TLS、`limit_req 10r/s burst 20`、`limit_conn 20`、`/health` 免限流; `/mcp` 必須 `proxy_http_version 1.1`、`Connection ""`、`proxy_buffering off`、`proxy_cache off`、`proxy_read_timeout/send_timeout 3600s`、透傳 `Authorization`;`client_max_body_size 8m`(對齊 express 8mb)。 WHY:少任何一行 SSE 就會「能呼叫工具但收不到串流回應」。 17. **MUST** `SERVER_MODE=development` 時 gateway **不驗證用戶端**——只能在內網使用;要對公網開放,必須切 enterprise 模式(`mcpjungle init-server`、`create mcp-client --allow`)或在 nginx 加驗證。MCPJungle dashboard(`location /`)**MUST NOT** 對公網開放,只 allow 內網段。 18. **MUST** docs-mcp 等內層 server 不發佈 port(compose 內沒有 `ports:`),只在 `mcpjungl` 網路可達;dev 可不設 token,對外硬化時設 `DOCS_MCP_AUTH_TOKEN` 並在 `servers/*.json` 加 `bearer_token`。 19. **MUST** 機密只放 `.env`(已 `.gitignore`)或 GitOps 工具的 env 編輯器 / GitHub secrets。本 repo 用到的機密名稱: `MCPJUNGLE_DATABASE_URL`、`DOCS_MCP_AUTH_TOKEN`、`MCP_AUTH_TOKEN`、`FC_MCP_AUTH_TOKEN`、`GHCR_PAT`、`NPM_TOKEN`、`HF_TOKEN`(`GITHUB_TOKEN` 為內建)。 ### CI / 發佈 20. **MUST** 所有 workflow 的 `uses:` 釘**完整 commit SHA**並在行尾註解版本(`@<40-char-sha> # v7`)。WHY:`83cb805`——org policy 要求 SHA 釘選。 注意:其他團隊 skill(`elf-stack` 等)寫 `@v5` 版本標籤,與本 repo 衝突,見待確認。 21. **MUST** GHCR 登入用 `password: ${{ secrets.GHCR_PAT || secrets.GITHUB_TOKEN }}`,**MUST NOT** 改成 step `if: secrets...`。 WHY:`5c42768`——`secrets` context 不能用在 step `if:`(報 `Unrecognized named-value: 'secrets'`)。 22. **MUST** `docker-publish.yml` 維持:matrix 每個 image 一筆(`docs-mcp-server` → `./mcp/docs-mcp-server`、`docs-registrar` → `./mcpjungle`(自訂 `dockerfile`)、 `fc-designer-mcp` → `./mcp/legacy/fc-designer-mcp`、`sqlsugar-mcp` → `./mcp/legacy/sqlsugar-mcp/sqlsugar-mcp-server`;context 已隨 `fb1d578` 搬到 `mcp/` 下,image 名**不變**以免線上斷)、`linux/amd64,linux/arm64`、 `metadata-action` tags(semver 三層、branch、`sha-<short>`、預設分支 `latest`、手動 tag)、GHA cache `scope=<image>`、`provenance: false`。 23. **MUST** 新 server 同 PR 加進:`docker-publish.yml` matrix、`ci.yml` 的 `build-test` 與 `docker-build` matrix、`dependabot.yml`(含 `semver-major` ignore)。範本 `templates/docker-publish.matrix-entry.yml`。 WHY:證據 E4——核心 `docs-mcp-server` 至今不在 CI matrix 與 dependabot;證據 E3——拿掉 major ignore 後主版本分岔。 24. **MUST** commit / PR 標題走 Conventional Commits(CI `basics` 用 `webiny/action-conventional-commits` 擋)。 --- ## 3. 標準範本與操作 | 檔案 | 用途 | |---|---| | `templates/docker-compose.mcpjungle.yml` | 現行正式 compose(原樣) | | `templates/.env.example` | 所有 env 與預設值說明 | | `templates/Dockerfile.registrar`、`templates/registrar.sh` | registrar(原樣,**不可精簡**) | | `templates/servers/*.json` | 四種註冊檔 | | `templates/group.json` | tool group(`mcpjungle create group -c group.json` → `/v0/groups/<name>/mcp`) | | `templates/nginx.example.conf` | 對外反向代理(改 ① server_name ② 憑證 ③ `<GATEWAY_LAN_IP>`) | | `templates/docker-publish.matrix-entry.yml` | 新 server 要加的 CI / publish / dependabot 片段 | 部署(根目錄): ```bash docker compose up -d --build # 零設定即可起(DB 內建);build 法:現場 build 最新源碼 cp .env.example .env # 只在要改預設值時才需要(密碼、外部 DB、port、TZ…) docker compose -f docker-compose.pull.yml up -d # pull 法:拉 GHCR 映像(docs-mcp-server、docs-registrar,pull_policy: always) # GHCR 為 private 時,部署端先:echo "$GHCR_PAT" | docker login ghcr.io -u <github-user> --password-stdin ``` 驗證(每次部署後): ```bash docker compose ps # mcpjungle-server Up、docs-mcp-server Up、registrar Exited (0) docker logs $(docker compose ps -aq registrar) # 「registrar: 完成」+清單 docker exec mcpjungle-server /mcpjungle list servers docker exec mcpjungle-server /mcpjungle list tools docker exec mcpjungle-server /mcpjungle invoke sqlsugar-zh-tw__docs_list_corpora --input '{}' curl -s http://<host>:18800/health # 外部可達性(經 nginx 則打 https://<domain>/health) ``` 用戶端連線:`http://<host>:18800/mcp`(全部工具)、`http://<host>:18800/v0/groups/<group>/mcp`(tool group);對外一律 `https://<domain>/mcp`。 修改既有註冊(registrar 冪等不會覆寫): ```bash docker exec mcpjungle-server /mcpjungle deregister <name> docker compose up -d --force-recreate registrar ``` 手動註冊(host 上裝官方 CLI):`REGISTRY=http://localhost:18800 ./register.sh [per-book|all|none]`,`WITH_TOOLS=0` 略過官方工具。 --- ## 4. 禁止簡化 > 每條附證據(`references/simplification-evidence.md`)或 commit。違反須在 PR 說明並取得人工同意。 1. **MUST NOT** 移除 registrar 的等待 / 重試 / 冪等任一段,或把上限調到「不重試」。WHY:`3eaebfd` 修的競態;沒有 healthcheck-gated `depends_on`,重試是唯一保護。 2. **MUST NOT** 把 registrar 改回 bind mount 或刪掉 `Dockerfile.registrar`。WHY:`54a9c08`。 3. **MUST NOT** 把 gateway runtime 降回 Node 20、移除 uv / `tzdata`、或改用不含 npx/uvx 的 image。WHY:`67b8864`、`bafd8e8`。 4. **MUST NOT** 把 `MCP_SERVER_INIT_REQ_TIMEOUT_SEC` 改回 10 或刪掉。WHY:`bafd8e8`(npx 首次下載)。 5. **MUST NOT** 刪 nginx `/mcp` 的 SSE 設定、rate limit、`/health` 例外、dashboard 限內網。WHY:`nginx.example.conf` 註解的每一行都對應一個「極難查」的故障。 6. **MUST NOT** 把 action 從 SHA 改回 `@vN` 標籤,或新增未釘 SHA 的 `uses:`。WHY:`83cb805` org policy。 7. **MUST NOT** 把 GHCR 登入的 `||` fallback 拆成 step `if:`。WHY:`5c42768`。 8. **MUST NOT** 刪 `dependabot.yml` 的 `semver-major` ignore(新增時一律帶上),或把 server 從 CI matrix 拿掉。WHY:證據 E3、E4。 9. **MUST NOT** 刪發版能力而不留替代。WHY:證據 E1——`0115d50` 刪了 `release.yml`(建 tag / Release / dispatch publish),之後沒有任何東西產生 `v*.*.*` tag,`docker-publish` / `npm-publish` 的 tag 觸發形同虛設。要恢復請依 `elf-cicd-versioning`。 10. **MUST NOT** 在 `servers/*.json` 寫明文 token,或把 `description` 簡化成一個詞。WHY:安全;證據 E10。 11. **MUST NOT** 改 compose service 名 / container 名 / image 名而不全域搜尋更新 README 與 CLAUDE.md。WHY:證據 E11(`docs-mcp` → `docs-mcp-server` 後 README 指令失效)。 --- ## 5. 檢查清單 - [ ] `.env` 只含 `.env.example` 列出的變數(全部選用);用內建 DB 時**不要**設 `MCPJUNGLE_DATABASE_URL`,改密碼則兩處同步 - [ ] `docker compose config` 無錯;gateway `build.context: ./MCPJungle`、`Dockerfile.fullbuild` - [ ] `MCP_SERVER_INIT_REQ_TIMEOUT_SEC=30`、四服務 `TZ`;`postgres` 有 healthcheck 且 gateway `depends_on: service_healthy` - [ ] 新 `servers/<name>.json`:name 合規且唯一、description 列齊工具、無明文 token - [ ] `REGISTER_LIST` 兩處(registrar.sh / dockhand compose)按需更新 - [ ] registrar `Exited (0)`;`list servers` / `list tools` / 一次 `invoke` 成功並貼到 PR - [ ] 對外:nginx 範本三處已改;dashboard 限內網;development 模式未直接暴露公網 - [ ] workflow 新增的 `uses:` 皆為完整 SHA+版本註解 - [ ] 新 image 已加 docker-publish / ci / dependabot(含 semver-major ignore) - [ ] README / CLAUDE.md 的服務名、預設清單、port 已同步 --- ## 6. 常見錯誤 | 症狀 | 原因 | 修正 | |---|---|---| | `duplicate key value violates unique constraint "idx_mcp_servers_name" (SQLSTATE 23505)` | 同名 server 已被 registrar 自動註冊 | 換 name,或先 `deregister` | | registrar `mkdir /app: read-only file system` | GitOps 工具下使用相對 bind mount | 用烤好設定的 `docs-registrar` image(`54a9c08`) | | registrar 反覆「上游尚未就緒」後放棄 | docs-mcp 啟動失敗或不在 `mcpjungl` 網路 | `docker logs docs-mcp-server`;確認 `networks: [mcpjungl]` | | registrar「等 gateway 逾時(120s)」 | gateway 連不上 DB:改了 `POSTGRES_PASSWORD` 卻沒同步 `MCPJUNGLE_DATABASE_URL`,或覆寫的外部 DB 位址不通 | `docker logs mcpjungle-server`;兩處密碼對齊(規則 2b),或移除覆寫回用內建 DB | | stdio server 註冊時 init timeout | `npx -y` 首次下載慢 | `MCP_SERVER_INIT_REQ_TIMEOUT_SEC=30`(或更高) |
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub