layershot
Let AI agents see, open, click and type in Windows apps behind the one you're using. They never take over your screen, mouse or keyboard.
- See: screenshot any window, even covered or minimized ones.
- Open: launch apps behind your windows; they can't grab focus.
- Act: click, type, press keys and scroll in background windows through accessibility (UI Automation) or direct window messages. Your real mouse never moves, and dialogs and popups stay behind your app.
- Watch: a floating AI view shows a live picture of the window the agent is working in, its cursor, and what it's doing. You can move it, close it, show/hide it with Ctrl+Alt+L, and Bring forward puts the AI's window in front when you want it.
AI agents (Claude Code, Codex, Hermes, computer-use tools…) usually look at an app by switching to it and grabbing the screen. That yanks the window in front of you, and you can't use your PC until the agent is done. layershot captures the window where it is, in whatever layer of the stack, so you keep working.
> layershot list
0 FG WindowsTerminal.exe my terminal <- you, working
1 Spotify.exe Spotify Premium <- covered
2 MIN Obsidian.exe Graph view - vault <- minimized
> layershot shot 2 -o obsidian.png
saved obsidian.png 1024x768 layer 2 Graph view - vault
Your terminal stays in front and keeps keyboard focus the whole time.
How it works
| Window state | What layershot does |
|---|---|
| Covered by other windows | Reads the window's own DWM surface with PrintWindow(PW_RENDERFULLCONTENT). Nothing moves. |
| Minimized | Restores it without activating it and at the bottom of the z-order (behind everything), captures it, then minimizes it again. Pass keep_open to leave it restored at the back. |
| Chromium / Electron apps (Chrome, VS Code, Obsidian, Discord…) | These stop painting while fully covered, so layershot forces a repaint and retries until the image isn't blank (up to 3 s). |
Opening apps works the same way. When an agent needs an app for its own work, open_app
launches it behind your windows. Most apps grab focus on startup no matter how they're
launched, so layershot holds Windows' foreground lock (LockSetForegroundWindow) for the
launch. Apps can't activate themselves during that time, but your own clicks and Alt+Tab still work. In our tests Paint
went from holding focus for about 3.5 s to never getting it. When you ask the agent to open
something, it passes front=True and the app opens normally in front.
It's pure ctypes + Pillow: no native build and no admin rights.
The full technical notes (the exact Win32 calls, measurements, the Chromium blank-frame
issue, and what can't be captured) are in docs/HOW-IT-WORKS.md.
Install
Requires Windows 10 1809+ and Python 3.10+.
The easy way: ask your agent. Paste this into Claude Code, Codex or any agent with a shell:
Install layershot from https://github.com/Kishore180994/layershot and run
layershot setup.
Or do it yourself. It takes two commands:
pip install git+https://github.com/Kishore180994/layershot
layershot setup
layershot setup finds the agents on your PC and configures each one:
| Agent | What setup does |
|---|---|
| Claude Code | claude mcp add --scope user layershot, plus the agent rule in ~/.claude/CLAUDE.md |
| Codex | [mcp_servers.layershot] in ~/.codex/config.toml, plus the rule in ~/.codex/AGENTS.md |
| Claude Desktop | mcpServers.layershot in claude_desktop_config.json |
| Cursor | mcpServers.layershot in ~/.cursor/mcp.json |
| Hermes Agent | mcp_servers.layershot in config.yaml (restart Hermes) |
It's safe to re-run: it skips what's already there and updates the rule in place. The rule lives
between <!-- layershot:begin --> / <!-- layershot:end --> markers. layershot setup --remove takes
everything back out, and --dry-run shows what would change. --agents codex,cursor limits it to those agents.
Even without setup, the MCP server sends the same rule as its server instructions, which clients
like Claude Code put into the model's prompt automatically.
Manual install:
pip install git+https://github.com/Kishore180994/layershot
Or run it without installing, using uv:
uvx --from git+https://github.com/Kishore180994/layershot layershot list
From a clone:
git clone https://github.com/Kishore180994/layershot && cd layershot && pip install -e .
Use it from an AI agent (MCP)
Claude Code
claude mcp add --scope user layershot -- layershot-mcp
# or without installing anything:
claude mcp add --scope user layershot -- uvx --from git+https://github.com/Kishore180994/layershot layershot-mcp
Claude Desktop / Cursor / any MCP client (mcpServers config):
{
"mcpServers": {
"layershot": { "command": "layershot-mcp" }
}
}
Codex (~/.codex/config.toml):
[mcp_servers.layershot]
command = "layershot-mcp"
Hermes Agent (config.yaml):
mcp_servers:
layershot:
command: layershot-mcp
Tip: tell your agent in its system prompt or instructions to use layershot instead of focusing a window to look at it. Agents otherwise tend to reach for the full-screen screenshot tool they already know.
Tools:
list_windows(include_minimized=True)returns the windows front to back, each withlayer,hwnd,title,process,minimizedandforeground.screenshot_window(layer | title | process | hwnd, keep_open=False, max_width=1600, format="png", save_to=None)returns the image plus a one-line description.open_app(target, args=None, cwd=None, front=False, wait=8)opens an exe, file, folder or URL. It opens in the background by default;front=Trueis for when the user asked to see it. It returns the new window(s) with their layers.bring_to_front(layer | title | process | hwnd)restores and focuses a window, for user requests only.find_elements(window, query=None)lists buttons, fields, tabs and menu items with anid, arect(in the pixels of the agent's last screenshot) and theactionseach supports.click(window, element=… | x=…, y=…, button="left", double=False)tries accessibility first, then window messages at the AI's own invisible cursor.type_text(text, window, element=None, append=False, enter=False),press_key(key, window)andscroll(window, x, y, amount).
After a click, screenshot_window draws the AI's cursor on the agent's copy, so it can see where it
clicked. It's a normal arrow with a blue highlight, and the same cursor appears in the AI view.
It never appears anywhere else on your screen.
Custom AI cursor: point the theme at any RGBA PNG:
{ "theme": { "base": "hud", "cursor_image": "C:/path/ai_cursor.png",
"cursor_hotspot": [6, 4], "cursor_height": 34 } }
(in %LOCALAPPDATA%\layershot\prefs.json; cursor_hotspot is the pixel of the PNG that marks the
point). Without one, the cursor's highlight follows the theme: blue for default, cyan for hud.
The AI view (floating window)
The first time an agent does something, a small window opens at the bottom-right of your screen showing a live picture of the window the agent is working in. It's live even while that window is behind yours, because it uses the same DWM thumbnails as taskbar previews. You also see the AI's cursor and its last actions.
- It never takes focus or keystrokes, and it isn't in Alt+Tab.
- Drag the top bar to move it and drag the corner to resize; both are remembered.
- ✕ closes it, and it stays closed until you press Ctrl+Alt+L, which shows/hides it any time.
- Bring forward (or double-click the picture) puts the AI's window in front.
layershot viewer --theme hudswitches to a cyan HUD look (navy panel, corner brackets, spaced caps).defaultis neutral dark. For your own colors, set"theme": {"base": "hud", "accent": "#ff9900"}in%LOCALAPPDATA%\layershot\prefs.json.layershot viewer --hotkey "ctrl+shift+f9"changes the hotkey,layershot viewer --offstops it from opening automatically, andlayershot viewer --quitcloses it.
What works in the background
| App type | See | Click / type |
|---|---|---|
| Classic Win32 (dialogs, Character Map, most older apps, wxWidgets like Bambu Studio) | ✅ | ✅ by element and by x,y |
| Chrome / Edge / Electron (VS Code, Obsidian, Discord) | ✅ | ✅ by element (accessibility) |
| WinUI / new Windows 11 apps (Notepad, Paint) | ✅ | ✅ by element and typing; ❌ x,y clicks and menus that need an active window |
| Games, full-screen DirectX, 3D-view drags | ✅ (mostly) | ❌, so the agent asks you first |
| Ctrl/Alt shortcuts | ❌ (Windows reads modifiers from the real keyboard) |
Titles match loosely: "Bambu Studio" finds plate - BambuStudio (bambu-studio.exe).
The agent rule (in layershot/rules.py) is sent as MCP server
instructions, and layershot setup writes it into CLAUDE.md / AGENTS.md. In short: look with
screenshot_window and never switch to the window; when the agent needs an app for its own work,
open_app opens it in the background; only when the user asks to open or show something does the agent
use open_app(front=True) or bring_to_front. For another agent, paste the rule into its
system prompt.
Use it from Python
import layershot
for w in layershot.list_windows():
print(w.layer, w.process, w.title)
img, win = layershot.capture(title="Obsidian") # PIL.Image
img, win = layershot.capture(2) # by layer
img, win = layershot.capture(process="code.exe", keep_open=True)
layershot.open_app("mspaint.exe") # background, focus stays with the user
layershot.open_app(r"C:\models\part.3mf") # default app, background
layershot.open_app("notepad.exe", front=True) # the user asked to see it
layershot.bring_to_front(title="Bambu Studio")
CLI
layershot list [--json] [--no-minimized]
layershot shot <layer|title> [--process exe] [--hwnd 0x...] [-o out.png] [--keep-open]
layershot open [--front] <app|file|url> [args...]
layershot front <layer|title>
layershot setup [--agents a,b] [--no-rules] [--remove] [--dry-run]
layershot elements <window> [--query text]
layershot click <window> <e12 | x y> [--right] [--double]
layershot type <window> "text" [--element e3] [--enter]
layershot key <window> <enter|tab|esc|...>
layershot viewer [--off | --on | --quit | --hotkey "ctrl+alt+l"]
layershot serve # MCP server on stdio (same as layershot-mcp)
Limits
- Windows 10 1809+ only.
PW_RENDERFULLCONTENTis needed for GPU-drawn apps. - Minimized windows: while one is restored for the capture (about 0.3–1 s), it sits behind your other windows, so it can show through gaps on an uncovered part of the desktop. It never takes focus.
- Restore size: a window minimized from maximized comes back at its normal size for the capture, because Windows can't maximize a window without activating it.
- Hardware-protected content (DRM video, some games running fullscreen) captures black, the same as in any screenshot tool.
- Other virtual desktops: those windows are skipped by
list_windows. - Background opening: an app that's already open and gets reused (a URL opening as a new tab in your existing browser, for example) stays where it is. If it still manages to activate itself, layershot hands focus back to you right away.
- Background input can't do everything: see the table above. When an agent can't do something in the background, the rule tells it to ask you before taking over the screen.
Development
pip install -e .
python tests/mcp_smoke.py # starts the MCP server, lists windows, captures one titled "Obsidian"
python tests/test_installer.py # setup add / re-run / --remove against fake configs in a temp home
Edit the title in tests/mcp_smoke.py to match a window you have open. Issues and PRs are
welcome, especially for background input (see the roadmap in
docs/HOW-IT-WORKS.md).
License
MIT. See LICENSE.
Metadata
Release files for layershot 0.3.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| layershot-0.3.3.tar.gz | 43.6 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| layershot-0.3.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 84.4 kB
Release files / layershot-0.3.3.tar.gz
| Download URL | layershot-0.3.3.tar.gz |
|---|---|
| Size | 43.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
39aa00e64c8c62a928080e522ec3b6c93fa066c37beb34fda4b14b59641a3960
|
|
BLAKE2b-256 checksum How to use checksums |
3a4b4c81e1eda800f6525b3514df1394a3429fbcb7d19302252d06b0fbd3c29c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / layershot-0.3.3-py3-none-any.whl
| Download URL | layershot-0.3.3-py3-none-any.whl |
|---|---|
| Size | 40.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
841d90c56709043136f28cdfca34c7e7f0a4a00f148b31ebf3be890bf39144dc
|
|
BLAKE2b-256 checksum How to use checksums |
568df9c70d6a6e990fbaac69cc8363eefac6d592a38ee81ae16c5b2171f5f129
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for layershot 0.3.3
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.