playwright-spa-mcp
<div align="center">
# ๐ญ Playwright SPA MCP
**The Playwright MCP server built for modern web apps โ persistent sessions, React/Vue/Angular support, 143+ devices and smart DOM waiting.**
[](LICENSE)


**[Installation](#installation) ยท [Why](#why-this-server) ยท [Tools](#tools) ยท [Examples](#examples) ยท [Limitations](#limitations)**
</div>
## Overview
An [MCP](https://modelcontextprotocol.io) server that drives a real Chromium browser, purpose-built for Single Page Applications. Your assistant can log in, click, type and screenshot โ and the SPA's state survives across calls:
> **You:** Log into app.example.com, open the dashboard and screenshot it.
>
> **Assistant:** *(calls `spa_session_start`, then `spa_chain` [type-realistic โ click โ wait-for-idle], then `spa_screenshot`)*
> Logged in and captured the dashboard โ React state preserved across the session.
## Why this server?
| Feature | playwright-spa-mcp | Other Playwright MCPs |
|---------|:------------------:|:---------------------:|
| Persistent sessions | โ
| โ |
| React/Vue/Angular detection | โ
| โ |
| Smart DOM idle waiting | โ
| โ |
| Realistic typing | โ
| โ |
| Device emulation | 143+ | Limited |
| Action chains | โ
| โ |
| HTTP with browser cookies | โ
| โ |
## Installation
```bash
# One command โ no clone needed
claude mcp add playwright-spa -- npx -y github:manganate006/playwright-spa-mcp
# Install the Chromium browser Playwright drives
npx playwright install chromium
```
**Claude Desktop** (`claude_desktop_config.json`):
```json
{
"mcpServers": {
"playwright-spa": {
"command": "npx",
"args": ["-y", "github:manganate006/playwright-spa-mcp"]
}
}
}
```
<details>
<summary>Alternative: clone & install locally</summary>
```bash
git clone https://github.com/manganate006/playwright-spa-mcp.git
cd playwright-spa-mcp
npm install
npx playwright install chromium
claude mcp add playwright-spa -- node $(pwd)/src/index.js
```
</details>
## Tools
22 tools, `spa_` prefix. Chain-action list, 143+ device shortcuts and CLI usage: **[docs/ADVANCED.md](docs/ADVANCED.md)**.
| Group | Tools |
|---|---|
| ๐ธ Screenshot & navigation | `spa_screenshot`, `spa_navigate`, `spa_go_back`, `spa_go_forward`, `spa_wait_idle` |
| ๐ Interactions | `spa_click`, `spa_fill`, `spa_type_realistic`, `spa_upload`, `spa_drag` |
| โ๏ธ Chains | `spa_chain` (run many actions in one call) |
| ๐ Sessions | `spa_session_start`, `spa_session_end`, `spa_session_list` |
| ๐ Inspection | `spa_get_text`, `spa_assert`, `spa_evaluate`, `spa_http_request` |
| ๐ผ๏ธ iframes | `spa_iframe_click`, `spa_iframe_fill` |
| โน๏ธ Discovery | `spa_list_devices`, `spa_list_actions` |
## Examples
**Log into a React app and keep the session** โ realistic typing fires React's `onChange`:
```js
spa_session_start({ session: "myapp" })
spa_chain({
session: "myapp",
url: "https://app.example.com/login",
spaMode: "react",
chain: [
{ action: "type-realistic", selector: "#email", value: "user@example.com" },
{ action: "type-realistic", selector: "#password", value: "secret123" },
{ action: "click", selector: "button[type=submit]" },
{ action: "wait-for-idle" },
{ action: "screenshot" }
]
})
// Same session โ React state is preserved
spa_click({ session: "myapp", selector: ".dashboard-item" })
```
**Mobile device emulation:**
```js
spa_screenshot({ url: "https://example.com", device: "iPhone 15 Pro", fullPage: true })
```
**Authenticated API call reusing the session cookies:**
```js
spa_http_request({ session: "myapp", url: "https://api.example.com/user/profile", method: "GET" })
```
## Limitations
- **Chromium only** โ Firefox/WebKit are not wired up; requires `npx playwright install chromium`
- **Local browser** โ spawns a real browser process; not suited to sandboxes without one
- **SPA heuristics** โ React/Vue/Angular readiness is detected heuristically (`spaMode: "auto"`); heavy custom frameworks may need explicit `wait-for` / `wait-for-idle`
- **Sessions are in-memory** โ they live with the server process and don't persist across restarts
## SPA framework support
Detects and waits correctly for **React** (finishes rendering, proper `onChange`), **Vue** (v-model bindings) and **Angular** (Zone.js stability). Use `spaMode: "auto"` to detect automatically. Details in [docs/ADVANCED.md](docs/ADVANCED.md#spa-framework-support).
## Built with this MCP
| Project | Description |
|---|---|
| [atp.mangi.fr](https://atp.mangi.fr) | ๐พ ATP tennis stats since 1968 |
| [piscinade.com](https://piscinade.com) | ๐ Pool-party finder in France |
## License
[MIT](LICENSE) ยฉ manganate006
TDQS
Scored across 22 tools
Most tools map cleanly to distinct actions, such as navigate, click, fill, screenshot, and session management. The main ambiguity is between spa_fill and spa_type_realistic for input handling, and spa_chain subsumes many individual actions, but the descriptions generally clarify intent.
All tools share the consistent spa_ prefix and use snake_case, which makes the set feel coherent. Some names are verb-first like spa_get_text, while others are object-first like spa_iframe_click or noun-like spa_http_request, so the pattern is not perfectly uniform.
With 22 tools, the server sits in the heavy 16-25 range. Many tools are justified for browser automation, but iframe variants, the chain command, and session management tools could potentially be consolidated, making the surface feel slightly bloated.
The tool surface covers the core SPA testing lifecycle well: navigation, clicking, filling, typing, iframes, uploads, drag-and-drop, assertions, sessions, screenshots, and JavaScript evaluation. Minor gaps like explicit dropdown/select handling, hover, and dedicated element-wait commands exist, but they can be worked around with spa_evaluate or spa_chain.