MCP server timeout errors occur when a tool call takes longer than the default 60-second limit. You will see 'Request timeout (-32001)' in the logs. Fix this by increasing the timeout via the MCP_SERVER_REQUEST_TIMEOUT environment variable, sending progress notifications for long-running operations, implementing pagination for large data returns, and optimizing slow API calls within your tools.
Fixing MCP Server Timeout Errors
When an MCP tool call takes too long, the host cancels the request and reports a timeout error. The default timeout is typically 60 seconds, which is sufficient for most operations but too short for tools that call slow external APIs, process large files, or perform complex computations. This tutorial covers multiple strategies for handling long-running operations without hitting timeout limits.
Prerequisites
- An MCP server with tools that are timing out
- Understanding of which tool calls are slow
- Access to your MCP host configuration
Step-by-step guide
Identify the timeout error in your logs
Identify the timeout error in your logs
The timeout error appears as a JSON-RPC error with code -32001 and a message like 'Request timeout'. Check your MCP host logs and server stderr output for this error. In Claude Desktop, check ~/Library/Logs/Claude/mcp*.log. In Cursor, check the Output panel under MCP. The error typically includes the tool name and the timeout duration, helping you identify which specific tool is too slow.
1# Error messages you might see:23# In MCP host logs:4# "Request timeout (-32001)"5# "Tool execution timed out after 60000ms"6# "MCP server request timed out"78# In JSON-RPC format:9# {10# "jsonrpc": "2.0",11# "error": {12# "code": -32001,13# "message": "Request timed out"14# },15# "id": 116# }1718# Check logs:19tail -n 50 ~/Library/Logs/Claude/mcp*.log | grep -i timeoutExpected result: You have identified the timeout error and know which tool call is triggering it.
Increase the timeout via environment variable
Increase the timeout via environment variable
The quickest fix is to increase the timeout duration. Set the MCP_SERVER_REQUEST_TIMEOUT environment variable in your host configuration's env block. The value is in milliseconds. Setting it to 120000 gives tools two minutes, and 300000 gives five minutes. This is a valid approach for tools that are inherently slow (like web scraping or complex API calls) and cannot be optimized further.
1// claude_desktop_config.json — increase timeout to 2 minutes2{3 "mcpServers": {4 "slow-api-server": {5 "command": "node",6 "args": ["./dist/index.js"],7 "env": {8 "MCP_SERVER_REQUEST_TIMEOUT": "120000",9 "API_KEY": "your-key"10 }11 }12 }13}Expected result: Tool calls have more time to complete before the host triggers a timeout.
Send progress notifications for long-running tools
Send progress notifications for long-running tools
The MCP protocol supports progress notifications that keep the host informed during long operations. By sending periodic progress updates, you signal that the tool is still working and prevent some hosts from timing out. This also provides a better user experience, showing the user what the tool is doing. Send progress notifications from within your tool handler using the server's notification mechanism.
1import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";2import { z } from "zod";34const server = new McpServer({5 name: "progress-demo",6 version: "1.0.0",7});89server.tool(10 "process-large-dataset",11 "Process a large dataset with progress updates",12 { datasetId: z.string() },13 async ({ datasetId }, { sendNotification }) => {14 const totalItems = 1000;1516 for (let i = 0; i < totalItems; i += 100) {17 // Process batch...18 await processBatch(datasetId, i, i + 100);1920 // Send progress notification21 await sendNotification({22 method: "notifications/progress",23 params: {24 progressToken: datasetId,25 progress: i + 100,26 total: totalItems,27 },28 });29 }3031 return {32 content: [{ type: "text", text: `Processed ${totalItems} items` }],33 };34 }35);Expected result: The host receives progress updates and shows them to the user while the tool continues processing.
Implement pagination for large results
Implement pagination for large results
If your tool returns large amounts of data, it may timeout during response serialization rather than during processing. Break large results into pages using a cursor parameter. The first call returns the first page plus a cursor for the next page. Subsequent calls use the cursor to fetch more data. This keeps individual tool calls fast while still allowing access to all the data.
1server.tool(2 "search-records",3 "Search records with pagination",4 {5 query: z.string(),6 cursor: z.string().optional(),7 pageSize: z.number().optional().default(50),8 },9 async ({ query, cursor, pageSize }) => {10 const offset = cursor ? parseInt(cursor, 10) : 0;11 const results = await searchDatabase(query, offset, pageSize);12 const hasMore = results.length === pageSize;13 const nextCursor = hasMore ? String(offset + pageSize) : null;1415 return {16 content: [17 {18 type: "text",19 text: JSON.stringify({20 results,21 nextCursor,22 hasMore,23 }, null, 2),24 },25 ],26 };27 }28);Expected result: Each tool call returns a manageable page of results within the timeout window.
Optimize slow external API calls
Optimize slow external API calls
Often the timeout is caused by a slow third-party API call within your tool. Add request timeouts with AbortController, implement caching for repeated queries, and consider parallelizing independent API calls. For tools that must call multiple APIs, set individual timeouts shorter than the MCP timeout to leave room for response serialization. If your MCP tools interact with complex API architectures and you need help optimizing performance, the RapidDev engineering team can assist with architecture design.
1// Add timeout to fetch calls within tools2async function fetchWithTimeout(3 url: string,4 timeoutMs: number = 300005): Promise<Response> {6 const controller = new AbortController();7 const timeout = setTimeout(() => controller.abort(), timeoutMs);89 try {10 const response = await fetch(url, { signal: controller.signal });11 return response;12 } finally {13 clearTimeout(timeout);14 }15}1617// Use in your tool handler18server.tool("api-call", "Call external API", { id: z.string() },19 async ({ id }) => {20 try {21 const response = await fetchWithTimeout(22 `https://api.example.com/items/${id}`,23 30000 // 30 second timeout for API call24 );25 const data = await response.json();26 return { content: [{ type: "text", text: JSON.stringify(data) }] };27 } catch (error) {28 if (error.name === "AbortError") {29 return {30 content: [{ type: "text", text: "API call timed out after 30s" }],31 isError: true,32 };33 }34 throw error;35 }36 }37);Expected result: External API calls fail gracefully within the timeout rather than causing the entire MCP request to timeout.
Complete working example
1import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";2import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";3import { z } from "zod";45const server = new McpServer({6 name: "timeout-resilient-server",7 version: "1.0.0",8});910// Helper: fetch with timeout11async function fetchWithTimeout(12 url: string,13 timeoutMs: number = 3000014): Promise<Response> {15 const controller = new AbortController();16 const timeout = setTimeout(() => controller.abort(), timeoutMs);17 try {18 return await fetch(url, { signal: controller.signal });19 } finally {20 clearTimeout(timeout);21 }22}2324// Tool with pagination25server.tool(26 "search",27 "Search with pagination",28 {29 query: z.string(),30 cursor: z.string().optional(),31 limit: z.number().optional().default(25),32 },33 async ({ query, cursor, limit }) => {34 const offset = cursor ? parseInt(cursor, 10) : 0;35 const url = `https://api.example.com/search?q=${query}&offset=${offset}&limit=${limit}`;36 const response = await fetchWithTimeout(url, 30000);37 const data = await response.json();3839 return {40 content: [41 {42 type: "text",43 text: JSON.stringify({44 results: data.items,45 nextCursor: data.hasMore ? String(offset + limit) : null,46 }, null, 2),47 },48 ],49 };50 }51);5253const transport = new StdioServerTransport();54await server.connect(transport);Common mistakes when fixing MCP server timeout errors
Why it's a problem: Setting the MCP timeout higher than the host's maximum allowed timeout
How to avoid: Check your host's documentation for maximum timeout values. Some hosts cap the timeout regardless of environment variable settings.
Why it's a problem: Not adding timeouts to fetch calls inside tool handlers
How to avoid: Always add a timeout to external HTTP requests using AbortController. A hanging fetch call will consume the entire MCP timeout.
Why it's a problem: Returning extremely large JSON responses that take time to serialize
How to avoid: Implement pagination to return smaller chunks. A 10MB JSON response may timeout during serialization even if the data is ready.
Why it's a problem: Running synchronous CPU-heavy operations that block the event loop
How to avoid: Use worker threads for CPU-intensive tasks or break them into async chunks with periodic await to keep the event loop responsive.
Best practices
- Set explicit timeouts on all external API calls shorter than the MCP timeout
- Implement pagination for tools that return more than 50 results
- Send progress notifications for operations expected to take more than 10 seconds
- Use the MCP_SERVER_REQUEST_TIMEOUT env var as a safety net, not the primary solution
- Cache repeated API responses to speed up subsequent calls
- Log timing information for tool calls to identify which tools need optimization
- Return partial results with a continuation cursor rather than failing on timeout
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
My MCP tool is timing out with 'Request timeout (-32001)'. The tool calls [describe API/operation] which takes about [X] seconds. Help me: 1) increase the timeout, 2) add progress notifications, and 3) implement pagination. Show me the TypeScript code.
My MCP server tool times out when processing large datasets. Add pagination support with a cursor parameter and progress notifications. The tool currently fetches all results in one call — refactor it to return pages of 50 items with a nextCursor field.
Frequently asked questions
What is the default MCP timeout?
Most MCP hosts default to 60 seconds (60000ms) per tool call. This can vary by host — Claude Desktop and Cursor use approximately 60 seconds. Check your host's documentation for the exact default.
Can I set different timeouts for different tools?
Not directly through the MCP protocol. The timeout is set per server, not per tool. If you have one slow tool and several fast ones, consider moving the slow tool to its own server with a higher timeout.
Do progress notifications actually prevent timeout?
This depends on the host implementation. Some hosts reset their timeout counter when they receive a progress notification. Others use a fixed timeout regardless. Progress notifications always improve user experience even if they do not affect the timeout.
What happens to in-progress work when a timeout occurs?
The host cancels the request, but the server may continue processing. There is no guaranteed cancellation mechanism in MCP. Design your tools to handle this gracefully — avoid leaving resources in a partially modified state.
Is there a maximum timeout I can set?
The MCP protocol does not define a maximum, but hosts may enforce their own limits. Setting a timeout over 5 minutes is generally not recommended as it creates a poor user experience.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation