How ConnectionManager Handles TCP Connection Timeouts and Failures in revit-mcp
The ConnectionManager in revit-mcp implements a dual-layer timeout strategy: a 5-second connection establishment timeout in withRevitConnection and a 2-minute per-command timeout in RevitClientConnection, with comprehensive error handling via socket event listeners and automatic resource cleanup.
The revit-mcp repository provides a Model Context Protocol (MCP) server for Autodesk Revit, requiring robust TCP communication handling between the Node.js server and the Revit client. Understanding how the ConnectionManager handles TCP connection timeouts and failures is critical for building reliable integrations that gracefully manage network instability or unresponsive Revit instances.
Connection Establishment Timeout Handling
The primary entry point for TCP connectivity is the withRevitConnection function defined in src/utils/ConnectionManager.ts. This utility implements a Promise-based wrapper that enforces strict timeout boundaries during the initial handshake phase.
The withRevitConnection Wrapper
When a tool requires Revit connectivity, it invokes withRevitConnection with an async operation callback. The function instantiates a new RevitClientConnection targeting localhost:8080 and attempts to establish the socket connection:
const revitClient = new RevitClientConnection("localhost", 8080);
If the socket is not already connected, the function creates a Promise that manages the connection lifecycle through event listeners and timeout guards.
Timeout Guard Implementation
The connection mechanism implements a 5-second timeout using setTimeout to prevent indefinite hanging during TCP handshake attempts. The architecture follows this sequence:
- Listener Attachment: Registers
connectanderrorevent handlers on the socket instance - Connection Initiation: Calls
revitClient.connect()to start the TCP handshake - Timeout Scheduling: Creates a 5-second timer that monitors for successful connection
- Resolution Logic: If the timer expires before connection or error events fire, it removes all listeners and rejects the Promise with
new Error("连接到Revit客户端失败")
Upon successful connection, the timer is cleared, listeners are removed, and the supplied operation executes with the connected client.
Error Event Handling
Socket-level errors during connection attempts are captured through the error event listener. When the underlying TCP socket emits an error (such as connection refused, network unreachable, or reset by peer), the handler:
- Clears the 5-second timeout timer
- Removes the event listeners to prevent memory leaks
- Rejects the Promise with
new Error("connect to revit client failed")
This ensures that connection failures surface immediately to the calling tool, allowing for appropriate error messaging or retry logic at the application level.
Command-Level Timeout Protection
Once the TCP connection is established, the RevitClientConnection class in src/utils/SocketClient.ts manages individual JSON-RPC command execution with independent timeout safeguards.
RevitClientConnection Architecture
The socket client maintains connection state through an isConnected flag and implements error handling at the socket level. The socket's error event handler logs the error details and sets isConnected = false, ensuring that subsequent operations recognize the broken connection and trigger reconnection logic through withRevitConnection.
Per-Command Timeout Mechanism
Each command sent via sendCommand receives a 2-minute timeout (120,000 milliseconds) to prevent indefinite waiting for Revit processing. The implementation uses a setTimeout that monitors the responseCallbacks Map:
setTimeout(() => {
if (this.responseCallbacks.has(requestId)) {
this.responseCallbacks.delete(requestId);
reject(new Error(`Command timed out after 2 minutes: ${command}`));
}
}, 120000);
If the Revit client fails to respond within the 2-minute window, the callback is removed from the Map and the Promise rejects with a descriptive timeout message. This protects the MCP server from hanging when Revit is busy processing complex geometry or when the add-in becomes unresponsive.
Complete Error Handling Flow
The revit-mcp connection architecture implements a comprehensive error handling strategy that addresses both transient network issues and application-level failures.
Socket Error Recovery
When the underlying TCP socket encounters an error after successful connection, the error handler in RevitClientConnection performs immediate state cleanup:
- Logs the error details for debugging
- Sets
isConnected = falseto mark the connection as invalid - Allows
withRevitConnectionto detect the disconnected state on subsequent calls and initiate a fresh connection attempt
This recovery mechanism ensures that a single socket error does not permanently disable the integration, instead allowing for automatic reconnection when the next tool operation requires Revit access.
Cleanup and Resource Management
Both connection and command operations implement rigorous cleanup protocols to prevent memory leaks and resource exhaustion:
- Listener Removal: Event listeners for
connectanderrorevents are explicitly removed after resolution or timeout - Timer Clearing: All
setTimeoutinstances are cleared usingclearTimeoutbefore Promise resolution - Socket Disconnection: The
finallyblock inwithRevitConnectionensuresrevitClient.disconnect()executes regardless of operation success or failure, properly closing the TCP socket
These cleanup measures are critical for long-running MCP server processes that may handle hundreds of Revit operations without restarting.
Practical Implementation Example
The following example demonstrates robust error handling when retrieving wall elements from Revit:
import { withRevitConnection } from "./utils/ConnectionManager.js";
async function listAllWalls() {
// Attempts connection with 5-second timeout
return await withRevitConnection(async (client) => {
// This call will timeout after 2 minutes if Revit does not respond
const walls = await client.sendCommand("GetAllWalls");
return walls;
});
}
listAllWalls()
.then((walls) => console.log("Retrieved walls:", walls))
.catch((err) => {
// Catches both connection timeouts (5s) and command timeouts (2min)
console.error("Revit operation failed:", err.message);
});
This pattern ensures that network interruptions, unresponsive Revit instances, or long-running geometry operations all surface as actionable errors rather than hanging the MCP server indefinitely.
Summary
- ConnectionManager in
src/utils/ConnectionManager.tsprovides thewithRevitConnectionwrapper that enforces a 5-second timeout during TCP handshake and handles connection errors via socket event listeners. - RevitClientConnection in
src/utils/SocketClient.tsmanages the TCP socket state and implements a 2-minute timeout for individual JSON-RPC commands to prevent indefinite waiting. - Error recovery includes immediate socket error logging,
isConnectedstate reset, and automatic cleanup of timers and event listeners to prevent memory leaks. - Resource management guarantees socket disconnection through
finallyblocks, ensuring TCP connections close properly regardless of operation success or failure.
Frequently Asked Questions
How long does the ConnectionManager wait before timing out a TCP connection attempt?
The ConnectionManager waits 5 seconds before timing out a TCP connection attempt. This timeout is implemented in src/utils/ConnectionManager.ts using setTimeout within the withRevitConnection function. If the socket does not emit either a connect or error event within this window, the Promise rejects with the error message "连接到Revit客户端失败" (Connection to Revit client failed).
What is the difference between connection timeouts and command timeouts in revit-mcp?
Connection timeouts (5 seconds) occur during the initial TCP handshake phase when withRevitConnection attempts to establish a socket connection to the Revit client. Command timeouts (2 minutes or 120,000 milliseconds) occur after a successful connection, when RevitClientConnection.sendCommand waits for a JSON-RPC response from Revit. The connection timeout prevents hanging during network setup, while the command timeout prevents indefinite waiting when Revit is processing complex operations or becomes unresponsive.
How does revit-mcp handle socket errors after a successful connection?
When a socket error occurs after connection, the RevitClientConnection class in src/utils/SocketClient.ts handles it through the socket's error event listener. The handler logs the error details for debugging and immediately sets isConnected = false. This state change ensures that subsequent calls to withRevitConnection detect the broken connection and initiate a fresh TCP handshake rather than attempting to use the dead socket. The error does not crash the server; instead, it triggers the reconnection logic on the next operation.
What cleanup mechanisms prevent memory leaks during connection failures?
The ConnectionManager implements several cleanup mechanisms to prevent memory leaks. First, all event listeners (connect and error) are explicitly removed using socket.removeListener after the Promise resolves, rejects, or times out. Second, all setTimeout timers are cleared using clearTimeout before any Promise resolution. Third, the finally block in withRevitConnection guarantees that revitClient.disconnect() executes regardless of success or failure, closing the TCP socket and releasing system resources. These measures are essential for the long-running MCP server process.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →