Skip to main content

elf-mcp-server

Elf Express 撰寫 / 修改 MCP server(TypeScript + @modelcontextprotocol/sdk)的團隊規範與完整範本:專案結構、工具命名與描述、 zod 輸入 schema、唯讀 annotations、錯誤以文字結果回傳、輸出截斷、stdio 與 Streamable HTTP 雙 transport、Bearer token、 /health、stderr 日誌、測試、Dockerfile,以及「禁止簡化」清單。當任務涉及新建 MCP server、修改 src/index.ts 的 registerTool、 tool description / inputSchema、http.ts transport、MCP 相關測試或 Dockerfile,或重構 / 精簡既有 MCP server 程式碼時觸發。

Quellinformationen

Repository
elf-express/skillshare-source
Letzte Quellaktivität
20. Oktober 2026 um 03:37
Erkannte Sprache von SKILL.md
Chinesisch
Sterne
0
Forks
0

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
18 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
elf-mcp-server
description
Elf Express 撰寫 / 修改 MCP server(TypeScript + @modelcontextprotocol/sdk)的團隊規範與完整範本:專案結構、工具命名與描述、 zod 輸入 schema、唯讀 annotations、錯誤以文字結果回傳、輸出截斷、stdio 與 Streamable HTTP 雙 transport、Bearer token、 /health、stderr 日誌、測試、Dockerfile,以及「禁止簡化」清單。當任務涉及新建 MCP server、修改 src/index.ts 的 registerTool、 tool description / inputSchema、http.ts transport、MCP 相關測試或 Dockerfile,或重構 / 精簡既有 MCP server 程式碼時觸發。
metadata
{"version":"1.4.1","owner":"Elf Express"}
# MCP Server 撰寫規範(Elf Express) > 參考實作:`elf-express/mcp-library` 的 `mcpjungle/docs-mcp-server/`(核心、多語料,`origin/release` @ `73b8206`)是**結構**參考; > 工具**描述寫法**的範例是已刪除的 legacy server(`0a37607` 刪除,E-126),用 `git show 0a37607^:mcp/legacy/sqlsugar-mcp/sqlsugar-mcp-server/src/index.ts` > 與 `git show 0a37607^:mcp/legacy/fc-designer-mcp/src/index.ts` 讀;docs-mcp 的描述還沒有「範例:」行(證據 E6)。 > 完整範本在 `templates/`,Node 24.18 下已實測 `tsc`、原有 9+新增 22 個測試與 stdio 通過;實際解析值及基線/候選逐檔 coverage 見證據 E17 與「E-322 遷移驗證結果」。pnpm 11 安裝缺陷已由 E-353 解決:範本只核准 esbuild;全新副本 install/frozen-install、build、31 測試通過,coverage 86.28%,見 evidence「E-353 安裝核准驗證結果」。 相關 skill: - **`elf-mcp-book`** — 來源是「一本書」時的素材與上架規範(`mcpjungle/books/<書名>/source/`) - **`elf-mcp-knowledge`** — 「新增知識庫」主流程。**要掛新文件 / 新書時先看它**:90% 的情況是新增語料,不需要寫新 server - `elf-mcp-gateway` — 寫好的 server 如何註冊到 MCPJungle、部署、CI - `elf-stack`(版本)、`elf-unit`(測試放置 / 覆蓋率 55%)、`elf-cicd-docker`(映像)、`elf-cicd-review`(PR AI review) - 第三方 `mcp-builder`(Anthropic):通用設計原則;與本 skill 衝突時以本 skill 為準 --- ## 1. 何時使用 - 新建一個 MCP server(只在 `elf-mcp-knowledge` 的決策樹判定為路線 C 時:需要即時資料、非文字資料、另一個 runtime) - 修改任何 MCP server 的 `registerTool`、description、zod schema、handler、`http.ts`、Dockerfile、測試 - 「重構 / 精簡 / 整理」MCP server 程式碼 —— **先讀第 4 節「禁止簡化」** --- ## 2. 固定規則 ### 專案結構與分層 1. **MUST** 三層分檔:`src/index.ts`(工具註冊薄殼+啟動)、`src/http.ts`(transport)、`src/<domain>.ts`(純邏輯,不 import SDK / zod)。 WHY:`docs-mcp-server` 的 `corpus.ts` 可以不起 server 直接單元測試;設計文件 `bc6760c` §8 明定此邊界。 2. **MUST** 純邏輯函式一律回傳 `string`(Markdown),handler 一行 `textResult(doX(...))`。 WHY:邏輯可測、工具殼不長邏輯;現有 8 個工具全是這個形狀。 3. **MUST** 新專案依團隊標準:Node 24.18、pnpm 11.x、TypeScript、`*.test.ts` 與原始碼同目錄、vitest 行覆蓋率 ≥ 55%(範本 `vitest.config.ts`)。 現況落差(**不要順手「統一」,列在待確認**;`origin/release` @ `73b8206` 實測):`mcpjungle/docs-mcp-server` 用 npm+`package-lock.json`、`tests/` 目錄(15 個測試檔)、 無覆蓋率門檻(沒有 `vitest.config.*`,`test` script 是 `vitest run`,沒有 `--coverage`,`package.json:23`)、Docker `node:26-alpine`(`Dockerfile:5,14`)、 CI `node-version: 24`(`ci.yml:165` 的 `build-test` matrix;`ci.yml:116` 的 `node-version: 20` 是 gateway dashboard 建置,不是 docs-mcp-server)、`engines >=18`(`package.json:27`)。 ### SDK 與版本(實際在用的) 4. **MUST** 用 `@modelcontextprotocol/sdk` 的高階 API:`McpServer` + `server.registerTool(name, {title, description, inputSchema, annotations}, handler)`; stdio 用 `StdioServerTransport`,HTTP 用 `StreamableHTTPServerTransport`。package.json 宣告 `^1.32.1`;lockfile 實際解析到 **1.32.1** (`mcpjungle/docs-mcp-server/package-lock.json:514`,`origin/release` @ `73b8206`;重現:`npm ci && npm ls @modelcontextprotocol/sdk --depth=0`)。 5. **MUST** 新 server 的 zod、`@modelcontextprotocol/sdk` 主版本跟 docs-mcp-server 在 `origin/release` 上的 lockfile 解析值一致。 實測(`73b8206`,`npm ls zod express @modelcontextprotocol/sdk --depth=0`):zod 4.6.6(`package-lock.json:3327`)、SDK 1.32.1(`:514`)、express 5.3.0(`:1775`)。之後再升級只更新實測值。 **已解決(E-321/E-322)**:`templates/` 已實測遷移至 zod 4/express 5,SDK 主版本與參考實作一致;依賴解析值、9+N、coverage 與協定驗證證據見 E17。coverage 已由 E-322 解決(86.28%);pnpm 11 安裝缺陷另由 E-353 加入 esbuild 核准並以全新副本驗證解決,見 evidence「E-353 安裝核准驗證結果」。原始失敗基線保留。 WHY:證據 E3——拿掉 semver-major ignore 後,主版本曾在同一 repo 內分岔;以參考實作的實測值為準,各 server 才不會各自漂移。 (分岔當時:`docs-mcp-server`/`sqlsugar` 是 zod 3.25.76、`fc-designer-mcp` 被 dependabot 升到 zod 4.4.3;legacy 兩個 server 已於 `0a37607` 刪除。) ### 工具設計 6. **MUST** 工具名 `snake_case`、`<domain>_<verb>`(例:`docs_search`、`sqlsugar_read_note`),**不含 `__`**、不以 `_` 結尾、只用 ASCII。 WHY:MCPJungle 以第一個 `__` 切 `<server>__<tool>`;gateway 後面的 AI 看到的是 `<server>__<domain>_<verb>`。 7. **MUST** 一個 server 的工具數**固定**,資料種類(語料 / 書 / 資料集)是**參數**,不是新工具;能力差異用 capability gating。 WHY:`docs-mcp-server` 工具數恆為 8,不隨語料膨脹;gateway 底下掛越多 server,工具總數越要可控。 8. **MUST** description 固定四段:一句用途 →「用途:」何時用、與哪個工具分工 →「參數:」逐項(型別、範圍、預設)→「回傳:」格式 +「範例:」真實值。 有 scope 的 server 追加 `scopeNote`。範本見 `templates/src/index.ts`。 WHY:證據 E6 —— legacy 描述有範例,docs-mcp 的 `docs_code_*` / `docs_symbol` 描述只剩一行參數名。 9. **MUST** 每個輸入欄位:zod 型別+`min`/`max`(字串長度、數字範圍)+`default`(選填數值)+`.describe()`;物件 `.strict()`;schema 放模組層級。 WHY:`dd4853a` 把 schema 從 `createServer` 內移出(每 session 重建);`.strict()` 讓打錯參數名的呼叫直接失敗而不是被默默忽略。 10. **MUST** 唯讀工具帶 `annotations: READ_ONLY`(`readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false`)。有寫入的工具另立 annotations 並在 description 開頭寫明副作用。 11. **MUST** 「找不到 / 未啟用 / 缺參數」回**文字結果**,內容含下一步(改用哪個工具、可用值清單);**MUST NOT** 為這類情況 throw。 只有 transport 層意外才走 JSON-RPC error(`http.ts` 的 500 `-32603`)。WHY:AI 能讀文字自我修正;例外只會變成一行「tool failed」。 12. **MUST** 所有輸出經 `truncateIfNeeded`(`CHARACTER_LIMIT = 25000`),截斷訊息要告訴 AI 怎麼縮小範圍;列表類工具要有 `limit`(`docs_search` 1–50 預設 15、`docs_code_search` 1–30 預設 10、`docs_symbol` 1–30 預設 8)。 13. **MUST** 讀檔只從「目錄列舉結果」中比對(`findNotes` / `findFiles`),**MUST NOT** `path.join(root, userInput)`。WHY:天然防 `../` 穿越;範本有測試 `never escapes the data root`。 ### Transport / 設定 / 日誌 14. **MUST** 同一份程式支援兩種 transport:`TRANSPORT` 未設 = stdio(本機 `.mcp.json` / npx),`TRANSPORT=http` = Streamable HTTP(Docker / gateway)。Dockerfile 內建 `ENV TRANSPORT=http`。 15. **MUST** HTTP 模式:每 session 一個 transport(以 `mcp-session-id` 為鍵、`onclose` 刪除);`POST /mcp` 非 initialize 又無 session → 400;`GET`/`DELETE` 以 session 找 transport;`GET /health` **免驗證**並回載入數量。 16. **MUST** `MCP_AUTH_TOKEN` 有設就要求 `Authorization: Bearer <token>`(401 + JSON-RPC `-32001`);`/health` 永遠公開。 17. **MUST** `/mcp*` 路由在驗證前限流:`express-rate-limit` 掛在每條 `/mcp*` 路由的第一個 middleware,且只計驗證失敗——`skip: (req) => isAuthorized(req)`,`skip` 與驗證共用同一個 `isAuthorized()`。 `trust proxy` 設 false;`standardHeaders: 'draft-8'`;預設 15 分鐘 20 次,用 `MCP_RATE_LIMIT_MAX`/`MCP_RATE_LIMIT_WINDOW_MS` 覆寫;每次回 429 寫 stderr 日誌(IP、次數/上限、時間窗);`/health` 不限流(證據 E15,`mcpjungle/docs-mcp-server/src/http.ts`;docs-mcp-server 的環境變數用 `DOCS_MCP_` 前綴(`DOCS_MCP_RATE_LIMIT_MAX`/`_WINDOW_MS`,`http.ts:180-181`))。 WHY:Bearer token 不能被無限次嘗試;經 gateway 部署時所有請求的來源 IP 都是 gateway,只計 401 才不會讓正常流量共用額度;限流只拖慢猜測並留下日誌,不能取代長的隨機 `MCP_AUTH_TOKEN`。 18. **MUST** 所有日誌走 `console.error`(stderr),前綴 `[<server-name>]`。**MUST NOT** 在任何路徑 `console.log`。 WHY:stdio 模式 stdout 就是 JSON-RPC 串流,一行 log 就讓用戶端解析失敗。 19. **MUST** 環境變數一覽寫進 README:`TRANSPORT`、`PORT`、`MCP_AUTH_TOKEN`、`MCP_RATE_LIMIT_MAX`、`MCP_RATE_LIMIT_WINDOW_MS`、資料目錄覆寫(如 `DOCS_CORPORA_DIR`)、scope(如 `DOCS_SCOPE`)。 20. **MUST** 啟動時印出:資料目錄、載入數量、找不到資料時的警告、HTTP 監聽位址、驗證是否啟用(`logStartupInfo` 模式)。 21. `express.json({ limit: "8mb" })` 與 nginx `client_max_body_size 8m` 保持一致(mcp-library 根目錄的 `nginx.example.conf:47-48` 註明對齊;docs-mcp-server 在 `mcpjungle/docs-mcp-server/src/http.ts:54`)。 ### 時區 22. 容器設 `TZ=${TZ:-Asia/Taipei}`(`bafd8e8`;`73b8206` 的 `compose.yaml:16,42,61,89` 四個 service(postgres、gateway `mcpjungle`、docs-mcp `docs-mcp-server`、registrar)都設 `TZ=${TZ:-Asia/Taipei}`,四者一致)。本 repo 的 server 不寫 DB;**若 server 會讀寫 PostgreSQL,時間一律以 UTC `timestamptz` 儲存(`elf-postgresql`),只在輸出給人看時轉台北時間**——TZ 只影響日誌與顯示。 ### 測試 23. **MUST** 純邏輯每個 `do*` 至少測:命中、未命中(回提示且含下一步)、邊界(空輸入、上限截斷)、gating(若有)、路徑穿越。 24. **MUST** fixture 用 `fs.mkdtempSync` 臨時目錄+環境變數覆寫資料根(`corpus.test.ts` 模式),`afterAll` 還原 env 並刪目錄;快取模組要匯出 `_clearCaches()` 在 `beforeEach` 呼叫。 25. **MUST** 另做一次 stdio 冒煙:build 後送 `initialize` → `tools/list` → `tools/call`(見第 3 節),確認工具數與描述正確。 --- ## 3. 標準範本 `templates/` 內容(全部可直接複製,替換 `<...>`): | 檔案 | 說明 | |---|---| | `package.json` | ESM、`bin`、scripts(build/test/test:coverage)、engines ≥24.18、`packageManager` | | `pnpm-workspace.yaml` | `allowBuilds.esbuild: true`,只核准工具鏈所需 esbuild 的建置腳本;Docker 兩階段安裝前一併複製 | | `tsconfig.json` | 與 docs-mcp-server 相同(ES2022 / Node16 / strict),排除 `*.test.ts` | | `vitest.config.ts` | colocated 測試、v8 覆蓋率 lines 55 | | `src/index.ts` | 3 個唯讀工具(list/search/read)、引用共用的模組層級 strict schema、完整四段描述、雙 transport 啟動 | | `src/schemas.ts` | 三個共用模組層級 strict schema,保留每個欄位的上下限、default 與 describe | | `src/http.ts` | Streamable HTTP(createHttpApp/runHttp、session map、/mcp 與 /mcp/:scope 六路由、401/404/400/413/500、`/mcp*` 驗證前限流(只計驗證失敗、回 429)、/health、JSON-RPC 錯誤包裝) | | `src/knowledge.ts` | 純邏輯:資料根解析、列舉、mtime 快取、去 BOM、模糊比對、AND 搜尋、截斷 | | `src/knowledge.test.ts` | 9 個測試:排序、AND、未命中提示、空查詢、讀檔、巢狀、路徑穿越、過濾、截斷 | | `src/schema.test.ts` | 新增 8 個 schema 回歸,涵蓋 Zod 4 issues 格式、strict、邊界、預設值與描述 | | `src/protocol.test.ts` | 新增 2 個真實 stdio 回歸,涵蓋三工具與錯誤輸入後session仍可用 | | `src/http.test.ts` | 新增 12 個 HTTP 回歸,涵蓋六路由、session、驗證/限流及 Express 5 錯誤處理 | | `Dockerfile` / `.dockerignore` / `docker-compose.yml` / `.env.example` | 多階段 build、prod-only deps、`TRANSPORT=http`、healthcheck、TZ | 佔位符:`<server-name>`(kebab-case,= image 名 = container 名 = gateway server 名)、`<domain>`(工具前綴,snake_case)、 `<PORT>`(現有:docs 5690;已刪除的 legacy 曾用 sqlsugar 5688、fc 5689;新 server 往後取號)、`<ENV_DATA_DIR>`、`<data-dir>`、`<NODE_IMAGE_TAG>`、`<PNPM_VERSION>`(見 `elf-stack`)。 stdio 冒煙(bash;Windows 請在 Git Bash 執行,依據 mcp-library `CLAUDE.md:65`「需 sh,Windows 用 Git Bash」): ```bash pnpm run build (printf '%s\n' \ '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"smoke","version":"1"}}}' \ '{"jsonrpc":"2.0","method":"notifications/initialized"}' \ '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \ '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"<domain>_search","arguments":{"query":"<real keyword>"}}}'; sleep 2) \ | node dist/index.js ``` HTTP 冒煙:`$env:TRANSPORT="http"; pnpm start` 後 `curl http://localhost:<PORT>/health`,再照 `mcpjungle/docs-mcp-server/README.md`「二、本機 Docker 測試」(`:131-160`)裡「驗證 MCP 流程」的 initialize → 帶 `mcp-session-id` 呼叫。 (`$env:TRANSPORT="http"; pnpm start` 是 PowerShell 寫法;Git Bash 寫成 `TRANSPORT=http pnpm start`。) --- ## 4. 禁止簡化 > 使用者痛點:「AI 越寫越簡單」。以下每條附證據編號(見 `references/simplification-evidence.md`)或現行程式碼位置。 > **任何 PR 讓下列基準值下降,必須在 PR 描述逐條說明並取得人工同意。** 基準值(`mcpjungle/docs-mcp-server`,`origin/release` @ `73b8206`,2026-10-17 實測;前次基準為 HEAD `f05dcb1`、2026-10-04,測試 57 個(2+4+25+7+10+4+5))——修改前後都跑一次並貼到 PR: ```bash cd mcpjungle/docs-mcp-server grep -c 'registerTool(' src/index.ts # 8 grep -c '\.strict()' src/index.ts # 8 grep -c 'annotations: READ_ONLY' src/index.ts # 8 cat tests/*.ts | grep -cE '^\s*it\(' # 128(2+4+25+7+7+7+10+4+12+5+19+4+4+13+5) ``` 1. **MUST NOT** 刪工具、合併工具、或把「收編」說成「簡化」。WHY:證據 E5——收斂成 docs-mcp 時丟了 C# 範例工具與 `include_index`,事後才補。任何工具變動要附「舊工具 → 新位置」對照表。 2. **MUST NOT** 縮短 description、拿掉「參數 / 回傳 / 範例」段落。WHY:證據 E6;gateway 後的 AI 只能靠描述選工具。 3. **MUST NOT** 刪 zod 的 `min`/`max`/`default`/`.describe()`/`.strict()`,或把欄位改成 `z.any()` / `z.string()` 無上限。WHY:輸入上限是唯一的防濫用閘(搜尋 200 字、檔名 300 字、limit ≤ 50)。 4. **MUST NOT** 把友善文字錯誤改成 throw,或把錯誤訊息的「下一步建議」刪掉。WHY:證據 E8,`dd4853a` 補回被寫短的缺 corpus 訊息。 5. **MUST NOT** 為了讓測試通過放寬比對或加 fallback;**MUST NOT** 刪測試、改成 `it.skip`、把斷言改寬(`toMatch(/./)`)。WHY:證據 E7(`e3d8808` 移除讓測試假通過的 token-OR fallback)。 6. **MUST NOT** 移除 `truncateIfNeeded`、mtime 快取、BOM 處理、`SKIP_DIRS`、副檔名白名單。WHY:分別防爆 context、確保改檔即時生效、避免亂碼、避免掃進 `node_modules`/`bin`/`obj`。 7. **MUST NOT** 移除 HTTP 模式的 `/health`、Bearer 驗證、404(未知 scope)、400(無 session)、500 包裝與 `onclose` 清理。WHY:gateway healthcheck、registrar 等待、MCPJungle 連線都依賴這些行為(`mcpjungle/docs-mcp-server/src/http.ts`)。 8. **MUST NOT** 移除 stdio 或 HTTP 任一 transport。WHY:stdio 給本機 `.mcp.json` / `node <repo>/mcpjungle/docs-mcp-server/dist/index.js`(`<repo>` 是 elf-express/mcp-library 的本機 clone;先在 `mcpjungle/docs-mcp-server` 執行 `npm ci && npm run build` 才有 `dist/index.js`;npm 套件已停止發布,見證據 E16),HTTP 給 gateway 與遠端連接器(CLAUDE.md「A–D 四種接法」)。 9. **MUST NOT** 用 `console.log`。WHY:stdio 串流污染。 10. **MUST NOT** 只改程式不改檔頭註解 / README / `corpus.json` 的 description(registrar 依它產生註冊描述,`gen-book-configs.mjs:62`)。WHY:證據 E8、E9、E10。 11. **MUST NOT** 讓 dependabot 或「順手升級」跨主版本(zod、express、typescript、@types/node、SDK)而不經人工評估。WHY:證據 E3——拿掉 `semver-major` ignore 後,docs-mcp-server 與兩個 legacy server 的主版本分岔(legacy 於 `0a37607` 刪除後分岔才消失,沒有機制防止再發生)。 12. **MUST NOT** 移除 `/mcp*` 限流、把它移到驗證之後、或改成計數所有請求。WHY:證據 E15——限流是 Bearer token 在應用層的防暴力猜測閘;移到驗證後等於沒擋,計數所有請求會讓 gateway 後的正常流量共用同一個 IP 額度。 --- ## 5. 檢查清單 - [ ] 三層分檔;純邏輯不 import SDK / zod - [ ] 每個工具:snake_case `<domain>_<verb>`、四段描述含真實範例、strict schema(上下限 / default / describe)、READ_ONLY - [ ] 找不到 / 缺參數 → 文字結果含下一步;無 throw - [ ] 輸出經 `truncateIfNeeded`;列表有 `limit` - [ ] 讀檔只從列舉結果比對;有路徑穿越測試 - [ ] stdio 與 HTTP 都能跑;`/health` 免驗證;設 token 後未帶 → 401 - [ ] /mcp* 有限流,且在驗證之前、只計驗證失敗;/health 不受限流影響;trust proxy 為 false - [ ] 全部日誌 `console.error`;`grep -rn "console.log" src/` 無結果 - [ ] 測試 colocated、覆蓋率 ≥ 55%(新專案);第 4 節基準值未下降(既有專案) - [ ] stdio 冒煙輸出(tools/list 工具數、一次 tools/call)貼到 PR
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen