fix(mcp): skip base64 image response when filename param is provided in browser_take_screenshot - #40507

Closed
Jio Kim (MeroZemory) wants to merge 1 commit into
microsoft:mainfrom
MeroZemory:fix/screenshot-omit-base64-when-filename-provided
Closed

fix(mcp): skip base64 image response when filename param is provided in browser_take_screenshot#40507
Jio Kim (MeroZemory) wants to merge 1 commit into
microsoft:mainfrom
MeroZemory:fix/screenshot-omit-base64-when-filename-provided

Conversation

@MeroZemory

@MeroZemoryJio Kim (MeroZemory) commented Apr 30, 2026

Copy link
Copy Markdown

Closesmicrosoft/playwright-mcp#1581

Problem

When browser_take_screenshot is called with an explicit filename parameter, the screenshot is correctly saved to disk, but the raw image buffer is still registered in the MCP response as base64. A typical 1280x720 PNG becomes 400 KB–1 MB of base64 text, which can consume 70–80% of a 200k-token context window in a single call.

Root cause

In screenshot.ts, registerImageResult() is called unconditionally regardless of whether filename was provided:

awaitresponse.addFileResult(resolvedFile,data);awaitresponse.registerImageResult(data,fileType);// always runs

The only workaround today is the global --image-responses omit flag, which also suppresses images when the LLM genuinely needs them for visual analysis.

Fix

Skip registerImageResult() when filename is explicitly provided. The caller already knows where the file is saved and does not need the raw buffer in the LLM context.

awaitresponse.addFileResult(resolvedFile,data);if(!params.filename)awaitresponse.registerImageResult(data,fileType);

When filename is omitted the behaviour is unchanged: the image is still sent to the LLM for immediate visual analysis.

Related closed issues

When browser_take_screenshot is called with an explicit filename, the
screenshot is saved to disk but the raw image buffer was still registered
in the MCP response, embedding up to 1 MB of base64 into the LLM context.
Only call registerImageResult() when no filename was supplied, preserving
the current behaviour for inline visual analysis while eliminating the
redundant payload for file-save calls.
Fixesmicrosoft/playwright-mcp#1581
@Skn0tt

Copy link
Copy Markdown
Contributor

Could you add a test for the behaviour?

@github-actions

Copy link
Copy Markdown
Contributor

Test results for "MCP"

18 failed
❌ [chrome] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-chrome
❌ [chrome] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-chrome
❌ [chrome] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-chrome
❌ [chromium] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-chromium
❌ [chromium] › mcp/dashboard.spec.ts:207 › should enter annotate mode on fresh dashboard.tsx mount with -s --annotate @mcp-windows-latest-chromium
❌ [chromium] › mcp/dashboard.spec.ts:231 › should switch screencast to -s session on show --annotate @mcp-windows-latest-chromium
❌ [chromium] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-chromium
❌ [chromium] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-chromium
❌ [firefox] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-firefox
❌ [firefox] › mcp/http.spec.ts:103 › http transport browser lifecycle (isolated) @mcp-windows-latest-firefox
❌ [firefox] › mcp/http.spec.ts:142 › http transport browser sigint @mcp-windows-latest-firefox
❌ [firefox] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-firefox
❌ [firefox] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-firefox
❌ [msedge] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-msedge
❌ [webkit] › mcp/dashboard.spec.ts:185 › should start dashboard and annotate when no dashboard is running @mcp-macos-latest-webkit
❌ [webkit] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-webkit
❌ [webkit] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-webkit
❌ [webkit] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-webkit

6803 passed, 927 skipped


Merge workflow run.

@MeroZemory

Copy link
Copy Markdown
Author

Closing this in favor of #40577 makes sense since it includes the same fix plus the requested test update.

For context, this PR originally proposed the same registerImageResult guard before #40577 was opened. If #40577 does not end up landing for any reason, I am happy to help continue from here.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

browser_take_screenshot: image still returned as base64 in MCP response when filename param is provided

2 participants

@MeroZemory@Skn0tt
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(mcp): skip base64 image response when filename param is provided in browser_take_screenshot - #40507

Closed
Jio Kim (MeroZemory) wants to merge 1 commit into
microsoft:mainfrom
MeroZemory:fix/screenshot-omit-base64-when-filename-provided
Closed

fix(mcp): skip base64 image response when filename param is provided in browser_take_screenshot#40507
Jio Kim (MeroZemory) wants to merge 1 commit into
microsoft:mainfrom
MeroZemory:fix/screenshot-omit-base64-when-filename-provided

Conversation

@MeroZemory

@MeroZemoryJio Kim (MeroZemory) commented Apr 30, 2026

Copy link
Copy Markdown

Closesmicrosoft/playwright-mcp#1581

Problem

When browser_take_screenshot is called with an explicit filename parameter, the screenshot is correctly saved to disk, but the raw image buffer is still registered in the MCP response as base64. A typical 1280x720 PNG becomes 400 KB–1 MB of base64 text, which can consume 70–80% of a 200k-token context window in a single call.

Root cause

In screenshot.ts, registerImageResult() is called unconditionally regardless of whether filename was provided:

awaitresponse.addFileResult(resolvedFile,data);awaitresponse.registerImageResult(data,fileType);// always runs

The only workaround today is the global --image-responses omit flag, which also suppresses images when the LLM genuinely needs them for visual analysis.

Fix

Skip registerImageResult() when filename is explicitly provided. The caller already knows where the file is saved and does not need the raw buffer in the LLM context.

awaitresponse.addFileResult(resolvedFile,data);if(!params.filename)awaitresponse.registerImageResult(data,fileType);

When filename is omitted the behaviour is unchanged: the image is still sent to the LLM for immediate visual analysis.

Related closed issues

When browser_take_screenshot is called with an explicit filename, the
screenshot is saved to disk but the raw image buffer was still registered
in the MCP response, embedding up to 1 MB of base64 into the LLM context.
Only call registerImageResult() when no filename was supplied, preserving
the current behaviour for inline visual analysis while eliminating the
redundant payload for file-save calls.
Fixesmicrosoft/playwright-mcp#1581
@Skn0tt

Copy link
Copy Markdown
Contributor

Could you add a test for the behaviour?

@github-actions

Copy link
Copy Markdown
Contributor

Test results for "MCP"

18 failed
❌ [chrome] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-chrome
❌ [chrome] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-chrome
❌ [chrome] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-chrome
❌ [chromium] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-chromium
❌ [chromium] › mcp/dashboard.spec.ts:207 › should enter annotate mode on fresh dashboard.tsx mount with -s --annotate @mcp-windows-latest-chromium
❌ [chromium] › mcp/dashboard.spec.ts:231 › should switch screencast to -s session on show --annotate @mcp-windows-latest-chromium
❌ [chromium] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-chromium
❌ [chromium] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-chromium
❌ [firefox] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-firefox
❌ [firefox] › mcp/http.spec.ts:103 › http transport browser lifecycle (isolated) @mcp-windows-latest-firefox
❌ [firefox] › mcp/http.spec.ts:142 › http transport browser sigint @mcp-windows-latest-firefox
❌ [firefox] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-firefox
❌ [firefox] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-firefox
❌ [msedge] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-msedge
❌ [webkit] › mcp/dashboard.spec.ts:185 › should start dashboard and annotate when no dashboard is running @mcp-macos-latest-webkit
❌ [webkit] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-webkit
❌ [webkit] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-webkit
❌ [webkit] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-webkit

6803 passed, 927 skipped


Merge workflow run.

@MeroZemory

Copy link
Copy Markdown
Author

Closing this in favor of #40577 makes sense since it includes the same fix plus the requested test update.

For context, this PR originally proposed the same registerImageResult guard before #40577 was opened. If #40577 does not end up landing for any reason, I am happy to help continue from here.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

browser_take_screenshot: image still returned as base64 in MCP response when filename param is provided

2 participants

@MeroZemory@Skn0tt
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(mcp): skip base64 image response when filename param is provided in browser_take_screenshot - #40507

Closed
Jio Kim (MeroZemory) wants to merge 1 commit into
microsoft:mainfrom
MeroZemory:fix/screenshot-omit-base64-when-filename-provided
Closed

fix(mcp): skip base64 image response when filename param is provided in browser_take_screenshot#40507
Jio Kim (MeroZemory) wants to merge 1 commit into
microsoft:mainfrom
MeroZemory:fix/screenshot-omit-base64-when-filename-provided

Conversation

@MeroZemory

@MeroZemoryJio Kim (MeroZemory) commented Apr 30, 2026

Copy link
Copy Markdown

Closesmicrosoft/playwright-mcp#1581

Problem

When browser_take_screenshot is called with an explicit filename parameter, the screenshot is correctly saved to disk, but the raw image buffer is still registered in the MCP response as base64. A typical 1280x720 PNG becomes 400 KB–1 MB of base64 text, which can consume 70–80% of a 200k-token context window in a single call.

Root cause

In screenshot.ts, registerImageResult() is called unconditionally regardless of whether filename was provided:

awaitresponse.addFileResult(resolvedFile,data);awaitresponse.registerImageResult(data,fileType);// always runs

The only workaround today is the global --image-responses omit flag, which also suppresses images when the LLM genuinely needs them for visual analysis.

Fix

Skip registerImageResult() when filename is explicitly provided. The caller already knows where the file is saved and does not need the raw buffer in the LLM context.

awaitresponse.addFileResult(resolvedFile,data);if(!params.filename)awaitresponse.registerImageResult(data,fileType);

When filename is omitted the behaviour is unchanged: the image is still sent to the LLM for immediate visual analysis.

Related closed issues

When browser_take_screenshot is called with an explicit filename, the
screenshot is saved to disk but the raw image buffer was still registered
in the MCP response, embedding up to 1 MB of base64 into the LLM context.
Only call registerImageResult() when no filename was supplied, preserving
the current behaviour for inline visual analysis while eliminating the
redundant payload for file-save calls.
Fixesmicrosoft/playwright-mcp#1581
@Skn0tt

Copy link
Copy Markdown
Contributor

Could you add a test for the behaviour?

@github-actions

Copy link
Copy Markdown
Contributor

Test results for "MCP"

18 failed
❌ [chrome] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-chrome
❌ [chrome] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-chrome
❌ [chrome] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-chrome
❌ [chromium] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-chromium
❌ [chromium] › mcp/dashboard.spec.ts:207 › should enter annotate mode on fresh dashboard.tsx mount with -s --annotate @mcp-windows-latest-chromium
❌ [chromium] › mcp/dashboard.spec.ts:231 › should switch screencast to -s session on show --annotate @mcp-windows-latest-chromium
❌ [chromium] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-chromium
❌ [chromium] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-chromium
❌ [firefox] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-firefox
❌ [firefox] › mcp/http.spec.ts:103 › http transport browser lifecycle (isolated) @mcp-windows-latest-firefox
❌ [firefox] › mcp/http.spec.ts:142 › http transport browser sigint @mcp-windows-latest-firefox
❌ [firefox] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-firefox
❌ [firefox] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-firefox
❌ [msedge] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-msedge
❌ [webkit] › mcp/dashboard.spec.ts:185 › should start dashboard and annotate when no dashboard is running @mcp-macos-latest-webkit
❌ [webkit] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-webkit
❌ [webkit] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-webkit
❌ [webkit] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-webkit

6803 passed, 927 skipped


Merge workflow run.

@MeroZemory

Copy link
Copy Markdown
Author

Closing this in favor of #40577 makes sense since it includes the same fix plus the requested test update.

For context, this PR originally proposed the same registerImageResult guard before #40577 was opened. If #40577 does not end up landing for any reason, I am happy to help continue from here.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

browser_take_screenshot: image still returned as base64 in MCP response when filename param is provided

2 participants

@MeroZemory@Skn0tt
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(mcp): skip base64 image response when filename param is provided in browser_take_screenshot - #40507

