MCP QEMU VM Control

Dale a tu IA acceso completo a la computadora, de forma segura. Permite que Claude (o cualquier LLM compatible con MCP) vea tu pantalla, mueva el mouse, escriba en el teclado y ejecute comandos, todo dentro de una máquina virtual QEMU aislada. Perfecto para automatización impulsada por IA, pruebas y experimentos de uso de computadora sin arriesgar tu sistema anfitrión.

Documentación

MCP QEMU VM Control

Dale a tu IA acceso completo a la computadora — de forma segura.

Permite que Claude (o cualquier LLM compatible con MCP) vea tu pantalla, mueva el mouse, escriba en el teclado y ejecute comandos — todo dentro de una máquina virtual QEMU aislada. Perfecto para automatización impulsada por IA, pruebas y experimentos de uso de computadora sin arriesgar tu sistema anfitrión.

Un servidor de Model Context Protocol (MCP) para controlar máquinas virtuales QEMU mediante SSH. Este servidor permite que los LLM interactúen con las VMs mediante control de mouse/teclado, capturas de pantalla y ejecución de comandos SSH.

Tabla de Contenidos

Características

  • Control de Mouse - Mover el cursor y hacer clic en botones
  • Entrada de Teclado - Escribir texto y enviar combinaciones de teclas
  • Agrupación de Acciones - Ejecutar secuencias de acciones de UI en una sola llamada
  • Capturas de Pantalla - Capturar y recuperar capturas de pantalla de la VM
  • Ejecución de Comandos SSH - Ejecutar comandos de shell en la VM
  • Transferencia de Archivos - Subir y descargar archivos mediante SFTP
  • Gestión de Proyectos - Organizar salidas en carpetas de proyecto con registros, resultados y consejos
  • Sistema de Consejos - Guardar y recuperar consejos para futuras sesiones de LLM

Requisitos Previos

Sistema Anfitrión

  • Python 3.12+
  • uv (recomendado) o pip
  • QEMU/KVM con libvirt
  • virt-manager (opcional, para gestión mediante GUI)

Requisitos de la VM

  • Linux con entorno de escritorio X11
  • Servidor SSH habilitado
  • Paquetes requeridos: openssh, xdotool, scrot, xrandr, xinput

Configuración de QEMU/libvirt

1. Instalar paquetes de virtualización

Arch/Manjaro:

sudo pacman -S qemu-full libvirt virt-manager dnsmasq iptables-nft

Debian/Ubuntu:

sudo apt install qemu-kvm libvirt-daemon-system libvirt-clients virt-manager bridge-utils

Fedora:

sudo dnf install @virtualization

2. Configurar libvirt

# Enable and start libvirtd
sudo systemctl enable --now libvirtd

# Add your user to libvirt group
sudo usermod -aG libvirt $USER

# Log out and back in, then verify
groups  # should show 'libvirt'

3. Configurar la red predeterminada

libvirt proporciona una red NAT predeterminada (192.168.122.0/24) que las VMs usan para comunicarse con el anfitrión:

# Check network status
virsh -c qemu:///system net-list --all

# If 'default' is not active, start it
virsh -c qemu:///system net-start default

# Enable autostart
virsh -c qemu:///system net-autostart default

La configuración de red predeterminada:

  • Puente: virbr0
  • IP del anfitrión: 192.168.122.1
  • Rango DHCP: 192.168.122.2 - 192.168.122.254
  • Modo: NAT (las VMs pueden acceder a internet, el anfitrión puede acceder a las VMs)

4. Crear una VM con virt-manager

  1. Iniciar virt-manager
  2. Crear una nueva VM (Archivo → Nueva Máquina Virtual)
  3. Seleccionar el medio de instalación (ISO)
  4. Asignar recursos:
    • Memoria: 4096 MB recomendado
    • CPUs: 2+ recomendado
  5. Importante: En "Selección de red", elegir "Red virtual 'default': NAT"
  6. Completar la instalación

5. Configurar la VM

Después de instalar el sistema operativo invitado:

# Inside the VM - Install required packages

