Let AI agents see your UI — point at elements, annotate, and get instant visual context for AI-powered code editing.
Project description
Peek
Point at your UI. Your AI agent sees it.
Why
AI coding agents can edit code, but they can't see your page. You end up describing layouts in words, copy-pasting HTML, or dragging screenshots into chat.
Peek fixes this. Your AI agent can screenshot any local page on its own. You can also point at specific elements with a bookmarklet, and the agent gets the screenshot plus element data (selectors, styles, bounding boxes).
Quick Start
Claude Code (CLI or VS Code) — recommended
# 1. Install uv (one-time, manages Python for you)
brew install uv
# or: curl -LsSf https://astral.sh/uv/install.sh | sh
# 2. Install peek (uv auto-downloads a compatible Python)
uv tool install peek-mcp
# 3. One-shot setup
peek setup
peek setup does everything for you: installs Playwright Chromium, registers Peek as an MCP server in Claude Code (user scope, all projects), opens the bookmarklet page in your browser. Just drag the blue button to your bookmark bar.
Using pip directly?
pip install peek-mcpworks too, but requires Python 3.10+ already installed.uv tool installavoids the version-mismatch headache.
Other AI tools (Cursor / Windsurf / Claude Desktop)
uv tool install peek-mcp
playwright install chromium # ~150 MB, one-time
Add to your MCP config file (e.g. .cursor/mcp.json, ~/.codeium/windsurf/mcp_config.json, or claude_desktop_config.json):
{
"mcpServers": {
"peek": {
"command": "/absolute/path/to/peek",
"args": ["mcp"]
}
}
}
Run which peek in your terminal to get the absolute path. Then visit http://localhost:8899 to grab the bookmarklet.
Recommended: tell your agent your dev port
Add this to your project's CLAUDE.md (or .cursorrules, etc.):
Dev server runs on http://localhost:3000
When using Peek's screenshot tool and you don't know the dev server URL, ask me which port the app is running on before taking a screenshot.
Replace 3000 with your actual port. This prevents your agent from guessing the wrong port.
Use it
Agent-driven screenshots (no bookmarklet needed):
Just ask your agent — "screenshot the page", "take a look at localhost:3000", "check if the button looks right". The agent calls screenshot(url) directly.
User-pointed captures (bookmarklet):
- Run
peek mcp(or let your AI tool start it via MCP) - Open http://localhost:8899, drag the blue button to your bookmark bar
- Open your app, click the bookmarklet, pick a mode:
| Mode | Shortcut | You do | Agent gets |
|---|---|---|---|
| Region | Alt+R |
Drag a rectangle | Screenshot + elements in that area |
| Element | Alt+S |
Click an element | Screenshot + selector, styles, HTML |
| Annotate | Alt+A |
Draw (pen, box, arrow) | Screenshot + your drawings + elements |
- Tell your agent: "check what I just selected" — it calls
get_user_selection()
Supported URLs
Peek works with local and LAN addresses:
| URL | Supported |
|---|---|
http://localhost:3000 |
Yes |
http://127.0.0.1:8080 |
Yes |
http://0.0.0.0:5000 |
Yes |
http://192.168.1.5:3000 |
Yes (LAN) |
http://myapp.local:3000 |
Yes (.local domain) |
http://myapp.test:8080 |
Yes (.test domain) |
https://google.com |
No (public URLs blocked) |
MCP Tools
| Tool | When to use |
|---|---|
screenshot(url) |
Default — agent fetches any local/LAN URL in a fresh headless browser. Use for general "look at the page" requests, verifying code changes, etc. |
get_user_selection() |
Reads what you captured with the bookmarklet — a region, element, or annotation. Use when you want the agent to see exactly what you pointed at, including any state that requires your interaction (login, uploaded data, clicked buttons). |
For Element mode captures, the agent gets structural context to find the right code:
- ancestor chain (
body > main > section.settings > div.flex) - sibling position (
2 of 3 <button> siblings) - nearest heading (
h2: Settings) - parent layout (
flex, row)
This means when you point at a button, the agent knows which button on which section — no guessing.
How It Works
Your AI agent calls screenshot(url)
-> Playwright takes a fresh headless screenshot (stateless view)
-> Agent sees the default render of the page
You point at something with the bookmarklet
-> Bookmarklet renders your CURRENT browser view to PNG client-side
(so login, uploaded data, clicked buttons are all preserved)
-> Peek also grabs element metadata (selector, styles, DOM context)
-> Agent calls get_user_selection() and sees what you see
-> "The chart legend overlaps the bars. Fixing the CSS..."
One process, two jobs: MCP server talks to your AI agent over stdio, bridge server receives bookmarklet data on port 8899. peek mcp runs both.
Client-side rendering uses modern-screenshot (MIT, vendored at
static/modern-screenshot.js). If it fails on an exotic page, the bridge falls back to the old Playwright re-fetch automatically. To force fallback globally, setPEEK_DOM_SNAPSHOT=0.
Troubleshooting
Playwright Chromium is not installed
Run playwright install chromium. This downloads the headless browser engine (~150 MB). Only needed once.
ERR_CONNECTION_REFUSED when taking a screenshot
Your dev server isn't running on that port. Start your dev server first, then ask the agent to screenshot. If you see this repeatedly, make sure the port in your CLAUDE.md matches your actual dev server.
Agent screenshots the wrong port
Add the CLAUDE.md snippet from step 3 above. Without it, the agent will guess.
Bookmarklet not working (click does nothing)
The Peek bridge server isn't running, or is on a different port. Start it via peek mcp, or let Claude Code launch it through MCP. If you changed the port with --port, re-drag the bookmarklet from the new URL — the old one points at the old port.
peek: command not found after install
If you used uv tool install peek-mcp, make sure ~/.local/bin is in your PATH. Run uv tool update-shell to fix it automatically, then open a new terminal.
If you used pip install peek-mcp, your Python scripts directory may not be in PATH. Find it with pip show peek-mcp | grep Location.
No matching distribution found for peek-mcp
Your Python is older than 3.10 (common on macOS system Python). Switch to uv tool install peek-mcp — uv downloads a compatible Python for you.
Using Peek from Safari, Firefox, or a different Chrome profile
The bookmarklet lives in each browser's bookmark bar independently — there's no cross-browser sync. To use it in another browser, open http://localhost:8899 in that browser and drag the blue "Peek" button to its bookmark bar. One-time setup per browser. The screenshot tool itself is browser-agnostic (it uses Playwright's own headless Chromium), so it works regardless of which browser you develop in.
Screenshot shows a blank or default page instead of what I see
screenshot(url) renders in a fresh headless session with no cookies or session data — it shows what an anonymous visitor would see, not your current view. If your page requires login, upload, or any user interaction to show its real content:
- Click the Peek bookmarklet on the page you want captured, then ask the agent to check your selection. The bookmarklet renders your actual browser view to PNG client-side (since v0.5), so post-login / post-upload / post-interaction state is preserved.
- If the bookmarklet capture itself looks off on an unusual page, try
PEEK_DOM_SNAPSHOT=0 peek mcpto force the old Playwright re-fetch path. Or take an OS screenshot (Cmd+Shift+4on macOS) and paste it directly into your chat.
Agent returns a screenshot that doesn't match what I'm looking at
get_user_selection returns the last bookmarklet capture, which could be from hours or days ago. The metadata includes an age_seconds field — if that number is large, the agent should recognize it and fall back to screenshot(url). To force a fresh capture, click the bookmarklet again on your current page.
Limitations
- Local/LAN only. Public URLs are blocked for security (SSRF prevention).
screenshot(url)is stateless. It opens a fresh headless browser — no cookies, no login. For your actual session state, use the bookmarklet →get_user_selection().- Bookmarklet capture quality depends on the page. Most CSS renders faithfully; exotic features (WebGL, cross-origin iframes, playing videos) may render with gaps. Set
PEEK_DOM_SNAPSHOT=0to fall back to the Playwright re-fetch path if needed. - Captures may contain sensitive data. Passwords and hidden inputs are redacted, but visible text and screenshots are saved as-is to
~/.peek/captures/. Peek keeps the 50 most recent captures automatically; older ones are pruned.
CLI
peek mcp # start everything (recommended)
peek mcp --port 9000 # different bridge port
Requirements
uv(recommended — manages Python for you) or Python 3.10+- Playwright Chromium (auto-installed by
peek setup, or runplaywright install chromiummanually)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file peek_mcp-0.5.1.tar.gz.
File metadata
- Download URL: peek_mcp-0.5.1.tar.gz
- Upload date:
- Size: 71.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.28 {"installer":{"name":"uv","version":"0.9.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
93285fd87dcc8937499362a380712a9033922f684a345306d94e119c730b2bd2
|
|
| MD5 |
1555b09cfaf1bd58681cacfa26711ced
|
|
| BLAKE2b-256 |
6d539a2125973e22f5afd62bb484a738c99ac5dfcf52c62b08e87ab60e38ccd5
|
File details
Details for the file peek_mcp-0.5.1-py3-none-any.whl.
File metadata
- Download URL: peek_mcp-0.5.1-py3-none-any.whl
- Upload date:
- Size: 41.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.28 {"installer":{"name":"uv","version":"0.9.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b117ababe803779c909692df5175c171e07dcbcd06a538876269cf8a1dbf6f40
|
|
| MD5 |
1022935df0b6f1780edc804608718bfd
|
|
| BLAKE2b-256 |
093dc269ef2d22b6ad6a08ea3198263c55dc50fd7be1d69b9faa186542632fbd
|