Claude Code on your Steam machine, controlled from your phone — built to debug Steam without leaving Gaming Mode.
Install · How it works · Usage · Roadmap
Tap Start Remote Session in the Quick Access menu, open the link in the Claude app on your phone, and Claude is on your machine: it inspects the Steam client from the inside, reads logs, looks at your screen and presses buttons — while you watch from the couch.
| Phone-controlled Claude Code | The plugin starts claude --remote-control in a working directory you pick and shows the session link. Open it in the Claude app and drive the session from your phone. |
| Steam UI debugger | Steam's Gaming Mode UI is embedded Chromium with a DevTools debugger on localhost:8080. Claude runs JavaScript inside it (steam_ui_eval), reads real client state — downloads, library, login, settings — and triggers real actions instead of hunting for pixels. |
| Eyes and hands | screenshot shows Claude what's on screen; send_key, type_text and mouse_move_click press keys and click. |
| Always-loaded debugging skill | The steam-debugger skill (workflow, log locations, safe-fix rules, per-topic references) is injected into every session's system prompt — on start and on resume. |
| Keeps itself up to date | Checks GitHub for new releases and installs them through Decky's own installer. |
In-game help (asking Claude about the game you're playing) uses the same tools; the tooling is tuned for Steam debugging.
Note
Gaming Mode only. Screen capture goes through gamescope — the same capture the controller's screenshot button uses. There is deliberately no desktop fallback: gamescope has no Wayland screencopy protocol, and each desktop would need its own capture path. In Desktop Mode the session still works; screenshot and input just report that they need Gaming Mode.
flowchart LR
phone["Claude app<br/>on your phone"]
subgraph machine["Steam machine — Gaming Mode"]
panel["Quick Access panel<br/>(React)"]
backend["main.py<br/>Decky backend"]
claude["claude --remote-control"]
mcp["mcp_server.py<br/>stdio MCP server"]
steam["Steam client<br/>CEF debugger :8080"]
gs["gamescope<br/>screenshots"]
input["keyboard / mouse<br/>input"]
end
phone <-- "claude.ai/code session" --> claude
panel --> backend
backend -- "starts, injects skill + config" --> claude
claude --> mcp
mcp -- "steam_ui_eval / steam_snippet" --> steam
mcp -- "screenshot" --> gs
mcp -- "send_key / type_text / click" --> input
Everything written into the working directory (.mcp.json, the CLAUDE.md block, the skill link) is removed when the session stops. mcp_server.py is stdlib-only Python: no pip dependencies, no daemon, no open ports — it only lives as a child of the Claude session.
| Tool | Purpose |
|---|---|
steam_snippet |
Curated, pre-verified SteamClient queries: downloads, login, library, running apps, client info, library refresh, update check |
steam_ui_targets |
List Steam's live UI pages (CDP targets) |
steam_ui_eval |
Run JavaScript inside the Steam client (SharedJSContext hosts the SteamClient API) |
screenshot |
Capture the display as a PNG |
send_key / type_text |
Key press / type a string into the focused window |
mouse_move_click |
Move to (x, y) and click |
session_context |
Live check: Gaming or Desktop Mode, and whether the session runs inside the plugin |
session_context — why it exists
Two facts change what Claude can safely do, and both can change mid-session:
- Display mode.
gamingif agamescope/gamescope-wlprocess is running for the user, otherwisedesktop. Screenshots and input only work ingaming; in Desktop Mode Claude usessteam_ui_eval/steam_snippet. - Session origin. Whether the Claude process descends from Decky's
PluginLoader. A session started from the plugin ends instantly whenplugin_loader.servicerestarts and does not resume on its own; a standalone terminal session using the same tools is unaffected.
The tool returns the evidence (processes, sockets, the process ancestry, the systemd unit) plus a one-line consequence for each fact. Claude is told to call it before screenshots, input, or restarting the loader.
Important
Needs Decky Loader and Claude Code installed and logged in.
1. Install Claude Code (Desktop Mode, once):
npm install -g @anthropic-ai/claude-code
claude # log in2. Install the plugin — through Decky, no terminal needed:
- Decky → Settings → General → enable Developer mode.
- Decky → Settings → Developer → Install Plugin from URL:
https://github.com/TuxLux40/decky-claude/releases/latest/download/decky-claude.zip
The plugin isn't in the Decky store, so it won't show up in the in-app store listing.
Keyboard and mouse input tools (optional, for send_key / type_text / mouse_move_click)
These use xdotool (primary) and ydotool (fallback for pure-Wayland sessions). Neither ships by default, so run once in Desktop Mode:
./scripts/setup-input-tools.shA dependency-free replacement that injects input through Steam's own virtual controller is being researched — see feat/game-input.
Updates
Decky only offers updates for store plugins, so decky-claude checks for them itself. It asks GitHub for the latest release (at most every 6 hours) and shows the installed and latest version under Plugin Updates at the bottom of the panel.
- Install vX hands the release to Decky's own installer — Decky asks for confirmation, verifies the zip's sha256 and reloads the plugin.
- Auto-update (on by default) does this by itself. It never runs while a remote session is active, because reloading the plugin would end it.
Every change merged to main is built by CI and published as a release (v1.0.1, v1.0.2, …). Settings survive updates.
Other platforms
Not on SteamOS? That works too. The plugin resolves the desktop user from DECKY_USER_HOME / DECKY_USER (falling back to the account the backend runs as), so it doesn't assume a deck user or uid 1000.
From source
The bundled skill is a git submodule, so clone recursively:
git clone --recursive https://github.com/TuxLux40/decky-claude.git
cd decky-claude
pnpm install
pnpm buildCopy dist/, skills/, main.py, mcp_server.py, deck_common.py, machine_profile.py, plugin.json and package.json to ~/homebrew/plugins/decky-claude/ and restart Decky Loader. Copy skills/ with cp -rL — it's a symlink into the submodule. Don't symlink the plugin folder itself to a checkout: Decky changes ownership of whatever it points to.
- In Gaming Mode, open Quick Access (⋯) and pick the Claude icon in the sidebar — or Decky tab → Claude Code.
- Leave Session on New session and pick a working directory, or choose a past session to continue it. Tap Start Remote Session / Resume Session.
- Open the shown link in the Claude app on your phone.
- Describe the problem — "downloads are stuck", "Steam won't stay logged in", "the game crashes at the menu". Claude inspects Steam from the inside, reads logs, looks at the screen and walks you through the fix.
- Stop the session from the panel when you're done; everything it set up is cleaned away.
Tip
The sidebar icon can be turned off under Settings → Show in Quick Access sidebar at the bottom of the panel. It relies on Decky internals; if a Decky update breaks them, the toggle shows as unavailable and the plugin stays reachable through the Decky tab.
Done
- Phone-controlled
claude --remote-controlsessions from the Quick Access menu - Steam UI debugging via Chrome DevTools Protocol (
steam_ui_eval) and curatedsteam_snippetqueries - Screenshot and keyboard/mouse/text tools
- steam-debugger skill from the skills repo, always loaded into every session
- Resume past sessions from a compact dropdown (#8)
- Own icon in the Quick Access sidebar (optional)
- Self-updating releases through Decky's installer
-
session_context: live Gaming/Desktop Mode and session-origin check
In progress
- Game input through Steam's virtual controller — press buttons on your own controller's slot, no extra tools or root (
feat/game-input)
Planned
- Model and effort selection in the panel (#9)
- Live log watcher for in-game performance troubleshooting (#7)
- Guide + setup script for sudo via YubiKey in Gaming Mode (#5)
- Self-improvement loop: memories and skills that grow from your sessions, made for Gaming Mode users (#6)
Ideas
- VL-JEPA perception sidecar for watching live gameplay (#10)
Files and what they do
| Path | What |
|---|---|
main.py |
Decky backend: session lifecycle, skill preload, working-dir setup/cleanup, updates, panel API |
mcp_server.py |
Stdlib-only stdio MCP server: CDP client, screenshot, input, session_context |
machine_profile.py |
Probes hardware/OS/session/Steam into the session's CLAUDE.md; run standalone to inspect |
deck_common.py |
Display environment and xdotool/ydotool commands shared by both |
src/index.tsx |
Quick Access panel (React, built to dist/ by rollup) |
src/sidebarTab.tsx |
Optional Quick Access sidebar tab (Decky internals, isolated) |
src/update.tsx |
Update check / auto-update UI |
skills/steam-debugger |
Symlink into the skills submodule (real files at packaging time) |
vendor/skills/ |
Git submodule: TuxLux40/skills |
assets/ |
Plugin icon (SVG source and PNG) |
.github/workflows/release.yml |
Builds decky-claude.zip and publishes a release on every push to main |
.github/dependabot.yml |
Daily skill-submodule bumps, weekly GitHub Actions bumps |
CLAUDE.md |
Rules for anyone (human or agent) changing this repo |
MIT — see LICENSE.