# Arch/Manjaro
sudo pacman -S --needed openssh xdotool scrot xorg-xrandr xorg-xinput

# Debian/Ubuntu
sudo apt install openssh-server xdotool scrot x11-xserver-utils xinput

# Enable SSH
sudo systemctl enable --now sshd

6. Crear el usuario de automatización

En la VM:

# Create vmrobot user
sudo useradd -m -s /bin/bash vmrobot
sudo passwd vmrobot

# Set up SSH key authentication
sudo -u vmrobot mkdir -p /home/vmrobot/.ssh
sudo -u vmrobot chmod 700 /home/vmrobot/.ssh

En el anfitrión:

# Copy your public key to the VM
ssh-copy-id vmrobot@192.168.122.XX

# Or manually add to /home/vmrobot/.ssh/authorized_keys on VM

7. Otorgar acceso X11 a vmrobot

El usuario vmrobot necesita permiso para acceder a la pantalla X. En la VM, como el usuario que posee la sesión de escritorio:

# Quick fix (run once per session)
xhost +local:vmrobot

# Permanent fix - add to ~/.xprofile or ~/.xinitrc
echo "xhost +local:" >> ~/.xprofile

8. Elegir la estrategia de usuario SSH

Hay dos enfoques para el usuario SSH:

Opción A: Usuario vmrobot dedicado (predeterminado)

  • Más seguro — permisos limitados, no puede romper accidentalmente la configuración del escritorio
  • Requiere xhost +local:vmrobot para acceso X11 (paso 7)
  • Configurar VM_DESKTOP_USER si necesitas comandos que requieran el contexto del usuario de escritorio (portapapeles, gestor de contraseñas, dbus):
    # On the VM, allow vmrobot to run commands as your desktop user
    echo 'vmrobot ALL=(sergey) NOPASSWD: ALL' | sudo tee /etc/sudoers.d/vmrobot-desktop
    sudo chmod 440 /etc/sudoers.d/vmrobot-desktop
    
    Luego configurar VM_DESKTOP_USER=sergey en tu configuración. Usar ssh_execute("xclip -selection clipboard -o", as_desktop_user=True).

Opción B: SSH directamente como el usuario de escritorio

  • Más simple — acceso completo al escritorio de fábrica, sin necesidad de xhost ni sudo
  • Configurar VM_USER con tu nombre de usuario de escritorio (ej., sergey)
  • Todos los comandos se ejecutan con permisos completos de escritorio
  • Mejor para VMs personales/de desarrollo donde el aislamiento no es una preocupación

9. Encontrar la dirección IP de tu VM

# From the host
virsh -c qemu:///system domifaddr manjaro

# Or from inside the VM
ip addr show | grep "inet 192.168.122"

10. Probar la conexión

# Test SSH
ssh vmrobot@192.168.122.XX

# Test X11 automation
ssh vmrobot@192.168.122.XX 'DISPLAY=:0 xdotool getmouselocation'

# Test screenshot
ssh vmrobot@192.168.122.XX 'DISPLAY=:0 scrot /tmp/test.png && echo Success'

Instalación

1. Clonar el repositorio

git clone https://github.com/Neanderthal/mcp-qemu-vm.git
cd mcp-qemu-vm

2. Instalar dependencias

Usando uv (recomendado):

uv venv && source .venv/bin/activate
uv pip install -r requirements.txt

Usando pip:

python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

Configuración

Configurar variables de entorno o crear un archivo .env:

VariablePredeterminadoDescripción
VM_HOST192.168.122.79Dirección IP de la VM
VM_USERvmrobotNombre de usuario SSH
VM_PORT22Puerto SSH
VM_DISPLAY:0Pantalla X11
VM_IDENTITY(vacío)Ruta de clave privada SSH (opcional)
VM_DESKTOP_USER(vacío)Propietario de la sesión de escritorio, si es diferente de VM_USER
VM_LOCALEC.UTF-8Locale UTF-8 forzado para xdotool type (entrada no ASCII)
VM_KNOWN_HOSTS(ninguno)Ruta del archivo known_hosts de SSH (opcional)
VM_CONNECT_TIMEOUT10Tiempo de espera de conexión SSH en segundos

