尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

【Bug已解决】MCP error -32001 (Request timed out) when connecting Claude to Node.js MCP server 解决方案

【Bug已解决】MCP error -32001 (Request timed out) when connecting Claude to Node.js MCP server 解决方案 【Bug已解决】MCP error -32001 (Request timed out) when connecting Claude to Node.js MCP server 解决方案一、现象长什么样你把自写的 Node.js MCP server 接进 Claude调用工具时收到MCP error -32001 (Request timed out)或Request timed out在工具调用几秒后返回Claude 侧显示工具超时但你的 Node server 其实收到了请求、只是还没返回有时 server 完全没收到请求卡在初始化握手阶段本地直连 server用mcpCLI 或 inspector有时正常一接 Claude 就超时工具逻辑里若有阻塞同步操作大文件读取、同步网络请求更容易触发。一句话MCP 客户端Claude在规定时间内没收到 server 的响应于是按 JSON-RPC 协议返回 -32001 Request timed out——本质是 server 响应太慢或根本没响应。二、背景MCP 基于 JSON-RPC 2.0客户端每发一个请求如tools/call都期待一个响应。协议层通常有一个超时若 server 在超时窗口内未回response或progress客户端就主动判定Request timed out-32001。Node.js MCP server 常见超时来源初始化握手慢server 启动要做重活加载大模型、连库initialize阶段就超时工具调用是同步阻塞在request处理器里写了fs.readFileSync大文件、或axios同步等待外部 API事件循环被卡住响应发不出去忘了await/ 返回 Promisehandler 里漏了return客户端永远等不到结果server 抛错但没回复错误响应异常未被捕获请求悬空。三、根因根因是server 在超时窗口内未能返回合法的 JSON-RPC 响应// 伪代码问题 handler server.setRequestHandler(CallToolRequestSchema, async (req) { // 同步阻塞事件循环卡住超时 const data fs.readFileSync(/huge/file); // 阻塞 // 或漏了 return / await doSomethingAsync(req); // 没 return客户端等不到 response });修复方向把阻塞操作改成异步、确保 handler 一定return一个响应、给慢操作加进度通知progress、或在客户端侧调大超时。四、最小可运行复现下面用 Node 模拟handler 忘记 return导致请求永不响应// 用 modelcontextprotocol/sdk 的简化示意 const handlers {}; function register(name, fn) { handlers[name] fn; } // bugasync 里没 return register(tools/call, async (req) { await new Promise(r setTimeout(r, 100)); // 漏了 return 响应 - 客户端超时 const result { content: [{ type: text, text: done }] }; // 应该 return result; 但忘了 }); // 模拟客户端等待 超时判定 function clientCall(timeoutMs) { return new Promise((resolve, reject) { const t setTimeout(() reject(new Error(MCP error -32001 (Request timed out))), timeoutMs); Promise.resolve(handlers[tools/call]({})).then(res { clearTimeout(t); resolve(res); }); }); } clientCall(50).catch(e console.log(ERR:, e.message));运行后因为 handler 没返回客户端在 50ms 后抛-32001 Request timed out。五、解决方案第一层最小直接修复最小修复确保 handler异步化且一定返回响应并给客户端合理超时import { readFile } from fs/promises; server.setRequestHandler(CallToolRequestSchema, async (req) { // 1. 用异步 API不阻塞事件循环 const data await readFile(/huge/file, utf8); // 2. 务必 return 响应 return { content: [{ type: text, text: String(data.length) }], }; });客户端侧如 Claude Desktop 配置若 server 确实慢可确认是否有超时配置可调部分 MCP 客户端支持设置更长超时。同时在慢操作里发进度通知await server.sendProgress({ progressToken: req.meta?.progressToken, progress: 0.5, total: 1 });六、解决方案第二层结构化改进把工具调用超时治理抽成策略集中管理超时、异步化、以及必须返回响应from dataclasses import dataclass, field from typing import Awaitable, Callable, Dict, Any dataclass(frozenTrue) class McpTimeout32001Policy: MCP 工具调用策略防 -32001 超时。 规则 - 每个 handler 必须返回响应禁止漏 return - 阻塞操作必须异步化调用方用 asyncio.to_thread - 慢操作发进度通知重置客户端超时预期 client_timeout_ms: int 10_000 async def run_handler( self, handler: Callable[[Dict], Awaitable[Dict]], req: Dict, slow_work: Callable[[], Any] None, ) - Dict: # 若有阻塞工作丢到线程池避免卡事件循环 import asyncio if slow_work is not None: await asyncio.to_thread(slow_work) result await handler(req) if not isinstance(result, dict) or content not in result: raise ValueError(handler 必须返回含 content 的响应对象) return result def ensure_returns(self, handler) - Callable: async def wrapped(req): res await handler(req) assert res is not None, handler 禁止返回 None会导致 -32001 return res return wrapped def demo() - None: policy McpTimeout32001Policy() wrapped policy.ensure_returns(lambda req: {content: [{type: text, text: ok}]}) import asyncio print(asyncio.run(policy.run_handler(wrapped, {}))) if __name__ __main__: demo()七、解决方案第三层断言 / CI 守护import asyncio import pytest from your_module import McpTimeout32001Policy def test_handler_must_return_content(): policy McpTimeout32001Policy() async def bad(req): return {no_content: 1} with pytest.raises(ValueError): asyncio.run(policy.run_handler(bad, {})) def test_handler_none_rejected(): policy McpTimeout32001Policy() wrapped policy.ensure_returns(lambda req: None) with pytest.raises(AssertionError): asyncio.run(wrapped({})) def test_valid_handler_ok(): policy McpTimeout32001Policy() async def good(req): return {content: [{type: text, text: ok}]} res asyncio.run(policy.run_handler(good, {})) assert res[content][0][text] ok def test_slow_work_offloaded(): import time policy McpTimeout32001Policy() def blocking(): time.sleep(0.01) async def h(req): return {content: [{type: text, text: done}]} res asyncio.run(policy.run_handler(h, {}, slow_workblocking)) assert res[content]把这些测试加进 MCP server 的 CI确保每个工具 handler 都返回合规响应从根上杜绝 -32001。八、排查清单server 是否收到了请求看 server 日志没收到说明卡在初始化握手。handler 里是否有同步阻塞readFileSync / 同步网络改成异步。handler 是否return了响应漏 return 是超时头号原因。是否抛了异常却没回复错误响应异常要转成 JSON-RPC error 返回。慢操作是否发了 progress 通知没进度客户端会判超时。客户端超时是否可调大确认 MCP 客户端配置。是否用 inspector 单独测过 server先排除 server 自身问题。九、小结MCP error -32001 (Request timed out)是客户端在超时窗口内没收到 server 的 JSON-RPC 响应。根因几乎都在 server 侧handler 同步阻塞、漏了return、异常未回复、或初始化过慢。最小修复是异步化阻塞操作、确保 handler 一定返回含content的响应、给慢操作发 progress结构化做法是抽成McpTimeout32001Policy集中治理超时与响应合规最后用 pytest 守护handler 必须返回合规响应从根上消除 -32001。
返回列表