LangChain4j 在 Windows 上跑 MCP stdio 报
JsonParseException: Unrecognized token 'Microsof...',看着像序列化问题,其实是子进程根本没起来。npx.cmd的路径带空格,命令又拼成单个字符串,进程启动失败,stderr 的中文报错被拿去当 JSON 解析。先查进程启动,别顺着 Jackson 查。
Issue & Env(案发现场与环境)
- 环境:Windows 11 + Spring Boot 3.5.3 + langchain4j 1.1.0-beta7。
- MCP server 通过
npx启动,命令路径是C:\Program Files\nodejs\npx.cmd,注意路径里有空格。 - 现象:应用启动时 MCP 连接失败,抛
JsonParseException。问题来自 langchain4j issue #3357。
StackTrace(堆栈日志)
issue #3357 原文:
2025-07-14T10:18:22.575+08:00 DEBUG 27572 --- [spring-ai-study] [ main] d.l.m.c.t.stdio.StdioMcpTransport : Starting process: [C:\Program Files\nodejs\npx.cmd, -y @smithery/cli@latest run @wonderwhy-er/desktop-commander --key 39b09f76-4bb9-4f04-b393-370129f56bcc]
2025-07-14T10:18:22.582+08:00 DEBUG 27572 --- [spring-ai-study] [ main] d.l.m.c.t.stdio.StdioMcpTransport : PID of the started process: 27784
2025-07-14T10:18:23.049+08:00 DEBUG 27572 --- [spring-ai-study] [ Thread-2] d.l.m.c.t.stdio.ProcessStderrHandler : [ERROR] 文件名、目录名或卷标语法不正确。
Exception in thread "Thread-1" java.lang.RuntimeException: com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'Microsof
看 Starting process: 那行,npx.cmd 的路径带着空格被打了进去。紧接着 ProcessStderrHandler 收到一行本地化中文报错,再往下就是 JsonParseException。
Reproduce(复现代码)
常见踩法:把整条命令拼成一个字符串传给 command(...):
val transport = StdioMcpTransport.Builder()
.command("C:\\Program Files\\nodejs\\npx.cmd -y @smithery/cli@latest run @wonderwhy-er/desktop-commander --key mykey")
.build()
Windows 上 npx 实际是 npx.cmd。单个字符串传给 command(...) 时,路径里的空格被错误处理,子进程起不来。
Root Cause(根因分析)
- 表象是 JSON 解析失败,根因在进程启动阶段。别顺着
JsonParseException去查序列化。 - Windows 上
npx是npx.cmd,装在C:\Program Files\这种带空格的目录下。命令以单个字符串拼进StdioMcpTransport.Builder().command(...),空格被错误拆分,子进程没真正起来,或参数被拆错。 - 子进程启动失败后,stderr 吐出本地化的中文错误「文件名、目录名或卷标语法不正确。」。
ProcessStderrHandler与协议帧解析没有把「非 JSON 的人类可读错误」和「JSON-RPC 消息」区分开,于是 Jackson 去解析一行根本不是 JSON 的文本,抛出JsonParseException: Unrecognized token 'Microsof...。 - 报错里的
'Microsof很怪。结合ProcessStderrHandler的输出看,被拿去解析的并不是协议帧,而是进程输出的片段。这一句的确切来源,issue 里没有给出完整后续,社区说法,截至撰写时未核实。 - 真正要抓的事实是「子进程没起来」。这层原因在 StdioMcpTransport 源码 的启动路径里就能看到。
The Fix(解决方案)
命令要拆成 program + 参数列表,不要把整条命令拼成一个字符串。Windows 上优先用 cmd.exe /c 包一层:
val transport: McpTransport = StdioMcpTransport.Builder()
.command(
listOf(
"cmd.exe", "/c", "npx", "-y",
"@smithery/cli@latest", "run",
"@wonderwhy-er/desktop-commander", "--key", "mykey"
)
)
.logEvents(true)
.build()
- 或者把 Node 装到没有空格的目录(如
C:\nodejs),用npx.cmd的绝对路径加独立 args。 - 永远给 MCP 进程加关闭钩子:进程内调用
close()(mcpClient.close())。 - 排查期间先手工验证
cmd.exe /c npx -y <包>能跑起来,再交给 LangChain4j。 - 打开
.logEvents(true)。先看Starting process:那行打印的命令长什么样,再看ProcessStderrHandler的真实 stderr。这两行比JsonParseException有用得多。 - 跨平台建议:生产上优先把 MCP server 换成 SSE 或 Streamable HTTP 端点。stdio transport 在 Windows 上问题最多。
小结
JsonParseException: Unrecognized token 'Microsof...是表象,进程没起来才是根因。- Windows 下命令必须拆成 program + 参数列表,路径带空格就上
cmd.exe /c。 - 排查先开
.logEvents(true),看Starting process:和ProcessStderrHandler两行。
References(参考来源)
- windows use mcp error — langchain4j issue #3357github-issue
- MCP — LangChain4j 官方文档langchain4j-docs
- MCP stdio server — LangChain4j 官方文档langchain4j-docs
- McpTransport / StdioMcpTransport 源码 — langchain4jgithub