Closed
Jio Kim (MeroZemory) wants to merge 1 commit into
microsoft:mainfrom
MeroZemory:fix/screenshot-omit-base64-when-filename-provided
Closed

fix(mcp): skip base64 image response when filename param is provided in browser_take_screenshot#40507
Jio Kim (MeroZemory) wants to merge 1 commit into
microsoft:mainfrom
MeroZemory:fix/screenshot-omit-base64-when-filename-provided

Conversation

@MeroZemory

@MeroZemoryJio Kim (MeroZemory) commented Apr 30, 2026

Copy link
Copy Markdown

Closesmicrosoft/playwright-mcp#1581

Problem

When browser_take_screenshot is called with an explicit filename parameter, the screenshot is correctly saved to disk, but the raw image buffer is still registered in the MCP response as base64. A typical 1280x720 PNG becomes 400 KB–1 MB of base64 text, which can consume 70–80% of a 200k-token context window in a single call.

Root cause

In screenshot.ts, registerImageResult() is called unconditionally regardless of whether filename was provided:

awaitresponse.addFileResult(resolvedFile,data);awaitresponse.registerImageResult(data,fileType);// always runs

The only workaround today is the global --image-responses omit flag, which also suppresses images when the LLM genuinely needs them for visual analysis.

Fix

Skip registerImageResult() when filename is explicitly provided. The caller already knows where the file is saved and does not need the raw buffer in the LLM context.

awaitresponse.addFileResult(resolvedFile,data);if(!params.filename)awaitresponse.registerImageResult(data,fileType);

When filename is omitted the behaviour is unchanged: the image is still sent to the LLM for immediate visual analysis.

Related closed issues

When browser_take_screenshot is called with an explicit filename, the
screenshot is saved to disk but the raw image buffer was still registered
in the MCP response, embedding up to 1 MB of base64 into the LLM context.
Only call registerImageResult() when no filename was supplied, preserving
the current behaviour for inline visual analysis while eliminating the
redundant payload for file-save calls.
Fixesmicrosoft/playwright-mcp#1581
@Skn0tt

Copy link
Copy Markdown
Contributor

Could you add a test for the behaviour?

@github-actions

Copy link
Copy Markdown
Contributor

Test results for "MCP"

18 failed
❌ [chrome] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-chrome
❌ [chrome] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-chrome
❌ [chrome] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-chrome
❌ [chromium] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-chromium
❌ [chromium] › mcp/dashboard.spec.ts:207 › should enter annotate mode on fresh dashboard.tsx mount with -s --annotate @mcp-windows-latest-chromium
❌ [chromium] › mcp/dashboard.spec.ts:231 › should switch screencast to -s session on show --annotate @mcp-windows-latest-chromium
❌ [chromium] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-chromium
❌ [chromium] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-chromium
❌ [firefox] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-firefox
❌ [firefox] › mcp/http.spec.ts:103 › http transport browser lifecycle (isolated) @mcp-windows-latest-firefox
❌ [firefox] › mcp/http.spec.ts:142 › http transport browser sigint @mcp-windows-latest-firefox
❌ [firefox] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-firefox
❌ [firefox] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-firefox
❌ [msedge] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-msedge
❌ [webkit] › mcp/dashboard.spec.ts:185 › should start dashboard and annotate when no dashboard is running @mcp-macos-latest-webkit
❌ [webkit] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-webkit
❌ [webkit] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-webkit
❌ [webkit] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-webkit

6803 passed, 927 skipped


Merge workflow run.

@MeroZemory

Copy link
Copy Markdown
Author

Closing this in favor of #40577 makes sense since it includes the same fix plus the requested test update.

For context, this PR originally proposed the same registerImageResult guard before #40577 was opened. If #40577 does not end up landing for any reason, I am happy to help continue from here.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

browser_take_screenshot: image still returned as base64 in MCP response when filename param is provided

2 participants

@MeroZemory@Skn0tt
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(mcp): skip base64 image response when filename param is provided in browser_take_screenshot - #40507

