Understanding MCP Server Timeout Errors
Timeout errors are among the most frustrating MCP issues because they can appear intermittently and have multiple root causes. When an MCP client sends a request to a server and does not receive a response within the expected time window, it terminates the connection and throws a timeout error. Unlike connection refused errors (which fail immediately) or ENOENT errors (which prevent startup), timeouts mean the server started and received the request but was too slow to respond.
Timeout errors typically manifest with messages like Request timed out after 30000ms, Server failed to respond within timeout period, or MCP connection timed out. The exact message varies by client, but the underlying problem is always the same: the server took too long. Understanding whether the timeout is caused by slow server startup, network latency, large response payloads, or misconfigured timeout limits is the first step toward a permanent fix.
Common Causes of Timeout Errors
1. Slow Server Startup
Many MCP servers need to initialize connections to databases, load large models, compile TypeScript, or download dependencies before they can respond to any request. If initialization takes longer than the client's connection timeout (typically 10-30 seconds), you will see a timeout on the very first request. This is especially common with servers that connect to remote APIs, load machine learning models, or use npx for the first time (which downloads the package).
Symptoms: Timeout occurs immediately when the client starts. Subsequent restarts sometimes work because dependencies are cached. The server logs show initialization steps still in progress when the client gives up.
2. Network Latency
When using Streamable HTTP transport instead of stdio, network conditions directly affect response times. High latency connections, VPN tunnels, corporate proxy servers, or remote servers across geographic regions can push response times beyond the timeout threshold. This is particularly common in corporate environments with strict network policies or when using MCP servers deployed in the cloud.
3. Large Response Payloads
MCP servers that return large datasets - such as database query results with thousands of rows, recursive file system listings, or full web page content from scraping - may exceed the timeout while the response is still being serialized and transmitted. A query returning 10,000 rows or a directory listing with 50,000 files can easily take 30+ seconds to process and format.
4. Resource Contention
On machines running many MCP servers simultaneously, CPU and memory contention can slow individual server responses. A garbage collection pause in one Node.js server can delay its response by several seconds. When this combines with other overhead, the total response time may exceed the timeout threshold.
Client Timeout Configuration
Each MCP client has its own default timeout, and they vary significantly. Knowing the defaults and how to adjust them is essential for working with slow servers:
| Client | Connection Timeout | Request Timeout | Configurable? | How to Configure |
|---|---|---|---|---|
| Claude Desktop | 30s | 30s | Not directly | Optimize server speed instead |
| Cursor | 30s | 30s | Yes | Settings, MCP section |
| VS Code (Copilot) | 60s | 60s | Yes | settings.json or .vscode/mcp.json timeout field |
| Windsurf | 30s | 30s | Yes | MCP config timeout field |
| Claude Code (CLI) | 60s | 60s | Yes | settings.json |
Configuring VS Code Timeouts
In VS Code, add a timeout field to your server configuration in .vscode/mcp.json or settings.json:
// .vscode/mcp.json
{
"servers": {
"my-slow-server": {
"command": "node",
"args": ["server.js"],
"timeout": 120000
}
}
}
// Or in settings.json
{
"mcp": {
"servers": {
"my-slow-server": {
"command": "node",
"args": ["server.js"],
"timeout": 120000
}
}
}
}
Working Around Claude Desktop's Fixed Timeout
Claude Desktop does not expose a direct timeout setting. The only approach is to make your server respond within 30 seconds. Strategies include lazy initialization, caching, response pagination, and implementing server-side timeouts that return partial results before the client gives up. See the server-side code examples below.
Server-Side Timeout Handling
The best way to prevent timeout errors is to handle them on the server side. Implement server-side timeouts that return a graceful error response before the client's timeout expires, chunked responses for large data, and progress reporting where supported.
TypeScript Server - Timeout with AbortController
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
const server = new McpServer({
name: "timeout-resilient-server",
version: "1.0.0",
});
server.tool(
"long-running-query",
"Executes a database query with built-in timeout protection",
{
query: z.string().describe("The SQL query to execute"),
timeoutMs: z.number().optional().describe("Custom timeout in ms (default 25000)"),
},
async ({ query, timeoutMs = 25000 }) => {
// Use 25s default to respond before 30s client timeout
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), timeoutMs);
try {
const result = await executeQuery(query, {
signal: controller.signal,
});
return {
content: [{ type: "text", text: JSON.stringify(result) }],
};
} catch (error) {
if (error instanceof DOMException && error.name === "AbortError") {
return {
content: [{
type: "text",
text: `Query timed out after ${timeoutMs}ms. Try:
- Adding LIMIT to your query
- Narrowing WHERE conditions
- Increasing timeoutMs parameter`,
}],
isError: true,
};
}
throw error;
} finally {
clearTimeout(timeout);
}
}
);
// Paginated results for large datasets
server.tool(
"paginated-query",
"Executes a query with pagination to avoid timeouts on large results",
{
query: z.string().describe("The SQL query to execute"),
page: z.number().optional().describe("Page number (default 1)"),
pageSize: z.number().optional().describe("Results per page (default 100)"),
},
async ({ query, page = 1, pageSize = 100 }) => {
const offset = (page - 1) * pageSize;
const paginatedQuery = `${query} LIMIT ${pageSize} OFFSET ${offset}`;
const result = await executeQuery(paginatedQuery);
const totalCount = await executeQuery(`SELECT COUNT(*) as count FROM (${query}) sub`);
return {
content: [{
type: "text",
text: JSON.stringify({
data: result,
page,
pageSize,
totalPages: Math.ceil(totalCount[0].count / pageSize),
totalResults: totalCount[0].count,
}),
}],
};
}
);
Python Server - asyncio Timeout and Retry
import asyncio
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("timeout-resilient-server")
@mcp.tool()
async def long_running_query(query: str, timeout_seconds: int = 25) -> str:
"""Executes a database query with configurable timeout.
Args:
query: The SQL query to execute
timeout_seconds: Maximum seconds to wait (default 25, under client timeout)
"""
try:
result = await asyncio.wait_for(
execute_query(query),
timeout=timeout_seconds,
)
return str(result)
except asyncio.TimeoutError:
return (
f"Query timed out after {timeout_seconds}s. "
"Try narrowing your query with WHERE clauses or LIMIT, "
"or increase the timeout_seconds parameter."
)
@mcp.tool()
async def resilient_api_call(
url: str,
max_retries: int = 3,
timeout_per_attempt: int = 8
) -> str:
"""Makes an API call with retry logic and per-attempt timeouts.
Args:
url: The API endpoint to call
max_retries: Number of retry attempts (default 3)
timeout_per_attempt: Seconds per attempt (default 8)
"""
import aiohttp
last_error = None
for attempt in range(max_retries):
try:
async with aiohttp.ClientSession() as session:
async with session.get(
url, timeout=aiohttp.ClientTimeout(total=timeout_per_attempt)
) as response:
return await response.text()
except (asyncio.TimeoutError, aiohttp.ClientError) as e:
last_error = str(e)
if attempt < max_retries - 1:
await asyncio.sleep(1) # Brief pause before retry
return f"Failed after {max_retries} attempts. Last error: {last_error}"
@mcp.tool()
async def chunked_file_read(path: str, max_chars: int = 50000) -> str:
"""Reads a file with size limit to avoid timeout on large files.
Args:
path: Path to the file to read
max_chars: Maximum characters to return (default 50000)
"""
import aiofiles
content = []
total = 0
async with aiofiles.open(path, "r") as f:
while True:
chunk = await f.read(8192)
if not chunk:
break
content.append(chunk)
total += len(chunk)
if total >= max_chars:
content.append(f"\n... truncated at {max_chars} chars. File is larger.")
break
return "".join(content)
Actual Timeout Error Messages
Here are real timeout error messages you might encounter, along with what each means and which client produces them:
# Claude Desktop
Error: MCP server "my-server" failed to respond within 30000ms
Connection to MCP server timed out
# Cursor
[MCP] Request to "my-server" timed out after 30s
Error: Server did not respond in time
# VS Code
MCP server connection timed out. The server at "my-server" did not respond.
TimeoutError: MCP request exceeded 60000ms limit
# Generic stdio transport
Error: Timeout waiting for server response on stdin/stdout
ETIMEDOUT: Server process did not produce output within deadline
Each of these points to the same root cause - the server did not respond fast enough - but the fix depends on why it was slow. The server-side timeout code above returns a useful error message before the client timeout expires, which gives the user actionable information instead of a generic timeout error.
Performance Optimization Strategies
Beyond configuring timeouts, these strategies help your servers respond faster and avoid timeouts entirely:
- Lazy initialization: Do not connect to databases or load models until the first request that needs them. Return tool listings immediately during server startup. This prevents startup timeouts.
- Connection pooling: Reuse database connections and HTTP clients instead of creating new ones per request. Opening a new database connection can take 100-500ms each time.
- Response pagination: If a tool returns large datasets, implement pagination so each response stays small. Let the AI request additional pages as needed. A page of 100 results responds in under 1 second; 10,000 results may take 30+ seconds.
- Caching: Cache frequently requested data with a reasonable TTL. If a tool returns the same result for the same input within a short window, serve the cached version instantly.
- Health checks: Implement a simple health check tool that returns immediately, allowing clients to verify the server is alive without triggering a full operation.
- Async initialization: If your server needs to load data from external sources, do it in the background after the server starts accepting connections. Report a "still initializing" status for tools that depend on the loaded data.
For more on running multiple servers without hitting resource limits, see our how many MCP servers guide.
Common Pitfalls
- Setting timeouts too high: A 5-minute timeout means users wait 5 minutes before seeing an error. Better to fail fast with a useful message and let users retry with a narrower query.
- Ignoring startup time: Your server might start fine on your fast machine but timeout on a user's older laptop. Test with cold starts on modest hardware.
- Not handling AbortSignal: If you do not pass abort signals to downstream operations (database queries, HTTP requests), your server keeps working even after the client has given up, wasting resources.
- Blocking the event loop (Node.js): Synchronous file operations or CPU-intensive work (JSON.parse on large objects, crypto operations) blocks the event loop, preventing the server from responding to any requests including keepalive pings.
- Missing error responses: If your server throws an unhandled exception instead of returning a structured error response, the client sees a timeout instead of a useful error message.
- Server-side timeout greater than client-side: If your server-side timeout is 60 seconds but the client gives up at 30 seconds, the server's timeout is meaningless. Always set server-side timeouts a few seconds shorter than the client timeout.
Security Considerations
Timeout configuration has security implications. Servers without proper timeout limits can be exploited for resource exhaustion attacks - a malicious prompt could trigger a query that runs forever, consuming CPU and memory. Always set reasonable upper bounds on timeout values, even when making them configurable via tool parameters. Cap user-specified timeouts at a maximum value (for example, 120 seconds). For a deeper dive into MCP server security, see our MCP server security guide.