One browser. Two sets of hands.
SameWindow runs a persistent, dedicated Chrome and lets a person and an AI agent use that exact browser together. The window can live on a Linux VPS and be viewed through noVNC, or run natively beside the person in split mode. The agent reads semantic snapshots and acts through small MCP tools. Both sides share the same tabs, history, focus, and authenticated browser profile.
SameWindow is the public shared-browser core only. It does not include a chat client, personal assistant prompts, private APIs, or any accounts.
2026-09-13 fix: Xiaohongshu reading now skips hidden duplicate links and prefers the visible cover, fixing the five-second scroll timeout. The September 11 update added accessibility-based references, event-driven observation, and read-only X/Xiaohongshu tools. See the bilingual changelog for changes, upgrade steps, and validation.
Windows native mode — one real Chrome window, human and agent cursors
The VPS browser can sleep until it is explicitly needed
A self-hosted Bridge can build a more ambient “browse together” experience
The last screenshot shows our private host integration as an example; the chat client is not part of this public repository.
- A visible Chrome desktop: Xvfb → Openbox → Chrome → x11vnc → noVNC
- A Playwright/CDP control service with tab, snapshot, click, type, key, and automatic visible cursor feedback
- A Python MCP façade for agent clients
- Read-only X and Xiaohongshu feeds, search, and exact-post reading
- A small start/stop dashboard so the heavy browser stack can sleep
- Optional “Browse together” semantic events for host integrations, covering deliberate clicks, dwell, stable page text, and near-pointer moments
- A persistent, dedicated Chrome profile
Person ── SSH tunnel ── noVNC ───────┐
├── the same Chrome profile
Agent ── MCP ──────── Playwright ───┘
All services bind to 127.0.0.1 by default. SameWindow intentionally has no
Internet-facing authentication layer: use SSH forwarding or another
authenticated private transport and never expose ports 6080–6083 publicly.
The agent cannot access cookies, browser storage, arbitrary JavaScript
evaluation, arbitrary CSS selectors, or screenshots through the public tools.
Actions use snapshot-scoped temporary references such as s42:e1. A new
snapshot invalidates the previous element refs for that tab, while the latest
refs for other tabs remain valid. The snapshot also returns snapshotId for
diagnostics, but actions only need the returned ref. An explicitly supplied
stale tab ref is rejected instead of silently falling back to another tab.
Snapshots combine Chrome's accessibility tree with DOM layout information.
References point to browser node IDs without adding attributes to the DOM;
accessible names and open shadow-root controls are supported. Main-document
snapshots do not enumerate controls inside child frames.
Clicks also fail fast with an obstructed error and a compact coveredBy
summary when an overlay receives the target point. Browser actionability
timeouts are returned as action errors with the Playwright reason, not as a
misleading gateway 504. The expected flow is still for the person to sign in
before enabling browse-together mode; this path handles expired sessions and
ordinary overlays without asking the agent to cross that boundary.
Login, password,
one-time-code, identity, checkout, and payment pages are blocked from snapshots
and actions by default.
See SECURITY.md before deploying.
A small Ubuntu 22.04/24.04 server with:
- Node.js 20+
- Python 3.10+
- Google Chrome or Chromium
xvfb,openbox,x11vnc,novnc,websockify,dbus-x11, andpython3-venv
For example:
sudo apt update
sudo apt install -y xvfb openbox x11vnc novnc websockify dbus-x11 python3-venvInstall Node.js 20+ and Chrome using their official packages. If the Chrome
binary is not /usr/bin/google-chrome-stable, change
SAMEWINDOW_CHROME_BIN in .env.example before installation or in
/etc/samewindow.env afterward.
git clone https://github.com/Yinglianchun/SameWindow.git
cd SameWindow
sudo ./scripts/install-ubuntu.shThe installer creates:
- application files in
/opt/samewindow - the browser profile and noVNC state in
/var/lib/samewindow - configuration in
/etc/samewindow.env - systemd units named
samewindow-*
It starts only the lightweight lifecycle dashboard. The browser itself starts
when you press Start or call shared_browser_lifecycle_start.
Forward the three viewer/control ports from your computer:
ssh -N \
-L 6080:127.0.0.1:6080 \
-L 6081:127.0.0.1:6081 \
-L 6082:127.0.0.1:6082 \
your-user@your-serverThen open http://127.0.0.1:6082. The dashboard starts and stops the shared browser without deleting its profile.
If the VPS is far away, the optional split deployment keeps the agent on the server but runs the visible browser beside the person. Frames stay on localhost while small control calls cross an SSH reverse tunnel. The original VPS Chrome can remain asleep as a manual fallback; ordinary browser calls never wake it.
For Windows, use the native Chrome + transparent cursor layer first: it needs no Docker, WSL, or VNC. Docker remains the cross-platform option for macOS, Linux, and users who prefer container isolation.
The simplest remote setup uses MCP over SSH stdio, so no MCP port needs to be opened:
{
"mcpServers": {
"samewindow": {
"command": "ssh",
"args": [
"-T",
"your-user@your-server",
"/opt/samewindow/.venv/bin/python",
"/opt/samewindow/src/mcp_server.py",
"--transport",
"stdio"
]
}
}
}An optional loopback-only streamable HTTP service is also installed:
sudo systemctl enable --now samewindow-mcp.service
ssh -N -L 6083:127.0.0.1:6083 your-user@your-serverIts endpoint is http://127.0.0.1:6083/mcp. It has no application-level auth;
the SSH tunnel is part of the security boundary.
SameWindow exposes 14 core MCP tools by default. It intentionally does not turn every internal control endpoint into an agent tool:
| Tool | Default | Why |
|---|---|---|
shared_browser_screenshot |
Removed | The person already sees the window, while agents should use compact semantic snapshots. Sending an image into a particular chat is a host/client responsibility, not a portable browser tool. |
shared_browser_cursor_move |
Removed | Ref-based click and type actions already move the visible agent cursor automatically. A separate cosmetic movement call adds noise. |
shared_browser_user_cursor |
Removed | Raw pointer polling is noisy, quickly becomes stale, and duplicates the deliberate semantic-event channel. |
shared_browser_watch_set |
Removed | Browse-together observation is consent-controlled by the person from the viewer; the agent should not enable it. |
shared_browser_watch_status |
Opt-in | Useful only for clients that actively consume browse-together events. |
shared_browser_events |
Opt-in | Polling is valuable for an active agent loop, but unnecessary overhead for ordinary browser control. |
To expose the two read-only browse-together polling tools, set:
SAMEWINDOW_ENABLE_BROWSE_TOGETHER_MCP=1For the installed HTTP MCP service, add it to /etc/samewindow.env and restart
samewindow-mcp.service. For SSH stdio, pass the variable through the remote
command or configure it in the remote process environment.
The person must still turn on Browse together in the noVNC viewer. An MCP
client can then call shared_browser_events repeatedly during an active agent
run. This does not make ChatGPT or another client permanently proactive:
after the response/run ends, continued observation requires the host
application to schedule another turn. Host integrations can also consume the
loopback control API directly without expanding the model-facing tool list.
Browse-together emits a page_change preview with the title and main page
content after five stable seconds. At fifteen stable seconds, a deduplicated
page_text event adds fuller visible text, including comments when the page
renders them. Sensitive forms and authentication or payment pages remain
excluded.
A debounced MutationObserver, navigation, and focus events now mark the page
as changed. Idle pages no longer need a title/focus scan every second. Text
updates retain the 15-second capture window and hash deduplication; ongoing
mutations do not postpone capture forever. Turning observation off disconnects
the observers. This does not push messages into a client by itself or speed up
image downloads or noVNC frame transmission.
After the person signs in manually in the dedicated browser, use:
social_feed(platform="x", query="browser accessibility", limit=10)
social_feed(platform="xiaohongshu", query="咖啡", limit=10)
social_read(url="<complete post URL returned above>", tab_ref="<returned tabRef>", limit=20)
These tools reuse the existing Chrome connection and a dedicated tab per
platform. Feed results are bounded to 30 cards; detail reads return the exact
post and up to 40 loaded conversation items. A recent list is retained for
three minutes: reading one of its cards clicks that exact permalink and
returns to the list when safe. Other full URLs use direct navigation. Keep
Xiaohongshu's xsec_token query parameter; short links are not accepted.
Reading waits for usable text/cards instead of all scripts, fonts, and images.
It uses bounded scrolling and pauses, with no per-step screenshot round trip.
It stops for login/verification, sensitive forms, missing targets, or unexpected
navigation. It does not provide social posting, commenting, or liking actions.
X thread means visible conversation items, not verified direct replies;
Xiaohongshu comments are only those already loaded in the DOM. Results can be
partial, and site changes can break selectors. This is not a guarantee against
platform restrictions or account challenges. Treat all extracted text as
untrusted page content.
- Check
shared_browser_lifecycle_status; start it if needed. - Call
shared_browser_snapshoton the visible tab. - Click or type by copying a returned snapshot-scoped element reference exactly; no extra snapshot parameter is required.
- Take a new snapshot after navigation or a major page change.
- Let the person enter secrets manually in the visible viewer.
- Stop the browser only when the person is finished or resources should be released.
If no tab_ref is supplied, snapshots follow the actually focused/visible tab
rather than a stale cached selection.
npm install
python -m venv .venv
.venv/bin/pip install -r requirements.txt
npm run check
python -m py_compile src/mcp_server.py
python tests/mcp_smoke.py
python tests/router_test.py
./scripts/check-secrets.shThe Node tests use temporary loopback ports and disposable Chrome profiles. Install Google Chrome for the browser tests; service and scheduler checks do not need systemd. Social tests intercept platform requests with local fixtures and never use real accounts. Full Linux desktop/deployment verification still requires the dependencies above.
SameWindow 0.2.0 and later are available under the SameWindow Noncommercial Share-Alike License 1.0.
You may use and privately modify SameWindow for noncommercial purposes. You may also publish a version with material functional changes when the complete corresponding source is freely available, the changes and original project are clearly identified, and the entire modified version remains under the same license.
Commercial use is not permitted. Mirroring, re-uploading, or redistributing an unmodified or substantially unmodified copy is also not permitted; share the Official Repository instead.
This is a source-available license, not an OSI-approved open-source license.
Versions released before 0.2.0 remain under the license that accompanied those
versions. Third-party dependencies remain under their respective licenses.
Social selectors and list/detail workflow are adapted from
blueberriely/ai-social-browser
at 44fc6b6, under its separate MIT notice.