Ver .env.example para una plantilla documentada.

Uso

Configuración del Cliente MCP

Agregar a la configuración de tu cliente MCP (ej., Claude Desktop claude_desktop_config.json):

{
  "qemu-vm-control": {
    "command": "python3",
    "args": ["/path/to/mcp-qemu-vm/server.py"],
    "env": {
      "VM_HOST": "192.168.122.79",
      "VM_USER": "vmrobot",
      "VM_PORT": "22",
      "VM_DISPLAY": ":0"
    }
  }
}

Ubicaciones del archivo de configuración:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%/Claude/claude_desktop_config.json
  • Linux: ~/.config/Claude/claude_desktop_config.json

Desarrollo con MCP Inspector

uv run mcp dev server.py

# With custom environment
VM_HOST=192.168.122.79 VM_USER=vmrobot uv run mcp dev server.py

Ejecución Independiente

python server.py

Referencia de Herramientas

Gestión de Proyectos

Los proyectos organizan todas las salidas (capturas de pantalla, registros, resultados, consejos) en carpetas con marca de tiempo bajo data/projects/.

HerramientaDescripción
project_init(name, description)Crear un nuevo proyecto (requerido antes de las capturas de pantalla)
project_load(project_path)Cargar un proyecto existente
project_list()Listar todos los proyectos
project_info()Obtener estadísticas del proyecto actual
project_log(message, level)Agregar una entrada de registro
project_read_logs(lines, level_filter)Leer registros del proyecto
project_save_result(filename, content)Guardar un archivo de resultado
project_save_advice(title, content)Guardar consejos para futuras sesiones
project_read_advice()Leer todos los consejos guardados

Mouse y Teclado

HerramientaDescripción
move_mouse(x, y, mode)Mover el cursor (modo: "absolute" o "relative")
click(button, count, x, y)Hacer clic en un botón; x, y opcional para mover y hacer clic en una sola operación
click_in_window(x, y, button, count)Hacer clic en coordenadas relativas al área de cliente de la ventana activa
get_active_window_info()ID de ventana activa, título, posición y geometría
scroll(direction, amount)Desplazamiento con rueda del mouse (arriba/abajo/izquierda/derecha) en el cursor
drag(x1, y1, x2, y2, button)Presionar al inicio, arrastrar hasta el final, soltar (seleccionar/deslizador/arrastrar y soltar)
type_text(text, human)Escribir texto (nuevas líneas → Return, seguro UTF-8); human=True escribe a un ritmo más lento y realista (velocidad variable por palabra + pausas aleatorias)
press_keys(keys)Presionar combinación de teclas, ej., ["Ctrl", "L"]
key_down(keys) / key_up(keys)Mantener / soltar una tecla o modificador (ej., Shift-clic)
set_clipboard(text)Cargar el portapapeles de la VM (inserción rápida para ASCII grande)
paste(text)Configurar portapapeles (si text) y Ctrl+V
activate_window(title, window_id)Enfocar y elevar una ventana por título o ID
wait(seconds)Pausar la ejecución
run_actions(actions)Ejecutar una secuencia de acciones en una sola llamada

Ejemplo de Acciones por Lote

[
  {"action": "press_keys", "keys": ["Ctrl", "Shift", "p"]},
  {"action": "wait", "seconds": 0.5},
  {"action": "type_text", "text": "Terminal: Focus Terminal"},
  {"action": "press_keys", "keys": ["Return"]}
]

Localización de Objetos (OCR)

Localizar elementos en pantalla por su texto visible — coordenadas de píxeles exactas, sin adivinar coordenadas. El anfitrión aplica OCR a la captura de pantalla de resolución completa (tesseract) y mapea la coincidencia directamente en la ruta de clic. Funciona con cualquier texto visible, incluidos entornos Citrix/web anidados donde las APIs de accesibilidad no llegan; no encuentra iconos sin etiquetar.

