Hyprland Computer Use
Hyprland/Wayland computer-use MCP: grim screenshots + uinput click/type + hyprctl targeting
Installation
Ajoutez ceci à la configuration MCP de votre client (Claude Desktop : claude_desktop_config.json ; Cursor : .cursor/mcp.json ; VS Code et les autres utilisent la même forme « mcpServers »).
hypr-cua-mcpclaude mcp add hyprland-computer-use -- uvx hypr-cua-mcp==0.1.2
{
"mcpServers": {
"hyprland-computer-use": {
"command": "uvx",
"args": [
"hypr-cua-mcp==0.1.2"
]
}
}
}
Documentation
Tirée du README du projet sur GitHub, actualisée chaque semaine. Les liens ouvrent GitHub.
Sommaire
hypr-cua-mcp
A Hyprland/Wayland computer-use MCP server. It gives an MCP client (Claude Code, etc.) the ability to see your desktop, understand it via the Hyprland tree, and drive it with mouse + keyboard. It runs locally over stdio.
Why this exists: generic Linux computer-use servers are blind to the compositor —
they hand the model a giant multi-monitor screenshot and hope its pixel guesses
land. This server crops to one output (or one window) with grim, downscales to
the vision-API budget, and exposes hyprctl's tree so the model targets windows
by identity, not luck. Coordinates are always given in the screenshot the model
was just shown; the server maps them back to global layout pixels.
Built for Wayland/Hyprland: grim (capture) + a self-owned uinput device
(input) + hyprctl (tree/control) + wl-clipboard. It is the Wayland successor
to i3-computer-use-mcp (X11).
Tools
Window ids are Hyprland addresses (0x…) from list_windows — not integers.
See
| Tool | What it does |
|---|---|
list_windows | Every Hyprland window: id (address), class, title, workspace, output, global rect, floating/focus/visibility |
get_outputs | Active monitors: global geometry, scale, workspace |
screenshot(output?) | Screenshot one output (default: focused), downscaled to the API budget |
screenshot_window(con_id) | Full-res crop of a single window (by address) — best for small text |
get_mouse | Current global pointer location |
Act
| Tool | What it does |
|---|---|
click / double_click / move_mouse / drag / scroll | Pointer actions; coords in a screenshot's pixel space (pass its frame_id). move_mouse(follow=True) also focuses the window under the pointer |
type_text(text, con_id?) / key(keys, con_id?) | Keyboard input; with a con_id, focus that window first and refuse to type if focus doesn't land |
read_clipboard(selection?) / write_clipboard(text, selection?, secret?) | Exact text in/out of the clipboard/primary selection (no OCR); write returns only a length, never the value |
Control Hyprland (needs a Lua config, see Requirements)
| Tool | What it does |
|---|---|
focus_window(con_id) | Focus a window by address (best-effort; see the follow_mouse caveat under Safety) |
launch(command) | Launch an app via Hyprland's exec dispatcher |
goto_workspace(ws) / move_window_to_workspace(con_id, ws) | Switch to / move a window (by address) to a workspace |
set_floating(con_id, enable?) | Toggle a window's floating state |
layout_message(message) | Send a message to the active layout engine (e.g. togglesplit) |
run_dispatch(dsp_expr) | Raw Hyprland dispatcher escape hatch (exit blocked) |
Work off-screen (no window, no focus stolen)
| Tool | What it does |
|---|---|
tmux_list / tmux_new / tmux_run / tmux_keys / tmux_read / tmux_kill | Drive terminals in detached tmux sessions on an isolated hypr-cua socket; it never touches your own tmux sessions |
task_start / task_status / task_list / task_send / task_stop | Run a command as a background task and poll it (running → done + exit code); answer prompts with task_send |
cdp_targets / cdp_open / cdp_navigate / cdp_read / cdp_eval / cdp_click / cdp_type / cdp_close | Drive tabs in a dedicated Chrome over the DevTools protocol, never your main profile (see Browser channel) |
The coordinate model (the important bit)
The Hyprland tree uses global layout pixels spanning all monitors. Screenshots are cropped + downscaled, so every screenshot records a mapping frame (region origin + per-axis scale). Pointer tools accept coordinates in the screenshot's pixel space and translate them:
global_x = region.x + screenshot_x / scale_x
global_y = region.y + screenshot_y / scale_y
Practically: take a screenshot (it returns a frame_id), then call
click(x, y, frame_id=…) with the pixel you see in that image. Pointer tools
reject a missing or stale frame_id rather than guessing against the wrong one.
The uinput pointer positions absolutely: global pixels map to the device's abs
axes via the layout extent read from hyprctl monitors (verified ~1px).
Requirements
- Hyprland on Wayland. Developed and tested on Hyprland 0.56 across three monitors at scale 1.
- A Lua config (
hyprland.lua, Hyprland 0.55+) for the window-control tools. These tools go throughhyprctl evaland Hyprland's Luahl.dispatchAPI:focus_window,launch,goto_workspace,move_window_to_workspace,set_floating,layout_message,run_dispatch, plustype_text/keywith acon_idandmove_mouse(follow=True). Everything else works with any config: screenshots, pointer and keyboard input, the window tree, the clipboard, and the tmux/task/browser channels. - Read-write access to
/dev/uinputfor mouse and keyboard input (see below). - Python 3.11+ and
uv. - It runs inside your Hyprland session: it needs
WAYLAND_DISPLAY,XDG_RUNTIME_DIRandHYPRLAND_INSTANCE_SIGNATUREfrom the environment. MCP clients launched from your desktop pass these through.
Install the dependencies
The server shells out to a handful of CLI tools. screenshot needs grim, the
clipboard tools need wl-clipboard, and everything reads/drives Hyprland through
hyprctl; tmux and a Chromium/Chrome are only needed for the optional terminal
and browser channels.
| Tool | Needed for | Arch | Debian/Ubuntu |
|---|---|---|---|
hyprctl | tree + control (required) | bundled with hyprland | bundled with hyprland |
grim | screenshot / screenshot_window (required) | pacman -S grim | apt install grim |
wl-clipboard | read_clipboard / write_clipboard | pacman -S wl-clipboard | apt install wl-clipboard |
uv | runs the server + syncs Python deps | pacman -S uv | see astral.sh/uv |
tmux | tmux_* / task_* tools (optional) | pacman -S tmux | apt install tmux |
| Chromium / Chrome | cdp_* browser tools (optional) | pacman -S chromium | apt install chromium |
Arch, in one go (Hyprland assumed already installed):
sudo pacman -S grim wl-clipboard uv tmux chromium
Input uses a self-owned uinput device, so the process needs read-write on
/dev/uinput. It needs no root and no ydotoold. Run getfacl /dev/uinput. If it
lists your user with rw (typically through a uaccess udev rule), you're set. If
not, add a rule that grants access to the user logged in at the seat:
echo 'KERNEL=="uinput", SUBSYSTEM=="misc", TAG+="uaccess", OPTIONS+="static_node=uinput"' \
| sudo tee /etc/udev/rules.d/60-hypr-cua-uinput.rules
sudo udevadm control --reload && sudo udevadm trigger
The Python deps (mcp, pillow, evdev, websocket-client) are synced by uv
on first launch.
Miss one and the matching tool says so: calling screenshot without grim
returns "grim not found; install it …" straight to the model, so it can tell you
what to install instead of failing on a cryptic traceback.
Register with your MCP client
It's a local stdio server. The client starts it on demand, and you don't need a clone.
Claude Code:
claude mcp add hypr-cua -- uvx hypr-cua-mcp
Clients with an mcpServers JSON config (Cursor, Gemini CLI, Cline, …):
{
"mcpServers": {
"hypr-cua": {
"command": "uvx",
"args": ["hypr-cua-mcp"]
}
}
}
Codex CLI (~/.codex/config.toml):
[mcp_servers.hypr-cua]
command = "uvx"
args = ["hypr-cua-mcp"]
From a local checkout: the wrapper syncs deps on first launch and makes the browser channel attach-only (see Browser channel).
claude mcp add hypr-cua -- /path/to/hypr-computer-use-mcp/bin/hypr-cua-mcp
Reconnect your client (in Claude Code: /mcp → reconnect) to pick up the server.
Smoke-test the plumbing first (read-only, no clicks):
uvx hypr-cua-mcp --selftest
Browser channel
The cdp_* tools drive Chrome/Chromium tabs over the DevTools protocol, so
browser work needs no window focus. They never open your main profile. By default
they attach to a browser on port 9222. If nothing is listening there, they launch a
throwaway Chrome with a fresh temp profile.
For logins that persist, run a dedicated automation profile and make the server
attach-only with HYPR_CUA_CDP_AUTOLAUNCH=0. The checkout's bin/hypr-cua-mcp
wrapper sets that for you, and bin/hypr-cua-chrome starts the browser. With
uvx, the equivalent is:
chromium --user-data-dir="$HOME/.local/share/hypr-cua-chrome" \
--remote-debugging-port=9222 --remote-allow-origins=http://127.0.0.1:9222 \
--no-first-run --no-default-browser-check
Log in only to what you're happy to automate. Any local process that can reach the debug port can drive that profile.
| Variable | Default | Effect |
|---|---|---|
HYPR_CUA_CDP_PORT | 9222 | DevTools port to attach to (or launch on) |
HYPR_CUA_CDP_AUTOLAUNCH | 1 | 0 = attach-only: never launch a browser, error if nothing is listening |
HYPR_CUA_CDP_USER_DATA_DIR | fresh temp dir | Profile directory for an auto-launched browser |
HYPR_CUA_CDP_BIN | first Chrome/Chromium on PATH | Browser binary to auto-launch |
Set them in your client's server config. In Claude Code:
claude mcp add hypr-cua -e HYPR_CUA_CDP_AUTOLAUNCH=0 -- uvx hypr-cua-mcp. In JSON
configs, add an "env": {…} block next to command.
Safety
- No auth — it's a local stdio server. Don't expose it over a socket.
- There is no sandbox on Wayland: it drives your real desktop and can click anything you can — terminals with sudo, your password manager, Slack, email. Supervise live runs.
- Emergency stop: kill the MCP server (its uinput device dies with it).
follow_mouse=1caveat: Hyprland keyboard focus tracks the pointer, sofocus_window(address)is best-effort — the reliable way to focus a window for typing is to click it via screenshot coordinates.type_text(con_id=…)raises rather than typing into an unverified window.- Input is real uinput, so it resets the compositor idle timer naturally (no
synthetic-input self-lock, unlike X11). See
docs/gotchas.md.
Known limitations
- Typing is US-ASCII only:
type_textskips any other character. For anything else,write_clipboardand paste. - Capture on monitors with scale ≠ 1 is untested, and the crop is likely off there.
- The synthetic pointer doesn't hover-focus. Click to focus, or use
move_mouse(follow=True). cdp_click/cdp_typeinject JavaScript, so pages that insist on trusted input events won't react.
Details, plus the task runner's edge cases:
KNOWN-ISSUES.md.
Project status
Stable and maintained. hypr-cua-mcp does what it set out to do, so the commit log is quiet on purpose. Expect bug fixes, Hyprland-compatibility updates and dependency bumps, not a stream of new features. A quiet repo here means done, not abandoned. Issues and PRs still get answered.
Contributing
Bug reports and fixes are welcome; for anything bigger, open an issue first. See
CONTRIBUTING.md,
and AGENTS.md
for setup, tests, and the Hyprland conventions that bite.
Registry
On PyPI as hypr-cua-mcp and in the MCP
registry as com.smirnovlabs/hypr-cua-mcp (manifest:
server.json).
mcp-name: com.smirnovlabs/hypr-cua-mcp
License
MIT — see LICENSE.
Questions fréquentes
Qu'est-ce que Hyprland Computer Use ?
Hyprland/Wayland computer-use MCP: grim screenshots + uinput click/type + hyprctl targeting
Comment installer Hyprland Computer Use ?
Il s'exécute sur votre machine. Dans Claude Code : claude mcp add hyprland-computer-use -- uvx hypr-cua-mcp==0.1.2. Pour les autres clients, collez la configuration JSON de cette page dans leurs réglages MCP.
Faut-il une clé API pour Hyprland Computer Use ?
Son entrée au registre n'indique aucune clé ni secret obligatoire. Certaines fonctions peuvent tout de même demander un compte auprès du service auquel il se connecte.
Hyprland Computer Use est-il open source ?
Oui. Son code est sur GitHub sous licence MIT.
Qui maintient Hyprland Computer Use ?
Il est publié par Smirnov-Labs. AI.tn le référence sans le maintenir ni le recommander.
Issu du registre MCP officiel. Être listé n'est pas une recommandation : vérifiez le code et les permissions d'un serveur avant de lui donner accès à vos données.