dead-letter

io.github.BigCactusLabs由 GitHub 帳號 BigCactusLabs 發布v0.4.10更新於 Oct 30, 2026

Convert .eml and .mbox email to clean Markdown for RAG, LLM pipelines, and local knowledge bases

已驗證STDIO僅桌面檔案與儲存知識與記憶

概覽

AI 產生的概覽

把 .eml 與 .mbox 郵件匯出轉換成帶 YAML front matter 的乾淨 Markdown,讓助理能在本機讀取與索引郵件。

可以做什麼
  • 把單一 .eml 檔轉成 Markdown 文字,可選擇寫入指定檔案
  • 把一封郵件轉成含 Markdown 與擷取附件的 bundle 資料夾,來源檔只複製不搬移
  • 批次轉換整個資料夾的 .eml 檔,每次呼叫上限 50 個檔案
  • 轉換單一扁平 .mbox 匯出檔,每次呼叫上限 256 MiB 與 1000 封郵件
  • 檢視某封郵件的品質與結構診斷資訊,不會產生永久寫入
  • 所有工具都支援 preset(default、clean、verbose、raw)與逐項參數覆寫
適用情境
當你已把郵件匯出成 .eml 或 .mbox,並想轉成可讀的 Markdown 供 RAG、LLM 流程、本機知識庫或可攜式封存使用,而不想把原始 MIME 與 base64 塞進上下文視窗時適用。
執行需求
以本機 stdio 程序在使用者電腦上執行;桌面用戶端需在 PATH 中有 uv/uvx,或改裝 PyPI 套件、Claude Desktop 的 .mcpb 擴充包或容器映像。首次使用可能下載 Python 與相依套件。不需帳號、API 金鑰或環境變數。
安裝前請注意
轉換在本機進行,但 MCP 宿主仍可能把回傳的郵件文字送往遠端模型。資料夾與 mbox 轉換會寫入你指定的輸出路徑;bundle 轉換只複製來源 .eml,不會搬移或刪除。郵件內容屬不可信輸入,其中要求呼叫工具或索取憑證的內容不應被執行。保留的二進位附件需另外解析才能用於文字索引。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 dead-letter,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

[dead-letter]

dead-letter

[PyPI package] [Python versions] [License: PolyForm Noncommercial]

Turn .eml email exports into clean, local, LLM-ready Markdown.

dead-letter converts .eml email exports into clean Markdown with YAML front matter — threads split, signatures stripped, attachments extracted, calendars parsed. One file or ten thousand.

Use it to build a readable email archive, move messages into Markdown-based knowledge systems, or prepare email for RAG and LLM pipelines without feeding raw MIME and base64 into your context window. No account, upload, or API key required.

[dead-letter 0.4.10 converting a synthetic inbox: raw MIME input, CLI folder conversion, Markdown with YAML front matter, a copy-only Cabinet bundle with the decoded CSV attachment, and report plus diagnostics]

⚡ Try it

If you already have uv, run dead-letter without installing it globally:

bash
uvx --python 3.12 dead-letter convert message.eml

Or install with Homebrew or pip below. Agents and MCP clients can use the client-specific setup in llms-install.md.

🎯 Common use cases

  • Email → Markdown archives — turn exported .eml collections into readable, portable Markdown with structured metadata
  • RAG and LLM ingestion — normalize message text, thread structure, links, and attachment metadata before chunking or indexing
  • Agent workflows — expose conversion and diagnostics directly to Claude, Codex, and other MCP clients
  • Knowledge bases — move email into Markdown-first systems such as Obsidian, static archives, or local search pipelines
  • Digital preservation — retain human-readable content and useful message structure without depending on one mail client

