InboxWall: How a Local MCP Server Blocks Email Prompt Injection

InboxWall uses a local MCP server to sanitize email HTML before the LLM sees it, blocking prompt injection at the source.
InboxWall is a local-first open-source MCP server that sanitizes email HTML into safe plain text before it enters the LLM context, blocking prompt injection techniques such as hidden CSS text, zero-width Unicode characters, and HTML comment instructions. Its sanitization pipeline has six steps: lenient HTML parsing, tag allowlisting, attribute allowlisting, CSS removal, Unicode normalization, and text extraction. Architecturally it connects to no email provider and exposes only a single tool interface, leaving callers to fetch and submit HTML themselves. Its core claim is that security preprocessing belongs outside the LLM trust boundary and should not rely on prompt engineering. The stateless design simplifies deployment but leaves deduplication, error handling, and rate limiting to the agent; aggressive filtering may remove legitimate content, and it is unsuitable for scenarios requiring rich HTML rendering.
InboxWall: How a Local MCP Server Blocks Email Prompt Injection
When an AI agent connects to an email inbox, it doesn't just read the message body that humans see. HTML emails may contain hidden text in white-on-white, zero-width Unicode characters, or even instructions embedded in HTML comments meant specifically for the model to read. This class of attack is called prompt injection, and InboxWall's solution is simple: clean out the dangerous content before the LLM ever sees the email.
InboxWall is an open-source MCP (Model Context Protocol) server that runs local-first and sanitizes email HTML into safe plain text. It doesn't connect to any email provider; it exposes a single tool interface that lets callers hand it raw HTML for processing. Behind this design lies an architectural question worth exploring: where exactly should the security boundary sit in an agent toolchain?