Closed
Jio Kim (MeroZemory) wants to merge 1 commit into
microsoft:mainfrom
MeroZemory:fix/screenshot-omit-base64-when-filename-provided
Closed

fix(mcp): skip base64 image response when filename param is provided in browser_take_screenshot#40507
Jio Kim (MeroZemory) wants to merge 1 commit into
microsoft:mainfrom
MeroZemory:fix/screenshot-omit-base64-when-filename-provided

Conversation

@MeroZemory

@MeroZemoryJio Kim (MeroZemory) commented Apr 30, 2026

Copy link
Copy Markdown

Closesmicrosoft/playwright-mcp#1581

Problem

When browser_take_screenshot is called with an explicit filename parameter, the screenshot is correctly saved to disk, but the raw image buffer is still registered in the MCP response as base64. A typical 1280x720 PNG becomes 400 KB–1 MB of base64 text, which can consume 70–80% of a 200k-token context window in a single call.

Root cause

In screenshot.ts, registerImageResult() is called unconditionally regardless of whether filename was provided:

awaitresponse.addFileResult(resolvedFile,data);awaitresponse.registerImageResult(data,fileType);// always runs

The only workaround today is the global --image-responses omit flag, which also suppresses images when the LLM genuinely needs them for visual analysis.

Fix

Skip registerImageResult() when filename is explicitly provided. The caller already knows where the file is saved and does not need the raw buffer in the LLM context.

awaitresponse.addFileResult(resolvedFile,data);if(!params.filename)awaitresponse.registerImageResult(data,fileType);

When filename is omitted the behaviour is unchanged: the image is still sent to the LLM for immediate visual analysis.

Related closed issues

When browser_take_screenshot is called with an explicit filename, the
screenshot is saved to disk but the raw image buffer was still registered
in the MCP response, embedding up to 1 MB of base64 into the LLM context.
Only call registerImageResult() when no filename was supplied, preserving
the current behaviour for inline visual analysis while eliminating the
redundant payload for file-save calls.
Fixesmicrosoft/playwright-mcp#1581
@Skn0tt

Copy link
Copy Markdown
Contributor

Could you add a test for the behaviour?

@github-actions

Copy link
Copy Markdown
Contributor

Test results for "MCP"

18 failed
❌ [chrome] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-chrome
❌ [chrome] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-chrome
❌ [chrome] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-chrome
❌ [chromium] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-chromium
❌ [chromium] › mcp/dashboard.spec.ts:207 › should enter annotate mode on fresh dashboard.tsx mount with -s --annotate @mcp-windows-latest-chromium
❌ [chromium] › mcp/dashboard.spec.ts:231 › should switch screencast to -s session on show --annotate @mcp-windows-latest-chromium
❌ [chromium] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-chromium
❌ [chromium] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-chromium
❌ [firefox] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-firefox
❌ [firefox] › mcp/http.spec.ts:103 › http transport browser lifecycle (isolated) @mcp-windows-latest-firefox
❌ [firefox] › mcp/http.spec.ts:142 › http transport browser sigint @mcp-windows-latest-firefox
❌ [firefox] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-firefox
❌ [firefox] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-firefox
❌ [msedge] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-msedge
❌ [webkit] › mcp/dashboard.spec.ts:185 › should start dashboard and annotate when no dashboard is running @mcp-macos-latest-webkit
❌ [webkit] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-webkit
❌ [webkit] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-webkit
❌ [webkit] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-webkit

6803 passed, 927 skipped


Merge workflow run.

@MeroZemory

Copy link
Copy Markdown
Author

Closing this in favor of #40577 makes sense since it includes the same fix plus the requested test update.

For context, this PR originally proposed the same registerImageResult guard before #40577 was opened. If #40577 does not end up landing for any reason, I am happy to help continue from here.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

browser_take_screenshot: image still returned as base64 in MCP response when filename param is provided

2 participants

@MeroZemory@Skn0tt
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(mcp): skip base64 image response when filename param is provided in browser_take_screenshot - #40507

