Agents that "use a computer" usually take over your screen. They move your mouse, steal focus and drive your logged-in browser. agent-workspace-linux gives the agent its own desktop: a private X11 or native Wayland display with its own window manager or compositor, apps, clipboard and browser, on any of six backends. The agent launches apps, types, clicks, takes screenshots and browses there. You watch, pause or stop it from a small floating viewer. It speaks MCP over stdio, so it works with Claude Code, Codex and other MCP hosts.
Why this project
This is not a tool for driving your actual desktop. For that, use its sibling computer-use-linux. The two complement each other.
Install
1. System packages. These cover the default X11 backend:
sudo apt install xvfb openbox xdotool xauth x11-utils imagemagick xclip \
dbus-daemon pkg-config libxkbcommon-x11-dev
Other backends need their own packages.
2. The binary. Pick one:
# From a clone: builds into ~/.local/bin/ and installs the bundled skill (safe to rerun)
./install.sh
# npm: downloads the prebuilt binary from the matching release and checks its sha256
npm install -g @agent-sh/agent-workspace-linux
# cargo: builds from git and installs only the binary (add --tag v0.6.0 to pin)
cargo install --git https://github.com/agent-sh/agent-workspace-linux
Prebuilt x86_64 and aarch64 binaries with .sha256 sidecars are on GitHub Releases. Verify with sha256sum -c, chmod +x, and put the binary on your PATH.
3. Register the MCP server. For hosts that read .mcp.json:
{
"mcpServers": {
"agent-workspace-linux": {
"command": "/home/YOU/.local/bin/agent-workspace-linux",
"args": ["mcp"]
}
}
}
./install.sh installs the skill to ~/.codex/skills/ by default. Codex MCP registration is opt-in: use --codex-configure only for generic MCP-host workflows. In Codex for Linux, use the dedicated Agent Workspaces feature page to configure the backend and permission ceiling, so the generic MCP settings pages stay clean. If an older install still appears there, run ./install.sh --clean-codex-config to remove the stale server and tool tables.
Other flags: --permissions, --skills-dir, --no-skill, --dry-run. See install.sh --help.
With cargo or a prebuilt binary, copy skills/agent-workspace-linux/ into your skills directory if you want the bundled skill.
Quick start
# 1. What can this machine run? (dependencies, displays, backends)
agent-workspace-linux doctor
# 2. Preview a workspace without creating anything
agent-workspace-linux workspace start --dry-run
# 3. Create the hidden workspace (explicit acknowledgement required)
agent-workspace-linux workspace start --ack-hidden-workspace --purpose "QA run"
# 4. Watch it in the floating viewer
agent-workspace-linux viewer
# 5. Launch an app, look at it, stop the workspace
agent-workspace-linux workspace launch --name editor -- xterm
agent-workspace-linux workspace observe --screenshot --output /tmp/ws.png
agent-workspace-linux workspace stop
Through an MCP host you don't run these by hand. The agent calls the matching tools, routed by the bundled skill.
Desktop backends
Choose a backend with --session on workspace start and workspace open-profile, or with "session": {"backend":"hyprland"} in MCP calls and saved profiles. Explicit CLI or MCP input overrides the profile. With neither, the workspace uses X11.
doctor reports ready_for_<backend>_workspace for each one and lists anything missing.
Backend packages
Extra packages on top of the X11 set, for Ubuntu 26.04:
"Render node" means your user can read and write a /dev/dri/renderD* device, usually through the render group. On a host with an NVIDIA GPU and another GPU, Hyprland renders on the other one.
Ubuntu does not package cosmic-comp. Install the Pop!_OS build that the COSMIC probe pins, with its libdisplay-info1 dependency:
curl -fLO https://apt.pop-os.org/release/pool/noble/cosmic-comp/8cb1802caada438bb5cc7670dfabdaca5fad0502/cosmic-comp_0.1~1792508925~24.04~8cb1802_amd64.deb
curl -fLO https://archive.ubuntu.com/ubuntu/pool/universe/libd/libdisplay-info/libdisplay-info1_0.1.1-2build1_amd64.deb
cat > cosmic.sha256 <<'EOF'
f493771502e56245158906e92b4da0b5a7f0bc2fb1926f3feb81d6294541b427 cosmic-comp_0.1~1792508925~24.04~8cb1802_amd64.deb
a3bfce18d53aa9d635d899f1f555e84bbbf2c859d80cef3b4eb1c36b2c772026 libdisplay-info1_0.1.1-2build1_amd64.deb
EOF
# Installs only when both checksums match
sha256sum -c cosmic.sha256 && sudo apt install \
./cosmic-comp_0.1~1792508925~24.04~8cb1802_amd64.deb ./libdisplay-info1_0.1.1-2build1_amd64.deb
Scale and coordinates
Use --scale 1.5 (or "scale":1.5 in a profile or MCP session) on KWin, GNOME, Hyprland or COSMIC for a 150% output. On every native Wayland backend, width, height, window geometry and pointer coordinates are screenshot pixels:
- KWin converts between those pixels and its logical coordinates. It aligns output sizes to logical pixels; status reports the effective size.
- GNOME, Hyprland and COSMIC use physical pixels throughout, including at fractional scales.
Apps use native Wayland by default. Rootless XWayland stays available for legacy clients. The viewer, IPC, permission ceiling and Chrome profile import rules are the same on every backend.
Backend notes
KWin runs as a native Wayland virtual compositor, with no Xvfb underneath. Each workspace owns its Wayland socket, session D-Bus, settings, input and clipboard. This is KWin, not the full Plasma shell.
GNOME Shell and Mutter run with a private headless output and software rendering, with no GPU or parent compositor.
agent-workspace-linux workspace start --ack-hidden-workspace \
--session gnome --scale 1.5 --width 1200 --height 900
- Scale. Mutter validates scale against the physical output mode: 1200x900 supports 150%, 960x720 does not. Unsupported combinations fail at startup. GTK clients may use a larger integer buffer scale on fractional outputs.
- Text input. Native Unicode text needs a focused Wayland text-input field. A request that cannot be typed in full is rejected before any key is sent. Legacy editable fields (GTK, Qt, Chromium, Firefox, Electron) use the private accessibility bus, with client PID, focus, caret, selection and text readback checks. Unsupported legacy fields return an error rather than drop characters.
- xterm. Text goes through PRIMARY and xterm's Shift+Insert binding. A short-lived X client owns PRIMARY, serves the text once to that xterm only, then exits and leaves PRIMARY empty. CLIPBOARD is not touched.
- Screenshots. Window screenshots paint the window offscreen and do not change focus or stacking.
- Locked-down shell. The session has private session and accessibility buses and an empty system bus. A bundled Shell extension exposes fixed controls only to the workspace daemon. The overview, run dialog, settings launchers and unrelated extensions are disabled, and Shell Eval and extension installation are denied on its bus.
Hyprland runs on a private KWin renderer with a separate headless output, native toplevel capture and authenticated XWayland. See the Hyprland guide.
cosmic-comp runs under a software-only Weston headless parent, with no host display, input device or GPU. It supports fractional scaling and authenticated XWayland clients. See the COSMIC guide.
A private Xvfb display with i3 at scale 1. The private i3 config has no launcher bindings, bar or startup commands. Moving or resizing a tiled window makes it floating. Minimize uses the i3 scratchpad; show returns the window to the current workspace. Window capture leaves focus unchanged.
Browser automation
Browser commands need a workspace-owned Chrome with loopback DevTools. open-browser launches one with the right flags:
# Launch workspace-owned Chrome (--dry-run prints the argv first)
agent-workspace-linux workspace open-browser --dry-run
agent-workspace-linux workspace open-browser "http://127.0.0.1:8111"
# Drive it; the URL is positional
agent-workspace-linux workspace browser-navigate "http://127.0.0.1:8111/page" --wait-ms 1500
agent-workspace-linux workspace browser-snapshot --max-text-chars 2000
browser-navigate and browser-click re-read the page after acting, so target.url and target.title describe the page you landed on. If that refresh fails, the response carries a warning rather than stale metadata.
DevTools binds to an ephemeral loopback port, and Chrome rejects WebSocket upgrades whose Origin is not allow-listed, port included. No static --remote-allow-origins value can match (* would, but it disables that CSRF protection). Omit the Origin header instead:
ws = websocket.create_connection(ws_debugger_url, suppress_origin=True)
Security model
The workspace is not a sandbox. Apps launched in it run as you, with your files, network, keyring and SSH agent. Only their windows stay in the workspace. An agent acting there acts for you, so the controls decide what it may do, not what it can reach.
What decides what an agent does
What a workspace app gets
- Your session. Your home, config and data directories, network, SSH and GPG agents, sound, your settings (dconf) and your keyring: the workspace relays the Secret Service, KWallet and dconf from your session bus, so Chrome on your profile has your saved logins.
- The workspace's display and bus. A private display and its own session bus, so a single-instance app opens its window in the workspace, not on your desktop.
See docs/permission-model.md and SECURITY.md.
Cleanup
Managed terminal sessions end when their workspace stops, even after the terminal window has closed. Stale cleanup uses the saved launch and session identity. Verified terminal cleanup uses Linux pidfds; older kernels without SO_PEERPIDFD use pidfd_open with process, session and exact socket-argument checks. If ownership cannot be verified, stop and cleanup keep the runtime and report an error instead of signaling an unrelated process.
Core concepts
The skill
The MCP exposes about 86 tools. To keep them out of the agent's context, it ships a skill at skills/agent-workspace-linux/SKILL.md. Only its short description stays loaded. When a task needs a background desktop or browser, the agent reads the skill, which routes it through the phases (orient, start, observe, act, stop) and loads tool schemas on demand.
./install.sh installs it to ~/.codex/skills/. Use --skills-dir ~/.claude/skills for Claude.
Features
- Hidden X11 or native Wayland workspaces on six backends, with window listing, screenshots, keyboard and mouse input, clipboard and per-app logs, all scoped to the workspace.
- Apps that act as you: your files, network, keyring and SSH agent, with windows kept in the workspace.
- Read-only, write and destructive marks on every tool, and an optional daemon-enforced app allowlist.
- Workspace-owned browser control over loopback CDP: discover targets, read pages, navigate, extract results.
- A native floating viewer with live pause, read-only and stop.
- Saved profiles with setup and startup commands.
- A bundled skill for low-context, on-demand tool use.
Limitations
- Linux only. The viewer is validated on X11 and XWayland; native Wayland support is still maturing.
- Pre-1.0. Interfaces and tool schemas can change between versions.
- Not a sandbox. Workspace apps run as you. Do not launch code you would not run yourself.
- One instance per profile. An app that keeps one process per profile hands a second launch to the one already running, and the window opens there. A Chromium browser launched without
--user-data-dirwhile it runs on your desktop is refused for that reason. Electron apps such as Slack can do the same: start them in the workspace only when they are not open on your desktop. - Single-user trust model. The control socket is a same-uid Unix socket (mode 0600), with no cross-user protection by design.
- Live viewer control is not the hard boundary. The app allowlist is. Unreadable live-control state fails closed for mutating daemon IPC.
Validation
Each native backend has a disposable Ubuntu 26.04 acceptance probe. Build the binary, then run the script; each prints its artifact directory and returns nonzero on failure.
Portal consent in the computer-use-linux probe applies only to the disposable workspace.
For Slack or GitHub account-session validation, the only human step is approving the Chrome or Chromium user-data directory:
node scripts/mcp_real_profile_browser_session_dogfood.js \
--approved-user-data-dir ~/.config/google-chrome \
--site github
The helper refuses obvious active-profile hazards, copies the approved profile to a disposable directory, launches the browser-session template through the MCP and proves the logged-in page with workspace browser tools, not the host Chrome bridge. Use --site slack for Slack, set BROWSER_BIN=chromium when needed, and add --expect-text for account-specific page text.
Docs
- Permission boundary: the authority model.
- Hyprland backend and COSMIC backend: dependencies, internals and probes.
- GPUI viewer direction: the visible control surface.
- SECURITY.md: trust model and vulnerability reports.
Related
computer-use-linux drives the user's real Linux desktop. It is the inverse of this project: it automates the desktop you are on, while agent-workspace-linux gives the agent a separate one in the background. Use them together.
Contributing
Build with cargo build --locked. Before pushing, run cargo fmt --check, cargo clippy --locked -- -D warnings, cargo test --locked, git diff --check and, for runtime changes, scripts/integration_smoke.sh. See CONTRIBUTING.md and CODE_OF_CONDUCT.md.
License
MIT © Avi Fenesh