# Build the AI KVM: a small ESP32S3 board that lets an AI agent work a second computer

In the reel, a Seeed XIAO ESP32S3 board was plugged into a second laptop, an
MSI GL65 Leopard. The board acts as that laptop's USB keyboard. It gets its
commands over Wi-Fi from Jason's main PC. A USB webcam on the main PC pointed
at the laptop's screen. The agent on the main PC read the MSI Click BIOS
screen through that webcam and moved the selection in the BIOS with the board.
Nothing was installed on the laptop.

![How the AI KVM connects: the board on the target's USB, Wi-Fi through a router to the host, and the host sees the target's screen through an HDMI capture card (option 1) or a webcam (option 2)](https://www.graniteai.co/tools/ai-kvm/how-it-connects.webp)

The diagram shows both ways to see the target's screen. Option 1 is an HDMI to
USB capture card. Option 2 is a webcam pointed at the screen. Many laptops send
nothing over HDMI until Windows loads, so the reel used the webcam for the BIOS.

This guide builds the same setup from the AiKVM repository:
[github.com/ClariSortAi/ai-kvm](https://github.com/ClariSortAi/ai-kvm). It then
connects the board to Claude Code as an MCP server, so Claude Code can see the
second computer's screen and press its keys.

## Who this is for

You use Claude Code and know the basics. You start it in a folder, type a task
in plain words, and watch it read files and run commands. You do not need to
know ESP32 boards, firmware or KVMs. This guide explains each one the first
time it comes up.

The steps are for Windows, because Windows is the only system we have tested
the controller software on. macOS and Linux should work too, with one change:
the pairing file uses Windows DPAPI, which locks a secret to your Windows
account, so on those systems the pairing secret needs a different store.

## What is new here

- A KVM (keyboard, video, mouse) is a box that lets you use a computer's
  keyboard, mouse and screen from somewhere else. Network KVMs are common in
  server rooms.
- The XIAO ESP32S3 is a small board from Seeed Studio, about the size of a
  thumbnail. It has a USB-C port and Wi-Fi. With this firmware, the computer
  it plugs into sees it as a USB keyboard and mouse. Firmware is the program
  that runs on the board.
- HID means human interface device: a keyboard or a mouse. A HID works before
  Windows starts, so it can work in the BIOS. The BIOS (or UEFI) is the setup
  screen a computer shows before the operating system loads.
- An MCP server is a program that gives an agent more tools. This repository
  has one. It gives Claude Code four tools: `kvm_status`, `kvm_observe`,
  `kvm_act` and `kvm_stop`.
- A capture device gives the main PC a picture of the second computer's
  screen. It can be an HDMI capture card or a USB webcam pointed at the screen.
  Windows sees both as a camera.

The repository does not call a model. Claude Code supplies the model.

## What works today, and what is not tested

The repository records these results on Jason's two computers (firmware 0.2.0):

- Wireless keyboard and mouse input to Windows on the MSI laptop passed all 13
  checks of the bench test.
- In the BIOS, the board's DOWN key moved the selection, seen through the
  webcam and saved as before and after pictures.

These are not tested or not working yet:

- BIOS video over HDMI. When the MSI laptop was in its BIOS, the HDMI capture
  card showed No Signal. The webcam is what showed the BIOS in the reel.
- Recovery after a reboot of the second computer. The repository has a test
  for it. It has not been run.
- Other BIOS keys beyond DOWN in a recorded run, and BIOS screens on other
  computers. A pass applies to one computer, one firmware, one USB port and one
  capture path.
- A Claude Code session. The repository records an MCP test of the server
  itself, not of a client. Claude Code must pass the tool's pictures to a model
  that can read images.

## What you need

### Parts

1. **Seeed Studio XIAO ESP32S3, the plain board with 8MB flash.** Not the
   XIAO ESP32C3 or ESP32C6. The firmware image is built for this board only.
   Primer: [Seeed's XIAO ESP32S3 getting-started page](https://wiki.seeedstudio.com/xiao_esp32s3_getting_started/).
2. **The board's Wi-Fi antenna.** The board needs it attached for Wi-Fi.
3. **A USB-C data cable** that fits a USB port on the second computer. Some
   cables carry power only. A power-only cable shows no device in Windows.
4. **A capture device**, one of these:
   - A USB webcam that you point at the second computer's screen. Jason used a
     GAMDIAS IRIS M1. This showed the BIOS in the reel.
   - An HDMI-to-USB capture card. Jason used a UGREEN 15389. It showed the
     MSI laptop's Windows desktop at 1920x1080. It did not show the BIOS on
     that laptop.
5. **A 2.4GHz WPA Wi-Fi network** that both the board and your main PC can
   reach. The board does not join 5GHz networks.

You do not need to solder.

### The two computers

| Computer | Its job | What runs on it |
|---|---|---|
| The controller (your main PC) | Runs Claude Code and the AiKVM software. The capture device plugs into it. | Windows, Python 3.14, Git, Claude Code |
| The target (the second computer, Jason's MSI laptop) | The computer that the agent controls. The board plugs into it. | Nothing. You install no software on it. |

During setup, the board plugs into the controller. At the end you move it to
the target.

### Software on the controller

1. **Git for Windows.** Primer: [git-scm.com](https://git-scm.com/downloads/win).
2. **Python 3.14.** Wi-Fi control needs Python 3.13 or later with TLS-PSK
   support. The repository was tested on 3.14. Primer:
   [python.org downloads](https://www.python.org/downloads/windows/). Install
   it with the Python launcher (`py`).
3. **Claude Code.** You already have it.

New to MCP? Read
[What is the Model Context Protocol?](https://modelcontextprotocol.io/docs/getting-started/intro)
first.

## Set it up on Windows

Type each command in PowerShell on the controller. Press Enter after each one.

### Part 1: Get the code and the tools

**1. Clone the repository.** Use this folder. The example MCP configs use this
path.

```powershell
git clone https://github.com/ClariSortAi/ai-kvm.git C:\Dev\AiKVM
Set-Location C:\Dev\AiKVM
```

**2. Check Python.**

```powershell
py -3.14 --version
```

If it prints an error, install Python 3.14 first, then open a new terminal.

**3. Make the Python environment.** The first command makes a private Python
folder, `.venv`. The script installs the pinned packages and the `aikvm`
command into it.

```powershell
py -3.14 -m venv .venv
.\scripts\Install-Python.ps1
```

**4. Install the flash tools.** This script downloads Arduino CLI 1.3.1, the
Espressif ESP32 core 3.3.2 and ArduinoJson 7.4.2 into `.tools` in the
repository. The flash script uses the esptool that comes with the core. You do
not need to compile the firmware. A built image is in
`artifacts\firmware`.

```powershell
.\scripts\Install-Toolchain.ps1
```

**5. Check the firmware image.** This checks the image hashes. It does not
open a device.

```powershell
.\scripts\Flash-Device.ps1 -Port COM999 -CheckOnly
```

You should see `All image hashes verified.`

**6. Run the doctor.** It lists COM ports and checks the setup. It changes
nothing.

```powershell
.\.venv\Scripts\aikvm.exe doctor
```

Look for `"tls_psk_available": true`. If it shows `false`, the Python in
`.venv` is older than 3.13.

### Part 2: Flash the firmware

**7. Put the board in download mode.** Download mode lets the PC write new
firmware to the board.

1. Disconnect the board's USB cable.
2. Hold the **B** (BOOT) button on the board.
3. Connect the USB cable to the controller.
4. Release B.

If no new COM port shows up, hold B, press and release **R** (RESET), then
release B.

**8. Find the board's COM port.** Connect only this board.

```powershell
.\.venv\Scripts\aikvm.exe doctor
```

Find the new port in the `ports` list, for example `COM7`.

**9. Flash.** Put your port in place of `COM7`:

```powershell
.\scripts\Flash-Device.ps1 -Port COM7
```

The script checks that the chip is an ESP32-S3. It saves a backup of the
board's full 8MB flash first, then writes the image. The backup and a flash
log go in `artifacts`. Keep the backup on your computer. It can contain the
board's earlier settings.

**10. Restart the board.** Disconnect the USB cable. Connect it again without
holding B. The COM port usually changes after a flash. Run the doctor again
and find the new port, for example `COM8`:

```powershell
.\.venv\Scripts\aikvm.exe doctor
```

### Part 3: The bench test over USB

The bench test checks that keys and mouse moves arrive in Windows. The board
stays on the controller for this test.

**11. Read the board's details.** This connects and never arms input.

```powershell
.\.venv\Scripts\aikvm.exe info --port COM8
```

**12. Run the bench test.** Use one monitor for this test. Close other
programs that use the COM port.

```powershell
.\.venv\Scripts\aikvm.exe bench --port COM8
```

A test window opens. Click **Start input tests** and keep the window in front.
The test types text, uses Ctrl+A, moves the mouse, clicks three buttons,
scrolls, and checks that STOP releases a held key. It saves the result in
`artifacts\host-bench-*.json`. Press Escape or click **STOP / release-all** to
stop it.

The text test can fail with a non-US keyboard layout or with Caps Lock on.

### Part 4: Wi-Fi and pairing

**13. Attach the antenna** to the board.

**14. Find the controller's IP address.**

```powershell
.\scripts\List-Devices.ps1
```

It lists the COM ports, cameras and IPv4 addresses. Pick the controller's
address on the 2.4GHz network, for example `192.168.1.50`. The board connects
to this address, so it must not change. Reserve it for the controller in your
router.

**15. Pair the board.** Put your port and address in the command:

```powershell
.\.venv\Scripts\aikvm.exe provision --port COM8 --controller-ip 192.168.1.50 --pairing .local\pairing.json
```

It asks for the Wi-Fi network name and password in the terminal. Type them
there, not into a chat. The command then does these things:

- It makes a random 256-bit pairing key. It saves the key in
  `.local\pairing.json`, locked to your Windows account.
- It sends the network name, the password and the key to the board over USB.
  The board saves them. The controller does not save the Wi-Fi password.

The board then connects out to the controller on TCP port 8787. The
connection uses TLS 1.2 with the pairing key. A device without the key cannot
send commands. No cloud service is involved.

**16. Let the connection through the firewall.** Do this once, in PowerShell
opened with **Run as administrator**:

```powershell
Set-Location C:\Dev\AiKVM
.\scripts\Enable-Wireless.ps1 -ControllerIP 192.168.1.50
```

Use the same address as in step 15. The rule allows TCP 8787 to that address
only, for the `.venv` Python only, from your local network only. To see the
rule before you add it, add `-CheckOnly`.

**17. Check the Wi-Fi link.** Back in the normal PowerShell window:

```powershell
.\.venv\Scripts\aikvm.exe info --pairing .local\pairing.json
```

If the board does not connect, press its **R** button once. On Jason's board,
the saved Wi-Fi settings worked only after that press.

**18. Run the bench test over Wi-Fi.** The board is still on the controller's
USB port, but now the commands go over Wi-Fi.

```powershell
.\.venv\Scripts\aikvm.exe bench --pairing .local\pairing.json
```

Click **Start input tests**. When it passes, close the test window.

### Part 5: Move the board to the target

**19. Set the BIOS profile.** A profile is the kind of USB device the board
shows to a computer. The `desktop` profile is a keyboard, a mouse and a serial
port for setup. The `bios` profile is a plain boot keyboard only, which a BIOS
screen can read. Set it while the board is still on the controller:

```powershell
.\.venv\Scripts\aikvm.exe profile --port COM8 bios
```

The board keeps its Wi-Fi settings and pairing. The new profile starts the
next time the board starts.

**20. Connect the hardware.**

1. Disconnect the board from the controller.
2. Connect the board to a USB port on the target.
3. Connect the capture device to the controller:
   - Webcam: plug it into the controller and point it at the target's screen.
   - HDMI capture card: connect the target's HDMI out to the card's HDMI IN.
     Connect the card's USB to the controller.
4. Close OBS and any other app that uses the camera or the capture card. Only
   one program can use it at a time.

**21. Open the qualification console.** This window shows the camera picture
and sends single test keys. It also helps you check that the camera and the
board point at the same computer. Start with camera index `0`:

```powershell
.\.venv\Scripts\aikvm.exe qualify --pairing .local\pairing.json --camera 0 --target "MSI GL65"
```

Put your target's name after `--target`. If the picture shows a different
camera, close the window and try `--camera 1`, then `--camera 2`. Write down
the number that shows the target's screen. In Jason's setup the webcam was
index 1 and the HDMI card was index 2.

**22. Test one key in the BIOS.**

1. Check that the picture shows the target. Tick the box that says you
   verified the board belongs to the target.
2. Click **Connect / arm**.
3. Restart the target and open its BIOS. On the MSI laptop, the key is Delete
   at the MSI logo. You can press it on the target's own keyboard, or pick it
   in the list next to **Send firmware entry key** and click that button.
4. When the picture shows the BIOS, click **Record BIOS baseline**.
5. Click **DOWN**. Look at the picture. If the selection moved, click
   **Confirm navigation**.
6. Click **STOP / release**.

The console saves pictures and a receipt in
`artifacts\qualification-<date>-<time>\`.

**23. Close the console.** Only one program can hold the board's Wi-Fi link.
The MCP server in Part 6 needs it.

### Part 6: Connect Claude Code

**24. Make a work folder.** Claude Code reads MCP servers for a project from a
file named `.mcp.json` in the folder you start it from.

```powershell
mkdir C:\Dev\kvm-run
Set-Location C:\Dev\kvm-run
```

**25. Write `.mcp.json`.** This is the repository's
`mcp-config.wireless.example.json`:

```json
{
  "mcpServers": {
    "aikvm-target": {
      "command": "C:\\Dev\\AiKVM\\.venv\\Scripts\\aikvm.exe",
      "args": ["serve", "--pairing", "C:\\Dev\\AiKVM\\.local\\pairing.json", "--target", "target-pc", "--capture", "uvc", "--camera", "0"]
    }
  }
}
```

With this file, Claude Code can see the screen, but the board sends no keys.
The server starts disarmed. To let Claude Code press keys, do these two
things:

- Set `--camera` to the number from step 21.
- Add `"--confirm-target-binding", "--arm"` at the end of `args`.
  `--confirm-target-binding` says that you checked in step 21 that this camera
  shows the computer the board is plugged into. The server does not arm a
  webcam or capture card setup without it.

Jason's webcam setup, with camera index 1, looks like this:

```json
{
  "mcpServers": {
    "aikvm-target": {
      "command": "C:\\Dev\\AiKVM\\.venv\\Scripts\\aikvm.exe",
      "args": ["serve", "--pairing", "C:\\Dev\\AiKVM\\.local\\pairing.json", "--target", "target-pc", "--capture", "uvc", "--camera", "1", "--confirm-target-binding", "--arm"]
    }
  }
}
```

The model cannot arm input. Only this file can.

**26. Start Claude Code.**

```powershell
claude
```

Claude Code asks whether to use the MCP server in `.mcp.json`. Approve it.
Type `/mcp` and check that `aikvm-target` is connected with four tools.

## Your first BIOS run

1. Restart the target and open its BIOS, as in step 22.
2. Paste this prompt into Claude Code:

```text
Use the aikvm-target MCP tools. The target ID is target-pc.

1. Call kvm_status and tell me the capture source and whether input is armed.
2. Call kvm_observe and tell me what is on the screen.
3. If it is a BIOS or UEFI setup screen, press DOWN once with kvm_act, using the frame ID from step 2. Then tell me what moved.
4. Change no settings. Do not press Enter or F10. Stop after step 3.
```

What to expect:

- Each `kvm_act` call asks for your permission in Claude Code, as other tools
  do. You approve each key.
- `kvm_observe` returns a picture and a frame ID. A frame is good for input
  for five seconds. After that, the agent must observe again.
- After each action, `kvm_act` returns a new picture. The acknowledgment only
  says that the board sent the key. The picture shows whether it worked.
- In the `bios` profile, use keys only: `press`, `type`, `key_down` and
  `key_up`. Named keys include ENTER, ESC, TAB, the arrows, DELETE and F1 to
  F12. Typed text is US ASCII.
- For mouse control in Windows, set the profile back to `desktop` (see Undo).

## How to stop

| Where | How |
|---|---|
| Claude Code | Ask it to call `kvm_stop`. It disarms and releases all keys. To arm again, restart the MCP server: reconnect it from `/mcp` or restart Claude Code. |
| Console or bench window | Click **STOP / release** (or **STOP / release-all**), or press Escape. |
| Automatic | A held key lets go after about 1.5 seconds. If the controller goes away, the board drops input after about 2 seconds. These are firmware timers, not measured guarantees. |
| The physical stop | Unplug the board from the target. |

The board never resends a key on its own. If a reply is lost, the connection closes.

## If something goes wrong

| What you see | What to do |
|---|---|
| No new COM port when you connect the board | Try a different cable. Many USB-C cables carry power only. Use a USB port on the PC itself, not a hub. Enter download mode again (step 7). Check Device Manager before you install a driver. This board does not need a CH340 driver. |
| PowerShell says that running scripts is disabled | Run the script with `powershell -ExecutionPolicy Bypass -File .\scripts\<name>.ps1` and its options. This changes the policy for that one command only. |
| The flash cannot connect to the board | Close the bench window, the console, Claude Code and any serial monitor. Enter download mode again and check the COM port again. The flash script does not reset the board on its own. |
| The COM port changed after the flash | This is normal. Run `aikvm.exe doctor` again and use the new port. |
| No keyboard and no COM port after the flash | Disconnect and connect again, then press R. If that fails, enter download mode and flash again. |
| `tls_psk_available` is `false` | The `.venv` Python is older than 3.13. Delete `.venv` and repeat step 3 with Python 3.14. |
| The board does not connect after you pair it | Press the board's R button once. On Jason's board, the saved settings worked after that press. Check that the network is 2.4GHz and that the controller's IP address did not change. |
| The board pairs but the link never comes up | Windows may block port 8787. Run step 16 in an administrator PowerShell. |
| The picture shows the wrong camera, or no picture | Camera numbers change when you connect or disconnect cameras. Try `--camera 0`, `1` and `2` in the console. Then put the right number in `.mcp.json`. |
| No picture from the capture card | OBS or another app is holding it. Close that app, then open the console again. |
| HDMI shows No Signal in the BIOS | Some laptops send no HDMI signal in the BIOS. On Jason's MSI laptop, the HDMI card showed No Signal in the BIOS. Point a USB webcam at the target's screen and use its camera number. |
| The console's arrow buttons send nothing | They need a recorded BIOS baseline first. Record it when the picture shows the BIOS. Without a picture, tick **I am watching the physical BIOS (diagnostic only)** and use the **Test** buttons while you watch the target's screen. |
| `/mcp` shows the server failed | The console may still hold the Wi-Fi link. Close it. Run one controller program at a time. |
| Claude Code gets "Observation missing or stale" | The frame is older than five seconds. Have it call `kvm_observe` again before the next key. |
| Absolute mouse moves are rejected | The `desktop` bench rejects absolute moves with more than one monitor. Use one monitor. |
| The bench text test fails | Check for a non-US keyboard layout or Caps Lock. |
| You want to change the profile | Use `aikvm.exe profile`, not a new flash. A full flash erases the Wi-Fi settings and the pairing. |

## Undo

- **Back to keyboard and mouse:** while the board is on Wi-Fi, run
  `.\.venv\Scripts\aikvm.exe profile --pairing .local\pairing.json desktop`.
  Then disconnect the board and connect it again.
- **Restore the board's old firmware:** enter download mode. Run the esptool
  in `.tools` with
  `--chip esp32s3 --port COM7 --before no-reset write-flash 0` and the exact
  path of your backup file from step 9. Check the backup's SHA256 file first.
  Do not use a backup from another board.
- **Remove the firewall rule:** `Enable-Wireless.ps1` prints its
  `Remove-NetFirewallRule` command when it adds the rule.
- **Remove the MCP server:** delete `.mcp.json` from your work folder.

Written on October 27, 2026 from the AiKVM repository, firmware 0.2.0. The
repository's `README.md` and `docs\bench-protocol.md` have the full detail.

---

Want one built for you? Comment BUY on the reel.