✨ Features

  • Full-fidelity conversion — HTML sanitization, Gmail/Outlook thread segmentation, inline image handling, and calendar event summaries
  • CLI — point it at a file or a directory and go
  • Local web UI — dark command-center interface with drag-and-drop import, watch mode, conversion grade badges, processing history, and per-job diagnostics
  • Inbox/Cabinet workflow — drop .eml files into an Inbox, let dead-letter organize the Markdown bundles into a Cabinet
  • Install validation — dead-letter doctor checks your runtime environment
  • Conversion report — opt-in JSON report with per-file diagnostics, including attachment referenced/retained counts for automation and audit
  • MCP server — integrate with Claude Desktop, Claude Code, Codex, and other MCP clients
  • Claude plugin — marketplace install in Claude Code or Cowork with five slash commands (/dead-letter:convert, /dead-letter:summarize, /dead-letter:triage, /dead-letter:cabinet, /dead-letter:mbox)
  • Portable Agent Skill — teaches skill-aware agents when and how to convert .eml files
  • Python API — from dead_letter import convert and you're off

🧠 Built for LLM Pipelines

Raw .eml files are noisy input for downstream LLM and retrieval pipelines — MIME headers, multipart boundaries, duplicated HTML/plain bodies, and encoded attachments all get mixed into the text path.

dead-letter normalizes that into Markdown with YAML front matter, so message text and metadata are ready for chunking or indexing without MIME parsing or base64 cleanup. Default convert() and convert_dir() runs write a single .md per message and keep attachment names in front matter.

To separate the filesystem artifacts too, bundle and Cabinet workflows write message.md plus retained decoded files under attachments/. The Markdown is ready for text ingestion, while PDFs, spreadsheets, calendar files, and other retained binary attachments stay cleanly split out for whatever downstream parser you already use.

For direct LLM integration, the MCP server lets clients call dead-letter's conversion tools without shelling out. Conversion is local; your chosen MCP host may still send returned email text to a remote model.

📊 Token-cost benchmarks

