How the SocketClient Handles Command Responses in Revit MCP: Asynchronous TCP Communication Deep Dive

The SocketClient handle command responses mechanism in the Revit MCP architecture relies on a robust JSON‑RPC‑style protocol layered over raw TCP sockets. By coupling unique request identifiers with promise‑based callbacks, the RevitClientConnection class enables reliable, non‑blocking communication between the MCP server and Autodesk Revit, even when network fragmentation splits messages into multiple packets.

The Core Mechanism: Request IDs and Callback Registration

When invoking sendCommand, the SocketClient first generates a unique request ID via generateRequestId and immediately stores a resolver function in an internal responseCallbacks map keyed by that ID. This registration (lines 111‑130 in src/utils/SocketClient.ts) ensures that when the server eventually echoes the same ID back, the client can match the response to the original caller. The callback wraps a Promise, allowing sendCommand to return immediately while waiting asynchronously for Revit's reply.

Buffering and Parsing TCP Streams

TCP sockets do not guarantee intact JSON delivery; messages may arrive fragmented. The client addresses this with a buffering strategy outlined in lines 23‑27:

Step Implementation Explanation
Buffering socket.on("data", …) → this.buffer += dataString (lines 23‑27) Accumulates partial data fragments until a complete JSON object is available.
Parsing processBuffer() (lines 42‑52) Attempts JSON.parse(this.buffer). Success means the buffer contains a full response; otherwise, the client waits for additional data.
Dispatching handleResponse(this.buffer) (lines 46‑48) → const requestId = response.id || "default" (lines 80‑81) → this.responseCallbacks.get(requestId) (line 83) Maps the echoed id to the stored callback.
Callback Execution Callback defined inside sendCommand (lines 111‑130) Parses the JSON and resolves the Promise with response.result or rejects on response.error.
Timeout setTimeout (lines 135‑141) If no response arrives within 2 minutes, the callback is purged and the Promise rejects, preventing memory leaks.

Real‑World Usage Examples

Sending a Command and Awaiting Results

The following pattern demonstrates how SocketClient handle command responses in practice:

import { withRevitConnection } from "./utils/ConnectionManager.js";

async function getProjectInfo() {
  return await withRevitConnection(async (client) => {
    // `sendCommand` returns a Promise that resolves when the callback runs
    const result = await client.sendCommand("GetProjectInfo", { /* params */ });
    console.log("Project info:", result);
    return result;
  });
}

Key points demonstrated

  • withRevitConnection guarantees the socket is connected before invoking sendCommand.
  • sendCommand internally registers a callback (responseCallbacks.set) and resolves the promise when the server echoes the same id.

Executing Multiple Commands Concurrently

Because each SocketClient handle command responses operation is independent, you can parallelize calls safely:

import { withRevitConnection } from "./utils/ConnectionManager.js";

async function parallelQueries() {
  return await withRevitConnection(async (client) => {
    const promises = [
      client.sendCommand("GetElementIds", { viewId: 1 }),
      client.sendCommand("GetFamilyTypes", { familyName: "Door" }),
      client.sendCommand("GetCurrentViewInfo")
    ];
    const [ids, types, viewInfo] = await Promise.all(promises);
    console.log({ ids, types, viewInfo });
  });
}

How it works

  • Each call generates its own request ID and stores a distinct callback.
  • Responses may arrive in any order; the matching callback resolves the appropriate Promise.

Key Source Files

File Role Direct Link
src/utils/SocketClient.ts Core socket wrapper that creates request IDs, buffers inbound data, parses JSON, and maps responses to callbacks. SocketClient.ts
src/utils/ConnectionManager.ts Helper that establishes the TCP connection, waits for the connect event, and ensures clean disconnection after the operation. ConnectionManager.ts
src/tools/send_code_to_revit.ts Example tool script that invokes withRevitConnection and sendCommand to perform Revit operations. send_code_to_revit.ts

Summary: Why This Matters for SocketClient Handle Command Responses

By combining unique request identifiers, TCP buffering, and promise‑based callbacks, the Revit MCP SocketClient achieves rock‑solid asynchronous communication. The explicit timeout handling (lines 135‑141) guards against orphaned promises, while the callback registry (responseCallbacks) permits concurrent command execution without blocking the event loop. Developers interacting with Revit via sendCommand or withRevitConnection benefit from a clean, async/await interface that abstracts away the underlying socket complexity, ensuring that every SocketClient handle command responses cycle completes reliably—even when network latency or message fragmentation occurs.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →