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

> Discover how SocketClient handles asynchronous command responses in Revit MCP. Learn about TCP communication, callbacks, and reliable non-blocking interactions.

- Repository: [MCP servers for Revit/revit-mcp](https://github.com/mcp-servers-for-revit/revit-mcp)
- Tags: deep-dive
- Published: 2026-02-16

---

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`](https://github.com/mcp-servers-for-revit/revit-mcp/blob/main/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:

```ts
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:

```ts
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`](https://github.com/mcp-servers-for-revit/revit-mcp/blob/main/src/utils/SocketClient.ts) | Core socket wrapper that creates request IDs, buffers inbound data, parses JSON, and maps responses to callbacks. | [SocketClient.ts](https://github.com/mcp-servers-for-revit/revit-mcp/blob/main/src/utils/SocketClient.ts) |
| [`src/utils/ConnectionManager.ts`](https://github.com/mcp-servers-for-revit/revit-mcp/blob/main/src/utils/ConnectionManager.ts) | Helper that establishes the TCP connection, waits for the `connect` event, and ensures clean disconnection after the operation. | [ConnectionManager.ts](https://github.com/mcp-servers-for-revit/revit-mcp/blob/main/src/utils/ConnectionManager.ts) |
| [`src/tools/send_code_to_revit.ts`](https://github.com/mcp-servers-for-revit/revit-mcp/blob/main/src/tools/send_code_to_revit.ts) | Example tool script that invokes `withRevitConnection` and `sendCommand` to perform Revit operations. | [send_code_to_revit.ts](https://github.com/mcp-servers-for-revit/revit-mcp/blob/main/src/tools/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.