Closed
Jio Kim (MeroZemory) wants to merge 1 commit into
microsoft:mainfrom
MeroZemory:fix/screenshot-omit-base64-when-filename-provided
Closed

fix(mcp): skip base64 image response when filename param is provided in browser_take_screenshot#40507
Jio Kim (MeroZemory) wants to merge 1 commit into
microsoft:mainfrom
MeroZemory:fix/screenshot-omit-base64-when-filename-provided

Conversation

@MeroZemory

@MeroZemoryJio Kim (MeroZemory) commented Apr 30, 2026

Copy link
Copy Markdown

Closesmicrosoft/playwright-mcp#1581

Problem

When browser_take_screenshot is called with an explicit filename parameter, the screenshot is correctly saved to disk, but the raw image buffer is still registered in the MCP response as base64. A typical 1280x720 PNG becomes 400 KB–1 MB of base64 text, which can consume 70–80% of a 200k-token context window in a single call.

Root cause

In screenshot.ts, registerImageResult() is called unconditionally regardless of whether filename was provided:

awaitresponse.addFileResult(resolvedFile,data);awaitresponse.registerImageResult(data,fileType);// always runs

The only workaround today is the global --image-responses omit flag, which also suppresses images when the LLM genuinely needs them for visual analysis.

Fix

Skip registerImageResult() when filename is explicitly provided. The caller already knows where the file is saved and does not need the raw buffer in the LLM context.

awaitresponse.addFileResult(resolvedFile,data);if(!params.filename)awaitresponse.registerImageResult(data,fileType);

When filename is omitted the behaviour is unchanged: the image is still sent to the LLM for immediate visual analysis.

Related closed issues

When browser_take_screenshot is called with an explicit filename, the
screenshot is saved to disk but the raw image buffer was still registered
in the MCP response, embedding up to 1 MB of base64 into the LLM context.
Only call registerImageResult() when no filename was supplied, preserving
the current behaviour for inline visual analysis while eliminating the
redundant payload for file-save calls.
Fixesmicrosoft/playwright-mcp#1581
@Skn0tt

Copy link
Copy Markdown
Contributor

Could you add a test for the behaviour?

@github-actions

Copy link
Copy Markdown
Contributor

Test results for "MCP"

18 failed
❌ [chrome] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-chrome
❌ [chrome] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-chrome
❌ [chrome] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-chrome
❌ [chromium] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-chromium
❌ [chromium] › mcp/dashboard.spec.ts:207 › should enter annotate mode on fresh dashboard.tsx mount with -s --annotate @mcp-windows-latest-chromium
❌ [chromium] › mcp/dashboard.spec.ts:231 › should switch screencast to -s session on show --annotate @mcp-windows-latest-chromium
❌ [chromium] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-chromium
❌ [chromium] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-chromium
❌ [firefox] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-firefox
❌ [firefox] › mcp/http.spec.ts:103 › http transport browser lifecycle (isolated) @mcp-windows-latest-firefox
❌ [firefox] › mcp/http.spec.ts:142 › http transport browser sigint @mcp-windows-latest-firefox
❌ [firefox] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-firefox
❌ [firefox] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-firefox
❌ [msedge] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-msedge
❌ [webkit] › mcp/dashboard.spec.ts:185 › should start dashboard and annotate when no dashboard is running @mcp-macos-latest-webkit
❌ [webkit] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-webkit
❌ [webkit] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-webkit
❌ [webkit] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-webkit

6803 passed, 927 skipped


Merge workflow run.

@MeroZemory

Copy link
Copy Markdown
Author

Closing this in favor of #40577 makes sense since it includes the same fix plus the requested test update.

For context, this PR originally proposed the same registerImageResult guard before #40577 was opened. If #40577 does not end up landing for any reason, I am happy to help continue from here.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

browser_take_screenshot: image still returned as base64 in MCP response when filename param is provided

2 participants

@MeroZemory@Skn0tt
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(mcp): skip base64 image response when filename param is provided in browser_take_screenshot - #40507

