MCP Server Security · Deep Link Injection

MCP server deep link injection security — custom URI scheme injection, protocol handler abuse, file:// links in tool output, and open redirect via deep links

Deep link injection occurs when MCP tool output (returned by the LLM or by a tool fetching external data) contains clickable links using custom URI schemes — file://, ms-msdt://, zoommtg://, vscode://, slack://, or any registered application handler — that trigger behavior beyond standard web navigation when clicked. In MCP UIs that auto-linkify tool output or render markdown links, a prompt injection payload can plant deep links that execute local commands, open sensitive local files, invoke Electron app handlers with attacker-controlled parameters, or trigger OAuth flows in registered apps.

High-impact deep link attack vectors in MCP contexts

URI schemeHandlerAttack capability
file:///etc/passwdBrowser / file managerOpen local sensitive files in browser; content visible in browser window on some OS configurations
ms-msdt://Windows MSDT (Follina CVE-2022-30190)Execute arbitrary commands on Windows systems — though patched, similar patterns in other diagnostic handlers remain
zoommtg://zoom.us/join?confno=…Zoom desktop clientAuto-join attacker-controlled meeting room; exfiltrate display via Zoom
vscode://vscode.github/…VS Code extension handlersTrigger extension actions with attacker-controlled parameters; extension handlers vary in security posture
slack://…Slack desktop appTrigger Slack actions (join channel, install app) with attacker parameters
tel:+1234567890Phone dialer / softphoneTrigger a call to an attacker-controlled number; call exfiltrates ambient audio
javascript:Browser JS executionDirect script execution; browsers block this in most contexts but some frameworks auto-linkify without checking scheme

MCP desktop clients (Electron apps) are the highest-risk context. In an Electron MCP client, file:// URLs can read local files, app:// deep links invoke app-internal handlers, and node:// or custom scheme handlers may expose privileged Node.js APIs depending on the Electron app's configuration. Tool output rendered in a Electron webview without URI scheme allowlisting is a code execution risk.

URI scheme allowlisting for MCP tool output

The primary defense is an allowlist of safe URI schemes applied before rendering any link from tool output. Only https: and http: (with a deprecation warning) should be allowed for auto-rendered links. All other schemes should be stripped, replaced with #, or rendered as plain text.

const SAFE_SCHEMES = new Set(['https:', 'http:', 'mailto:']);

function sanitizeUrl(url: string): string {
  try {
    const parsed = new URL(url);
    if (!SAFE_SCHEMES.has(parsed.protocol)) {
      // Replace dangerous scheme with a safe no-op
      return '#blocked-scheme';
    }
    return url;
  } catch {
    // Unparseable URL — treat as unsafe
    return '#invalid-url';
  }
}

// Apply when rendering markdown links from tool output
function renderMarkdownLink(text: string, href: string): string {
  const safeHref = sanitizeUrl(href);
  if (safeHref.startsWith('#')) {
    // Render as plain text with a visual indicator
    return `<span class="blocked-link" title="Link blocked: unsafe scheme">${escapeHtml(text)}</span>`;
  }
  return `<a href="${escapeHtml(safeHref)}" rel="noopener noreferrer" target="_blank">${escapeHtml(text)}</a>`;
}

function escapeHtml(str: string): string {
  return str
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;');
}

Don't rely on CSP to block dangerous scheme navigation. CSP navigate-to has limited browser support. CSP does not block href clicks that navigate to file:// or custom URI schemes — it only controls resource loading, not top-level navigation to protocol handlers. URI scheme validation must happen before the link is rendered, not at navigation time.

Deep link injection in Electron MCP clients

Electron apps can register custom deep link handlers via app.setAsDefaultProtocolClient('myapp'). If an MCP tool returns a link like myapp://internal-action?param=attacker-value and the Electron app's deep link handler doesn't validate parameters, the attacker controls inputs to internal app logic. Electron deep link security checklist:

// Electron main process: deep link handler with parameter validation
app.on('open-url', (event, url) => {
  event.preventDefault();

  const parsed = new URL(url);
  if (parsed.protocol !== 'myapp:') return;

  // Validate every parameter — treat as untrusted input
  const action = parsed.pathname.replace(/^\/\//, '').split('/')[0];
  const ALLOWED_ACTIONS = new Set(['view', 'open']);

  if (!ALLOWED_ACTIONS.has(action)) {
    log.warn(`deep-link: blocked unknown action "${action}"`);
    return;
  }

  const id = parsed.searchParams.get('id');
  if (!id || !/^[a-zA-Z0-9_-]{1,64}$/.test(id)) {
    log.warn('deep-link: invalid id parameter');
    return;
  }

  // Safe to proceed with validated action and id
  handleDeepLink(action, id);
});

// Electron webview: disable navigation to dangerous schemes
webContents.on('will-navigate', (event, navigationUrl) => {
  const parsed = new URL(navigationUrl);
  if (!['https:', 'http:'].includes(parsed.protocol)) {
    event.preventDefault();
    log.warn(`deep-link: blocked navigation to ${parsed.protocol}`);
  }
});

file:// link risks in MCP server responses

An MCP tool that operates on a server with access to local files may return file:// paths as part of its output — "I saved the report to file:///home/user/reports/q1.pdf." If the MCP UI auto-linkifies these paths and the user clicks them, the browser opens the local file. This is less severe in a standard browser context (the browser shows the file contents, not executes them), but in Electron contexts, navigation to file:// paths can expose the entire local filesystem.

The server-side defense is to never include raw file:// URLs in tool responses. Instead, return a server-side URL that proxies the file through the MCP server with appropriate authentication and Content-Disposition headers. The MCP server controls what files are exposed; the client never gets a direct filesystem path.

SkillAudit findings for deep link injection in MCP servers

CRITICAL −24Tool output auto-linkified without URI scheme validation in an Electron MCP client — file://, custom scheme, or javascript: links in tool responses reach the Electron webview's navigation handler with no filtering
HIGH −18MCP UI renders markdown links from tool output without URI scheme allowlist — non-https schemes (file://, tel:, ms-*, zoommtg://) are rendered as clickable links and activate OS protocol handlers on click
HIGH −16Electron deep link handler trusts parameters from open-url event without validation — prompt injection in tool output can plant a deep link that invokes internal app actions with attacker-controlled parameters
MEDIUM −12MCP server tool returns file:// paths in output — rendered as links in the UI, clicking them navigates to local filesystem paths; in Electron, this exposes the user's filesystem beyond the intended file
MEDIUM −8No rel="noopener noreferrer" on external links rendered from tool output — cross-origin window manipulation via window.opener reference possible from opened pages

See also: Open redirect security · Prompt injection · Electron security

Run a free SkillAudit on your MCP server →