跳至主要内容

claude_remote_playwright_mcp

可以做到,原理其實很單純。關鍵在於 Chrome DevTools Protocol (CDP)。

核心機制

當你在 Windows 上啟動 Chrome 加上 --remote-debugging-port=9222,Chrome 會在那個 port 開一個 HTTP + WebSocket 伺服器。這個伺服器接受 CDP 指令——也就是「導航到某網址」「點擊這個元素」「截圖」等等的控制協議。

換句話說,你的瀏覽器本身就變成了一個可以被網路遠端控制的服務。Playwright(以及 Playwright MCP)並不是非得自己啟動瀏覽器,它也可以用 connectOverCDP()連接一個已經存在的、開著 debug port 的 Chrome

[遠端 Linux: Playwright MCP] --CDP over WebSocket--> [Windows: Chrome :9222]

為什麼「遠端」這件事成立

對 Playwright MCP 來說,它連的只是一個 ws://host:9222/... 的 WebSocket endpoint。這個 endpoint 在本機(localhost)或在另一台機器,協議完全一樣。CDP 不在乎呼叫方在哪裡,它只是收 JSON 指令、回 JSON 結果。

所以從 Linux server 控制 Windows Chrome,本質上就是:Linux 上的程式對 Windows 上那個 9222 port 發 WebSocket 訊息。

但有幾個現實的坑

1. Chrome 預設只綁定 127.0.0.1 --remote-debugging-port=9222 預設只監聽 localhost,外部連不進來。較新版本 Chrome 基於安全考量,甚至不再允許簡單地用 --remote-debugging-address=0.0.0.0 對外暴露。

正確且安全的做法是 SSH tunnel(反向轉發): 從 Windows 端建立到 Linux server 的反向隧道,把 Windows 的 9222 映射到 Linux 的某個 localhost port:

# 在 Windows 上執行(需有 ssh client)
ssh -R 9222:localhost:9222 user@linux-server

這樣 Linux 上的 Playwright MCP 連 localhost:9222,流量就被加密轉發到 Windows 的 Chrome。對 Playwright 來說它以為在連本機,實際走的是隧道。

2. 絕對不要直接把 9222 暴露到公網 CDP 沒有任何驗證機制。誰連得上 9222,誰就能完全控制你的瀏覽器——讀 cookie、開任意網址、抓登入 session。這等同把瀏覽器的鑰匙交出去。SSH tunnel 之所以是標準解法,正是因為它幫你解決了加密 + 認證。

3. 連 endpoint URL 帶 token Playwright 連的精確 WS URL 通常長這樣: ws://localhost:9222/devtools/browser/<UUID> 這個 UUID 可以從 http://localhost:9222/json/version 拿到。

你的場景下值得想清楚的一點

以你的需求(從遠端 Linux 操作本地 Windows),架構是反過來的——通常 MCP / 自動化主機在本地、瀏覽器也在本地。你這邊是控制端在遠端、被控端(瀏覽器)在本地,所以隧道方向、防火牆規則都要以「Windows 為服務提供者」來設計,反向 SSH 轉發是最乾淨的。

要我幫你寫一份完整的 Windows 端 Chrome 啟動指令 + 反向 tunnel + Linux 端 Playwright MCP 連線設定嗎? Chrome 啟動需要指定一個獨立的 user-data-dir,否則會去附掛到已開啟的 Chrome 實例,debug port 就掛不上。我把完整流程分四步給你。

1. Windows 端:啟動帶 debug port 的 Chrome

開 PowerShell 或 cmd,用獨立 profile 啟動:

& "C:\Program Files\Google\Chrome\Application\chrome.exe" `
--remote-debugging-port=9222 `
--user-data-dir="C:\chrome-debug-profile"

幾個重點:

  • --user-data-dir 指向一個全新或專用資料夾。這會是個乾淨 profile(沒有你平常的書籤/登入),如果你需要既有登入狀態,把它指到你真實 profile 的複本,別直接指原本那個正在用的。
  • 啟動後先在本機驗證:瀏覽器開 http://localhost:9222/json/version,有 JSON 跑出來就代表 CDP 起來了。
  • 確認 webSocketDebuggerUrl 欄位存在,這就是 Playwright 要連的 endpoint。

2. Windows 端:建立反向 SSH 隧道

Windows 10/11 內建 OpenSSH client,直接用:

ssh -N -R 9222:localhost:9222 user@linux-server
  • -R 9222:localhost:9222:把 Linux server 的 localhost:9222 反向轉發到 Windows 的 localhost:9222
  • -N:不開遠端 shell,純做轉發。
  • 這條指令要保持執行(隧道存活期間)。斷了就要重連。

一個常見坑: Linux server 的 sshd 預設 GatewayPorts no,反向轉發出來的 port 只綁在 server 的 127.0.0.1。這對你正好——你要的就是只有 server 本機(Playwright MCP)連得到,不對外。保持預設即可,不要去開 GatewayPorts yes

3. Linux 端:驗證隧道通了

在 Linux server 上:

curl http://localhost:9222/json/version

跑出跟你在 Windows 看到的同一份 JSON,就代表隧道 + CDP 全線打通。

4. Linux 端:Playwright MCP 連線設定

Playwright MCP 用 --cdp-endpoint 連既有瀏覽器,不要讓它自己啟瀏覽器:

{
"mcpServers": {
"playwright": {
"command": "npx",
"args": [
"@playwright/mcp@latest",
"--cdp-endpoint", "http://localhost:9222"
]
}
}
}

說明:

  • http://localhost:9222 即可,Playwright MCP 會自己去 /json/version 抓那個帶 UUID 的 webSocketDebuggerUrl,你不用手動填 WS URL。
  • 因為走隧道,Linux 這邊看起來就是在連本機,Playwright 完全無感。

如果你是用程式碼直接呼叫 Playwright 而非 MCP:

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.connect_over_cdp("http://localhost:9222")
context = browser.contexts[0] # 用既有 context
page = context.pages[0] if context.pages else context.new_page()
page.goto("https://example.com")
print(page.title())

整體資料流

[Linux: Playwright MCP]
│ 連 localhost:9222 (以為是本機)

[Linux sshd: 127.0.0.1:9222] ← 反向轉發出口
│ SSH 加密隧道

[Windows ssh client]
│ 轉回 localhost:9222

[Windows: Chrome CDP :9222] ← 真正的瀏覽器

給你的兩個提醒

穩定性:ssh -R 隧道斷線不會自動重連。長期跑的話在 Windows 端用 autossh,或在 ssh 指令加 keepalive:

ssh -N -R 9222:localhost:9222 -o ServerAliveInterval=30 -o ServerAliveCountInterval=3 user@linux-server

安全性:整條鏈路只要 GatewayPorts 保持 no,9222 就永遠不會暴露到公網,風險可控。唯一能控制你瀏覽器的就是 Linux server 上的本機程序——確保那台 server 你自己掌握存取權即可。

需要我幫你把 Windows 端那兩條指令(啟 Chrome + 開隧道)包成一個 .ps1 一鍵啟動腳本嗎?