# mcvnc VNC Client MCP Server

FastMCP server giving models interactive access to VNC sessions -- connect, screenshot, keyboard/mouse input, clipboard.

## Architecture

```
src/mcvnc/
├── _state.py       # FastMCP instance, lifespan, VncSession/SessionState
├── server.py       # entry point, side-effect imports, mcp.run()
├── _connect.py     # vnc_connect, vnc_disconnect, vnc_list_sessions, vnc_session_info
├── _screen.py      # vnc_screenshot, vnc_screenshot_region, vnc_screen_info, vnc_wait_for_screen_change
├── _keyboard.py    # vnc_type_text, vnc_press_key, vnc_key_combo
├── _mouse.py       # vnc_click, vnc_double_click, vnc_move, vnc_drag, vnc_scroll
├── _clipboard.py   # vnc_get_clipboard, vnc_set_clipboard
├── _resources.py   # vnc://sessions, vnc://sessions/{id}/screen
└── _prompts.py     # remote_desktop guided workflow
```

## Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `MCVNC_DEFAULT_PORT` | `5900` | Default VNC port |
| `MCVNC_CONNECTION_TIMEOUT` | `10` | Connection timeout in seconds |
| `MCVNC_SCREENSHOT_TIMEOUT` | `30` | Screenshot timeout in seconds (prevents infinite hang) |
| `MCVNC_SCREENSHOT_MAX_WIDTH` | `0` | Max screenshot width (0 = no limit) |
| `MCVNC_WAIT_CHANGE_TIMEOUT` | `5` | Wait-for-change timeout in seconds |

## Development

```bash
make install   # uv sync --group dev
make test      # pytest
make lint      # ruff check
make run       # start server
```

## Add to Claude Code

```bash
claude mcp add mcvnc -- uv run --directory /home/rpm/claude/mcvnc mcvnc
```

## Key Patterns

- **Per-session locking**: asyncio.Lock per VncSession prevents interleaved I/O on the same connection
- **asyncvnc2 lifecycle**: connect() is a context manager; we manually call `__aenter__`/`__aexit__` since connect/disconnect are separate tool calls
- **Lifespan cleanup**: disconnect_all() in finally block, atexit fallback for unclean shutdown
- **asyncvnc2 input methods are synchronous** -- they queue bytes into the writer buffer. `await client.drain()` flushes.

## License

GPL-3.0-or-later (required by asyncvnc2's GPL license)