HerramientaDescripción
find_text(query, min_conf)Aplicar OCR a la pantalla; devolver el centro y la caja de cada coincidencia
click_text(query, index, button, count)Encontrar texto y hacer clic en su centro (preciso)
click_text("Submit")          # finds "Submit" and clicks its exact center
find_text("File")             # lists all matches with coordinates
click_text("OK", index=1)     # click the 2nd "OK" if several match

Zoom (ampliar una región, luego hacer clic con precisión)

Cuando el detalle es demasiado pequeño/de bajo contraste para resolverse en la captura completa, ampliar una región y hacer clic dentro de ella. El servidor mantiene el mapeo del recorte, por lo que un punto que elijas en la imagen ampliada se mapea de vuelta al píxel exacto de pantalla completa — sin cálculos de coordenadas.

HerramientaDescripción
zoom(x, y, width, height, scale)Recortar alrededor de (x, y) y ampliar; devuelve una imagen visible + mapeo
click_zoomed(zx, zy, button, count)Hacer clic en un punto dado en las coordenadas de imagen del último zoom
zoom(800, 600, width=400, height=300, scale=4)  # view a 4× magnified crop
click_zoomed(610, 250)                           # click that spot → exact full-screen pixel

Set-of-Mark (elegir por número)

Para pantallas densas o ambiguas, superponer marcas numeradas en cada elemento de texto detectado y elegir uno por su número — una elección discreta mucho más confiable que estimar coordenadas.

HerramientaDescripción
mark_screen(min_conf, max_marks)Anotar la pantalla con cajas numeradas; devuelve la imagen + una leyenda
click_mark(n, button, count)Hacer clic en el elemento etiquetado n
mark_screen()        # view the annotated screenshot + legend (0 -> "File", 1 -> "Edit", …)
click_mark(1)        # click element #1 at its exact center

Requisitos del anfitrión: tesseract (el binario) más pillow y pytesseract en el entorno Python del servidor. Estos son opcionales — el resto del servidor funciona sin ellos; solo las herramientas OCR (find_text/click_text) y zoom (zoom) los necesitan (zoom solo necesita pillow):

# Arch/Manjaro host
sudo pacman -S tesseract tesseract-data-eng
uv pip install pillow pytesseract

Operaciones SSH

HerramientaDescripción
ssh_execute(command, as_desktop_user)Ejecutar un comando de shell en la VM
ssh_upload(local_path, remote_path)Subir archivo a la VM
ssh_download(remote_path, local_path)Descargar archivo de la VM
ssh_connection_info()Obtener estado de la conexión

Capturas de Pantalla

HerramientaDescripción
take_screenshot()Capturar pantalla (requiere proyecto activo)

Las capturas de pantalla se guardan en la carpeta screenshots/ del proyecto y se exponen como recursos MCP en vm://screenshot/{id}.

Calibración de Pantalla

HerramientaDescripción
display_calibration_info(recalibrate)Mostrar los factores de escala xdotool↔captura; recalibrate=True los vuelve a sondear

Los factores de escala se detectan automáticamente al inicio (HiDPI/desajustes de escala); las herramientas de coordenadas los aplican de forma transparente.

Flujo de Trabajo Típico

1. project_init("my-task", "Description")
2. take_screenshot()
3. ... perform VM operations ...
4. project_read_logs()
5. project_save_result("output.txt", data)
6. project_save_advice("Title", "Lessons learned...")

Para trabajo continuo:

1. project_list()
2. project_load("data/projects/...")  # Shows any saved advice
3. ... continue work ...

Mejores Prácticas para Automatización con LLM

Estas lecciones se aprendieron del uso en el mundo real y ayudan a evitar errores comunes.

1. Siempre Capturar Pantalla Antes de Acciones

Antes de CUALQUIER interacción:

  1. take_screenshot()
  2. Analizar la imagen
  3. Identificar el foco actual (qué ventana/campo está activo)
  4. Solo entonces proceder con acciones

Nunca omitir capturas de pantalla para "ahorrar tiempo" - las acciones a ciegas conducen a errores.

2. No Confiar en Clics del Mouse para el Foco

