I run Claude Code on a remote Linux server.
Not my laptop. Not my desktop. A headless VPS sitting in a datacenter somewhere.
Why? Because that’s where my apps live. That’s where my databases run. That’s where I deploy, test, and iterate. When something breaks at 2am, I don’t want to SSH in, pull logs, and guess what went wrong. I want Claude to see it, diagnose it, and fix it.
There’s just one problem.
Claude can’t see anything on a headless server.
Running AI agents remotely comes with a catch: they can read code, run commands, and edit files. But ask them to debug a UI or test a web app? They hit a wall.
No browser. No visual context. No way to see what’s happening.
I could ask Claude to “check if the modal opens correctly” and get nothing useful back. I could say “does the button look right on mobile?” and get a confident guess based on code inspection. Useless.
The whole point of having Claude on my server was autonomous work on my actual production environment. Without eyes, it was blind.
PinchTab is exactly what I needed and didn’t know existed.
It’s a lightweight HTTP server that gives AI agents direct control over Chrome. Instead of just reading code, the AI can navigate to pages, take screenshots, click buttons, fill forms, and extract the accessibility tree for structured analysis.
The part that matters: it’s token-efficient. A typical page screenshot costs thousands of tokens to process. PinchTab extracts text and accessibility data for around 800 tokens per page. That’s 5-13x cheaper when you’re running this dozens of times a day.
For someone running Claude constantly on a remote server, that adds up.
Without PinchTab, Claude can:
With PinchTab, Claude can:
The accessibility tree matters more than you’d think. Instead of guessing what “button with class btn-primary” does, Claude gets stable references like “Submit button on login form.” It can reason about the page semantically, not just visually.
I run some of my servers the Pieter Levels way.
One app per VPS. No Docker. No Kubernetes. No orchestration layer. Just a lean Ubuntu server running the bare minimum: PHP for the backend, HTML/CSS/jQuery on the frontend, SQLite for the database, Caddy as the web server. Telegram bot for alerting. Cron job backups to Cloudflare R2 Object Storage. Simple. Reliable. Cheap.
The whole stack lives on one machine. Claude manages all of it—the PHP files, the HTML templates, the CSS, the SQLite database, the Caddy config, even the firewall rules. It can read logs, edit configs, restart services, and run migrations. Everything you’d do manually, but faster.
But here’s what was missing: the feedback loop.
I could ask Claude to fix a CSS bug. It would edit the file. But then what? I’d have to open my browser, navigate to the site, and verify it worked. If it didn’t, back to Claude with another prompt. Back and forth. Slow.
With PinchTab, that loop closes.
Claude fixes the CSS. Navigates to the page. Takes a screenshot. Sees the result. Not right? It adjusts and tries again. All without me touching anything.
Same for PHP errors. Claude navigates to the broken page, sees the error rendered in the browser (not just in logs), traces it back to the code, fixes it, and reloads to verify. One continuous process. No context switching. No “let me check that for you” delays.
The Pieter Levels approach is about simplicity and speed. But simplicity doesn’t help when you’re blind to what’s actually happening. PinchTab opens those eyes. Now the same lean, single-app architecture that made deployment simple makes debugging simple too.
I can literally say “fix the mobile menu” and come back to find it working.
Installing PinchTab is supposed to be easy:
curl -sSL https://pinchtab.com/install.sh | bash
And it works. Except when it doesn’t.
Here’s what happened on Ubuntu 24.04.
sudo snap install chromium
PinchTab couldn’t launch it. Snap has sandbox restrictions that break automation tools. The browser would start, then immediately crash or refuse to connect.
I thought this would be the easy way. I was wrong.
sudo apt install chromium-browser
Same problem. Turns out chromium-browser on Ubuntu 24.04 is just a wrapper that redirects to the snap version. Same restrictions. Same failure.
Why does Ubuntu make this so hard? I get the security benefits of snap, but when you’re running automation tools on a server, those sandbox restrictions become walls instead of guardrails.
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo apt install ./google-chrome-stable_current_amd64.deb
This worked. Google Chrome doesn’t have the same sandbox restrictions as snap Chromium. It just runs.
Then configure PinchTab to use it:
CHROME_BIN=/usr/bin/google-chrome pinchtab
Claude had eyes.
Why go through all this instead of just running Claude locally?
When Claude runs on my laptop, it can see my local browser. But my apps don’t run locally. They run on my VPS. So:
I needed remote Claude with visual debugging.
Now when something breaks in production, I can ask Claude to navigate to the broken page, take a screenshot, identify the issue, read the rendered HTML, fix the code, and verify the fix. All without leaving my terminal. All without switching contexts. All without me manually reproducing issues at 2am.
Here’s what works on Ubuntu 24.04:
# Install Google Chrome (not snap Chromium)
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo apt install ./google-chrome-stable_current_amd64.deb
# Install PinchTab
curl -sSL https://pinchtab.com/install.sh | bash
# Set the environment variable and run
CHROME_BIN=/usr/bin/google-chrome pinchtab
Three commands. Maybe 5 minutes once you know the Chrome requirement.
Debug production UI visually. “The pricing table is misaligned on tablet” isn’t a mystery anymore. Claude navigates there, takes a screenshot, identifies the issue, fixes the CSS.
Test authentication flows. Persistent sessions mean Claude can log in, stay logged in, and test protected routes. Fill forms, submit them, verify the response.
Extract structured data. The accessibility tree gives semantic understanding of page elements. No more guessing what .btn-submit-2 actually does.
Run parallel browser instances. Multiple tests simultaneously. Different users, different sessions, different pages.
Automated visual regression. Screenshots before and after changes. Compare them. Know immediately if something broke.
If you’re setting this up:
Skip snap Chromium entirely. The sandbox restrictions make it unusable for automation. Don’t waste your time.
Use Google Chrome. The stable build from Google works out of the box. No restrictions. No headaches.
Set the CHROME_BIN variable. PinchTab needs to know where to find Chrome. Run CHROME_BIN=/usr/bin/google-chrome before starting the server.
Headless doesn’t mean blind. A headless server can still run a browser. You just need the right one.
The next step for me is integrating PinchTab into my regular workflow. I want Claude to automatically spin up a browser when I ask it to debug something, then shut it down when it’s done.
I also want to explore the accessibility tree more. That structured data is more useful than screenshots for most debugging tasks. The AI doesn’t need to “see” the page visually—it needs to understand the page semantically. PinchTab gives it both.
If you’re running Claude Code on a remote server and hitting the same wall I did, give PinchTab a shot. It turns a blind AI into one that can actually see what it’s working on.
Oh, and here you go. Copy/paste this prompt into your production Claude and get it to set up the same system:
I need you to set up PinchTab with headless Google Chrome on this Linux server. This is for browser automation to debug web pages.
## The Critical Fix (Don't Skip This)
On Ubuntu 24.04, the snap Chromium doesn't work for automation. You MUST use Google Chrome instead.
## Step-by-Step Setup
### 1. Install Google Chrome (NOT snap Chromium)
wget -q https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb -O /tmp/chrome.deb
sudo apt install -y /tmp/chrome.deb
Verify:
which google-chrome
# Must return: /usr/bin/google-chrome
### 2. Install Node.js (if not present)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
### 3. Install PinchTab
npm install -g --prefix "$HOME/.local" pinchtab
export PATH="$HOME/.local/bin:$PATH"
Add to PATH permanently:
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
### 4. Start PinchTab
# Kill any existing instance
pkill -f pinchtab 2>/dev/null || true
# Start with Google Chrome
CHROME_BIN=/usr/bin/google-chrome nohup pinchtab > /tmp/pinchtab.log 2>&1 &
# Wait for startup
sleep 4
# Verify it's running
curl -s http://localhost:9867/health
Expected: {"mode":"dashboard","status":"ok"}
### 5. Test It Works
pinchtab quick https://example.com
## Troubleshooting
If you get "profile already has active instance" error:
rm -rf ~/.pinchtab/profiles/*
pkill -f pinchtab
# Restart from Step 4
If Chrome won't launch, verify you're using Google Chrome (not snap):
which google-chrome
# Must be /usr/bin/google-chrome
## Commands You'll Use
- Start: CHROME_BIN=/usr/bin/google-chrome pinchtab &
- Navigate: pinchtab quick <url>
- Click: pinchtab click <ref>
- Type: pinchtab type <ref> text
- Elements: pinchtab snap -i
- Screenshot: pinchtab screenshot
If you want to see more about how I’m using AI for development, check out how I built my AI second brain in Obsidian .