Why Email HTML Is a Hotbed for Prompt Injection
Email clients need to parse HTML, CSS, and Unicode to render layouts. Attackers exploit exactly this by hiding instructions where humans can't see them but models will still read them. The original article lists three typical techniques.
The first is hidden CSS text — for example, white text on a white background, spans with zero opacity, or content positioned off-screen with absolute positioning. Invisible to the human eye, but preserved word-for-word in the HTML source.
The second is Unicode tricks, including right-to-left override characters, zero-width joiners, and homoglyphs. These characters can alter semantics without leaving any visual trace.
The third is nested instructions, such as "ignore previous instructions" written inside HTML comments or <style> blocks — content that exists only in the raw source.
When an agent reads email via IMAP or an email API, it typically receives the full HTML. If that HTML goes straight into the LLM context, the attacker effectively controls part of the prompt.
Prompt Injection is a core threat class in LLM application security: attackers mix carefully crafted instructions into data the model will read (emails, web pages, documents), causing the model to mistake data content for system instructions. Analogous to traditional SQL injection, its essence is "the boundary between data and instructions being broken." OWASP has ranked it as the top risk for LLM applications. Defense approaches generally fall into input-side (sanitizing before data enters context) and output-side (filtering model-generated content); InboxWall takes the former.
MCP Server Architecture Design
InboxWall runs as an MCP server and exposes only one tool externally: it accepts raw email HTML and returns sanitized plain text.
Its sanitization pipeline strips all CSS (inline styles, <style> tags, external stylesheets), JavaScript and event handlers, invisible Unicode characters (zero-width spaces, directional overrides, variation selectors), HTML comments, and suspicious attributes (data URIs, srcdoc, formaction).
The key point is the division of responsibilities: the server itself does not connect to email providers. It expects the caller to fetch email independently via IMAP, Exchange Web Services, or an email API, and then hand the HTML body to it for cleaning. This decoupling keeps the sanitization logic single-purpose.
Local-First Execution Model
InboxWall runs on the same machine as the agent, and email data never leaves the device. The original article notes that this changes the threat model: no need to send email content to a third-party sanitization service, no need to configure API keys for sanitization, and sanitization completes synchronously in-process or on localhost with millisecond latency.
The cost is operational burden. You need to run the MCP server as a separate process, manage its lifecycle, and ensure it's already started before the agent calls it.
MCP (Model Context Protocol) is an open protocol proposed by Anthropic in late 2024 to standardize connections between LLM applications and external tools and data sources. It borrows the ideas of LSP (Language Server Protocol): the client (agent host) and server (capability provider) communicate via JSON-RPC, and the server can expose three types of primitives — tools (callable functions), resources (readable data), and prompts (preset templates). Transport supports stdio (local process) and HTTP/SSE (remote). InboxWall wraps its sanitization capability as a single tool, which is exactly the typical usage of an MCP tool server: clear capability boundaries, reusable by any compatible client.
Internal Pipeline Flow
InboxWall's sanitization logic is a multi-stage filter, and the original article lays out six steps.
- HTML parsing: Parse the email body into a DOM tree using a lenient parser that can handle malformed markup.
- Tag allowlist: Keep only safe tags such as
<p>,<a>,<br>,<strong>,<em>,<ul>,<li>,<blockquote>, and discard everything else. - Attribute allowlist: For permitted tags, retain only
hrefon<a>; strip all other attributes. - CSS removal: Delete all
styleattributes,<style>tags, and<link rel="stylesheet">elements. - Unicode normalization: Perform NFC normalization first, then remove zero-width characters, directional overrides, and variation selectors.
- Text extraction: Flatten the sanitized DOM into plain text, preserving line breaks and basic structure.
The output is a plain-text email representation with all injection vectors removed.
Code Example of Unicode Cleaning
The original article provides a simplified Python snippet illustrating the Unicode cleaning step. The core idea is to maintain a list of zero-width and directional control characters, including \u200B zero-width space, \u200C zero-width non-joiner, \u200D zero-width joiner, \u200E left-to-right mark, \u200F right-to-left mark, the group of directional embedding and control characters from \u202A to \u202E, and \uFEFF zero-width no-break space.
The function performs NFC normalization first, then replaces these characters one by one, and finally uses the regex [\uFE00-\uFE0F] to remove variation selectors. This logic runs after HTML sanitization and before text extraction, specifically catching Unicode obfuscation that survives HTML parsing.
Unicode Normalization and Homoglyph Attacks: NFC is one of four Unicode normalization forms (C stands for Composition), merging base characters with combining marks into a single code point so equivalent strings have a unique representation. But normalization does not eliminate the homoglyph problem — for example, the Latin letter a and the Cyrillic letter а look nearly identical but have different code points, which attackers can exploit to bypass keyword matching. Directional control characters (\u202A–\u202E) were originally designed for bidirectional text layout but are abused to invert source order versus display order. Variation selectors (\uFE00–\uFE0F) can attach invisible style variants to the same character. Thorough defense requires a character-set allowlist or homoglyph normalization after normalization; InboxWall's character blacklist is a lightweight but continuously maintained approach.
Where Should the Security Boundary Sit
InboxWall's design reveals a pattern: security preprocessing should happen before the LLM sees the data, not after. This is the opposite of the output-filtering approach. The original article presents a comparison table of four approaches.
| Approach | Boundary Location | Pros | Cons |
|---|---|---|---|
| Pre-LLM sanitization | Before the context window | Blocks injection at the source; the model never sees the attack vectors | Requires a separate preprocessing step, adding latency |
| Post-LLM filtering | After generation | Simpler pipeline, no preprocessing overhead | The model may already be contaminated; subtle manipulation is hard to detect |
| Prompt engineering | Inside the LLM prompt | No external dependencies | Unreliable; attackers can craft prompts that override instructions |
| Retrieval-time filtering | At the data acquisition stage | Centralized control covering all downstream agents | Tight coupling between data source and security logic |
InboxWall chooses pre-LLM sanitization for a straightforward reason: it treats the LLM as untrusted. Even if you tell the model in the prompt to ignore hidden text, you can't rely on prompt engineering to stop every attack. Sanitization must happen outside the model's control.
How Agents Integrate
An agent using InboxWall follows a clear flow: fetch raw email via IMAP or an email API, parse the MIME structure to extract the HTML part, call the sanitize_email tool, get back plain text free of CSS, JavaScript, and Unicode tricks, then place it into the LLM prompt for summarization, classification, or reply generation.
The MCP server runs as a separate process, and the agent connects via stdio or HTTP depending on the MCP transport configuration. The server keeps no state between calls; each sanitization request is independent.
Trade-offs in State Management
InboxWall is stateless: it doesn't cache sanitized emails, doesn't track which messages have been processed, and doesn't store user preferences. This simplifies deployment but leaves three things to the agent: deduplication (repeated fetches mean repeated sanitization), error handling (skip or retry on sanitization failure), and rate limiting (how many emails per second to send to InboxWall).
Failure Modes and Observability
The original article candidly lists several failure possibilities for InboxWall. The parser may throw exceptions on extremely malformed HTML; overly aggressive filtering may delete legitimate content — for example, stripping all <a> tags would lose links the agent might need; new injection techniques (novel Unicode characters, CSS features) may bypass the allowlist until rules are updated; and a crash of the MCP server process would leave the agent without sanitization capability, requiring detection and restart or graceful degradation.
The project itself has no built-in observability; logs, metrics, and tracing must be added yourself. The original article suggests tracking four signals: sanitization latency (time from receiving HTML to returning plain text), rejection rate (proportion of emails failing due to parse errors), bytes removed (volume difference between input HTML and output text, reflecting how much content was stripped), and error type classification (parse crashes, timeouts, invalid input), to identify patterns.
Deployment Shape and Use Cases
A typical deployment looks like this: install a Python runtime, an HTML parsing library (such as BeautifulSoup or lxml), and an MCP server framework; start InboxWall as a background process listening on stdio or a local port; point the agent's MCP client at the server; and use a process supervisor like systemd or supervisord to restart on crash. Because it's local-first, there's no need to configure network firewalls, TLS certificates, or API authentication, and the attack surface converges on the local machine.
The original article also clearly defines the applicable boundaries. Suitable scenarios include: agents reading email from untrusted senders (customer service, public inboxes, mailing lists); wanting a security boundary that doesn't rely on prompt engineering; preferring local execution over cloud preprocessing; and needing a simple, stateless sanitization step that integrates into an existing MCP toolchain.
Unsuitable scenarios are equally clear: fully trusted email sources (internal corporate email with strict sender policies); agent contexts that require rich HTML rendering (links, formatting, images); existing cloud email security gateways already handling sanitization; and serverless agent environments where a separate MCP server process can't run.
Technical Judgment
InboxWall proves one thing: security preprocessing belongs outside the LLM trust boundary. Making sanitization an independent MCP server decouples security logic from agent orchestration, making filtering rules explicit and auditable. The local-first architecture keeps email data on the device, which matters for privacy-sensitive scenarios, at the cost of operational complexity — you have to manage the MCP server lifecycle and handle failures when it's unavailable.
The sanitization pipeline is aggressive: stripping all CSS, JavaScript, and invisible Unicode may also remove legitimate content. If the agent needs to preserve links or basic formatting, you'll need to fork the project and adjust the allowlist. For agents handling email from untrusted sources, InboxWall is a pragmatic starting point that demonstrates how to build a security boundary that doesn't depend on the LLM's own injection resistance.
Related articles

Chain of Thought Isn't Just for AI: How Humans Can Use CoT to Improve Thinking Quality
Chain of Thought isn't just a prompt trick for large models. Starting from a Hacker News discussion, this article analyzes why CoT works for human thinking too, breaks down actionable ways to transfer it, and highlights the key differences between human CoT and machine CoT.
Cloudflare Teams Up with OpenAI: Closi…
Cloudflare Teams Up with OpenAI: Closing the Loop from Vulnerability Discovery to Defense with AI
Cloudflare uses OpenAI Daybreak to investigate security vulnerabilities and generate reviewable remediation plans, while developers use Codex to boost coding efficiency and quality.

Orca Multi-Agent Orchestration in Practice: A Guide to Parallel Collaboration with Claude and Codex
Orca is Stable AI's free open-source agent workbench supporting 34 agents like Claude Code and Codex in parallel. Learn its Git worktree isolation, multi-model orchestration, and mobile management.