Closed
Jio Kim (MeroZemory) wants to merge 1 commit into
microsoft:mainfrom
MeroZemory:fix/screenshot-omit-base64-when-filename-provided
Closed

fix(mcp): skip base64 image response when filename param is provided in browser_take_screenshot#40507
Jio Kim (MeroZemory) wants to merge 1 commit into
microsoft:mainfrom
MeroZemory:fix/screenshot-omit-base64-when-filename-provided

Conversation

@MeroZemory

@MeroZemoryJio Kim (MeroZemory) commented Apr 30, 2026

Copy link
Copy Markdown

Closesmicrosoft/playwright-mcp#1581

Problem

When browser_take_screenshot is called with an explicit filename parameter, the screenshot is correctly saved to disk, but the raw image buffer is still registered in the MCP response as base64. A typical 1280x720 PNG becomes 400 KB–1 MB of base64 text, which can consume 70–80% of a 200k-token context window in a single call.

Root cause

In screenshot.ts, registerImageResult() is called unconditionally regardless of whether filename was provided:

awaitresponse.addFileResult(resolvedFile,data);awaitresponse.registerImageResult(data,fileType);// always runs

The only workaround today is the global --image-responses omit flag, which also suppresses images when the LLM genuinely needs them for visual analysis.

Fix

Skip registerImageResult() when filename is explicitly provided. The caller already knows where the file is saved and does not need the raw buffer in the LLM context.

awaitresponse.addFileResult(resolvedFile,data);if(!params.filename)awaitresponse.registerImageResult(data,fileType);

When filename is omitted the behaviour is unchanged: the image is still sent to the LLM for immediate visual analysis.

Related closed issues

When browser_take_screenshot is called with an explicit filename, the
screenshot is saved to disk but the raw image buffer was still registered
in the MCP response, embedding up to 1 MB of base64 into the LLM context.
Only call registerImageResult() when no filename was supplied, preserving
the current behaviour for inline visual analysis while eliminating the
redundant payload for file-save calls.
Fixesmicrosoft/playwright-mcp#1581
@Skn0tt

Copy link
Copy Markdown
Contributor

Could you add a test for the behaviour?

@github-actions

Copy link
Copy Markdown
Contributor

Test results for "MCP"

18 failed
❌ [chrome] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-chrome
❌ [chrome] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-chrome
❌ [chrome] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-chrome
❌ [chromium] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-chromium
❌ [chromium] › mcp/dashboard.spec.ts:207 › should enter annotate mode on fresh dashboard.tsx mount with -s --annotate @mcp-windows-latest-chromium
❌ [chromium] › mcp/dashboard.spec.ts:231 › should switch screencast to -s session on show --annotate @mcp-windows-latest-chromium
❌ [chromium] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-chromium
❌ [chromium] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-chromium
❌ [firefox] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-firefox
❌ [firefox] › mcp/http.spec.ts:103 › http transport browser lifecycle (isolated) @mcp-windows-latest-firefox
❌ [firefox] › mcp/http.spec.ts:142 › http transport browser sigint @mcp-windows-latest-firefox
❌ [firefox] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-firefox
❌ [firefox] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-firefox
❌ [msedge] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-msedge
❌ [webkit] › mcp/dashboard.spec.ts:185 › should start dashboard and annotate when no dashboard is running @mcp-macos-latest-webkit
❌ [webkit] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-webkit
❌ [webkit] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-webkit
❌ [webkit] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-webkit

6803 passed, 927 skipped


Merge workflow run.

@MeroZemory

Copy link
Copy Markdown
Author

Closing this in favor of #40577 makes sense since it includes the same fix plus the requested test update.

For context, this PR originally proposed the same registerImageResult guard before #40577 was opened. If #40577 does not end up landing for any reason, I am happy to help continue from here.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

browser_take_screenshot: image still returned as base64 in MCP response when filename param is provided

2 participants

@MeroZemory@Skn0tt
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(mcp): skip base64 image response when filename param is provided in browser_take_screenshot - #40507

