microsoft / microsoft/playwright
[MCP]: browser_click takes ~5 s longer when the click's fetch gets a 204 No Content
- Dominant language
- TypeScript
- Stars
- 96.3k
- Forks
- 6.5k
- Avg merge
- 1d 6h
- Merged PRs (30d)
- 180
Description
### What's going on?
`browser_click` takes about 6 s when the click makes the page `fetch` a URL that answers **`204 No Content`**. The same click answered with `200` takes about 1 s. The page is done within a few milliseconds either way, so the extra ~5 s looks like the post-action wait running to its timeout because it never sees the 204 request finish.
Real apps hit this often: many APIs return 204 for writes (save, delete, "mark as read"), so every such click costs an agent ~5 s.
**Repro.** A page with two buttons, each doing a `fetch` and writing to the DOM when it resolves:
```html
fetch 204
fetch 200
```
The server answers `/api?status=204` with `204` and an empty body, and `/api?status=200` with `200` and `{}` (`Content-Type: application/json`). Driving `@playwright/mcp@0.0.82 --headless --isolated` over stdio (`browser_navigate`, `browser_snapshot`, then `browser_click` on each button):
```
fetch 204: browser_click took 6.04 s
fetch 200: browser_click took 1.03 s
fetch 204: browser_click took 6.03 s
fetch 200: browser_click took 1.03 s
```
Same result on 0.0.81. The full script (Python MCP client plus the tiny server, ~60 lines) is below.
repro.py
```python
"""Playwright MCP: browser_click stalls ~6 s when the click triggers a fetch answered with 204 No Content."""
import asyncio
import http.server
import threading
import time
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
PAGE = b"""repro
fetch 204
fetch 200
class H(http.server.BaseHTTPRequestHandler):
def do_GET(self):
if self.path.startswith("/api"):
status = int(self.path.split("=")[1])
body = b"" if status == 204 else b"{}"
self.send_response(status)
if status == 200:
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
else:
self.send_response(200)
self.send_header("Content-Type", "text/html")
self.send_header("Content-Length", str(len(PAGE)))
self.end_headers()
self.wfile.write(PAGE)
def log_message(self, *a):
pass
async def main():
srv = http.server.ThreadingHTTPServer(("127.0.0.1", 0), H)
threading.Thread(target=srv.serve_forever, daemon=True).start()
url = f"http://127.0.0.1:{srv.server_port}/"
p = StdioServerParameters(command="npx", args=["-y", "@playwright/mcp@0.0.82", "--headless", "--isolated"])
async with stdio_client(p) as (r, w), ClientSession(r, w) as s:
info = await s.initialize()
print("server:", info.server_info.name, info.server_info.version)
await s.call_tool("browser_navigate", {"url": url})
snap = (await s.call_tool("browser_snapshot", {})).content[0].text
import re
refs = dict(re.findall(r'button "(fetch \d+)" \[ref=(e\d+)\]', snap))
for _ in range(2):
for label in ("fetch 204", "fetch 200"):
t0 = time.perf_counter()
await s.call_tool("browser_click", {"element": label, "target": refs[label]})
print(f"{label}: browser_click took {time.perf_counter() - t0:.2f} s")
asyncio.run(main())
```
**Expected:** a click whose request gets a 204 returns as fast as one that gets a 200.
### Version
`@playwright/mcp` 0.0.82 (server reports Playwright 1.64.0-alpha-1789764292000). macOS 26.6, Node 24.11, headless Chromium.
Contributor guide
Research direction
Start by running the provided repro.py script with the browser_click entry point and compare the 204 and 200 timings. Trace the post-action wait and network completion handling for the 204 request, then add regression coverage showing that browser_click returns promptly for both responses; done means the 204 case is close to the 200 case and the regression passes.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- playwright, typescript
- Domain
- testing-qa
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100