# How Desktop Commander MCP Buffers and Initializes Messages in FilteredStdioServerTransport

> Discover how Desktop Commander MCP buffers and initializes messages in FilteredStdioServerTransport. Learn about its custom buffer and replay mechanism for protocol compliance.

- Repository: [Eduard Ruzga/DesktopCommanderMCP](https://github.com/wonderwhy-er/DesktopCommanderMCP)
- Tags: internals
- Published: 2026-07-29

---

**Desktop Commander MCP uses a custom `FilteredStdioServerTransport` class that intercepts `console` output and `process.stdout` writes, storing them in a private `messageBuffer` array until `enableNotifications()` is called, then replaying them as JSON-RPC notifications to maintain protocol compliance.**

The **Model Context Protocol (MCP)** strictly requires that servers must not write to `stdout` before receiving the client's first JSON-RPC request. To solve this, the Desktop Commander MCP repository implements a specialized transport layer that wraps the standard SDK transport. This article examines how `FilteredStdioServerTransport` in [`src/custom-stdio.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/custom-stdio.ts) captures, buffers, and replays messages to ensure robust stdio communication.

## Transport Architecture and Construction

The `FilteredStdioServerTransport` class extends the base MCP transport functionality with stateful buffering capabilities. During instantiation, it preserves original I/O references and prepares internal storage for pre-initialization messages.

### Original Stream Preservation

In the constructor (lines 19-27 of [`src/custom-stdio.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/custom-stdio.ts)), the transport saves unmodified references to avoid losing output capabilities:

- **`originalConsole`** – Stores the native `console` object methods (`log`, `warn`, `error`, `debug`, `info`)
- **`originalStdoutWrite`** – Caches the original `process.stdout.write` implementation

These references allow the transport to intercept output while maintaining the ability to forward JSON-RPC messages transparently.

### Message Buffer Structure

The transport initializes a private **`messageBuffer`** array (lines 28-33) to hold objects with the following structure:

```typescript
{
  level: string,      // 'log', 'warn', 'error', 'debug', 'info'
  args: any[],        // Original arguments passed to console method
  timestamp: number   // Unix timestamp for chronological replay
}

```

This structure ensures that when `enableNotifications()` is eventually called, messages replay in the exact order they occurred.

## Console Output Interception

The transport overrides standard console methods to implement conditional buffering based on the `isInitialized` state flag.

### Conditional Buffering Logic

For each console method (`log`, `warn`, `error`, `debug`, `info`), the transport installs a proxy function (lines 27-38) that performs the following check:

```typescript
if (!this.isInitialized) {
  // Buffer the message for later replay
  this.messageBuffer.push({ level, args, timestamp: Date.now() });
} else {
  // Send immediately as JSON-RPC notification
  this.sendLog(level, args);
}

```

**Key behavior**: Any output generated during server startup—before the MCP client completes its handshake—is safely captured rather than written directly to `stdout`, preventing protocol violations that would crash the connection.

## Stdout Write Filtering

Beyond console methods, the transport must also handle raw writes to `process.stdout` that might originate from third-party libraries or direct stream access.

### JSON-RPC Detection and Buffering

The `setupStdoutFiltering()` method (starting at line 90) intercepts all `process.stdout.write` calls and distinguishes between protocol messages and log output:

1. **JSON-RPC Detection**: Checks if the written string parses as valid JSON-RPC (lines 94-101)
2. **Log Buffering**: Non-JSON strings are converted to log notifications and, if uninitialized, added to `messageBuffer` (lines 102-115)

This dual-path approach ensures that legitimate protocol traffic passes through unmodified while accidental debug output gets queued for later transmission.

## Initialization and Message Replay

The transition from buffering to active communication occurs when the server completes its connection handshake.

### Enabling Notifications

The **`enableNotifications()`** method (lines 60-94) serves as the critical state transition point:

```typescript
public enableNotifications(): void {
  this.isInitialized = true;
  
  // Send startup confirmation
  this.sendNotification('notifications/initialized', {});
  
  // Replay buffered messages in chronological order
  this.messageBuffer
    .sort((a, b) => a.timestamp - b.timestamp)
    .forEach(entry => {
      this.sendLog(entry.level, entry.args);
    });
    
  // Clear buffer to free memory
  this.messageBuffer = [];
}

```

**Sequence guarantee**: Messages replay sorted by `timestamp`, ensuring the client receives logs in causal order regardless of which console method generated them.

### Manual Log Transmission

The **`sendLog()`** method (lines 82-99) provides a programmatic interface for application code:

```typescript
public sendLog(level: string, ...args: any[]): void {
  if (!this.isInitialized) {
    // Queue if called before initialization
    this.messageBuffer.push({ level, args, timestamp: Date.now() });
    return;
  }
  
  // Emit as JSON-RPC log notification
  this.sendNotification('notifications/log', { level, message: args.join(' ') });
}

```

This method is exposed globally via `global.mcpTransport`, allowing any module to log safely without checking initialization state.

## Client-Specific Configuration

Different MCP clients handle notifications differently. The transport provides **`configureForClient()`** (lines 100-110) to adapt behavior before initialization:

- **Cline/VS Code compatibility**: Calling `configureForClient('cline')` disables notification replay for clients that treat stderr/stdout noise as errors
- **Conditional disabling**: Sets internal flags that prevent `enableNotifications()` from replaying buffered content

This configuration must occur before `enableNotifications()` is called to effectively suppress early output.

## Practical Implementation Examples

### Instantiating the Transport

In [`src/index.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/index.ts) (lines 53-58), the transport is created and exposed globally:

```typescript
import { FilteredStdioServerTransport } from "./custom-stdio.js";

const transport = new FilteredStdioServerTransport();
global.mcpTransport = transport;  // Global access for application modules

await server.connect(transport);
transport.enableNotifications();  // Triggers buffer replay

```

### Safe Logging from Application Code

Anywhere in the codebase, use the global transport reference:

```typescript
// This works safely regardless of initialization state
global.mcpTransport?.sendLog("info", "Cache warmed", { size: 42 });

```

If called before `enableNotifications()`, the message enters `messageBuffer` and automatically emits once the transport initializes.

### Disabling for Incompatible Clients

For clients that cannot handle notification traffic:

```typescript
// Call before enableNotifications()
transport.configureForClient("cline");
await server.connect(transport);
// Buffered messages will be discarded, not replayed
transport.enableNotifications();

```

## Summary

- **`FilteredStdioServerTransport`** in [`src/custom-stdio.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/custom-stdio.ts) wraps standard MCP stdio transport to prevent protocol violations during startup.
- **Pre-initialization buffering** captures all `console` output and `process.stdout` writes in a `messageBuffer` array with timestamps.
- **State-driven replay** occurs when `enableNotifications()` sets `isInitialized = true`, emitting buffered messages as JSON-RPC notifications in chronological order.
- **Global access** via `global.mcpTransport` allows safe logging from any module without initialization checks.
- **Client-specific adaptation** via `configureForClient()` prevents replay for clients like Cline that cannot handle notification traffic.

## Frequently Asked Questions

### What happens if I call `sendLog()` before the transport initializes?

The message is pushed onto the `messageBuffer` array with a timestamp and automatically replayed once `enableNotifications()` is called. No output is lost, and no protocol errors occur because the transport withholds all `stdout` writes until initialization completes.

### How does the transport distinguish between JSON-RPC protocol messages and log output?

The transport intercepts `process.stdout.write` calls (lines 90-115 in [`src/custom-stdio.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/custom-stdio.ts)) and attempts to parse the written string as JSON. Valid JSON-RPC payloads pass through to the original `stdout.write`, while plain text strings are wrapped in log notifications and buffered if the transport hasn't initialized yet.

### Can I disable message buffering for specific MCP clients?

Yes. Call `transport.configureForClient("cline")` (or other client identifiers) before `enableNotifications()`. This sets internal flags that prevent the buffered message replay, which is necessary for clients that treat unexpected stdout traffic as fatal errors rather than JSON-RPC notifications.

### Where is the message buffer stored, and what is its memory impact?

The buffer exists as a private `messageBuffer` array within the `FilteredStdioServerTransport` instance, storing objects with `level`, `args`, and `timestamp` properties. Memory usage scales with the verbosity of startup logging; the buffer clears automatically after `enableNotifications()` runs, so transient high-volume logging during initialization won't persist for the application lifetime.