Skip to main content
Glama
XLRC888
by XLRC888
README.md
# hypruse [hybrid-use]

Hyprland-native computer-use backend for MCP-capable agents (OpenCode, Claude Code, and anything speaking MCP).

No Xvfb, no X11 hacks. Uses Hyprland's native IPC + wlr virtual pointer protocols for cursor/keyboard injection on actual Wayland sessions.

## how it works

Hyprland has no xdotool. Wayland doesn't let clients inject input into other surfaces. But hypruse bridges the gap with:

- **grim** / **grimblast** for screen capture (wlr-screencopy)
- **hyprctl dispatch movecursor** for absolute cursor positioning (Hyprland's native IPC)
- **ydotool** (uinput) or **wlrctl** (wlroots virtual-pointer protocol) for click/type/scroll injection
- Coordinate math that maps screenshot pixels to compositor-space coordinates (handles multi-monitor)

## install

```bash
# install deps
sudo pacman -S grim ydotool  # or grimblast from AUR

# start ydotoold daemon
systemctl --user enable --now ydotoold

# install hypruse
pip install .
# or
uv tool install .
```

### backend options

| backend | pros | cons |
|---------|------|------|
| `ydotool` (default) | works on any compositor | needs `ydotoold` daemon + uinput perms |
| `wlrctl` | no daemon needed | wlroots-only (Hyprland qualifies) |

## usage

### CLI (manual testing)

```bash
hypruse screenshot -o test.png
hypruse click 800 450
hypruse move 400 300
hypruse type "hello world"
hypruse key ctrl+s
hypruse monitors
hypruse active
hypruse cursorpos
```

### MCP server (for agents)

```bash
hypruse-mcp
```

Flags: `--dry-run`, `--confirm-destructive`, `--target-width 1600`, `--target-height 900`, `--backend auto`

## MCP configuration

### OpenCode (`~/.config/opencode.json`)

```json
{
  "mcpServers": {
    "hypruse": {
      "command": "hypruse-mcp",
      "args": []
    }
  }
}
```

### Claude Code (`.mcp.json` in project, or `~/.mcp.json`)

```json
{
  "mcpServers": {
    "hypruse": {
      "command": "hypruse-mcp",
      "args": []
    }
  }
}
```

Or via CLI:

```bash
claude mcp add hypruse --cmd hypruse-mcp
```

### available MCP tools

| tool | params | what it does |
|------|--------|-------------|
| `screenshot` | `region?`, `monitor?` | captures screen, returns PNG + display dims |
| `zoom` | `region` | crops+scales a region from last screenshot |
| `move_mouse` | `x`, `y` | moves cursor (screenshot space -> compositor space) |
| `click` | `x`, `y`, `button?`, `clicks?`, `keys?` | move + click |
| `mouse_down` | `button?` | press and hold |
| `mouse_up` | `button?` | release |
| `drag` | `path`, `button?`, `keys?` | click-drag through points |
| `scroll` | `x?`, `y?`, `dx`, `dy` | move + scroll |
| `type_text` | `text` | keyboard type |
| `key` | `combo` | key combo (both `ctrl+s` and `["CTRL","S"]` work) |
| `hold_key` | `combo`, `duration_s?` | hold for duration |
| `wait` | `duration_s?` | sleep |
| `get_monitors` | - | monitor layout json |
| `get_active_window` | - | active window info |

## safety features

- **deny-list**: certain key combos (ctrl+c, alt+f4, etc) trigger confirmation prompts
- **action log**: every call logged to `~/.local/state/hypruse/actions.log`
- **dry-run**: `--dry-run` logs everything but executes nothing

## coordinate system

All coordinates are in the **last screenshot's pixel space**. Internally hypruse translates them to Hyprland's compositor space using `hyprctl monitors -j` offsets. The scale factor from original resolution to delivered screenshot is handled transparently.

## deny list config

`~/.config/hypruse/config.json`:

```json
{
  "deny_list": ["ctrl+c", "alt+f4", "super+q"],
  "target_width": 1600,
  "target_height": 900,
  "backend": "auto"
}
```