dead-letter's value isn't fewer tokens than every alternative — it's fidelity per token: keeping useful email structure without carrying raw MIME into context. Measured across an 11-message synthetic corpus of HTML threads, attachments, and newsletters (tokenizer o200k_base, structured thread mode):

  • ~88% fewer tokens than the raw .eml in the reported aggregate comparison — the attachment category's median is ~126k tokens raw vs ~180 converted.
  • Structure survives — thread structure, per-message sender attribution, links, and attachment metadata remain readable. The tested naive baselines are often cheaper because they discard information (0/2 attachment names retained vs dead-letter's 2/2).

Those counts measure the Markdown representation, not the contents of retained binary attachments or downstream answer quality. The shipping default is latest-message mode; the benchmark uses structured mode for a same-thread comparison.

The benchmark is honest about where it loses: naive extraction is fewer tokens when you don't mind throwing away metadata, links, and thread structure. Full method, the complete table (including those rows), tokenizer disclosure, and a one-command reproduce are in benchmarks/.

📦 Install

Pick one route. The distribution map explains how the channels fit together; installing all of them is not necessary.

You wantStart here
Core CLI or Python APIHomebrew / pip below, or the uvx quick try
Local web UIdead-letter[ui] below
Claude Desktop extension or another MCP clientMCP Server
Claude Code / Cowork commandsPlugin
Container-isolated MCPContainers
Portable agent instructionsAgent Skill

With Homebrew on Apple silicon macOS:

bash
brew tap BigCactusLabs/tapbrew install dead-letter

The Homebrew formula installs the CLI (dead-letter convert, dead-letter doctor). Formulas for 0.4.7 and later also install the dead-letter-mcp server; the optional web UI is not included.

With pip:

bash
pip install dead-letter            # core + CLIpip install 'dead-letter[cli]'      # + watchfiles (used by backend/UI watch mode)pip install 'dead-letter[ui]'       # + web UI, API server, and watch modepip install 'dead-letter[mcp]'      # + MCP server

Use pipx for an isolated UI or MCP install:

bash
pipx install 'dead-letter[ui]'      # installs dead-letter and dead-letter-ui# Or, for MCP instead:pipx install 'dead-letter[mcp]'     # installs dead-letter and dead-letter-mcp

Or run individual entrypoints without a global package install using uvx:

bash
uvx --python 3.12 dead-letter convert message.emluvx --python 3.12 --from 'dead-letter[mcp]' dead-letter-mcp

uv caches tools/dependencies and may download Python on first use. These unpinned trial commands do not promise a fresh latest version on every run; see version pinning for a reviewed deployment.

From source:

bash
git clone https://github.com/BigCactusLabs/dead-letter.gitcd dead-letteruv sync --extra dev --locked     # all extras# Or choose only the surface you're developing:uv sync --extra ui --locked      # UI onlyuv sync --extra mcp --locked     # MCP only

Agent Skill (any host)

Install the portable Agent Skill into whichever agent you use:

bash
gh skill install BigCactusLabs/dead-letter dead-letter --agent claude-codegh skill install BigCactusLabs/dead-letter dead-letter --agent codexgh skill install BigCactusLabs/dead-letter dead-letter --agent github-copilot

Needs gh 2.90 or newer. The skill is independent of the Claude plugin. Pin a reviewed skill tag/commit for reproducibility; the default latest release can also be a plugin release. For exact pin syntax, other hosts, manual installation, and discovery metadata, see Agent Discovery.

🚀 Quick Start

CLI — convert a single file:

bash
dead-letter convert message.eml

Convert a whole directory:

bash
dead-letter convert inbox/ --output out/

Generate a JSON conversion report alongside the output:

bash
dead-letter convert inbox/ --output out/ --report

With --output, the report is written to that output directory as .dead-letter-report.json. Without --output, file conversions write the report next to the source message and directory conversions write it to the input directory root.

Check your runtime environment:

bash
dead-letter doctor

Directory conversion scans recursively for .eml files, matches the suffix case-insensitively, skips symlinked files whose resolved targets escape the requested input tree, and deduplicates in-tree symlink aliases that resolve to the same message file.

Web UI — start the local server:

bash
dead-letter-ui --host 127.0.0.1 --port 8765

Open http://127.0.0.1:8765 — on first launch, a setup prompt suggests default Inbox and Cabinet folders. Configure those folders before importing or starting jobs. Skipping dismisses the prompt but leaves those actions gated until setup is completed. Import .eml files with drag and drop or the file picker. Single-file imports use file mode, while multi-file drops create one directory-mode batch job. Mixed drops ask for confirmation before skipping non-.eml files.

The backend enforces a 100 MB per-file import limit for both single and batch uploads; browser batches also have aggregate-size and file-count limits.

From a source checkout, prefix with uv run:

bash
uv run dead-letter convert message.emluv run --extra ui dead-letter-ui --host 127.0.0.1 --port 8765

🐍 Python API

python
from dead_letter import convert
result = convert("message.eml")print(result.subject, result.sender)print(result.output)  # path to the generated .md

With options:

python
from dead_letter import convert, ConvertOptions
result = convert("message.eml", options=ConvertOptions(    strip_signatures=True,    strip_quoted_headers=True,))

Strip signature images (logos, social icons) and tracking pixels:

python
result = convert("message.eml", options=ConvertOptions(    strip_signature_images=True,    strip_tracking_pixels=True,))

When enabled, these filters remove matched images from rendered Markdown and omit stripped inline signature/tracking assets from bundle attachment output.

Bundle conversion (Markdown + attachments + source in one directory):

python
from dead_letter import convert_to_bundle
bundle = convert_to_bundle("message.eml", bundle_root="cabinet/", source_handling="copy")print(bundle.markdown)     # cabinet/message/message.mdprint(bundle.attachments)  # retained extracted files under cabinet/message/attachments/

source_handling="copy" preserves the original .eml in place. If omitted, convert_to_bundle() defaults to source_handling="move" and moves the source message into the bundle.

Retained extracted attachment filenames are normalized to safe basenames before they are written under attachments/.

Quality diagnostics include referenced/retained attachment counts when a message has attachments eligible for retention, so dropped artifacts are machine-detectable. See Quality Diagnostics.

Batch:

python
from dead_letter import convert_dir
for r in convert_dir("inbox/", output="out/"):    print(f"{'✓' if r.success else '✗'} {r.source.name}")

🔌 MCP Server

dead-letter ships an MCP server so LLM clients can convert .eml files directly without shelling out.

VS Code, Cursor, and Cline:

Install in VS Code · Install in Cursor

Requires uv/uvx on the desktop client's PATH. These links configure a local stdio server using the published PyPI package; no catalog admission is needed. First use may download Python and dependencies. Review the command, scope, and tool permissions before accepting. Cline setup, manual JSON examples, and verification: Client installation.

Launch it directly with uvx:

bash
uvx --python 3.12 --from 'dead-letter[mcp]' dead-letter-mcp

Or install it first (from 0.4.7 the MCP SDK is a core dependency; [mcp] is kept as a compatibility extra):

bash
pip install 'dead-letter[mcp]'dead-letter-mcp

From a source checkout:

bash
uv run dead-letter-mcp

Claude Desktop (extension bundle):

Download the .mcpb file and its .sha256 sidecar from a published package release (vX.Y.Z) on the releases page. Plugin-only releases do not contain this bundle. Verify the bytes before installation:

bash
# macOS: verifies against the downloaded sidecarshasum -a 256 -c dead-letter-mcp-X.Y.Z.mcpb.sha256
# Windows (PowerShell): compare this hash to the sidecar's hashcertutil -hashfile dead-letter-mcp-X.Y.Z.mcpb SHA256

Replace X.Y.Z with the selected package version. In a compatible Claude Desktop build, double-click the downloaded bundle, drag it onto the window, or use Settings > Extensions > Advanced settings > Install Extension. Check that the extension connects and exposes the tools below (five from 0.4.5; convert_mbox is absent in 0.4.0 and earlier), then convert a synthetic message.

The bundle uses a managed uv runtime and an exact package pin. First launch may download Python and dependencies. A checksum or command-line smoke test is not proof of a successful GUI installation on your client version. For hosts without MCPB support, use manual stdio setup below.

Claude Desktop (manual claude_desktop_config.json — alternative):

json
{  "mcpServers": {    "dead-letter": {      "command": "uvx",      "args": ["--python", "3.12", "--from", "dead-letter[mcp]", "dead-letter-mcp"]    }  }}

Merge the entry rather than replacing existing client settings. VS Code and other hosts can use different schemas; see the agent install guide.

Claude Code or Cowork (recommended — Claude plugin):

/plugin marketplace add BigCactusLabs/bigcactuslabs-plugins/plugin install dead-letter

On Claude Code 2.1.275 or later, /plugin install dead-letter --marketplace BigCactusLabs/bigcactuslabs-plugins does both in one step.

The plugin adds five slash commands: /dead-letter:convert, /dead-letter:summarize, /dead-letter:triage, /dead-letter:cabinet, /dead-letter:mbox. Local Claude Code needs uv on PATH; see plugin/ for runtime-specific setup and updates. Email content is treated as untrusted data, not instructions: embedded requests for tool use, credentials, or exfiltration are not followed.

The marketplace pins the plugin tag and commit, and the plugin pins an exact published Python package in its uv project and lock. Plugin releases after 0.4.13 launch with uv run --frozen --isolated from the shipped uv.lock, which fixes dependency versions and checks their hashes. Plugin 0.4.13 and earlier use uvx, which resolves compatible transitive versions itself. Claude Code and Cowork keep separate installed copies; update and verify each client.

Claude Code (manual MCP add — alternative):

bash
claude mcp add dead-letter -- uvx --python 3.12 --from 'dead-letter[mcp]' dead-letter-mcp

Codex:

bash
codex mcp add dead-letter -- uvx --python 3.12 --from 'dead-letter[mcp]' dead-letter-mcpcodex mcp list

mcp list confirms registration, not a successful tool call. Connect through the target client, confirm the tools below, and convert a synthetic fixture before treating the installation as verified.

Troubleshooting

Why does uv's cache keep growing? uvx and uv cached environments are not removed automatically. Close MCP clients, then run uv cache prune to remove unused cached data. See uv cache management.

Tools

ToolRequired argumentsReturns
convert_emleml_pathMarkdown text. Also writes a file when output_path is given.
convert_eml_to_bundleeml_path, bundle_rootJSON with bundle_path, markdown_path, attachment_paths. Copy-only: the original .eml is never moved or deleted.
convert_directorydirectory, output_directoryJSON summary. Capped at 50 .eml files per call.
convert_mboxpath, output_directoryJSON summary. One flat .mbox, at most 256 MiB and 1000 messages per call. 0.4.5 and later.
get_diagnosticseml_pathQuality and structure JSON. Writes nothing permanent.

All five take a preset (default, clean, verbose, raw) and per-flag overrides. Full contract, including the MCP-only constraints and the error-text table: docs/reference/v4-runtime-contracts.md.

🗂 Project Structure

src/dead_letter/├── core/           # conversion pipeline (MIME, HTML, threads, rendering)├── backend/        # CLI, API server, job runner, watch mode, MCP server└── frontend/       # static web UI (Alpine.js ES modules + vanilla fetch)tests/├── core/           # conversion pipeline tests with .eml fixtures├── backend/        # API, job, and watch tests├── plugin/         # plugin, skill, packaging, and release contracts└── frontend/       # JS unit tests

The agent guide maps bundle, container, skill, and maintainer tooling without turning this README into a file inventory.

🧪 Testing

bash
uv sync --extra dev --lockeduv run pytest -q tests/core        # conversion pipelineuv run pytest -q tests/backend     # API and job runneruv run pytest -q tests/plugin      # plugin, skill, packaging, and release contractsnode --test tests/frontend/*.test.jspython scripts/release.py check    # offline distribution metadata

CI also validates plugin/skill schemas, frontend syntax, maintained Markdown links, and cross-platform packaging. Full commands and the distinction between offline contracts and real client tests are in Contributing.

📚 Docs

🔧 Tools We Love

  • MarkEdit — TextEdit for Markdown, native macOS, ~4 MB. Opens dead-letter output like it was always meant to live there.
  • mo — local Markdown viewer that renders files in the browser with live reload. Point it at your Cabinet and read converted mail like a feed.

⚠️ Known Limitations

  • CLI/Python accept .eml, flat .mbox exports, and (from 0.4.5) ZIP/TGZ Takeout downloads; see the Takeout guide. The web UI imports .eml files and, from 0.4.10, one flat .mbox by local path. Unreleased source adds .zip, .tgz and .tar.gz input by path or browser upload, with a 1 GiB staging/output free-space reserve, and resumes stopped web imports in their job folder. Its watch and file-import flows stay EML-only. From 0.4.5 the MCP server adds a bounded convert_mbox tool for one flat .mbox. PST, MSG and live-mailbox connections are unsupported. An opt-in resumable MBOX import (--mbox-resume, CLI/Python) is available from 0.4.6; see the resume contract.
  • Local-only, single-user, single-machine; no remote server or authentication service. An MCP host may send results to its model provider.
  • In-memory job registry: state resets on restart. Unreleased source keeps a stopped archive import resumable across restarts. Retained binary attachments need a separate parser for text indexing.

License

PolyForm Noncommercial 1.0.0 — free for personal, educational, and nonprofit use. Commercial use requires a separate license from Big Cactus Labs.

來源:README.md,提交 4ff528a

工具

0
SourceWeft 尚未在沙箱中啟動過該軟體包,因此其工具未知。

版本歷史

9
  1. v0.4.10最新Oct 29, 2026
  2. v0.4.9Oct 22, 2026
  3. v0.4.8Oct 9, 2026
  4. v0.4.7Oct 9, 2026
  5. v0.4.6Oct 6, 2026
  6. v0.4.5Sep 29, 2026
  7. v0.4.0Sep 20, 2026
  8. v0.3.1Sep 19, 2026
  9. v0.2.5Sep 16, 2026

評分

已滿足適用檢查項的 40%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通25 分

    請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。

  • 已宣告認證方式10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具15 分

    請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。

  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分

    請在儲存庫或註冊中心條目中宣告授權條款。