hypruse
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"
}
```
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessSyncing