Interaction & Data Flow
The model, manipulated by prompt injection or a poisoned tool, uses a legitimate tool (send-email, create-issue, upload-file) to leak private context to an attacker. Concrete output-channel carriers are tracked separately: MCP-220 (Markdown image) and MCP-221 (Markdown hyperlink).
The tools are not the problem; the composition is. A model with both a "read private docs" tool and a "send email" tool can be persuaded to send the private docs to an attacker. There is no single handler to fix — each tool is behaving as designed.
MCP specifically encourages this topology: install many small tools, let the model compose them. The more tools you attach, the more outbound channels exist, and the harder it becomes to reason about which combinations are dangerous.
# Both tools are "safe" in isolation; together they exfiltrate. |
@server.tool() |
def read_secret_doc(doc_id: str) -> str: ... |
@server.tool() |
def send_email(to: str, body: str) -> None: ... |
# Enforce outbound egress policy at the client layer. |
# E.g., require user confirmation for any tool that sends data |
# to a recipient not already in the conversation's trust scope. |
@server.tool(requires_confirmation=True, egress=True) |
def send_email(to: str, body: str) -> None: ... |
We classify each tool as source (reads private state), sink (emits data externally), or transform. Packages that expose both high-sensitivity sources and low-friction sinks are flagged with a composition warning.
See the full threat catalog for every documented detection.
MCPSafe runs this check — and every other rule in the catalog — on any MCP server you paste in.
Scan now