Playwright MCP in Claude Code/Codex: the accessibility snapshot in practice
Playwright MCP gives your coding agent a browser. It doesn't send a screenshot: it sends the accessibility snapshot, with a reference (ref) on each control, and the agent acts through those refs. In this lesson a script makes exactly the calls the agent would make, without a model, and then you connect the server to Claude Code or Codex.
3The common mistake: taking the snapshot before the page has finished loading
4Connect to Claude Code (.mcp.json) and Codex (config.toml)
Goal: do with Playwright MCP what the agent does: read the snapshot, find the control by its ref and act, and connect the server to your agent.
01 · From snapshot to click
Click each block.
After acting, a new snapshot
Click each block in the diagram to see what it does.
A ref is valid for that snapshot only. If the page changed, take another.
02 · What the agent receives and what it does
Reading the dry-run output.
1. The tools
The server exposes 25 browser tools (browser_navigate, browser_snapshot, browser_click, browser_type, browser_select_option, browser_wait_for, browser_console_messages…). The agent sees them all in the list and chooses.
The project's documentation today also recommends a CLI-and-skills route for coding agents; the kit uses the tools over MCP, which is what Claude Code and Codex read.
2. Refs, not coordinates
In the dry run, the script found textbox "Seu nome" = e13, combobox "Serviço" = e15 and button "Ver horários" = e18 and acted through them. No pixels.
That's why the HTML from course 0 matters: the label becomes the name, and the name is how the agent finds the ref.
3. Wait for the proof
The times arrive 1.5 s after the click. With browser_wait_for waiting for the text "horários livres para", the snapshot comes back complete. Without waiting, you get "Carregando horários…" and zero times.
Wait for the text that proves the result, not for a number of seconds.
Who pays for the mistakeAn agent that takes the snapshot too early answers "there are no times" with full confidence. Lesson 7 comes back to this case.
03 · The dry run: the agent's calls, without a model
ver-como-agente.mjs connects Playwright MCP over stdio, the way Claude Code would, and does the task "see the times for Revisão completa".
Terminal · do the task through Playwright MCP
cd ~/projetos/formacao-webmcp-navegador-v72/kit
node ver-como-agente.mjs; echo "saída: $?"
Terminal · the same, without waiting for the list
cd ~/projetos/formacao-webmcp-navegador-v72/kit
node ver-como-agente.mjs --sem-esperar; echo "saída: $?"
Example from the repository
→ browser_navigate {"url":"http://127.0.0.1:PORTA/agendar.html"}
→ browser_snapshot {}
achei: textbox "Seu nome" = e13 · combobox "Serviço" = e15 · button "Ver horários" = e18
→ browser_type {"target":"e13","text":"Ana"}
→ browser_select_option {"target":"e15","values":["Revisão completa"]}
→ browser_click {"target":"e18"}
→ browser_wait_for {"text":"horários livres para"}
→ browser_snapshot {}
status na página: 2 horários livres para Revisão completa em 2026-11-03.
tarefa: horários de Revisão completa em 2026-11-03 → 2 (08:00, 13:00) em 7 chamadas
saída: 0
Seven calls. Each line with → is a tool called by the MCP client; PORTA stands in for the local server's random port.
→ browser_navigate {"url":"http://127.0.0.1:PORTA/agendar.html"}
→ browser_snapshot {}
achei: textbox "Seu nome" = e13 · combobox "Serviço" = e15 · button "Ver horários" = e18
→ browser_type {"target":"e13","text":"Ana"}
→ browser_select_option {"target":"e15","values":["Revisão completa"]}
→ browser_click {"target":"e18"}
→ browser_snapshot {}
status na página: Carregando horários…
tarefa: horários de Revisão completa em 2026-11-03 → 0 (nenhum) em 6 chamadas
a lista ainda estava carregando quando o snapshot foi tirado
saída: 1
Without browser_wait_for, the snapshot catches "Carregando horários…". The script exits with 1, but a hurried agent would say there's no time available.
04 · Your site and your agent
First read-only on your site; then the server connected to Claude Code and Codex, with the kit's Chromium.
Terminal · snapshot of one of your sites
cd ~/projetos/formacao-webmcp-navegador-v72/kit
node ver-como-agente.mjs https://inema.club/; echo "saída: $?"
Terminal · connect to Claude Code and Codex
cd ~/projetos/formacao-webmcp-navegador-v72/kit
node configurar.mjs && claude mcp get playwright; echo "saída: $?"
Example from the repository
→ browser_navigate {"url":"https://inema.club/"}
→ browser_snapshot {}
- generic [active] [ref=e1]:
- banner [ref=e2]:
- generic [ref=e3]:
- link "INEMA.VIP Participe da Comunidade" [ref=e4] [cursor=pointer]:
- /url: https://inema.vip
- img "INEMA.VIP" [ref=e5]
- generic [ref=e6]: Participe da Comunidade
- link "INEMA.PRO Assine o INEMA.PRO" [ref=e7] [cursor=pointer]:
- /url: https://inema.pro
- img "INEMA.PRO" [ref=e8]
- generic [ref=e9]: Assine o INEMA.PRO
- generic [ref=e10]:
- heading "INEMA.CLUB Portal INEMA" [level=1] [ref=e11]
- paragraph [ref=e12]: "Aprenda Inteligência Artificial na prática: cursos, agentes, automação e comunidade"
- generic [ref=e13]:
(…)
ferramentas WebMCP vistas: 8 · buscar_conhecimento [readOnly], buscar_cursos [readOnly], comparar_cursos [readOnly], …
snapshot: 1040 linhas · 749 elementos com ref
saída: 0
Here the site is inema.club, INEMA's own. On your machine, swap in the address of one of your own sites: a browser agent only against your own site. The summary shows how many lines and refs the agent receives; inema.club has an origin trial token, so the kit's Chromium 156 also saw its 8 WebMCP tools, with no flag at all.
Chromium do kit: chromium-1248/chrome-linux-arm64/chrome
escrevi kit/pw-config.json e kit/.mcp.json (servidores: playwright, devtools)
# Codex: cole em ~/.codex/config.toml se quiser os mesmos servidores lá
[mcp_servers.playwright]
command = "~/projetos/formacao-webmcp-navegador-v72/kit/node_modules/.bin/playwright-mcp"
args = ["--config", "~/projetos/formacao-webmcp-navegador-v72/kit/pw-config.json"]
[mcp_servers.devtools]
command = "~/projetos/formacao-webmcp-navegador-v72/kit/node_modules/.bin/chrome-devtools-mcp"
args = ["--headless", "--isolated", "--executablePath", "~/.cache/ms-playwright/chromium-1248/chrome-linux-arm64/chrome", "--no-usage-statistics", "--no-performance-crux", "--chromeArg=--no-sandbox"]
playwright:
Scope: Project config (shared via .mcp.json)
Status: ⏸ Pending approval (run `claude` to approve)
Type: stdio
Command: ~/projetos/formacao-webmcp-navegador-v72/kit/node_modules/.bin/playwright-mcp
Args: --config ~/projetos/formacao-webmcp-navegador-v72/kit/pw-config.json
To remove this server, run: claude mcp remove playwright -s project
saída: 0
configurar.mjs writes kit/.mcp.json (Claude Code, project scope) and prints the Codex snippet (in the capture, ~ abbreviates the path; the script prints the absolute path, which is what Codex needs), which you paste into ~/.codex/config.toml if you want. A project server stays "Pending approval" until you open claude inside kit/ and approve it; after that, /mcp shows the server connected (that step was not captured).
05 · Practice
The dry run, your site, and your agent. Each exercise has a "done when".
E1 · The seven calls
10 min
Run the dry run, then the no-wait version. For each line with →, say in one sentence what the agent knew at that moment.
Done when:node ver-como-agente.mjs → 2 (08:00, 13:00) em 7 chamadas and saída: 0; with --sem-esperar → 0 (nenhum) and saída: 1.
E2 · The snapshot of your site
10 min
Run the read block with the address of one of your own sites. Open the same page in the browser and find three controls in the output by name.
Done when:node ver-como-agente.mjs with your site's address → snapshot: N linhas · M elementos com ref and saída: 0.
E3 · The server in your agent
10 min
Run configurar.mjs. In Claude Code, open claude inside kit/ and approve the project server. In Codex, paste the printed snippet into config.toml.
Done when:claude mcp get playwright → Scope: Project config and, after you approve, Status: ✓ Connected; in Codex, the server shows up in the /mcp list.
06 · Lesson quiz
Question 1 of 4
The snapshot shows textbox "Seu nome" [ref=e13]. How does the agent type into this field?skill
Correct
Playwright MCP acts through the snapshot ref. Coordinates are the computer use path; the console is another server (lesson 3).
Not that one
Playwright MCP acts through the snapshot ref. Coordinates are the computer use path; the console is another server (lesson 3).Review: From snapshot to click
Question 2 of 4
The agent clicked "Ver horários", took the snapshot right away, and saw "Carregando horários…". What was missing?
Correct
browser_wait_for with the text "horários livres para" waits for the proof. Reloading loses what was filled in; clicking again fires another search.
What is the element field for in click and type calls?
Correct
element is the human-readable description used to ask the person for permission; the target (the ref) is what finds the control.
Not that one
element is the human-readable description used to ask the person for permission; the target (the ref) is what finds the control.Review: From snapshot to click
Question 4 of 4
After configurar.mjs, claude mcp get shows "Pending approval". What should you do?
Correct
A project server (.mcp.json) needs approval the first time. The kit uses Playwright's Chromium; it doesn't need stable Chrome.