Closed
Jio Kim (MeroZemory) wants to merge 1 commit into
microsoft:mainfrom
MeroZemory:fix/screenshot-omit-base64-when-filename-provided
Closed

fix(mcp): skip base64 image response when filename param is provided in browser_take_screenshot#40507
Jio Kim (MeroZemory) wants to merge 1 commit into
microsoft:mainfrom
MeroZemory:fix/screenshot-omit-base64-when-filename-provided

Conversation

@MeroZemory

@MeroZemoryJio Kim (MeroZemory) commented Apr 30, 2026

Copy link
Copy Markdown

Closesmicrosoft/playwright-mcp#1581

Problem

When browser_take_screenshot is called with an explicit filename parameter, the screenshot is correctly saved to disk, but the raw image buffer is still registered in the MCP response as base64. A typical 1280x720 PNG becomes 400 KB–1 MB of base64 text, which can consume 70–80% of a 200k-token context window in a single call.

Root cause

In screenshot.ts, registerImageResult() is called unconditionally regardless of whether filename was provided:

awaitresponse.addFileResult(resolvedFile,data);awaitresponse.registerImageResult(data,fileType);// always runs

The only workaround today is the global --image-responses omit flag, which also suppresses images when the LLM genuinely needs them for visual analysis.

Fix

Skip registerImageResult() when filename is explicitly provided. The caller already knows where the file is saved and does not need the raw buffer in the LLM context.

awaitresponse.addFileResult(resolvedFile,data);if(!params.filename)awaitresponse.registerImageResult(data,fileType);

When filename is omitted the behaviour is unchanged: the image is still sent to the LLM for immediate visual analysis.

Related closed issues

When browser_take_screenshot is called with an explicit filename, the
screenshot is saved to disk but the raw image buffer was still registered
in the MCP response, embedding up to 1 MB of base64 into the LLM context.
Only call registerImageResult() when no filename was supplied, preserving
the current behaviour for inline visual analysis while eliminating the
redundant payload for file-save calls.
Fixesmicrosoft/playwright-mcp#1581
@Skn0tt

Copy link
Copy Markdown
Contributor

Could you add a test for the behaviour?

@github-actions

Copy link
Copy Markdown
Contributor

Test results for "MCP"

18 failed
❌ [chrome] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-chrome
❌ [chrome] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-chrome
❌ [chrome] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-chrome
❌ [chromium] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-chromium
❌ [chromium] › mcp/dashboard.spec.ts:207 › should enter annotate mode on fresh dashboard.tsx mount with -s --annotate @mcp-windows-latest-chromium
❌ [chromium] › mcp/dashboard.spec.ts:231 › should switch screencast to -s session on show --annotate @mcp-windows-latest-chromium
❌ [chromium] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-chromium
❌ [chromium] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-chromium
❌ [firefox] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-firefox
❌ [firefox] › mcp/http.spec.ts:103 › http transport browser lifecycle (isolated) @mcp-windows-latest-firefox
❌ [firefox] › mcp/http.spec.ts:142 › http transport browser sigint @mcp-windows-latest-firefox
❌ [firefox] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-firefox
❌ [firefox] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-firefox
❌ [msedge] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-msedge
❌ [webkit] › mcp/dashboard.spec.ts:185 › should start dashboard and annotate when no dashboard is running @mcp-macos-latest-webkit
❌ [webkit] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-macos-latest-webkit
❌ [webkit] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-windows-latest-webkit
❌ [webkit] › mcp/screenshot.spec.ts:222 › browser_take_screenshot (filename: "output.png") @mcp-ubuntu-latest-webkit

6803 passed, 927 skipped


Merge workflow run.

@MeroZemory

Copy link
Copy Markdown
Author

Closing this in favor of #40577 makes sense since it includes the same fix plus the requested test update.

For context, this PR originally proposed the same registerImageResult guard before #40577 was opened. If #40577 does not end up landing for any reason, I am happy to help continue from here.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

browser_take_screenshot: image still returned as base64 in MCP response when filename param is provided

2 participants

@MeroZemory@Skn0tt