Hacer clic en una ventana/terminal NO cambia el foco de manera confiable, especialmente en:

  • Entornos anidados (Citrix, escritorio remoto)
  • Conexiones de alta latencia
  • Aplicaciones con múltiples paneles (VS Code, IDEs)

Usar atajos de teclado en su lugar:

[
  {"action": "press_keys", "keys": ["Ctrl", "Shift", "p"]},
  {"action": "wait", "seconds": 0.5},
  {"action": "type_text", "text": "Terminal: Focus Terminal"},
  {"action": "wait", "seconds": 0.3},
  {"action": "press_keys", "keys": ["Return"]},
  {"action": "wait", "seconds": 0.5}
]

Luego take_screenshot() para verificar antes de escribir.

3. Tiempos de Espera Requeridos

Después de Esta AcciónTiempo de Espera
Abrir Paleta de Comandos0.5s
Escribir texto de búsqueda0.3s
Presionar Enter/Return0.5-1.0s
Ejecución de comando1.0-2.0s
Cambio de ventana/foco0.5s

Nunca disparar acciones rápidamente - pueden llegar fuera de orden.

4. Usar Acciones por Lote

Usar run_actions() en lugar de llamadas de herramienta separadas para reducir latencia y garantizar el orden:

# Instead of 5 separate calls:
run_actions([
    {"action": "press_keys", "keys": ["Ctrl", "Shift", "p"]},
    {"action": "wait", "seconds": 0.5},
    {"action": "type_text", "text": "command"},
    {"action": "wait", "seconds": 0.3},
    {"action": "press_keys", "keys": ["Return"]}
])

5. Limitación del Alcance SSH

ssh_execute solo alcanza la primera capa de VM. Para entornos anidados (VM → Citrix → Windows), usar automatización de UI para escribir comandos en la terminal visible.

6. Comandos de Recuperación

ProblemaSolución
Escribió en la ventana equivocada (pocos caracteres)Escape → u (deshacer en Vim)
Múltiples líneas en el lugar equivocadoEscape → uuuuuuu
Archivo corruptoEscape → :e! → Enter (recargar)
Revertir en VS CodeCtrl+Shift+P → "Revert File"

7. Errores Comunes a Evitar

  1. Escribir inmediatamente después de hacer clic en la terminal (el foco puede no haber cambiado)
  2. Omitir capturas de pantalla para "ahorrar tiempo"
  3. Usar ssh_execute para comandos de entorno anidado
  4. No esperar entre acciones
  5. Asumir que el foco cambió sin verificación

Arquitectura

┌─────────────┐         SSH          ┌──────────────┐
│             │ ◄──────────────────► │              │
│  MCP Server │                      │   QEMU VM    │
│   (Host)    │                      │   (Linux)    │
│             │                      │              │
└──────┬──────┘                      └──────────────┘
       │                                    │
       │ MCP Protocol                       │
       │ (stdio)                            │
       │                                    │
       ▼                                    ▼
┌─────────────┐                      xdotool, scrot
│  LLM Client │                      X11 automation
│  (Claude)   │
└─────────────┘

Topología de red:

┌────────────────────────────────────────────────────┐
│  Host (192.168.122.1)                              │
│  ┌──────────┐                                      │
│  │ virbr0   │◄── NAT bridge                        │
│  └────┬─────┘                                      │
│       │                                            │
│  ┌────┴─────┐                                      │
│  │ QEMU VM  │ 192.168.122.79                       │
│  │ (manjaro)│                                      │
│  └──────────┘                                      │
└────────────────────────────────────────────────────┘

Despacho de acciones de UI

Todas las interacciones de xdotool se construyen a partir de un pequeño conjunto de constructores de comandos puros (_type_cmd, _keys_cmd, _click_cmd, _move_cmd), por lo que el comando de shell para una acción se construye en exactamente un lugar. Cada constructor toma una cadena de visualización ya shlex.quote()da y devuelve el comando a ejecutar en la VM; los constructores también gestionan la validación de entrada (patrón de nombre de tecla, mapa de botones, límite de recuento de clics) y el prefijo de configuración regional UTF-8 para la escritura.

