npm package '@playwright/mcp@latest' is not installed in the npx cache
Playwrightnpm package '@playwright/mcp@latest' is not installed in the npx cache shows up when an MCP client (Claude Desktop, Claude Code, Cursor, or any other tool that launches npx @playwright/mcp@latest as a subprocess) tries to run the Playwright MCP server but npx refuses to download it first. Normally npx fetches a package it doesn't have and runs it in one step, but a lot of MCP client environments launch subprocesses with no network access, a restricted PATH, or an npx invocation that explicitly forbids installing anything — and that combination is exactly what produces this error.
Common mistake
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["@playwright/mcp@latest"]
}
}
}
This looks reasonable and even works the first time you run it manually in a terminal, because your shell's npx is allowed to hit the registry and populate ~/.npm/_npx. But MCP clients often spawn the server process in a sandboxed or restricted environment — no internet access, a stripped-down PATH that doesn't resolve to the same Node/npm install you tested with, or a read-only filesystem where npx can't write its cache. In any of those cases, npx falls back to "cache only" behavior, checks ~/.npm/_npx for a matching package, doesn't find one, and throws instead of installing.
The fix
The reliable fix is to stop asking npx to resolve and download the package at launch time. Install it globally once, then point the MCP config at the actual binary:
npm install -g @playwright/mcp@latest
which mcp-server-playwright # confirm the binary is on PATH
{
"mcpServers": {
"playwright": {
"command": "mcp-server-playwright"
}
}
}
If you'd rather keep using npx (useful for CI or throwaway environments with network access), force it to install rather than only read from cache, and pin a version instead of @latest so a stale or half-written cache entry can't silently break the next launch:
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["-y", "@playwright/mcp@0.0.29"]
}
}
}
If the cache itself is the problem — a previous install got interrupted, or it's owned by a different user and is now read-only — clear it and let npx rebuild it from a clean state:
rm -rf ~/.npm/_npx
npx -y @playwright/mcp@latest --version
Why it works
npx -y (or --yes) tells npx to proceed with installing the package without prompting or bailing out, which matters because non-interactive subprocesses spawned by an MCP client can't answer an interactive "ok to install?" prompt anyway — they just see the negative result and report it as "not installed in the npx cache." Installing globally with npm i -g sidesteps npx entirely: the package lives in your global node_modules and the MCP client just executes a binary that's already there, so there's no cache lookup, no network call, and no dependency on the subprocess having internet access at all. Pinning a version avoids a second failure mode where @latest resolves to different tarballs across environments or gets cached inconsistently between the version your shell has and the version the sandboxed MCP process can see.
Tips
- Run npm config get prefix and compare it against the PATH your MCP client's subprocess actually sees — a mismatched global prefix is the most common reason a package that installs fine in your terminal is invisible to the client.
- If the client supports it, set an absolute path in command (e.g. /usr/local/bin/mcp-server-playwright or the output of which) instead of relying on PATH resolution inside the subprocess.
- Sandboxed or air-gapped MCP runtimes (some CI agents, some desktop app sandboxes) block outbound network calls by design — for those, a pre-installed global package or a bundled node_modules is the only option, since no npx flag will make an offline install succeed.
- If the server does launch but immediately errors afterward, that's usually a different problem — see Playwright MCP error -32603 for the "target page, context or browser has been closed" case.