# How @escrcpy/madb Functions as an MCP Server for Android Automation

> Discover how escrcpy madb acts as an MCP server. It enables AI agents to control Android devices via JSON-RPC over ADB, offering concurrent access through a stateless TCP/WebSocket interface.

- Repository: [viarotel-org/escrcpy](https://github.com/viarotel-org/escrcpy)
- Tags: how-to-guide
- Published: 2026-09-10

---

**@escrcpy/madb implements a Multi-Client Protocol (MCP) server that exposes Android Debug Bridge (ADB) operations via JSON-RPC, enabling AI agents and UI components to control Android devices concurrently through a stateless TCP/WebSocket interface.**

In the viarotel-org/escrcpy monorepo, `@escrcpy/madb` acts as the middleware layer between the Electron main process and Android hardware. It transforms local ADB capabilities into a network-accessible API defined in [`packages/madb/AGENTS.md`](https://github.com/viarotel-org/escrcpy/blob/main/packages/madb/AGENTS.md), allowing multiple simultaneous clients—including built-in AI copilots, external automation scripts, and renderer processes—to execute commands without blocking the UI thread.

## Multi-Client Protocol (MCP) Architecture

The MCP server implements a stateless JSON-RPC interface over TCP sockets. Unlike direct ADB connections that lock to a single client, **madb** multiplexes commands from multiple agents while maintaining a shared device-state cache.

This architecture relies on four core components located in the shared utilities package:

- **Device Abstraction** ([`packages/shared/src/device.ts`](https://github.com/viarotel-org/escrcpy/blob/main/packages/shared/src/device.ts)): Wraps the 32-tool **yadb** suite to provide high-level methods for install, push, pull, shell execution, and screen capture.
- **ADBKit Integration** ([`packages/shared/src/adbkit.ts`](https://github.com/viarotel-org/escrcpy/blob/main/packages/shared/src/adbkit.ts)): Low-level wrapper around the `adbkit` library that manages ADB server connections and binary streams.
- **Network Layer** ([`packages/shared/src/network.ts`](https://github.com/viarotel-org/escrcpy/blob/main/packages/shared/src/network.ts)): Handles TCP socket creation, WebSocket upgrades, and client connection pooling for concurrent session management.
- **Security Module** ([`packages/shared/src/security.ts`](https://github.com/viarotel-org/escrcpy/blob/main/packages/shared/src/security.ts)): Validates all incoming JSON-RPC payloads and sanitizes shell arguments before passing them to the device layer.

## Integration with the Electron Main Process

The server runs as a separate Node.js process to prevent ADB I/O from blocking the Electron UI. During the build phase, [`desktop/electron-builder.config.js`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/electron-builder.config.js) copies the compiled bundle from `packages/madb/dist` into the Electron resources directory at `extra/common/madb`.

When the application launches, the main process spawns **madb** as a child process:

```javascript
import { spawn } from 'node:child_process';
import path from 'node:path';

const madbBin = path.resolve(__dirname, 'extra/common/madb', 'madb.js');
const madb = spawn('node', [madbBin], {
  stdio: ['ignore', 'inherit', 'inherit'],
  env: { ...process.env, NODE_ENV: 'production' },
});

```

The main process then retrieves the listening port from the child process and exposes it to renderer windows through **electron-ipcx**:

```javascript
madb.once('message', ({ port }) => {
  ipcMain.handle('madb-port', () => port);
});

```

## MCP Communication Flow

Client interactions follow a strict request-response pattern with asynchronous state broadcasting:

1. **Connection**: A renderer or external agent connects to the TCP/WebSocket endpoint published via `electron-ipcx`.
2. **Command Execution**: The client sends a JSON-RPC request such as `{method:"device.install", params:["/path/app.apk"]}`.
3. **Translation**: `madb` translates the call to the appropriate `adbkit` or yadb command in [`packages/shared/src/device.ts`](https://github.com/viarotel-org/escrcpy/blob/main/packages/shared/src/device.ts), executes it on the target device, and captures stdout/stderr using the **Promise** helper ([`packages/shared/src/promise.ts`](https://github.com/viarotel-org/escrcpy/blob/main/packages/shared/src/promise.ts)).
4. **Response**: Results are marshalled back over the JSON-RPC channel.
5. **Broadcasting**: Side-effects (e.g., new apps appearing, battery changes) trigger broadcasts to all connected clients via the network layer.

## Consuming the MCP Server from Client Code

Renderer processes connect via WebSocket after obtaining the port from the main process:

```typescript
import { ipcxRenderer } from '@escrcpy/electron-ipcx';

const port = await ipcxRenderer.invoke('madb-port');
const socket = new WebSocket(`ws://localhost:${port}`);

const call = (method: string, params?: any[]) =>
  new Promise((resolve, reject) => {
    const id = Math.random().toString(36).slice(2);
    socket.send(JSON.stringify({ jsonrpc: '2.0', id, method, params }));
    const listener = (msg: MessageEvent) => {
      const data = JSON.parse(msg.data);
      if (data.id === id) {
        socket.removeEventListener('message', listener);
        data.error ? reject(data.error) : resolve(data.result);
      }
    };
    socket.addEventListener('message', listener);
  });

// Install an APK
await call('device.install', ['/tmp/app-release.apk']);

```

Clients also listen for broadcast state updates:

```typescript
socket.addEventListener('message', e => {
  const data = JSON.parse(e.data);
  if (data.method === 'device.state') {
    console.log('Device state changed:', data.params);
  }
});

```

## Summary

- **@escrcpy/madb** implements a stateless MCP server using JSON-RPC over TCP/WebSocket to enable concurrent Android device control.
- It wraps the **adbkit** library and **yadb** tool suite (32 tools) through abstractions defined in [`packages/shared/src/device.ts`](https://github.com/viarotel-org/escrcpy/blob/main/packages/shared/src/device.ts) and [`packages/shared/src/adbkit.ts`](https://github.com/viarotel-org/escrcpy/blob/main/packages/shared/src/adbkit.ts).
- The server runs as a child process spawned by the Electron main process, with build-time bundling configured in [`desktop/electron-builder.config.js`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/electron-builder.config.js).
- **electron-ipcx** bridges the gap between the main process and renderer windows, securely transmitting the server port.
- Input validation occurs in [`packages/shared/src/security.ts`](https://github.com/viarotel-org/escrcpy/blob/main/packages/shared/src/security.ts), while network multiplexing is handled by [`packages/shared/src/network.ts`](https://github.com/viarotel-org/escrcpy/blob/main/packages/shared/src/network.ts).
- All connected clients receive broadcast updates when device state changes, ensuring UI synchronization across multiple agents.

## Frequently Asked Questions

### What does MCP stand for in @escrcpy/madb?

MCP stands for **Multi-Client Protocol**. It defines a network interface that allows multiple autonomous agents—such as AI copilots, automation scripts, and UI components—to issue ADB commands simultaneously without interfering with each other's sessions.

### How does madb communicate with Android devices?

The server communicates through the **adbkit** wrapper defined in [`packages/shared/src/adbkit.ts`](https://github.com/viarotel-org/escrcpy/blob/main/packages/shared/src/adbkit.ts), which manages ADB server connections. For extended functionality, it leverages the **yadb** suite—a collection of 32 specialized tools—to perform operations like high-speed file transfer, screen capture, and deep system queries.

### Can multiple AI agents control the same device concurrently?

Yes. Because **madb** is stateless and uses JSON-RPC multiplexing via [`packages/shared/src/network.ts`](https://github.com/viarotel-org/escrcpy/blob/main/packages/shared/src/network.ts), any number of agents can maintain parallel connections. The server isolates errors per-client and broadcasts state changes globally, ensuring that all agents see consistent device status.

### Where is the madb server binary located after installation?

According to [`desktop/electron-builder.config.js`](https://github.com/viarotel-org/escrcpy/blob/main/desktop/electron-builder.config.js), the compiled bundle from `packages/madb/dist` is copied to `extra/common/madb` within the Electron resources directory. The main process spawns [`madb.js`](https://github.com/viarotel-org/escrcpy/blob/main/madb.js) from this location at runtime, making it available to both internal renderers and external clients.