Skip to main content
Glama
BigCactusLabs

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.

⚡ Try it

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

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.

Related MCP server: DingusMail

🎯 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 want

Start here

Core CLI or Python API

Homebrew / pip below, or the uvx quick try

Local web UI

dead-letter[ui] below

Claude Desktop extension or another MCP client

MCP Server

Claude Code / Cowork commands

Plugin

Container-isolated MCP

Containers

Portable agent instructions

Agent Skill

With Homebrew on Apple silicon macOS:

brew tap BigCactusLabs/tap
brew install dead-letter

The Homebrew formula installs the core CLI only: dead-letter convert and dead-letter doctor. It intentionally does not bundle the optional web UI or MCP server dependency stacks.

With pip:

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

Use pipx for an isolated UI or MCP install:

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:

uvx --python 3.12 dead-letter convert message.eml
uvx --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:

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

Agent Skill (any host)

Install the portable Agent Skill into whichever agent you use:

gh skill install BigCactusLabs/dead-letter dead-letter --agent claude-code
gh skill install BigCactusLabs/dead-letter dead-letter --agent codex
gh 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:

dead-letter convert message.eml

Convert a whole directory:

dead-letter convert inbox/ --output out/

Generate a JSON conversion report alongside the output:

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:

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:

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:

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

🐍 Python API

from dead_letter import convert

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

With options:

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:

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):

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.md
print(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:

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:

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

Or install the MCP extra first:

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

From a source checkout:

uv run --extra mcp 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:

# macOS: verifies against the downloaded sidecar
shasum -a 256 -c dead-letter-mcp-X.Y.Z.mcpb.sha256

# Windows (PowerShell): compare this hash to the sidecar's hash
certutil -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):

