# viewpo-mcp
MCP server that gives AI coding assistants **eyes into responsive viewport rendering** via the [Viewpo](https://viewpo.app) macOS app.
Capture multi-viewport screenshots, extract DOM layout trees, and compare responsive behaviour across breakpoints — all from your AI assistant.
## What it does
AI coding assistants (Claude Code, Cursor, Windsurf, etc.) are **blind** when working on frontend UI. They write CSS and HTML but can't see the visual result. This creates a slow feedback loop:
> AI writes code → you check → you describe what's wrong → AI fixes → repeat
**viewpo-mcp** closes that loop. The AI can now:
1. **Screenshot** a URL at multiple viewport widths simultaneously
2. **Extract** the DOM layout tree with bounding rects and computed styles
3. **Compare** layouts at two different viewport widths to find responsive issues
## Requirements
- **[Viewpo](https://viewpo.app) macOS app** with MCP Bridge enabled (Settings → MCP Bridge → Start)
- **Node.js 20+**
## Setup
### 1. Install
```bash
npm install -g viewpo-mcp
```
Or run directly with npx (no install needed):
```bash
npx viewpo-mcp
```
### 2. Enable the MCP Bridge in Viewpo
1. Open Viewpo on your Mac
2. Go to **Settings → MCP Bridge**
3. Click **Start** to enable the bridge server
4. Copy the **auth token** (click the copy button next to the token)
### 3. Configure your AI assistant
#### Claude Code
Add to your MCP config (`~/.claude/mcp.json` or project `.mcp.json`):
```json
{
"mcpServers": {
"viewpo": {
"command": "npx",
"args": ["-y", "viewpo-mcp"],
"env": {
"VIEWPO_AUTH_TOKEN": "<paste token from step 2>"
}
}
}
}
```
#### Cursor
Add to your Cursor MCP settings (`.cursor/mcp.json`):
```json
{
"mcpServers": {
"viewpo": {
"command": "npx",
"args": ["-y", "viewpo-mcp"],
"env": {
"VIEWPO_AUTH_TOKEN": "<paste token from step 2>"
}
}
}
}
```
#### Other MCP-compatible assistants
Any assistant supporting the [Model Context Protocol](https://modelcontextprotocol.io) can use viewpo-mcp. Set the command to `npx -y viewpo-mcp` and provide the `VIEWPO_AUTH_TOKEN` environment variable.
## Tools
### `viewpo_screenshot`
Capture screenshots of a URL at one or more viewport widths. Returns base64 JPEG images.
```
url: "https://example.com" (required)
viewports: [{ width: 375, name: "phone" }, (optional, defaults to 1920px desktop)
{ width: 1920, name: "desktop" }]
```
Common widths: **375** (phone), **820** (tablet), **1920** (desktop).
### `viewpo_get_layout_map`
Extract the DOM layout tree at a given viewport width. Returns element hierarchy with tags, classes, bounding rects, and computed CSS styles.
```
url: "https://example.com" (required)
viewport: 1920 (optional, default 1920)
selector: ".main-content" (optional, scope to subtree)
```
### `viewpo_compare_viewports`
Compare the layout of a URL at two different viewport widths. Returns elements whose size or CSS styles differ between the two viewports.
```
url: "https://example.com" (required)
viewport_a: 375 (required)
viewport_b: 1920 (required)
selector: ".hero" (optional, scope to subtree)
```
## Environment variables
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `VIEWPO_AUTH_TOKEN` | Yes | — | Bearer token from Viewpo Settings → MCP Bridge |
| `VIEWPO_PORT` | No | `9847` | Port the Viewpo bridge server listens on |
## How it works
```
AI Assistant (Claude Code / Cursor / Windsurf)
| stdio (MCP protocol)
v
viewpo-mcp (this package)
| HTTP localhost:9847
v
Viewpo macOS app (NWListener)
| WKWebView rendering
v
Headless browser pool (off-screen, up to 4 concurrent pages)
```
The Viewpo app runs a local HTTP server on `localhost:9847`. This MCP server translates tool calls into HTTP requests to that bridge. The app loads pages in headless WKWebView instances with real CSS viewport simulation — media queries fire at the target width, not the physical screen width.
**Key advantage**: WKWebView bypasses X-Frame-Options entirely. Any website loads, regardless of security headers that would block iframe-based tools.
## Example workflow
```
You: "The hero section looks broken on mobile"
AI: 1. viewpo_screenshot("https://preview.example.com", [{width: 375}, {width: 1920}])
→ Sees the layout at both sizes
2. viewpo_compare_viewports("https://preview.example.com", 375, 1920)
→ Gets structured diff: ".hero img" is 1200px wide on mobile (overflowing)
3. Fixes the CSS
4. viewpo_screenshot("https://preview.example.com", [{width: 375}])
→ Verifies the fix
```
## Development
```bash
git clone https://github.com/littlebearapps/viewpo-mcp.git
cd viewpo-mcp
npm install
npm run build # compile TypeScript
npm run dev # watch mode
```
## Licence
MIT - see [LICENSE](LICENSE).
## Links
- [Viewpo app](https://viewpo.app) - The macOS/iOS app
- [Model Context Protocol](https://modelcontextprotocol.io) - MCP specification
- [GitHub](https://github.com/littlebearapps/viewpo-mcp) - Source code