Dos rutas consumen estos constructores:

  • Herramientas independientes (move_mouse, click, type_text, press_keys, wait) — herramientas MCP expuestas individualmente con firmas tipadas y docstrings completos.
  • run_actions — la ruta por lotes. Despacha a través de ACTION_HANDLERS, un registro {name: async handler} que es la única fuente de verdad sobre qué acciones admite un lote. Cada manejador comparte la firma async (app_ctx, display, action_dict) -> summary. Los nombres de acción desconocidos generan un error y detienen el lote (coherente con su contrato de "se detiene ante el primer error").
run_actions(actions)
      │  for each action
      ▼
ACTION_HANDLERS[name]  ──►  _act_*(app, display, action)
                                   │ uses
                                   ▼
                       _type_cmd / _keys_cmd / _click_cmd / _move_cmd
                                   │
                                   ▼
                             run_vm_cmd(ssh, …)  ──►  xdotool over SSH

Añadir una nueva acción de lote: escribe un manejador _act_<name>(app, display, action) (reutilizando o añadiendo un constructor _*_cmd) y añade una entrada a ACTION_HANDLERS. No se necesitan cambios en el bucle de despacho.

Estructura del proyecto

mcp-qemu-vm/
├── server.py           # Main MCP server (single file)
├── pyproject.toml      # Project metadata, ruff & pytest config
├── requirements.txt    # Python dependencies
├── .env.example        # Documented env var template
├── test_ssh_tools.py   # Unit tests (no-VM) + manual SSH smoke check
├── LICENSE             # MIT
├── data/
│   └── projects/       # Project folders
│       └── YYYYMMDD-HHMMSS_name/
│           ├── screenshots/
│           ├── logs/
│           ├── results/
│           └── advice/
└── README.md

Problemas conocidos y limitaciones

Problemas confirmados en uso real de entorno anidado (host → Citrix → Windows → Outlook). Cada uno enumera el síntoma, la causa raíz y la solución actual.

#1 y #2 están corregidos en server.py. #3–#6 son limitaciones inherentes del entorno anidado (política de sesión de Citrix/RDP) o de la arquitectura (SSH solo llega a la primera capa de VM) — no se pueden corregir en este servidor, por lo que las soluciones alternativas siguen siendo el enfoque recomendado.

1. type_text falla con texto cirílico / no ASCII — CORREGIDO

  • Síntoma: type_text (y cualquier xdotool type con texto no ASCII) falla con estado de salida 1. La ejecución directa revela: Invalid multi-byte sequence encountered / xdo_enter_text_window reported an error. El texto ASCII se escribe correctamente.
  • Causa raíz: xdotool type decodifica la entrada multibyte usando la configuración regional actual, pero el entorno SSH de vmrobot / usuario de escritorio no tiene configuración regional UTF-8 (LANG vacía, distribución de teclado us básica). Sin una LC_CTYPE UTF-8, el UTF-8 multibyte (cirílico, etc.) no se puede decodificar.
  • Corrección (aplicada): type_text y el paso de escritura run_actions ahora prefijan la invocación de xdotool con LC_ALL=$VM_LOCALE (por defecto C.UTF-8), por lo que el texto no ASCII funciona sin configuración adicional. Anula con la variable de entorno VM_LOCALE si la VM carece de C.UTF-8 (por ejemplo, establece VM_LOCALE=ru_RU.utf8; comprueba las configuraciones regionales disponibles con locale -a).

2. Los saltos de línea incrustados en el texto escrito se convierten en glifos literales, no en Enter — CORREGIDO

  • Síntoma: Escribir texto multilínea (por ejemplo, xdotool type con \n, o type --file -) en un editor enriquecido como Outlook produce un párrafo continuo con glifos sueltos de cuadro/carácter de control donde deberían estar los saltos de línea — los saltos de párrafo se pierden.
  • Causa raíz: En esta ruta anidada Citrix → Windows, el \n (LF) se entrega como un carácter de control literal al editor en lugar de interpretarse como una pulsación de Retorno.
  • Corrección (aplicada): type_text (y el paso de escritura run_actions) ahora dividen el texto en líneas, escriben cada línea a través de stdin y envían los saltos de línea como pulsaciones de tecla Return explícitas en lugar de un LF literal. \r\n y \r se normalizan primero. Esto funciona tanto en terminales como en editores enriquecidos — no se necesita división por parte del llamador.