{
  "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

The plugin launches the MCP server via uvx and 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 its launcher pins an exact published Python package. Claude Code and Cowork keep separate installed copies; update and verify each client. Those pins do not freeze every transitive dependency.

Claude Code (manual MCP add — alternative):

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

Codex:

codex mcp add dead-letter -- uvx --python 3.12 --from 'dead-letter[mcp]' dead-letter-mcp
codex 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.

Tools

Tool

Required arguments

Returns

convert_eml

eml_path

Markdown text. Also writes a file when output_path is given.

convert_eml_to_bundle

eml_path, bundle_root

JSON with bundle_path, markdown_path, attachment_paths. Copy-only: the original .eml is never moved or deleted.

convert_directory

directory, output_directory

JSON summary. Capped at 50 .eml files per call.

convert_mbox

path, output_directory

JSON summary. One flat .mbox, at most 256 MiB and 1000 messages per call. 0.4.5 and later.

get_diagnostics

eml_path

Quality 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

uv sync --extra dev --locked
uv run pytest -q tests/core        # conversion pipeline
uv run pytest -q tests/backend     # API and job runner
uv run pytest -q tests/plugin      # plugin, skill, packaging, and release contracts
node --test tests/frontend/*.test.js
python 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 is 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 only) is on main but unreleased; 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. 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.

Available Tools

5 tools
convert_directoryConvert email folderA

Batch convert all .eml files in a directory to Markdown.

Recursively finds .eml files (at most 50 per call; larger directories are rejected before any conversion). output_directory is required: Markdown is written there, mirroring subfolders, and source .eml files are left in place. Returns a JSON summary with total, successes, failures, output_paths, and errors.

Use convert_eml to retrieve individual converted file content.

ParametersJSON Schema
NameRequiredDescriptionDefault
presetNodefault
dry_runNo
directoryYes
thread_modeNolatest
thread_orderNooldest-first
include_raw_htmlNo
output_directoryYes
strip_signaturesNo
strip_disclaimersNo
embed_inline_imagesNo
include_all_headersNo
no_calendar_summaryNo
strip_quoted_headersNo
strip_tracking_pixelsNo
strip_signature_imagesNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does well: it discloses the 50-file limit, that oversized directories are rejected before any conversion, that source .eml files are left in place (non-destructive), and output folder mirroring. It omits auth requirements, runtime/performance expectations, and what happens on partial failure beyond the 'failures' count.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, each earning its place: the operation and scope, the batch constraint plus output semantics, then the routing hint to convert_eml. Front-loaded with no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The output schema exists, so listing the JSON summary fields is somewhat redundant but harmless, and the write/limit behavior is covered. The real gap is that a 15-parameter tool documents only one parameter, leaving the bulk of configurable conversion behavior unexplained.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% across 15 parameters, so the description must compensate, yet it only explains output_directory semantics. The 13 remaining options (preset, dry_run, thread_mode/order, and eight strip/include toggles) get no explanation of defaults or effect, leaving the agent to guess at conversion behavior.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Batch convert all .eml files in a directory to Markdown') and immediately distinguishes itself from the sibling convert_eml by routing single-file retrieval there. An agent can tell exactly what this does versus convert_mbox or convert_eml_to_bundle from the first sentence.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It names the alternative ('Use convert_eml to retrieve individual converted file content') and gives a clear batch context, plus the 50-file ceiling that defines when this tool is appropriate. It does not, however, explain when to choose this over convert_mbox or convert_eml_to_bundle, so it stops short of full when/when-not coverage.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

convert_emlConvert emailA

Convert a .eml email file to Markdown with YAML front matter.

Returns the full Markdown content (front matter + body). When output_path is provided, also writes the file to disk.

Attachments are listed in the front matter but not written to disk; use convert_eml_to_bundle to save the decoded attachment files.

thread_mode="structured" adds a section per earlier reply or forwarded message. Gmail HTML forwards and unquoted plain-text forwards are kept in latest mode too; a plain-text forward inside ">" quoting is not.

Presets bundle common flag combinations:

  • default: strips signatures, tracking pixels, signature images

  • clean: default + strips disclaimers and quoted headers

  • verbose: includes all headers and raw HTML

  • raw: no stripping, preserves everything

Individual flags override the preset when provided.

ParametersJSON Schema
NameRequiredDescriptionDefault
presetNodefault
eml_pathYes
output_pathNo
thread_modeNolatest
thread_orderNooldest-first
include_raw_htmlNo
strip_signaturesNo
strip_disclaimersNo
embed_inline_imagesNo
include_all_headersNo
no_calendar_summaryNo
strip_quoted_headersNo
strip_tracking_pixelsNo
strip_signature_imagesNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does well: it discloses that full Markdown is returned, that output_path additionally writes to disk, that attachments are listed but not decoded to disk, and the nuanced forwarding/quoted-text behavior in thread_mode. It omits error behavior and the exact effect of the unmentioned flags, which keeps it from a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the core purpose, then delivers return behavior, the attachment caveat, thread-mode nuance, and a scannable preset list. Every sentence carries information and nothing is padded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so the description needn't restate return values, and it appropriately focuses on behavior an agent can't infer. For a 14-parameter tool with zero schema documentation, the few flags left unexplained (thread_order, embed_inline_images, no_calendar_summary) are the only real gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% across 14 parameters, so the description must compensate, and it does for the critical ones: it enumerates the four preset values, defines the preset-to-flag mapping, states that individual flags override the preset, and explains output_path and thread_mode. It leaves several booleans (embed_inline_images, no_calendar_summary, thread_order) unexplained, so compensation is strong but incomplete.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Convert a .eml email file to Markdown with YAML front matter') and immediately clarifies the output shape. It also names the sibling convert_eml_to_bundle for the attachment-saving case, so an agent can separate this tool from its neighbors without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives clear routing advice for the attachment case ('use convert_eml_to_bundle to save the decoded attachment files') and explains when each preset applies and when thread_mode="structured" is appropriate. It stops short of contrasting with convert_mbox or convert_directory, which the sibling list shows are plausible alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

convert_eml_to_bundleConvert email to bundleB

Convert a .eml file to a self-contained bundle with markdown and attachments.

Creates a directory containing the converted markdown, extracted attachments, and optionally the original .eml source.

source_handling only accepts 'copy' over MCP: the original .eml is copied into the bundle and left untouched. The 'move' and 'delete' modes are rejected here — use the CLI or the Python API for those.

Returns JSON with bundle_path, markdown_path, attachment_paths, and optional diagnostics.

ParametersJSON Schema
NameRequiredDescriptionDefault
presetNodefault
eml_pathYes
bundle_rootYes
thread_modeNolatest
thread_orderNooldest-first
source_handlingNocopy
include_raw_htmlNo
strip_signaturesNo
strip_disclaimersNo
embed_inline_imagesNo
include_all_headersNo
no_calendar_summaryNo
strip_quoted_headersNo
strip_tracking_pixelsNo
strip_signature_imagesNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

B3.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does meaningful work: it discloses that the original .eml is copied and left untouched, that move/delete are rejected over MCP, and that the result is a new directory of extracted artifacts. It does not cover overwrite behavior if bundle_root already exists, so it is not exhaustive.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The purpose is front-loaded in the first sentence, followed by short, scannable paragraphs on output shape and the source_handling restriction. There is mild redundancy between 'self-contained bundle' and 'Creates a directory containing...' but nothing wasteful.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so enumerating return keys is largely redundant and the description need not explain return values. The real gap is the undocumented parameter surface: for a 15-parameter tool, an agent has no basis for setting the dozen optional flags.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% across 15 parameters, so the description must compensate. It only explains source_handling (and even that partially) while leaving preset, thread_mode, thread_order, and the ten include_*/strip_* booleans entirely undocumented in both schema and prose.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource ('Convert a .eml file to a self-contained bundle') and clarifies the output artifact (a directory with markdown, attachments, and optionally the source). It is distinguishable from convert_eml by the 'bundle' framing, but it never explicitly contrasts itself with that sibling, so it stops short of 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives a real usage constraint for one parameter: source_handling only accepts 'copy' over MCP, and move/delete must be done via CLI or Python API. However, it offers no guidance on when to choose this tool over convert_eml, convert_mbox, or convert_directory, so tool-level selection is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

convert_mboxConvert MBOX archiveA

Convert one flat .mbox archive (e.g. Gmail Takeout) to Markdown files.

Bounded: the archive must be at most 256 MiB, and conversion stops after 1000 messages (the response then has truncated=true; use the dead-letter CLI for larger archives). Compressed archives and Apple Mail .mbox directories are rejected. output_directory is required; one .md per message (or one bundle directory when bundles=true) is written there with a collision-safe JSON report. The source archive is never modified.

Returns a JSON summary (processed, converted, skipped, failed, truncated, report_path, and at most 20 failure entries), never message content. Cancellation is not supported; the bounds limit call duration.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYes
presetNodefault
bundlesNo
dry_runNo
thread_modeNolatest
thread_orderNooldest-first
include_raw_htmlNo
output_directoryYes
strip_signaturesNo
strip_disclaimersNo
embed_inline_imagesNo
include_all_headersNo
no_calendar_summaryNo
strip_quoted_headersNo
strip_tracking_pixelsNo
strip_signature_imagesNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does so well: it discloses size and message-count bounds, truncation signalling (truncated=true), rejection cases, the non-destructive guarantee ('The source archive is never modified'), collision-safe report output, and the absence of cancellation. These are exactly the operational traits an agent needs before committing to a long-running conversion.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core action, then organized into labelled blocks ('Bounded:', output behavior, 'Returns a JSON summary'). Dense but every sentence adds distinct operational information; nothing is filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 16-parameter tool with no annotations, it covers purpose, bounds, failure handling, side effects, and return shape (with the output schema handling the rest). An agent has enough to call it correctly and knows the edge cases that cause rejection or truncation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0% across 16 parameters, yet the description only explains two of them (output_directory is required, bundles=true switches to bundle directories). The remaining toggles (preset, thread_mode, strip_*, embed_inline_images) are undocumented, though most have self-describing names. Partial compensation only.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource ('Convert one flat .mbox archive ... to Markdown files') and immediately narrows scope with 'flat' plus rejection of compressed archives and Apple Mail .mbox directories. This lets an agent distinguish it from convert_eml and convert_directory without opening another schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives concrete when-to-use conditions (archive must be ≤256 MiB, ≤1000 messages) and an explicit alternative path ('use the dead-letter CLI for larger archives'). It does not directly route between convert_mbox, convert_eml, and convert_directory, but the format constraints make the boundary largely inferable.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_diagnosticsGet conversion diagnosticsB

Inspect email quality and structure without writing permanent files.

Use this to assess conversion quality before committing, or to troubleshoot problematic .eml files.

Always returns JSON with: state (normal/degraded/review_recommended), selected_body, segmentation_path, client_hint, confidence, fallback_used, and warnings. Two keys are conditional: stripped_images appears only when images were removed, and attachments only when the message had attachments eligible for retention.

ParametersJSON Schema
NameRequiredDescriptionDefault
presetNodefault
eml_pathYes
thread_modeNolatest
thread_orderNooldest-first
include_raw_htmlNo
strip_signaturesNo
strip_disclaimersNo
embed_inline_imagesNo
include_all_headersNo
no_calendar_summaryNo
strip_quoted_headersNo
strip_tracking_pixelsNo
strip_signature_imagesNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

B3.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does reasonable work: it discloses the non-destructive trait ('without writing permanent files'), the always-JSON return, the state enum values (normal/degraded/review_recommended), and that some keys are conditional. It omits error behavior and any cost/latency characteristics, but the core behavioral profile is present.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose and usage are front-loaded in two compact sentences, which is good. The closing enumeration of return keys largely duplicates the existing output schema and consumes several lines without adding routing or setup value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Return values are covered by the output schema, so the description's key list is redundant rather than additive. For a 13-parameter tool with zero schema coverage, the description leaves the whole configuration surface undocumented, which is a substantial completeness gap despite the adequate purpose/usage framing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There are 13 parameters at 0% schema description coverage, so the description is expected to compensate and instead says nothing about any of them. Preset, thread_mode, thread_order, and the five-plus strip_* toggles are entirely unexplained, leaving the agent no basis for setting them.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource ('Inspect email quality and structure') and adds a differentiating constraint versus the convert_* siblings: it operates 'without writing permanent files'. That implicitly separates it from the conversion tools, though no sibling is named explicitly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives clear trigger conditions — 'assess conversion quality before committing' and 'troubleshoot problematic .eml files' — which tells the agent when this inspection step is warranted. It stops short of naming an alternative tool or stating exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 2 tool updatesv0.4.5
    • Changedconvert_directory4 fields changed
      • removedInput schema / properties / output_directory / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • removedInput schema / properties / output_directory / default
        Removed value: -null
      • addedInput schema / properties / output_directory / type
        Added value: +"string"
      • changedInput schema / required
        Previous value: -[
        -  "directory"
        -]New value: +[
        +  "directory",
        +  "output_directory"
        +]
    • Addedconvert_mbox
  2. 4 tool updatesv0.2.4
    • First observedconvert_directory
    • First observedconvert_eml
    • First observedconvert_eml_to_bundle
    • First observedget_diagnostics

TDQS

A3.9/5.0

Scored across 5 tools

Disambiguation4/5

The tools are largely distinct: convert_eml, convert_directory, and convert_mbox differ by input source, and get_diagnostics is clearly separate. The only mild overlap is convert_eml vs convert_eml_to_bundle, but the descriptions make the boundary (markdown-only vs markdown+attachments on disk) clear.

Naming Consistency5/5

Names follow a predictable verb_noun pattern with consistent snake_case: convert_eml, convert_eml_to_bundle, convert_directory, convert_mbox, and get_diagnostics. The verb prefixes (convert_, get_) are used consistently and readably.

Tool Count5/5

Five tools is well-scoped for an email conversion server, with each tool covering a distinct input source (single .eml, .eml bundle, directory, .mbox) plus a diagnostics utility. No tool feels redundant or padded.

Completeness4/5

The surface covers the core conversion lifecycle across all expected input sources and offers diagnostics for quality assessment. Minor gaps exist: source_handling move/delete modes are CLI-only, and retrieving batch output content still requires convert_eml rather than a dedicated reader, but agents can work around these.

Maintenance

ActivityMaintained
ResponsivenessWithin a week

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    D
    maintenance
    A local MCP server that provides LLM clients with read/write access to email and calendar data from Gmail, iCloud, and generic IMAP providers. It runs entirely on your machine, keeping data private while enabling email management, calendar operations, and task handling through natural language.
    39
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    MCP server for parsing .eml email files, extracting metadata, content, and attachments with smart organization into folders. Enables AI to read and handle email files offline without triggering trackers.
    2
    2
    AGPL 3.0
  • F
    license
    Not graded
    quality
    B
    maintenance
    A private, single-user MCP server that unifies Gmail, Microsoft 365/Outlook, and IMAP mailboxes for LLMs to search and read emails live, without storing or caching mailbox contents.
    -
  • A
    license
    C
    quality
    B
    maintenance
    A local-first Python MCP server that turns Gmail, Outlook/Microsoft 365, iCloud Mail, and generic IMAP/SMTP mailboxes into a synchronized, searchable OKF knowledge layer, exposing 38 tools and four resources for mailbox actions, synchronization, retrieval, attachments, and optional semantic search while keeping the provider mailbox authoritative.
    38
    1
    MIT