Skip to main content
Glama

quickshell-mcp

Un servidor MCP que ejecuta una aplicación de quickshell (QML) y la maneja.

El servidor lanza la aplicación dentro de un sway headless. Toma capturas de pantalla de las ventanas, vuelca el árbol de objetos de una ventana y lee o escribe el estado de QML. El compositor proporciona la pantalla; un perfil proporciona los backends.

El servidor es agnóstico de la aplicación. El perfil le indica qué directorio de configuración ejecutar, qué archivo de entrada cargar, qué procesos de backend iniciar y qué macros ofrecer. Le das al servidor un perfil, y el servidor maneja esa aplicación.

Ejecutar

nix run inicia el servidor. Habla MCP por stdio.

Ejecuta el ejemplo counter:

nix run ~/git/quickshell-mcp -- --profile ~/git/quickshell-mcp/examples/counter/profile.json

Apunta --profile al JSON del perfil de cualquier aplicación.

Related MCP server: kwin-mcp

Manejar la aplicación

Operas la aplicación a través de herramientas. Cada llamada a una herramienta devuelve un resultado. Una llamada rechazada devuelve un error MCP con el mensaje <code>: <detail>.

El bucle:

  1. screenshot(window_index): ver la aplicación como PNG.

  2. windows(): enumerar las ventanas. tree(window_index, root_path, max_depth): mapear el árbol visual de una ventana. Cada nodo informa de un path (una cadena de índices de hijo como 0/2/1) y su objectName.

  3. find(name): obtener las rutas de índice de hijo de los nodos cuyo objectName es name.

  4. Construye un selector que nombra un nodo y un miembro.

  5. get_property / set_property / invoke / qml_eval / macro: leer o cambiar el estado.

  6. screenshot(window_index) de nuevo: ver el resultado.

Selectores

Un selector nombra un nodo y un miembro. Toma una de tres formas:

  • wN.prop: la propia ventana N, y luego el miembro prop. Ejemplo: w0.count. Las propiedades de la propia ventana viven aquí.

  • wN/path.prop: la ventana N, luego una ruta de índices de hijo bajo su elemento de contenido, y luego el miembro prop. Ejemplo: w0/0/2.text.

  • @objectName.prop: el primer nodo con ese objectName, y luego el miembro prop. Ejemplo: @saveBtn.enabled.

find devuelve rutas simples como 0/2/1. Conviértela en un selector como wN/<path>.<prop>, o pásala a tree(root_path=...).

Un selector @objectName se resuelve a la primera coincidencia. Una aplicación que repite un control por elemento añade el nombre del elemento como sufijo al objectName (activateToggle-alpha), así que cada control tiene un nombre único.

screenshot requiere que el elemento de contenido de la ventana contenga exactamente un hijo visual que cubra la ventana.

Herramientas

Herramienta

Argumentos

Efecto

ping

ninguno

Devuelve ready una vez que la aplicación se ha cargado, loading antes.

windows

ninguno

Enumera las ventanas: índice, tipo, título, visible, tamaño.

pegatina

window_index=0, full_page=false

Toma una ventana como PNG, componiendo el contenido sobre el color de fondo (opaco) de la ventana para que nunca sea transparente. full_page captura todo el contenido de una vista de desplazamiento.

tree

window_index=0, root_path="", max_depth=0

Vuelca el árbol visual de una ventana como JSON. Los campos de contraseña permanecen ocultos.

get_property

selector

Lee una propiedad QML como JSON.

set_property

selector, json_value

Escribe una propiedad QML a partir de un valor JSON.

invoke

selector, args_json="[]"

Llama a un método QML con un arreglo JSON de argumentos.

qml_eval

expr

Evalúa una expresión JavaScript en el ámbito de la aplicación, donde win(i) y app están disponibles.

find

name, window_index=0

Enumerafa las rutas de índice de hijo de los nodos cuyo objectName es name.

macro

name, params_json="{}"

Ejecuta una macro del perfil, rellenando los valores ${param}.

up

ninguno

Inicia la aplicación. Idempotente mientras la aplicación esté activa.

reset

ninguno

Apaga la aplicación y luego inicia una nueva con los fixtures vuelto a convertir.

down

ninguno

Apaga la aplicación, los backends y el compositor. Una llamada a herramienta mientras está apagada devuelve un error.

qml_eval puede alcanzar cualquier estado de la aplicación. win(i) te da la ventana i; app te da la raíz de la aplicación.

Códigos de error

Una llamada rechazada devuelve un error MCP. El código es uno de: unresolved, no-member, not-callable, too-many-args, no-window, no-node, not-visible, zero-size, grab-refused, multi-root, partial-window, bad-payload, threw.

Perfiles

Un perfil es un pequeño archivo JSON que escribes para cada aplicación. --profile es obligatorio.

Todos los campos viven en quickshell_mcp/profile.cue, con un comentario de documentación en cada uno. El cargador revisa tu perfil contra ese esquema con cue vet antes del inicio, así que un perfil roto falla al inicio con el error del esquema.

El perfil más pequeño ejecuta una aplicación independiente:

{ "config_dir": "." }

config_dir es el único campo obligatorio. Es absoluto, o relativo al archivo de perfil. entry por defecto es shell.qml.

Dentro de cualquier cadena de valor, ${PROFILE_DIR}, ${CONFIG_DIR}, ${WORK} y ${XDG_RUNTIME_DIR} se expanden al inicio.

Mira examples/counter/profile.json para un exemplo de macro, y examples/async-form/profile.json para un backend con una aprueba de preparación, un fixture de ejemplo y env_out.

Genera un JSON Schema para tu editor desde la misma fuente:

cue def --out jsonschema -e '#Profile' quickshell_mcp/profile.cue

Ejemplos

Cada ejemplo es autocontenido y hace las veces de prueba. Cada uno trae un check.py que arranca la aplicación de verdad y comprueba los estados que su README documenta.

python examples/check.py              # every example
python examples/check.py counter      # one of them
  • counter: una aplicación QML diminuta y autocontenida.

  • async-form: un flujo de formulario frontend/backend con un backend falso sobre un socket Unix, una lista de problemas, formularios por perfil con el mismo resultado, estado de ejecución por clave y un flujo de instalación simplificado. Consulta examples/async-form/README.md para ver las tablas de estados y los selectores.

Contribuir

Lanza el dev-shell:

nix develop ~/git/quickshell-mcp

MCP_BACKEND_DELAY_MS se pasa a cada backend que genera un perfil. Un backend que lo adopte ralentizará las transiciones asíncronas lo suficiente para que sean observables entre capturas de pantalla.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI agents to automate Linux desktop GUI by launching and interacting with Wayland applications in isolated virtual KWin sessions, or connecting to live desktops for collaborative automation.
    124 PyPI
    61
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    MCP server for automating and introspecting native Qt applications (QWidget and QML) without source changes. Enables AI agents to control running Qt apps through UI snapshots, element lookup, and real input simulation.
    25
    4
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Enables AI assistants to see, control, and debug PySide6 desktop applications without modifying their source code, by capturing screenshots, inspecting widget trees, performing clicks and typing, reading logs, and running Python inside the app process.
    22
    39 PyPI
    6
    MIT