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