3. La redirección del portapapeles puede estar deshabilitada en la sesión invitada

  • Síntoma: Establecer el portapapeles del host/X (xclip -selection clipboard) y pegar con Ctrl+V no transfiere texto a la capa de Windows/Citrix.
  • Causa raíz: La redirección del portapapeles está desactivada en la política de sesión de Citrix/RDP, por lo que la sesión interna tiene su propio portapapeles aislado.
  • Solución: no confíes en copiar/pegar para inyectar texto a través del límite de anidamiento; recurre a la escritura (ver problemas #1 y #2).

4. El foco se roba silenciosamente después de operaciones largas

  • Síntoma: Una secuencia larga de escritura/automatización tiene éxito, pero las pulsaciones posteriores (por ejemplo, BackSpace para corregir texto) no tienen efecto — verificado por una diferencia de píxeles cero entre las capturas antes/después.
  • Causa raíz: Una notificación de escritorio/correo (por ejemplo, ventana emergente de nuevo correo) roba el foco a mitad de camino, por lo que las teclas posteriores van a la ventana equivocada.
  • Solución: reafirma el foco haciendo clic en la ventana/campo de destino inmediatamente antes de cada ráfaga de teclado, y verifica el resultado con una captura de pantalla (recorta la región y compara) en lugar de confiar en el código de salida de la herramienta. Mantén las ráfagas de teclado cortas para que un robo de foco corrompa menos.

5. Los clics del ratón no son fiables para cambiar de ventana/foco

Ver Mejores prácticas §2. En entornos anidados, un clic a menudo eleva una ventana de fondo diferente a la prevista; no hay un Alt+Tab fiable (se filtra al WM del host). Prefiere la barra de tareas / controles de ventana de la aplicación y verifica cada cambio con una captura de pantalla.

6. ssh_execute solo llega a la primera capa de VM

ssh_execute llega solo al host/primera VM. Los comandos no llegan a las capas internas de Citrix / Windows — usa automatización de UI (type_text, press_keys, run_actions) para esas. Ver Mejores prácticas §5.

Solución de problemas

No se puede conectar a la VM

  1. Comprueba que la VM está en ejecución:

    virsh -c qemu:///system list
    
  2. Comprueba que la red está activa:

    virsh -c qemu:///system net-list
    # If default is inactive:
    virsh -c qemu:///system net-start default
    
  3. Comprueba que la VM tiene IP:

    virsh -c qemu:///system domifaddr <vm-name>
    
  4. Prueba la conectividad SSH:

    ssh vmrobot@192.168.122.XX
    

El ratón/teclado no funciona

  • Verifica que xdotool está instalado en la VM: which xdotool
  • Comprueba la pantalla X11: echo $DISPLAY (debería ser :0)
  • Prueba manualmente: DISPLAY=:0 xdotool getmouselocation
  • ¿El texto no ASCII falla con salida 1 / "Invalid multi-byte sequence"? Falta la configuración regional UTF-8 — ver Problemas conocidos #1.
  • ¿Los saltos de línea no funcionan / texto continuo? Ver Problemas conocidos #2.

Las capturas de pantalla fallan / Error de autorización X11

Si ves Authorization required, but no authorization protocol specified:

Solución rápida (ejecutar como propietario de la sesión X en la VM):

xhost +local:vmrobot

Solución permanente - Añade a ~/.xprofile:

xhost +local:

Verifica el acceso:

# Check current xhost settings
DISPLAY=:0 xhost

# Should show:
# access control enabled, only authorized clients can connect
# LOCAL:

Problemas de red de la VM

# Restart the default network
virsh -c qemu:///system net-destroy default
virsh -c qemu:///system net-start default

# Check virbr0 bridge exists
ip addr show virbr0

Licencia

Publicado bajo la Licencia MIT — © 2026 Sergey Istomin.

Relacionados