Node.js 实现 MCP Server 时进程意外退出怎么调试

文章导读
Node.js 实现 MCP Server 进程意外退出时,最推荐先检查标准错误输出 stderr 和进程退出码 exitCode,适用场景为本地开发或生产环境守护进程,风险边界是避免将日志误输出到 stdout 导致协议解析失败。
📋 目录
  1. 命令速用版
  2. 为什么会这样
  3. 分步处理
  4. 怎么验证是否生效
  5. 常见坑
  6. 常见问题
  7. 参考来源
A A

Node.js 实现 MCP Server 进程意外退出时,最推荐先检查标准错误输出 stderr 和进程退出码 exitCode,适用场景为本地开发或生产环境守护进程,风险边界是避免将日志误输出到 stdout 导致协议解析失败。

先说结论:进程意外退出通常由未捕获的异常、资源耗尽或协议通信冲突引起,需优先隔离日志流并监听进程信号。

  • 先确认:查看进程退出码(exit code)区分是正常退出还是崩溃。
  • 先处理:将应用日志重定向至 stderr,保留 stdout 专用于 MCP 协议通信。
  • 再验证:添加全局异常监听器后重启服务,观察是否仍出现非预期终止。

命令速用版

使用以下命令启动 Node.js 进程以追踪未捕获异常和查看退出状态:

node `--trace-uncaught` `--trace-deprecation` your-mcp-server.js
echo $?

若使用 PM2 守护进程,查看日志命令如下:

pm2 logs your-mcp-server `--lines` 100
pm2 describe your-mcp-server

为什么会这样

进程意外退出主要由未处理的 Promise rejection 或标准输出流冲突导致。MCP Server 通常通过 stdio 传输 JSON-RPC 消息,若 console.log 等日志混入 stdout,客户端解析失败可能触发服务端异常逻辑。Node.js 遇到未捕获异常时默认会终止进程,且退出码非零。内存溢出或信号中断(如 SIGTERM)也会导致类似现象。

分步处理

第一步:隔离日志输出流。MCP 协议要求 stdout 仅传输协议消息,所有调试日志必须写入 stderr。

console.error('Debug info:', data); // 正确
console.log('Debug info:', data);   // 错误,会破坏协议

第二步:添加全局异常监听。在入口文件顶部注册监听器,防止未捕获异常直接杀死进程。

Node.js 实现 MCP Server 时进程意外退出怎么调试
process.on('uncaughtException', (err) => {
  console.error('Uncaught Exception:', err);
  process.exit(1);
});
process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled Rejection at:', promise, 'reason:', reason);
});

第三步:捕获退出信号。处理 SIGINT 和 SIGTERM 以便优雅关闭连接。

process.on('SIGTERM', () => {
  console.error('Received SIGTERM, shutting down gracefully');
  server.close();
});

怎么验证是否生效

检查进程是否存活且退出码为零。手动触发错误后观察日志是否记录在 stderr 而非 stdout。使用命令 echo $? 查看上一条命令退出状态,0 代表正常,非 0 代表异常。查看日志文件确认错误堆栈已完整记录,且 MCP 客户端未收到非法 JSON 数据。

常见坑

第三方库默认使用 console.log 输出调试信息,这会污染 MCP 协议流。需全局重写 console.log 将其重定向至 stderr,或配置日志库输出目标。生产环境使用 systemd 或 Docker 时,若未配置重启策略,进程退出后不会自动恢复。避免在 MCP 消息处理函数中同步执行耗时操作,防止阻塞事件循环触发看门狗超时。

常见问题

退出码 1 和 130 有什么区别

退出码 1 通常表示应用内部错误,130 表示进程被 Ctrl+C 中断。排查内部错误需查看 stderr 日志,中断信号则需检查信号处理逻辑。

为什么加了监听器进程还是退出

某些致命错误如内存溢出(OOM)无法通过 JavaScript 层监听捕获。需检查服务器内存限制或 Node.js 进程内存配置。

可以在 stdout 输出少量日志吗

不可以,MCP 客户端会尝试解析 stdout 的所有内容为 JSON-RPC 消息。任何非协议文本都会导致客户端解析错误并可能断开连接。

参考来源

  • Node.js Official Documentation - Process Events: https://nodejs.org/api/process.html
  • Model Context Protocol TypeScript SDK: https://github.com/modelcontextprotocol/typescript-sdk