Skip to main content
Glama
masx200

Persistent Terminal MCP Server

by masx200
STDIO_FIX.md5.64 kB
# MCP Stdio 通道纯净性修复 ## 问题描述 在 Cursor 等 MCP 客户端中使用此 MCP 服务器时,会出现以下错误: ``` [error] Client error for command Unexpected token 'T', "Terminal c"... is not valid JSON ``` 这个错误会导致: - Cursor 无法正确解析 MCP 服务器的响应 - 执行几个命令后 Cursor 会卡住 - MCP 工具调用失败 ## 根本原因 MCP 协议使用 **stdio (标准输入/输出)** 进行 JSON-RPC 通信。这要求: 1. **stdout (标准输出)** 必须**只包含纯净的 JSON-RPC 消息** 2. 任何日志、调试信息都应该输出到 **stderr (标准错误)** 3. 不能有任何非 JSON 格式的文本污染 stdout ### 问题代码位置 修复前,以下位置存在问题: 1. **src/index.ts** - `console.error()` 在某些情况下会输出到 stdout - 错误处理中的日志输出 2. **src/mcp-server.ts** - `setupEventHandlers()` 中使用 `console.log()` 输出事件日志 - `shutdown()` 中使用 `console.log()` 输出关闭信息 3. **src/terminal-manager.ts** - `cleanupTimeoutSessions()` 中使用 `console.log()` - `shutdown()` 中使用 `console.log()` 和 `console.error()` ## 修复方案 ### 1. 统一日志输出策略 所有日志输出都改为使用 `process.stderr.write()`,确保不污染 stdout: ```typescript // 修复前 console.log("Terminal created:", terminalId); console.error("Error:", error); // 修复后 if (process.env.MCP_DEBUG === "true") { process.stderr.write(`[MCP-DEBUG] Terminal created: ${terminalId}\n`); } process.stderr.write(`[MCP-ERROR] Error: ${error}\n`); ``` ### 2. 调试模式控制 通过环境变量 `MCP_DEBUG` 控制调试日志的输出: ```typescript function log(message: string) { if (process.env.MCP_DEBUG === "true") { process.stderr.write(`[MCP-DEBUG] ${message}\n`); } } ``` ### 3. 修复的文件 #### src/index.ts - ✅ 修改 `log()` 函数使用 `process.stderr.write()` - ✅ 修改错误处理中的日志输出 - ✅ 修改未捕获异常的日志输出 #### src/mcp-server.ts - ✅ 修改 `setupEventHandlers()` 中的所有日志输出 - ✅ 修改 `shutdown()` 中的日志输出 - ✅ 添加 `MCP_DEBUG` 环境变量检查 #### src/terminal-manager.ts - ✅ 修改 `cleanupTimeoutSessions()` 中的日志输出 - ✅ 修改 `shutdown()` 中的所有日志输出 ## 测试验证 ### 测试 1: Stdio 通道纯净性测试 运行 `test-mcp-stdio.mjs` 验证 stdout 只包含 JSON-RPC 消息: ```bash node test-mcp-stdio.mjs ``` **预期结果:** ``` ✅ 测试通过!stdout 通道纯净,只有 JSON-RPC 消息 收到的 JSON-RPC 消息数量: 2 收到的非 JSON 行数: 0 ``` ### 测试 2: Cursor 使用场景测试 运行 `test-cursor-scenario.mjs` 模拟 Cursor 的实际使用场景: ```bash node test-cursor-scenario.mjs ``` **预期结果:** ``` ✅ 所有测试通过!MCP 服务器工作正常,stdout 通道纯净 通过: 7 失败: 0 非 JSON 输出行数: 0 ``` 测试覆盖的操作: 1. ✅ 初始化连接 2. ✅ 列出可用工具 3. ✅ 创建终端 4. ✅ 写入命令 5. ✅ 读取输出 6. ✅ 列出所有终端 7. ✅ 终止终端 ## 使用说明 ### 正常模式(生产环境) 默认情况下,服务器不会输出任何调试日志: ```bash node dist/index.js ``` ### 调试模式 如果需要查看调试日志,设置 `MCP_DEBUG=true`: ```bash MCP_DEBUG=true node dist/index.js ``` 调试日志会输出到 stderr,不会影响 MCP 通信。 ### 在 Cursor 中使用 更新 `mcp-config.toml` 或 Cursor 的 MCP 配置: ```toml [mcp_servers.persistent-terminal] command = "node" args = ["/path/to/node-pty/dist/index.js"] [mcp_servers.persistent-terminal.env] MAX_BUFFER_SIZE = "10000" SESSION_TIMEOUT = "86400000" # MCP_DEBUG = "true" # 取消注释以启用调试模式 ``` ## 技术细节 ### 为什么使用 process.stderr.write() 而不是 console.error()? 1. **console.error()** 在某些环境下可能会输出到 stdout 2. **process.stderr.write()** 明确保证输出到 stderr 3. 更精确的控制,避免意外的格式化或缓冲问题 ### 日志格式 所有日志都使用统一的前缀: - `[MCP-DEBUG]` - 调试信息(仅在 MCP_DEBUG=true 时输出) - `[MCP-ERROR]` - 错误信息(始终输出到 stderr) ### MCP 协议要求 根据 MCP (Model Context Protocol) 规范: > When using stdio transport, the server MUST only write JSON-RPC messages to stdout. > All logging and debugging output MUST be written to stderr. 参考:https://spec.modelcontextprotocol.io/specification/basic/transports/#stdio ## 影响范围 ### 修复后的改进 ✅ **兼容性** - 完全兼容 Cursor 和其他严格的 MCP 客户端 - 符合 MCP 协议规范 ✅ **稳定性** - 不会因为日志输出导致 JSON 解析错误 - 不会卡住客户端 ✅ **可调试性** - 保留了调试日志功能 - 通过环境变量灵活控制 ### 向后兼容性 ✅ **完全向后兼容** - API 没有任何变化 - 功能完全相同 - 只是修复了日志输出方式 ## 总结 这次修复解决了 MCP 服务器在 Cursor 等客户端中使用时的关键问题。通过确保 stdout 通道的纯净性,服务器现在可以: 1. ✅ 在 Cursor 中正常工作,不会卡住 2. ✅ 符合 MCP 协议规范 3. ✅ 保持良好的调试能力 4. ✅ 提供稳定可靠的服务 ## 相关文件 - `src/index.ts` - 主入口文件 - `src/mcp-server.ts` - MCP 服务器实现 - `src/terminal-manager.ts` - 终端管理器 - `test-mcp-stdio.mjs` - Stdio 纯净性测试 - `test-cursor-scenario.mjs` - Cursor 场景测试

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/masx200/persistent-terminal-mcp'

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