Summary
While auditing and hardening local MCP server usage, I reviewed @modelcontextprotocol/server-puppeteer@2025.5.12 and built a local hardened wrapper for our environment. I am opening this issue to share findings and possible upstream improvements.
Browser automation exposed as an MCP tool is high impact: it can navigate to arbitrary targets, execute page JavaScript, capture screenshots, submit forms, and interact with local/private network targets if not constrained. The current server appears designed as a reference/demo server; the suggestions below are intended to make it safer for default agent use.
Package/runtime reviewed
- Package:
@modelcontextprotocol/server-puppeteer@2025.5.12
- npm latest observed:
2025.5.12
- License observed: MIT
- Author observed: Anthropic, PBC
- Issue tracker declared by package:
https://github.com/modelcontextprotocol/servers/issues
- Maintainers observed from npm metadata:
jspahrsummers, thedsp, ashwin-ant
- Local dependency resolution:
@modelcontextprotocol/server-puppeteer@2025.5.12
+-- @modelcontextprotocol/sdk@1.0.1
`-- puppeteer@23.11.1
Audit finding: old MCP SDK bundled with published package
Using a temporary lockfile for @modelcontextprotocol/server-puppeteer@2025.5.12, npm audit --omit=dev --audit-level=moderate reports:
@modelcontextprotocol/sdk <=1.25.1
Severity: high
Anthropic's MCP TypeScript SDK has a ReDoS vulnerability - https://github.com/advisories/GHSA-8r9q-7v3j-jr4g
Model Context Protocol (MCP) TypeScript SDK does not enable DNS rebinding protection by default - https://github.com/advisories/GHSA-w48q-cv73-mx4w
No fix available
node_modules/@modelcontextprotocol/sdk
@modelcontextprotocol/server-puppeteer *
Depends on vulnerable versions of @modelcontextprotocol/sdk
2 high severity vulnerabilities
The package currently depends on @modelcontextprotocol/sdk@1.0.1, so consumers inherit these advisories.
Behavioral findings
From local inspection of dist/index.js and README behavior:
-
Arbitrary URL schemes are accepted by page.goto(args.url).
There is no explicit http/https restriction, embedded-credential rejection, host allowlist/denylist, or private-network blocking.
-
Redirect/subresource requests are not guarded.
A public page can load or redirect to local/private targets unless request interception or equivalent policy is applied.
-
Caller-supplied launchOptions are powerful.
The server has a dangerous-args list, but callers can still influence browser launch behavior. Safer defaults should be stricter for autonomous agent environments.
-
NPX default opens a visible non-headless browser.
This may be surprising for unattended MCP sessions.
-
Screenshots and console logs are stored in memory without obvious caps.
A long-running server can accumulate unbounded screenshots/log entries.
-
puppeteer_fill echoes filled values.
If an agent fills a password/token field, echoing the value back increases the risk of credential retention in chat/session logs.
-
puppeteer_evaluate lacks explicit script/result caps and redaction.
Evaluation is useful, but bounded input, bounded output, timeout, and redaction make it safer.
Local hardening behavior that worked well
For our local wrapper, we preserved the upstream 7 tool names exactly:
puppeteer_navigate
puppeteer_screenshot
puppeteer_click
puppeteer_fill
puppeteer_select
puppeteer_hover
puppeteer_evaluate
We added:
@modelcontextprotocol/sdk@1.29.0 locally instead of the package's bundled SDK 1.0.1
- default
headless=true
- isolated profile under a user data directory, with refusal to use
node_modules
- top-level
http/https only by default
- optional explicit
LCV_PUPPETEER_ALLOW_FILE_URLS=1 override
- embedded-credential rejection
- DNS/private/reserved/loopback/link-local address blocking
- request interception so subresources also pass through the URL/IP guard
- optional allowlist/denylist host controls
- restricted launch options by default, with an explicit unsafe escape hatch
- screenshot dimensions and screenshot count caps
- selector/value/script/result caps
- console log ring buffer
- secret redaction
- no echo of filled values
Validation from local wrapper
7-tool parity:
514:server.registerTool("puppeteer_navigate", {
536:server.registerTool("puppeteer_screenshot", {
572:server.registerTool("puppeteer_click", {
584:server.registerTool("puppeteer_fill", {
601:server.registerTool("puppeteer_select", {
614:server.registerTool("puppeteer_hover", {
626:server.registerTool("puppeteer_evaluate", {
Smoke output:
puppeteer tools: puppeteer_click,puppeteer_evaluate,puppeteer_fill,puppeteer_hover,puppeteer_navigate,puppeteer_screenshot,puppeteer_select
puppeteer file block: Blocked unsupported URL scheme: file:
puppeteer loopback block: Blocked private or reserved IP address: 127.0.0.1
puppeteer example.com final=https://example.com/ title=Example Domain
puppeteer evaluate: Execution result:
puppeteer screenshot: Screenshot 'smoke' taken at 640x480
lcv-hardened-puppeteer 1.0.0 running on stdio; headless=true; block_private=true
Local dependency audit after using newer SDK and overriding ip-address:
$ npm ls ip-address express-rate-limit @modelcontextprotocol/sdk
lcv-workspace@ C:\Users\leona\lcv-workspace
`-- @modelcontextprotocol/sdk@1.29.0
`-- express-rate-limit@8.4.1
`-- ip-address@10.2.0 overridden
$ npm audit --audit-level=moderate
found 0 vulnerabilities
Suggested upstream changes
- Update
@modelcontextprotocol/sdk dependency to a non-vulnerable current release.
- Add default URL policy:
http/https only, reject embedded credentials, optional allowlist/denylist.
- Add DNS/private/reserved address blocking, including redirects and subresource requests.
- Add request interception to apply policy to all browser network requests.
- Default to headless mode for stdio agent usage, or document the non-headless default more prominently.
- Restrict
launchOptions to a safe subset by default. Keep an explicit ALLOW_DANGEROUS/unsafe override for users who need it.
- Add caps for screenshots, console logs, selectors, fill values, evaluate scripts, and evaluate result size.
- Do not echo filled values back to the model; return selector and character count instead.
- Redact common secret patterns in console/evaluate output.
- Add smoke tests for: scheme block, loopback/private IP block, tool list parity, screenshot cap, and launchOption rejection.
Compatibility note
These changes can preserve the current 7 tool names and schemas. The main compatibility risk is users who intentionally depend on local/private-network navigation or custom launch args. Those use cases can be preserved behind explicit environment flags.
Summary
While auditing and hardening local MCP server usage, I reviewed
@modelcontextprotocol/server-puppeteer@2025.5.12and built a local hardened wrapper for our environment. I am opening this issue to share findings and possible upstream improvements.Browser automation exposed as an MCP tool is high impact: it can navigate to arbitrary targets, execute page JavaScript, capture screenshots, submit forms, and interact with local/private network targets if not constrained. The current server appears designed as a reference/demo server; the suggestions below are intended to make it safer for default agent use.
Package/runtime reviewed
@modelcontextprotocol/server-puppeteer@2025.5.122025.5.12https://github.com/modelcontextprotocol/servers/issuesjspahrsummers,thedsp,ashwin-antAudit finding: old MCP SDK bundled with published package
Using a temporary lockfile for
@modelcontextprotocol/server-puppeteer@2025.5.12,npm audit --omit=dev --audit-level=moderatereports:The package currently depends on
@modelcontextprotocol/sdk@1.0.1, so consumers inherit these advisories.Behavioral findings
From local inspection of
dist/index.jsand README behavior:Arbitrary URL schemes are accepted by
page.goto(args.url).There is no explicit
http/httpsrestriction, embedded-credential rejection, host allowlist/denylist, or private-network blocking.Redirect/subresource requests are not guarded.
A public page can load or redirect to local/private targets unless request interception or equivalent policy is applied.
Caller-supplied
launchOptionsare powerful.The server has a dangerous-args list, but callers can still influence browser launch behavior. Safer defaults should be stricter for autonomous agent environments.
NPX default opens a visible non-headless browser.
This may be surprising for unattended MCP sessions.
Screenshots and console logs are stored in memory without obvious caps.
A long-running server can accumulate unbounded screenshots/log entries.
puppeteer_fillechoes filled values.If an agent fills a password/token field, echoing the value back increases the risk of credential retention in chat/session logs.
puppeteer_evaluatelacks explicit script/result caps and redaction.Evaluation is useful, but bounded input, bounded output, timeout, and redaction make it safer.
Local hardening behavior that worked well
For our local wrapper, we preserved the upstream 7 tool names exactly:
We added:
@modelcontextprotocol/sdk@1.29.0locally instead of the package's bundled SDK 1.0.1headless=truenode_moduleshttp/httpsonly by defaultLCV_PUPPETEER_ALLOW_FILE_URLS=1overrideValidation from local wrapper
7-tool parity:
Smoke output:
Local dependency audit after using newer SDK and overriding
ip-address:Suggested upstream changes
@modelcontextprotocol/sdkdependency to a non-vulnerable current release.http/httpsonly, reject embedded credentials, optional allowlist/denylist.launchOptionsto a safe subset by default. Keep an explicitALLOW_DANGEROUS/unsafe override for users who need it.Compatibility note
These changes can preserve the current 7 tool names and schemas. The main compatibility risk is users who intentionally depend on local/private-network navigation or custom launch args. Those use cases can be preserved behind explicit environment flags.