Skip to main content
Glama
lis186

Taiwan Holiday MCP Server

by lis186
process-exit-fix-2025-10-12.md7.27 kB
# Process Exit 處理修正 - 2025-10-12 ## 📋 概要 修復命令列參數處理中的 process exit 問題,確保 `--version` 和 `--help` 參數正確退出,同時保證 MCP 伺服器能夠正常啟動並持續運行。 ## 🎯 問題描述 ### 症狀 1. **版本參數測試失敗** - `tests/e2e/cross-platform.test.ts` 中的 `應正確處理版本參數` 測試失敗 - 退出代碼為 `null` 而非預期的 `0` - 測試錯誤訊息:`版本檢查失敗,退出代碼: null` 2. **MCP 端到端測試失敗** - `tests/e2e/build-and-package.test.ts` 中 4 個 MCP 流程測試失敗 - 伺服器過早退出,無法回應 JSON-RPC 請求 - 測試項目: - `應該正確處理 MCP 工具列表查詢` - `應該正確處理假期查詢` - `應該正確處理錯誤情況` - `記憶體洩漏測試:多次請求後記憶體應該穩定` ### 根本原因 在 `src/index.ts` 中: 1. **初始實作問題** ```typescript // 處理版本和幫助選項 if (args.showVersion) { showVersion(); return; // ❌ 只 return,沒有 exit } if (args.showHelp) { showHelp(); return; // ❌ 只 return,沒有 exit } ``` 2. **第一次修正嘗試(失敗)** ```typescript // 啟動應用程式 main().then(() => { process.exit(0); // ❌ 導致 MCP 伺服器過早退出 }); ``` 這導致: - `--version` 正確退出 - 但 MCP 伺服器在 `server.run()` 後也立即退出 - 無法處理 MCP 請求 ## ✅ 解決方案 ### 最終實作 1. **在 showVersion() 和 showHelp() 中直接 exit** ```typescript function showVersion(): void { try { const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')); console.error(`Taiwan Holiday MCP Server v${packageJson.version}`); console.error(`Node.js ${process.version}`); console.error(`Platform: ${process.platform} ${process.arch}`); } catch (error) { console.error('Taiwan Holiday MCP Server (版本資訊不可用)'); } process.exit(0); // ✅ 直接退出 } function showHelp(): void { console.error(`...幫助資訊...`); process.exit(0); // ✅ 直接退出 } ``` 2. **main() 函數保持簡單** ```typescript async function main(): Promise<void> { try { const args = parseArgs(); // 處理版本和幫助選項 if (args.showVersion) { showVersion(); // 會直接 exit(0) return; } if (args.showHelp) { showHelp(); // 會直接 exit(0) return; } // ... 其他初始化和啟動伺服器 await server.run(); // 保持運行 } catch (error) { console.error('Taiwan Holiday MCP 伺服器啟動失敗:', error); process.exit(1); } } // 啟動應用程式 main(); // ✅ 不加 .then(() => exit(0)) ``` ### 設計考量 **為何在函數內部呼叫 exit 而非在 main() 中?** 1. **語意清晰**:showVersion() 和 showHelp() 的語意就是「顯示資訊並結束程式」 2. **控制流明確**:避免在 async 函數鏈中處理 exit,減少時序問題 3. **避免競態條件**:確保輸出完成後才退出 4. **維護簡單**:main() 函數保持簡單的流程控制 ## 📊 測試結果 ### 修復前 ``` Test Suites: 2 failed, 18 passed, 20 total Tests: 5 failed, 2 skipped, 441 passed, 448 total ``` 失敗測試: - cross-platform.test.ts: 1 個失敗 - build-and-package.test.ts: 4 個失敗 ### 修復後 ``` Test Suites: 20 passed, 20 total Tests: 2 skipped, 446 passed, 448 total Coverage: Statements: 92.27%, Branches: 82.24%, Functions: 89.80%, Lines: 92.34% ``` ### 穩定性驗證 連續 3 次完整測試運行: ``` Run 1/3: Test Suites: 20 passed, Tests: 446 passed ✅ Run 2/3: Test Suites: 20 passed, Tests: 446 passed ✅ Run 3/3: Test Suites: 20 passed, Tests: 446 passed ✅ ``` **結論:100% 穩定,無間歇性失敗** ## 🔍 技術細節 ### Process Exit 行為分析 1. **正常流程(MCP 伺服器)** ``` main() → parseArgs() → setupEnvironment() → server.run() → [伺服器持續運行,等待 stdio 輸入] ``` 2. **--version 流程** ``` main() → parseArgs() → showVersion() → process.exit(0) → [程式正常退出,exit code = 0] ``` 3. **--help 流程** ``` main() → parseArgs() → showHelp() → process.exit(0) → [程式正常退出,exit code = 0] ``` ### Child Process Exit Code 捕獲 測試中的關鍵點: ```typescript child.on('close', (code) => { if (code === 0) { resolve(output); } else { reject(new Error(`退出代碼: ${code}`)); } }); ``` - 當 async 函數僅 `return` 時,child process 可能收到 `null` 作為 exit code - 明確呼叫 `process.exit(0)` 確保 child process 收到正確的退出代碼 ## 📝 相關檔案 ### 修改檔案 - `src/index.ts`:修正 showVersion()、showHelp() 和 main() 函數 ### 測試檔案 - `tests/e2e/cross-platform.test.ts`:版本參數測試 - `tests/e2e/build-and-package.test.ts`:MCP 端到端測試 ### 文件更新 - `CHANGELOG.md`:新增修正記錄 - `README.md`:更新測試覆蓋率數據 - `DEVELOPMENT.md`:新增測試指引和注意事項 ## 💡 經驗教訓 1. **Process Exit 處理需謹慎** - 在 async 函數中處理 exit 容易產生時序問題 - 直接在同步函數中 exit 更可靠 2. **測試快取問題** - Jest 快取可能導致修改後測試仍然失敗 - 建議測試前先執行 `npm test -- --clearCache` 3. **分離關注點** - 顯示資訊並退出 vs. 啟動服務並保持運行 - 這是兩種完全不同的執行路徑,應該明確區分 4. **穩定性驗證重要性** - 單次測試通過不代表穩定 - 需要多次運行確認無間歇性失敗 ## 🎯 後續建議 1. **CI/CD 增強** - 考慮在 CI 中執行 3 次測試確保穩定性 - 每次測試前清除快取 2. **測試文件化** - 在 DEVELOPMENT.md 中記錄測試最佳實踐 - 提醒開發者注意 process exit 處理 3. **程式碼審查檢查清單** - 確認 process exit 處理正確 - 驗證測試穩定性(多次運行) - 檢查是否有快取問題 ## 📊 影響評估 ### 正面影響 - ✅ 所有測試通過,100% 穩定 - ✅ 修復 5 個失敗測試 - ✅ 改善程式碼清晰度 - ✅ 增強測試信心 ### 風險評估 - ⚠️ 無已知風險 - ✅ 完全向後相容 - ✅ 不影響現有功能 ### 效能影響 - 無影響(exit 行為優化) ## 🏁 總結 這次修正解決了一個看似簡單但實際上涉及 Node.js process 生命週期、async 函數執行時序和 child process 通訊的複雜問題。通過將 `process.exit()` 呼叫移至具體的功能函數中,而非在 async 執行鏈中處理,我們確保了: 1. 命令列工具(--version, --help)能正確退出並返回正確的 exit code 2. MCP 伺服器能正常啟動並持續運行 3. 所有測試穩定通過(100% 通過率,連續 3 次驗證) 4. 程式碼更加清晰和易於維護 --- **作者**: Claude Code **日期**: 2025-10-12 **標籤**: #bug-fix #testing #process-exit #stability

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/lis186/taiwan-holiday-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server