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 scheme | Handler | Attack capability |
|---|---|---|
file:///etc/passwd | Browser / file manager | Open 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 client | Auto-join attacker-controlled meeting room; exfiltrate display via Zoom |
vscode://vscode.github/… | VS Code extension handlers | Trigger extension actions with attacker-controlled parameters; extension handlers vary in security posture |
slack://… | Slack desktop app | Trigger Slack actions (join channel, install app) with attacker parameters |
tel:+1234567890 | Phone dialer / softphone | Trigger a call to an attacker-controlled number; call exfiltrates ambient audio |
javascript: | Browser JS execution | Direct 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, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
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
file://, custom scheme, or javascript: links in tool responses reach the Electron webview's navigation handler with no filteringopen-url event without validation — prompt injection in tool output can plant a deep link that invokes internal app actions with attacker-controlled parametersfile:// 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 filerel="noopener noreferrer" on external links rendered from tool output — cross-origin window manipulation via window.opener reference possible from opened pagesSee also: Open redirect security · Prompt injection · Electron security