Skip to content

[MCP]: browser_click takes ~5 s longer when the click's fetch gets a 204 No Content #42786

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:

<button onclick="fetch('/api?status=204').then(() => out.textContent = 'done 204')">fetch 204</button>
<button onclick="fetch('/api?status=200').then(() => out.textContent = 'done 200')">fetch 200</button>
<p id="out"></p>

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
"""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"""<!doctype html><title>repro</title>
<button onclick="fetch('/api?status=204').then(() => out.textContent = 'done 204')">fetch 204</button>
<button onclick="fetch('/api?status=200').then(() => out.textContent = 'done 200')">fetch 200</button>
<p id="out"></p>"""


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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions