DeskVNC DeskVNC

Registering DeskVNC with Claude Code

Claude Code is Anthropic's terminal agent. It can open one of your saved machines over VNC, RDP, or SSH, read the screen, click, and type, all through the DeskVNC MCP server called dvv. The pair works because Claude Code speaks the Model Context Protocol over both stdio and Streamable HTTP, and dvv answers on both. The agent gets a real keyboard and a real mouse on a remote desktop that the user already saved in DeskVNCViewer, with no agent installed on the remote machine.

Prerequisite: the dvv binary

dvv is the executable that ships inside DeskVNCViewer. Install DeskVNCViewer once and open it so it can save a machine and its password. The binary sits next to the application:

The version on your PATH is the right one to point the agent at.

The registration step

Claude Code exposes claude mcp add for both transports. The two lines below are the verified shapes from the project's own documentation. The stdio form spawns dvv as a subprocess; the HTTP form connects to dvv listening on the loopback bearer URL http://127.0.0.1:7333/mcp once you have started it with a token.

# stdio, the default transport
claude mcp add --scope user deskvnc -- \
  /Applications/DeskVNCViewer.app/Contents/MacOS/dvv mcp --stdio

On Linux, swap the binary path to /usr/bin/dvv. On Windows, open PowerShell and use:

claude mcp add --scope user deskvnc -- `
  "$env:LOCALAPPDATA\DeskVNCViewer\dvv.exe" mcp --stdio

For the HTTP transport, set the bearer token first so it survives a restart of the agent, then add the entry:

export DVV_MCP_TOKEN="$(openssl rand -hex 32)"
claude mcp add --scope user --transport http deskvnc \
  http://127.0.0.1:7333/mcp \
  --header "Authorization: Bearer $DVV_MCP_TOKEN"

If you prefer the shell form that DeskVNC ships for any client, run dvv setup once. It writes the same MCP entry, copies the skill into ~/.claude/skills, and points every agent at the right dvv. Either path is fine; both produce the same MCP entry.

A first agent session

A typical first prompt in a Claude Code session is short and concrete. You name the saved machine, ask the agent to open it, and let the loop run.

You: Open "build-server" and tell me what is on the screen.

The agent drives the four-call loop:

dvv_hosts   {}
dvv_open    {"hostId": "build-server", "perceive": true}
dvv_control {"limbId": "limb-7f3a", "action": "acquire"}
dvv_screen  {"limbId": "limb-7f3a", "form": "full", "scale": 0.25}

The response from dvv_screen is a screenshot, a geometry_generation, and a content_generation. The agent computes a coordinate on the picture, reads the matching text on the screen, and decides where to click. It sends:

dvv_click   {"limbId": "limb-7f3a", "x": 612, "y": 348, "generation": 17}
dvv_type    {"limbId": "limb-7f3a", "text": "kubectl get pods", "wpm": 3000}
dvv_key     {"limbId": "limb-7f3a", "keys": "Return"}

A second dvv_screen confirms the terminal ran the command and printed the pod list. The whole observe-then-act cycle fits in tens of milliseconds, so a single short task feels immediate.

The coordinate generation fence

Every dvv_screen carries two fences: a geometry_generation and a content_generation. Clicks must echo the geometry generation they were computed against, and a click computed against a stale generation is refused rather than landing in the wrong place. This protects you from misclicks after a window resize or a display change.

Typing and keys are fenced by the content generation. dvv_type and dvv_key are refused with SCREEN_CHANGED when something window-sized has repainted since the last dvv_screen, because the focus may have moved to a new window. The fence stops an agent from typing into a dialog that appeared over the editor the agent thought it was driving. A single fresh dvv_screen clears the fence, and dvv_status does not, because it reads no pixels.

This is one of the strongest arguments for a protocol-level agent: the safety net sits inside the tool, not in the model, so a misaligned click is refused at the boundary.

Human takeover

A person at the remote machine can take control back at any moment. The DeskVNC viewer shows an "agent driving" badge while the agent holds the wheel, and one click in the viewer hands the desktop back to the person. The plane releases every held key and every held button the moment a person takes over, so a half-finished drag never strands the desktop. The agent observes that the lease has changed and rereads the screen before it does anything else. The recipient can also revoke the agent's control or end the session outright at any time.

This is attended automation by design. The agent never has a stronger position than the person at the keyboard.

Troubleshooting

Symptom Likely cause Try this
LIMB_GONE from any tool The last dvv_close or a viewer quit detached the limb Run dvv limbs to list live limbs, then dvv open <host> again
SCREEN_CHANGED on dvv_type or dvv_key A window-sized region repainted since the last dvv_screen Call dvv_screen once, then retry the action
claude mcp list does not show deskvnc The MCP entry was added to the wrong scope Re-run with --scope user or --scope project and restart Claude Code
HTTP transport returns 401 The bearer token changed since DVV_MCP_TOKEN was exported Re-export DVV_MCP_TOKEN to the same value, then restart the agent
Click lands in the wrong place Stale geometry_generation after a resize Take a fresh dvv_screen, then send the new generation with the click