# How revit-mcp Communicates with the Revit Plugin via TCP Sockets: JSON-RPC Architecture Explained

> Discover how revit-mcp communicates with the Revit plugin using JSON-RPC over TCP sockets. Learn about request IDs and buffer parsing for efficient command handling.

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

---

**revit-mcp communicates with the Revit plugin via a lightweight JSON-RPC 2.0 protocol over TCP sockets, using request-ID mapping and buffer-based parsing to handle concurrent commands between the CLI and Revit.**

The revit-mcp project enables programmatic control of Autodesk Revit through a Model Context Protocol (MCP) implementation. Understanding how revit-mcp communicates with the Revit plugin via TCP sockets reveals the architecture that allows external tools to drive Revit operations remotely through a robust JSON-RPC interface.

## The Seven-Step TCP Communication Flow

The communication between revit-mcp and the Revit plugin follows a precise sequence implemented in [`src/utils/SocketClient.ts`](https://github.com/mcp-servers-for-revit/revit-mcp/blob/main/src/utils/SocketClient.ts) and [`src/utils/ConnectionManager.ts`](https://github.com/mcp-servers-for-revit/revit-mcp/blob/main/src/utils/ConnectionManager.ts).

### Step 1: Creating the TCP Client

The `RevitClientConnection` class initializes a Node.js `net.Socket` configured to connect to `localhost:8080`. This establishes the endpoint where the Revit plugin listens for incoming commands.

### Step 2: Opening the Socket Connection

The `connect()` method initiates the TCP handshake. Upon successful connection, the socket emits a `"connect"` event, setting the internal `isConnected` flag to `true` and preparing the channel for data transmission.

### Step 3: Framing Requests as JSON-RPC 2.0

For every command, the `sendCommand()` method constructs a JSON-RPC 2.0 envelope. The payload includes:

- `jsonrpc`: "2.0"
- `method`: The Revit command name
- `params`: Command arguments
- `id`: A unique request identifier generated for correlation

### Step 4: Writing to the TCP Stream

The serialized JSON string is written directly to the socket using `socket.write()`. This transmits the command to the Revit plugin's TCP server without additional protocol overhead.

### Step 5: Buffering Incoming Data

Because TCP is a streaming protocol, response data may arrive in fragments. The client maintains an internal `buffer` string that accumulates chunks from the `"data"` event. The system attempts `JSON.parse()` on the buffer; if parsing fails due to incomplete data, it waits for the next chunk.

### Step 6: Dispatching Responses by Request ID

Once a complete JSON message is parsed, `handleResponse()` extracts the `id` field and looks up the corresponding callback in the `responseCallbacks` Map. The system resolves or rejects the original Promise based on the presence of an `error` field in the JSON-RPC response, enabling proper async/await patterns.

### Step 7: Connection Cleanup

The `withRevitConnection` helper in [`src/utils/ConnectionManager.ts`](https://github.com/mcp-servers-for-revit/revit-mcp/blob/main/src/utils/ConnectionManager.ts) wraps the entire lifecycle. After the operation completes, it calls `disconnect()` to destroy the socket and release system resources, ensuring no lingering TCP connections remain.

## Key Architectural Components

The socket communication relies on several sophisticated patterns that ensure reliability and concurrency.

### JSON-RPC 2.0 Protocol Standardization

The system uses the **JSON-RPC 2.0** specification to structure all messages. This standardization provides a clear separation between method invocation, parameter passing, and error handling, making the protocol extensible for future Revit commands.

### Request-ID Mapping for Concurrency

To handle multiple simultaneous commands, the client maintains a `Map<string, (response: string) => void>` called `responseCallbacks`. Each outgoing request receives a unique ID, and the corresponding Promise resolver is stored in this Map. When the response arrives, the ID ensures the correct callback receives the data, enabling true concurrent request handling over a single TCP socket.

### Buffer-Based Message Parsing

TCP streams do not respect message boundaries. The client implements a **buffer-based parsing** strategy where incoming data chunks are concatenated to an internal buffer. The system attempts to parse the buffer as JSON; if it receives a partial message, it simply waits for the next `"data"` event. This approach handles network fragmentation without complex framing protocols.

### Promise-Based Async API

The `sendCommand()` method returns a native JavaScript **Promise**, allowing higher-level tools to use `async/await` syntax. This abstraction hides the underlying socket complexity, making tool implementations in `src/tools/*.ts` clean and maintainable.

### Connection Manager Abstraction

The `withRevitConnection` function in [`src/utils/ConnectionManager.ts`](https://github.com/mcp-servers-for-revit/revit-mcp/blob/main/src/utils/ConnectionManager.ts) provides a **connection manager** pattern that handles the complete lifecycle: connection establishment, operation execution, and guaranteed cleanup. This prevents resource leaks and simplifies error handling across the codebase.

## Implementation Examples

### Using the Connection Manager Wrapper

The recommended approach uses the `withRevitConnection` helper to handle socket lifecycle automatically:

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

async function getSelectedElements() {
  const result = await withRevitConnection(async (client) => {
    // The method name matches a command implemented in the Revit plug‑in
    return client.sendCommand("GetSelectedElements");
  });
  console.log("Selected element IDs:", result);
}

getSelectedElements();

```

The `withRevitConnection` wrapper ensures the socket is opened, the command is sent, and the socket is closed afterward.

### Direct Client Implementation

For scenarios requiring manual connection control, instantiate `RevitClientConnection` directly:

```typescript
import { RevitClientConnection } from "./utils/SocketClient.js";

const client = new RevitClientConnection("localhost", 8080);

client.connect();

client
  .sendCommand("CreateWall", { start: [0, 0, 0], end: [10, 0, 0] })
  .then((wallId) => console.log("Created wall:", wallId))
  .catch((err) => console.error("Error:", err))
  .finally(() => client.disconnect());

```

Here we manually manage the socket lifecycle and send a custom command with parameters.

## Core Source Files

The TCP socket implementation spans several key files in the repository:

- **[`src/utils/SocketClient.ts`](https://github.com/mcp-servers-for-revit/revit-mcp/blob/main/src/utils/SocketClient.ts)** – Implements the `RevitClientConnection` class, handling TCP socket creation, JSON-RPC framing, request-ID tracking via `responseCallbacks`, and buffer-based response parsing.

- **[`src/utils/ConnectionManager.ts`](https://github.com/mcp-servers-for-revit/revit-mcp/blob/main/src/utils/ConnectionManager.ts)** – Provides the `withRevitConnection` helper that abstracts connection lifecycle management, ensuring proper cleanup after operations complete.

- **`src/tools/*.ts`** (e.g., [`send_code_to_revit.ts`](https://github.com/mcp-servers-for-revit/revit-mcp/blob/main/send_code_to_revit.ts), [`get_selected_elements.ts`](https://github.com/mcp-servers-for-revit/revit-mcp/blob/main/get_selected_elements.ts)) – High-level tool modules that utilize `withRevitConnection` to execute specific Revit commands.

- **[`src/index.ts`](https://github.com/mcp-servers-for-revit/revit-mcp/blob/main/src/index.ts)** – Entry point that registers CLI commands and initializes the tool ecosystem.

## Summary

- **revit-mcp** establishes TCP communication with the Revit plugin through a **JSON-RPC 2.0** protocol running over a plain TCP socket to `localhost:8080`.
- The **`RevitClientConnection`** class in [`src/utils/SocketClient.ts`](https://github.com/mcp-servers-for-revit/revit-mcp/blob/main/src/utils/SocketClient.ts) manages the socket lifecycle, implements **buffer-based parsing** to handle TCP fragmentation, and uses a **request-ID mapping** system to correlate responses with pending Promises.
- The **`withRevitConnection`** helper in [`src/utils/ConnectionManager.ts`](https://github.com/mcp-servers-for-revit/revit-mcp/blob/main/src/utils/ConnectionManager.ts) provides automatic resource management, ensuring sockets are properly closed after operations complete.
- All commands follow the **JSON-RPC 2.0** specification with unique request IDs, enabling concurrent command execution over a single persistent TCP connection.

## Frequently Asked Questions

### How does revit-mcp handle multiple concurrent commands over a single TCP socket?

revit-mcp handles concurrency through **request-ID mapping**. Each command sent via `sendCommand()` receives a unique identifier stored in the `responseCallbacks` Map. When the Revit plugin returns a response, the `handleResponse()` method uses the JSON-RPC `id` field to look up the correct Promise resolver, allowing multiple simultaneous operations without response mixing.

### What happens if a TCP message arrives in multiple fragments?

The client implements **buffer-based parsing** to handle TCP fragmentation. Incoming data chunks from the `"data"` event are appended to an internal `buffer` string. The system attempts `JSON.parse()` on the accumulated buffer; if parsing fails due to incomplete data, the client waits for the next chunk. This approach ensures reliable message reconstruction regardless of network packet boundaries.

### Why does revit-mcp use JSON-RPC 2.0 instead of raw JSON?

The **JSON-RPC 2.0** protocol provides standardized request/response envelopes with required `method`, `params`, `id`, and `error` fields. This standardization enables the request-ID correlation system, structured error handling, and future extensibility. Raw JSON would require custom framing logic and lack the built-in error semantics that JSON-RPC provides for the Revit command interface.

### How do I ensure the TCP connection closes properly after operations?

Use the **`withRevitConnection`** helper from [`src/utils/ConnectionManager.ts`](https://github.com/mcp-servers-for-revit/revit-mcp/blob/main/src/utils/ConnectionManager.ts) instead of manual socket management. This function automatically calls `disconnect()` to destroy the socket and release system resources after your operation completes, even if an error occurs. For manual control, always call `client.disconnect()` in a `finally` block as shown in the direct implementation examples.