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
  }
}
AttackWindow Management surfaceWhat 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

High Tool output rendered same-origin; injected code can call getScreenDetails() after a user gesture to enumerate all connected monitors without additional prompts. Multi-monitor layout — count, resolutions, positions, DPR, isInternal — creates a highly precise device fingerprint that does not change across sessions. Grade impact: −16.
High No Permissions-Policy: window-management=() header; full screen enumeration accessible to injected JavaScript after transient activation. Transient activation from user interaction with injected tool output content is sufficient to trigger getScreenDetails() in some browser implementations. Grade impact: −14.
Medium Cross-screen popup placement possible; secondary monitor coordinates allow popups outside primary user focus area. Phishing or credential-harvesting popups placed precisely on secondary monitors exploit reduced user vigilance on non-primary screens. Grade impact: −12.
Medium Primary screen data (window.screen) available without permission regardless of policy settings; contributes to fingerprint even with window-management=() applied. DPR, resolution, and availWidth values from window.screen persist as fingerprinting signals even when the full multi-screen API is blocked. Grade impact: −8.
Low isInternal field distinguishes laptop from desktop hardware class when permission is granted; hardware class useful for targeted attack selection. Knowing whether the user is on a laptop (isInternal display present) vs desktop informs attacker decisions about mobility assumptions, away-from-desk periods, and physical access risks. Grade impact: −5.

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 →