<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Min8T</title>
    <description>The latest articles on Forem by Min8T (@min8t).</description>
    <link>https://forem.com/min8t</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3909404%2F86f218e2-885d-42f9-a873-b58f0dea85b9.png</url>
      <title>Forem: Min8T</title>
      <link>https://forem.com/min8t</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/min8t"/>
    <language>en</language>
    <item>
      <title>How to Inline CSS for HTML Email, The Complete Guide</title>
      <dc:creator>Min8T</dc:creator>
      <pubDate>Thu, 14 May 2026 15:16:55 +0000</pubDate>
      <link>https://forem.com/min8t/how-to-inline-css-for-html-email-the-complete-guide-24pe</link>
      <guid>https://forem.com/min8t/how-to-inline-css-for-html-email-the-complete-guide-24pe</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Crosspost note.&lt;/strong&gt; Originally published at &lt;a href="https://min8t.com/articles/inline-css-for-email" rel="noopener noreferrer"&gt;min8t.com/articles/inline-css-for-email&lt;/a&gt;. I'm one of the maintainers of MiN8T, which makes the in-browser CSS inliner referenced at the end. The technical content here applies regardless of which inliner you reach for.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You write CSS in a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block, the way you have for two decades on the web. You hit send. Half your subscribers see your beautiful design. The other half see &lt;em&gt;something else&lt;/em&gt;: a colorless wall of text, a stack of mis-aligned images, a call-to-action that has lost its rounded corners and bright background and is now indistinguishable from the body copy.&lt;/p&gt;

&lt;p&gt;The reason: most email clients strip or partially strip &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks. Gmail mobile, Yahoo, AOL, several enterprise clients. They discard your stylesheet entirely. The fix is older than the problem: &lt;strong&gt;inline every style as a &lt;code&gt;style="..."&lt;/code&gt; attribute on the element it targets&lt;/strong&gt;. Inline styles are the only CSS form that every email client supports without exception.&lt;/p&gt;

&lt;p&gt;This guide walks through exactly what inlining does, what you can't inline (and what to do with those rules instead), the specificity gotchas that produce the wrong output, and a free in-browser tool that handles the whole thing correctly.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Why Email Needs Inline CSS
&lt;/h2&gt;

&lt;p&gt;The same HTML and CSS that renders cleanly in any modern browser becomes hostile inside an email client. Email clients are not browsers. They are a patchwork of rendering engines spanning four decades of decisions, and most of them treat your &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block as a suggestion rather than a contract.&lt;/p&gt;

&lt;h3&gt;
  
  
  What gets stripped, where
&lt;/h3&gt;

&lt;p&gt;The behavior varies by client. Roughly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gmail web (large viewport):&lt;/strong&gt; keeps &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks in &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;. Strips them in some forwarded contexts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gmail mobile (Android + iOS):&lt;/strong&gt; strips most &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks. The same campaign that looks fine on desktop Gmail can render unstyled on mobile.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outlook desktop (Windows, 2007â€“2019):&lt;/strong&gt; renders through Microsoft Word's HTML engine. Honors a small subset of style rules; ignores everything modern (flexbox, grid, transforms, transitions, custom properties, etc.).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outlook on the web:&lt;/strong&gt; closer to a modern browser; honors &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; in head.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apple Mail (iOS + macOS):&lt;/strong&gt; the most permissive major client. Honors &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks fully.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Yahoo, AOL, Outlook 2003 and below, several enterprise clients:&lt;/strong&gt; strip &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks entirely.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Inline styles, styles written directly on an element via the &lt;code&gt;style&lt;/code&gt; attribute, are the only CSS form supported by every email client without exception. That's why "inline your CSS before sending" is the universal pre-send hygiene step for HTML email.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you only remember one rule from this article: &lt;strong&gt;inline CSS is the lowest-common-denominator form that every email client respects.&lt;/strong&gt; Everything you ship to an inbox should pass through an inliner unless you have a build pipeline that already does it.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  2. What Inlining Actually Does
&lt;/h2&gt;

&lt;p&gt;An inliner takes HTML with a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block and emits the same HTML with each rule's declarations transferred to &lt;code&gt;style="..."&lt;/code&gt; attributes on the elements that match. The structure stays identical; only the way styles are attached changes.&lt;/p&gt;

&lt;p&gt;A minimal example. Before inlining:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
  &lt;span class="nc"&gt;.btn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#28ef91&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#2b312c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt; &lt;span class="m"&gt;24px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;999px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Read more&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After inlining:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt;
   &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"background: #28ef91; color: #2b312c; padding: 12px 24px; border-radius: 999px;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
   Read more
&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rule is gone from the &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block; the same declarations now live on the element. Yahoo, Gmail mobile, and every other style-stripping client now render the button correctly because the styles are attached to the element they target.&lt;/p&gt;

&lt;h3&gt;
  
  
  The browser does most of the work
&lt;/h3&gt;

&lt;p&gt;An inliner doesn't need to parse CSS from scratch. Modern browsers expose every parsed rule via &lt;code&gt;document.styleSheets&lt;/code&gt;. A well-built inliner loads the user's HTML into a sandboxed iframe, walks the parsed rules, computes which elements each selector matches via &lt;code&gt;querySelectorAll&lt;/code&gt;, and writes the matching declarations as inline styles. The browser handles selector parsing, specificity, and rule expansion; the inliner just orchestrates.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. What You Cannot Inline (And What To Do Instead)
&lt;/h2&gt;

&lt;p&gt;Not every rule can be inlined. Four categories of CSS depend on living in a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block, and a competent inliner leaves them there.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;code&gt;@media&lt;/code&gt; queries
&lt;/h3&gt;

&lt;p&gt;A media query says "apply this rule conditionally." That conditional logic doesn't translate to an inline attribute. An attribute fires unconditionally. &lt;code&gt;@media (max-width: 600px)&lt;/code&gt; is your responsive-email lifeline; it's how you stack columns vertically on mobile and resize hero images. &lt;strong&gt;Leave the &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block intact for these.&lt;/strong&gt; Apple Mail, Gmail web (large viewport), and modern Android Gmail all honor &lt;code&gt;@media&lt;/code&gt; queries inside &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.col&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;block&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Pseudo-classes (&lt;code&gt;:hover&lt;/code&gt;, &lt;code&gt;:focus&lt;/code&gt;, &lt;code&gt;:active&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Inline styles have no concept of state. Writing &lt;code&gt;style="color: red"&lt;/code&gt; inside an &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; applies that color always, not just on hover. So &lt;code&gt;a:hover { color: red }&lt;/code&gt; can't be inlined; it has to stay in the &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block. Apple Mail, Gmail web, and Yahoo support hover; Outlook desktop ignores it; the rule is harmless either way.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Pseudo-elements (&lt;code&gt;::before&lt;/code&gt;, &lt;code&gt;::after&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;The same logic. &lt;code&gt;::before&lt;/code&gt; creates a virtual element at parse time; an inline attribute can't simulate that. Pseudo-element rules stay in the &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block. Be aware that Outlook desktop strips them entirely.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. &lt;code&gt;@keyframes&lt;/code&gt;, &lt;code&gt;@font-face&lt;/code&gt;, &lt;code&gt;@supports&lt;/code&gt;, &lt;code&gt;@import&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;All four are at-rules with no element to attach to. They live in the &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block (or in some &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; elsewhere) by definition. &lt;code&gt;@keyframes&lt;/code&gt; animations work in Apple Mail, Gmail web, and Yahoo, useful for subtle motion. &lt;code&gt;@font-face&lt;/code&gt; works in Apple Mail and Outlook on the web; web fonts are stripped from Outlook desktop and Gmail mobile. &lt;code&gt;@supports&lt;/code&gt; is rarely useful in email but harmless. &lt;code&gt;@import&lt;/code&gt; is dangerous because remote stylesheet loads are mostly stripped by privacy-conscious clients.&lt;/p&gt;

&lt;h3&gt;
  
  
  The pattern: a good inliner inlines what it can, preserves the rest
&lt;/h3&gt;

&lt;p&gt;The output should always be:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Element-targeted rules, inlined as &lt;code&gt;style="..."&lt;/code&gt; attributes.&lt;/li&gt;
&lt;li&gt;Conditional + state-dependent rules, left in the &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block, untouched.&lt;/li&gt;
&lt;li&gt;Existing inline styles in your input, preserved unless an &lt;code&gt;!important&lt;/code&gt; rule from a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block overrides them.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  4. Specificity and the Cascade: The Rule That Trips People Up
&lt;/h2&gt;

&lt;p&gt;When two rules in your &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block both target the same property on the same element, only one wins. CSS specificity decides which. Most cheap inliners get this wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  The specificity tuple
&lt;/h3&gt;

&lt;p&gt;Per the CSS Selectors L3 spec, every selector has a specificity score expressed as a 3-tuple &lt;code&gt;(a, b, c)&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;a&lt;/strong&gt; = count of &lt;code&gt;#id&lt;/code&gt; selectors&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;b&lt;/strong&gt; = count of &lt;code&gt;.class&lt;/code&gt;, &lt;code&gt;[attr]&lt;/code&gt;, and &lt;code&gt;:pseudo-class&lt;/code&gt; selectors&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;c&lt;/strong&gt; = count of element types and pseudo-elements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Comparison is lexicographic: &lt;code&gt;(1, 0, 0)&lt;/code&gt; beats &lt;code&gt;(0, 99, 99)&lt;/code&gt;. &lt;code&gt;(0, 1, 0)&lt;/code&gt; beats &lt;code&gt;(0, 0, 99)&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  An example that goes wrong
&lt;/h3&gt;

&lt;p&gt;Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
  &lt;span class="nc"&gt;.promo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;orange&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;#hero&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;green&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"promo"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"hero"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Sale&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What color is the text? Green. The id selector &lt;code&gt;#hero&lt;/code&gt; has specificity &lt;code&gt;(1, 0, 0)&lt;/code&gt;; the class &lt;code&gt;.promo&lt;/code&gt; has &lt;code&gt;(0, 1, 0)&lt;/code&gt;. ID wins. A naive inliner that just walks rules in source order would write &lt;code&gt;style="color: green"&lt;/code&gt; first then &lt;code&gt;style="color: orange"&lt;/code&gt;, leaving orange as the final inline value. Wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;!important&lt;/code&gt; overrides specificity
&lt;/h3&gt;

&lt;p&gt;An &lt;code&gt;!important&lt;/code&gt; declaration beats any non-important declaration regardless of specificity. So:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.promo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;orange&lt;/span&gt; &lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;#hero&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;green&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;now resolves to orange. Inliners that don't track &lt;code&gt;!important&lt;/code&gt; separately will get this case wrong too.&lt;/p&gt;

&lt;h3&gt;
  
  
  Existing inline styles also count
&lt;/h3&gt;

&lt;p&gt;If your input HTML already has &lt;code&gt;style="color: yellow"&lt;/code&gt; on the element, that inline value has effective specificity &lt;code&gt;(1, 0, 0, 0)&lt;/code&gt;. It beats anything in the &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block &lt;strong&gt;except&lt;/strong&gt; &lt;code&gt;!important&lt;/code&gt; rules. A correct inliner preserves existing inline styles by default and only overrides them when an &lt;code&gt;!important&lt;/code&gt; rule has a higher claim.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why most cheap inliners get this wrong
&lt;/h3&gt;

&lt;p&gt;The naive implementation walks &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; rules top-down and writes each one as inline styles, letting "later writes win." That works only for simple cases. Once you have IDs, classes, and inline styles competing for the same property, last-write-wins produces output that doesn'et match what the browser would render. Test your inliner output against the browser's &lt;code&gt;getComputedStyle()&lt;/code&gt; to verify.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Build-Time vs Paste-Time Inliners
&lt;/h2&gt;

&lt;p&gt;There are two ways to inline CSS for email. Pick by workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build-time (juice, MJML, Maizzle, Mailgun's library)
&lt;/h3&gt;

&lt;p&gt;You wire an inliner into your build pipeline. Source HTML lives with &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks; the build emits inlined output. Best when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You ship many emails on a recurring schedule (transactional, drip campaigns).&lt;/li&gt;
&lt;li&gt;You version-control your email source.&lt;/li&gt;
&lt;li&gt;You want each campaign's HTML to be reproducible from source.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The downside: setup. You need a Node + build pipeline; you're committing to a specific tool's output style.&lt;/p&gt;

&lt;h3&gt;
  
  
  Paste-time (browser-based inliners)
&lt;/h3&gt;

&lt;p&gt;You paste HTML into a textarea, get inlined output back, copy it to your ESP. Best when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're sending one-off campaigns or last-minute fixes.&lt;/li&gt;
&lt;li&gt;The HTML came from a no-code tool that doesn't have its own build step.&lt;/li&gt;
&lt;li&gt;You're QA-ing an export from someone else's pipeline.&lt;/li&gt;
&lt;li&gt;You want zero install + zero learning curve.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For most email teams that aren't running a fully-automated send pipeline, paste-time is the realistic everyday choice. The build-time approach pays off only when the same template ships repeatedly.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Edge Cases and Gotchas
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Gmail 102 KB clip threshold
&lt;/h3&gt;

&lt;p&gt;Gmail clips emails larger than &lt;strong&gt;102 KB&lt;/strong&gt; of HTML/text content (images don't count) and shows a "View entire message" link to expand. Anything below the clip is invisible by default, which is bad for tracking pixels at the bottom of the email and bad for unsubscribe links that fall below it. Inlining grows file size because the same rule may be repeated as inline styles across many elements. A good inliner reports the final size and warns if you cross 102 KB. If you're over, the fix is usually to remove unused selectors before inlining, or to use a build-time inliner that supports tree-shaking.&lt;/p&gt;

&lt;h3&gt;
  
  
  CSS shorthand expansion
&lt;/h3&gt;

&lt;p&gt;When the browser parses &lt;code&gt;border: 1px solid black&lt;/code&gt;, it expands the shorthand into &lt;code&gt;border-top-color&lt;/code&gt;, &lt;code&gt;border-top-style&lt;/code&gt;, &lt;code&gt;border-top-width&lt;/code&gt;, and the same for the other three sides. Naively-implemented inliners then write 12 inline declarations where 1 would suffice, bloating output 5â€“10x. Better inliners keep the shorthand or compress longhand back to shorthand.&lt;/p&gt;

&lt;h3&gt;
  
  
  CSS variables (&lt;code&gt;var(--x)&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Most email clients don't support custom properties. Outlook desktop and Gmail mobile ignore them entirely. Apple Mail and Outlook web do support them. If your source uses CSS variables, the inliner should resolve them to literal values during inlining; otherwise your inline styles will be &lt;code&gt;color: var(--brand)&lt;/code&gt; which falls back to invalid in unsupported clients.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pseudo-class rules with the same selector
&lt;/h3&gt;

&lt;p&gt;If you have &lt;code&gt;a { color: black } a:hover { color: blue }&lt;/code&gt;, only the first rule should be inlined; &lt;code&gt;a:hover&lt;/code&gt; stays in the &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block. A buggy inliner that splits selectors on comma but not on pseudo-class boundaries will erase the hover rule entirely.&lt;/p&gt;

&lt;h3&gt;
  
  
  External stylesheet &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tags
&lt;/h3&gt;

&lt;p&gt;Most clients strip external stylesheets, they don't load the URL at all. Inliners that try to fetch the linked stylesheet to merge it into the inline output run into CORS issues. The pragmatic answer: don't ship &lt;code&gt;&amp;lt;link rel="stylesheet"&amp;gt;&lt;/code&gt; in email at all. Embed every rule in a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block in &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;, then inline.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Try It Now (Free, In Your Browser)
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://min8t.com/tools/css-inliner/" rel="noopener noreferrer"&gt;MiN8T CSS Inliner&lt;/a&gt; does everything described above. It runs entirely in your browser, nothing is uploaded. It uses your browser's native CSS engine for parsing and selector matching, which is more accurate than any custom parser, and it correctly handles specificity, &lt;code&gt;!important&lt;/code&gt;, pseudo-classes, &lt;code&gt;@media&lt;/code&gt; preservation, and existing inline styles.&lt;/p&gt;

&lt;h3&gt;
  
  
  What to do after inlining
&lt;/h3&gt;

&lt;p&gt;Inlining is one step in a pre-send pipeline. The full check before you ship:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Inline CSS&lt;/strong&gt;, the topic of this article.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify rendering across clients.&lt;/strong&gt; Free option: paste into a free preview tool. Paid option: Litmus or Email on Acid for pixel-perfect Outlook rendering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Score the spam triggers&lt;/strong&gt; with a content scanner that runs SpamAssassin-style rules with a per-rule breakdown.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify auth records&lt;/strong&gt; separate from the content side. Spam filters care about content + sender reputation + SPF/DKIM/DMARC alignment. The first is in this article; the rest is the deliverability stack.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  If you write MJML or use a build pipeline
&lt;/h3&gt;

&lt;p&gt;You probably don't need a paste-time inliner. MJML inlines on compile, and most email frameworks (Maizzle, Foundation for Emails, etc.) include an inliner step. Use any MJML-to-HTML converter for quick MJMLâ†’HTML compilation in the browser, then run the result through a CSS Inliner if you want to verify the inline output independently of MJML's own choices.&lt;/p&gt;

&lt;h3&gt;
  
  
  Putting it all together
&lt;/h3&gt;

&lt;p&gt;Inlining is a five-second pre-send step that prevents 80% of cross-client rendering issues. Combined with a quick visual verification, a content score, and DNS auth (SPF/DKIM/DMARC, separate work), you cover the entirety of the designâ†’deliverability stack. None of this should require a paid tool. The whole pipeline is free in your browser.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://min8t.com/articles/inline-css-for-email" rel="noopener noreferrer"&gt;min8t.com/articles/inline-css-for-email&lt;/a&gt;. I work on MiN8T, an email infrastructure, design, and deliverability platform. Questions or counter-examples in the comments welcome.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>email</category>
      <category>css</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Why email verification is 21 checks, not 1 (and why MCP makes it agent-ready)</title>
      <dc:creator>Min8T</dc:creator>
      <pubDate>Sat, 02 May 2026 18:14:17 +0000</pubDate>
      <link>https://forem.com/min8t/why-email-verification-is-21-checks-not-1-and-why-mcp-makes-it-agent-ready-3me6</link>
      <guid>https://forem.com/min8t/why-email-verification-is-21-checks-not-1-and-why-mcp-makes-it-agent-ready-3me6</guid>
      <description>&lt;h2&gt;
  
  
  "Valid email format" isn't enough.
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;hello@gmail.com&lt;/code&gt; and &lt;code&gt;hello@gmial.com&lt;/code&gt; both parse as RFC-5322 valid. One reaches a real inbox. The other bounces, hurts your sender reputation, and might land you on a blocklist.&lt;/p&gt;

&lt;p&gt;Real email-deliverability work is 20+ checks, not 1. Most "email verifier" services are a black box that returns &lt;code&gt;valid&lt;/code&gt; or &lt;code&gt;invalid&lt;/code&gt; and you get to trust them. I wanted something I could reason about end-to-end and that an AI agent could orchestrate piece by piece.&lt;/p&gt;

&lt;p&gt;This is what's actually inside the pipeline I shipped (&lt;code&gt;@deliveriq/mcp&lt;/code&gt; — open source, MIT, on npm), broken down stage by stage. If you've ever wondered why a single email check takes ~3 seconds and why each piece matters, this is the long version.&lt;/p&gt;




&lt;h2&gt;
  
  
  The 5 stages
&lt;/h2&gt;

&lt;p&gt;The verification flow runs &lt;strong&gt;5 stages with 21 checks total&lt;/strong&gt;, in priority order. Each stage runs in parallel where possible. A failure at an early stage short-circuits the rest (you don't run SMTP if the syntax is broken).&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 1 — Email format validation (2 checks)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1.1 Syntax validation (RFC 5322).&lt;/strong&gt; Local-part length, character set, proper &lt;code&gt;@&lt;/code&gt; placement, domain format. Catches things like &lt;code&gt;@@domain.com&lt;/code&gt; or 80-character local parts before any network calls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.2 Typo detection (Sift3 fuzzy match).&lt;/strong&gt; Compares the domain against a database of 1,200+ known misspellings. &lt;code&gt;gmial.com&lt;/code&gt; → &lt;code&gt;gmail.com&lt;/code&gt;. &lt;code&gt;outlokk.com&lt;/code&gt; → &lt;code&gt;outlook.com&lt;/code&gt;. Sift3 is a string-distance algorithm that's faster than Levenshtein for short strings — important when you're running this on every check.&lt;/p&gt;

&lt;p&gt;Total cost so far: zero network calls. If syntax is invalid, the pipeline returns immediately.&lt;/p&gt;




&lt;h3&gt;
  
  
  Stage 2 — Domain &amp;amp; provider checks (5 checks)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;2.1 Disposable domain detection.&lt;/strong&gt; A continuously maintained list of 164,000+ throwaway domains (Mailinator, Guerrilla Mail, 10minutemail, and 164k friends). Disposable addresses self-destruct, so they're flagged high-risk by default.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.2 Role-based detection.&lt;/strong&gt; 130+ role prefixes — &lt;code&gt;admin@&lt;/code&gt;, &lt;code&gt;info@&lt;/code&gt;, &lt;code&gt;support@&lt;/code&gt;, &lt;code&gt;noreply@&lt;/code&gt;, &lt;code&gt;postmaster@&lt;/code&gt;, &lt;code&gt;webmaster@&lt;/code&gt;, etc. These typically have lower engagement and higher bounce rates than personal addresses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.3 Free-provider check.&lt;/strong&gt; 200+ consumer email domains worldwide. Useful when the use case requires corporate addresses (B2B prospecting, etc.). Not a hard fail — just a signal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.4 Alias normalization.&lt;/strong&gt; Resolves &lt;code&gt;+tag&lt;/code&gt; and dot-aliases to canonical form. &lt;code&gt;user+newsletter@gmail.com&lt;/code&gt; → &lt;code&gt;user@gmail.com&lt;/code&gt;. &lt;code&gt;u.s.e.r@gmail.com&lt;/code&gt; → &lt;code&gt;user@gmail.com&lt;/code&gt;. This is how you collapse duplicates that look different.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.5 Email-pattern analysis.&lt;/strong&gt; Scores the local part for entropy, keyboard walks (&lt;code&gt;qwerty12@&lt;/code&gt;), and bot-style sequences. Distinguishes human-typed from auto-generated addresses.&lt;/p&gt;




&lt;h3&gt;
  
  
  Stage 3 — Mailbox verification (3 checks)
&lt;/h3&gt;

&lt;p&gt;This is where it gets interesting. The previous 7 checks are local — Stage 3 actually talks to the mail server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.1 MX record resolution.&lt;/strong&gt; DNS MX lookup with A-record fallback. Highest-priority MX is used for SMTP. Capped at the top 2 servers to prevent timeout stacking when a domain has 8 MX records.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.2 ISP identification.&lt;/strong&gt; MX-pattern matching against known ISPs (Gmail, Outlook, Yahoo, Apple iCloud, etc.). This matters because some major providers aggressively block SMTP probes — for those, the pipeline skips Stage 3.3 and uses heuristic scoring instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.3 SMTP handshake verification.&lt;/strong&gt; This is the real one. The pipeline opens a TCP connection on port 25 and runs the conversation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; EHLO verifier.example.com
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; MAIL FROM: &amp;lt;neutral@verifier.example.com&amp;gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; RCPT TO: &amp;lt;hello@target.com&amp;gt;     &lt;span class="c"&gt;# the email we're checking&lt;/span&gt;
&amp;lt; 250 OK                            &lt;span class="c"&gt;# accepted = exists&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; RCPT TO: &amp;lt;random-1234567@target.com&amp;gt;  &lt;span class="c"&gt;# catch-all probe&lt;/span&gt;
&amp;lt; 250 OK                            &lt;span class="c"&gt;# also accepted = catch-all server&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; QUIT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A second &lt;code&gt;RCPT TO&lt;/code&gt; with a random address detects catch-all servers (where the server accepts every address regardless). 5-second timeout per connection. &lt;strong&gt;No email is ever sent&lt;/strong&gt; — &lt;code&gt;MAIL FROM&lt;/code&gt; and &lt;code&gt;RCPT TO&lt;/code&gt; are setup commands; we close before &lt;code&gt;DATA&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is the slowest part of the pipeline. ~1–2 seconds typical, sometimes more if the receiving server greylists.&lt;/p&gt;




&lt;h3&gt;
  
  
  Stage 4 — Reputation &amp;amp; intelligence (7 checks)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;4.1 Gravatar lookup.&lt;/strong&gt; Whether the address has a registered Gravatar profile picture. Positive trust signal — someone set up a public identity with this address.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.2 DNSBL blacklist check.&lt;/strong&gt; Queries the domain across &lt;strong&gt;50 categorized DNSBL zones&lt;/strong&gt;: Spamhaus (SBL/XBL/PBL), SpamCop, SURBL, URIBL, SORBS, Barracuda BRBL, UCEProtect, DroneBL, and many more. Includes domain-based lists and IP-based lists. A single listing isn't a death sentence; multiple listings are.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.3 Domain age via RDAP.&lt;/strong&gt; Registration date, expiration, registrar, DNSSEC status, nameservers. Domains under 30 days are high-risk; under a year, elevated. Pulled from the registry, not WHOIS — RDAP is the modern replacement and returns structured JSON.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.4 HIBP breach check.&lt;/strong&gt; Have I Been Pwned database lookup. Compromised addresses are more likely to be abandoned or used by spammers leveraging credential dumps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.5 DKIM record check.&lt;/strong&gt; Probes 15 common DKIM selectors (&lt;code&gt;google&lt;/code&gt;, &lt;code&gt;default&lt;/code&gt;, &lt;code&gt;selector1&lt;/code&gt;, &lt;code&gt;s1&lt;/code&gt;, &lt;code&gt;mail&lt;/code&gt;, &lt;code&gt;k1&lt;/code&gt;, etc.) in parallel. Returns key type (RSA / Ed25519) and estimated key size. DKIM presence indicates the domain signs outbound email — a strong legitimacy signal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.6 Infrastructure analysis.&lt;/strong&gt; Evaluates 6 email-authentication standards in one composite score:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SPF record presence + syntax&lt;/li&gt;
&lt;li&gt;DKIM key availability&lt;/li&gt;
&lt;li&gt;DMARC policy strength (&lt;code&gt;none&lt;/code&gt; / &lt;code&gt;quarantine&lt;/code&gt; / &lt;code&gt;reject&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;MTA-STS for transport security&lt;/li&gt;
&lt;li&gt;BIMI for verified-sender brand indicators&lt;/li&gt;
&lt;li&gt;TLS-RPT for TLS reporting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Domains with the full stack score highest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.7 MX server reputation.&lt;/strong&gt; Reverse-DNS (PTR) and IP-DNSBL on the MX server itself. Servers without PTR records or with blacklisted IPs indicate lower-quality infrastructure.&lt;/p&gt;




&lt;h3&gt;
  
  
  Stage 5 — Scoring &amp;amp; classification (4 checks)
&lt;/h3&gt;

&lt;p&gt;The last stage rolls everything up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5.1 Spam-trap heuristic scoring.&lt;/strong&gt; 12 weighted signals:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Domain age (younger = more suspicious)&lt;/li&gt;
&lt;li&gt;Role-based address&lt;/li&gt;
&lt;li&gt;No Gravatar&lt;/li&gt;
&lt;li&gt;No SMTP deliverability&lt;/li&gt;
&lt;li&gt;Disposable domain&lt;/li&gt;
&lt;li&gt;DNSBL listing&lt;/li&gt;
&lt;li&gt;Catch-all server&lt;/li&gt;
&lt;li&gt;Email-pattern entropy&lt;/li&gt;
&lt;li&gt;MX server reputation&lt;/li&gt;
&lt;li&gt;Local-part entropy&lt;/li&gt;
&lt;li&gt;Email-pattern type&lt;/li&gt;
&lt;li&gt;MX IP blacklist status&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Plus classification of the trap type: &lt;strong&gt;Pristine&lt;/strong&gt; (ISP-created, never used by a real person), &lt;strong&gt;Recycled&lt;/strong&gt; (formerly active, repurposed after abandonment), or &lt;strong&gt;Typo&lt;/strong&gt; (misspelling of a legitimate domain). Each comes with a confidence score.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5.2 Domain trust score.&lt;/strong&gt; A 0–100 composite: age (25 pts) + infrastructure (25 pts) + reputation (25 pts) + trust signals like DNSSEC and registrar lock (25 pts). Maps to five trust levels: Trusted / Positive / Neutral / Suspicious / Malicious.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5.3 Deliverability score.&lt;/strong&gt; All preceding signals roll into a single 0–100 score weighing SMTP deliverability, infrastructure quality, spam-trap probability, and address characteristics. The model gets one actionable number instead of a dump of 21 booleans.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5.4 Reachability classification.&lt;/strong&gt; Score maps to &lt;strong&gt;Safe&lt;/strong&gt; (80–100, high confidence inbox) / &lt;strong&gt;Risky&lt;/strong&gt; (40–79, may bounce or land in spam) / &lt;strong&gt;Invalid&lt;/strong&gt; (1–39, almost certain bounce) / &lt;strong&gt;Unknown&lt;/strong&gt; (0, couldn't be determined due to greylisting / catch-all / timeout).&lt;/p&gt;




&lt;h2&gt;
  
  
  Why MCP, not "another dashboard"?
&lt;/h2&gt;

&lt;p&gt;Email deliverability is one of those problems where &lt;strong&gt;the right answer needs 6–8 API calls in sequence&lt;/strong&gt;, not one call.&lt;/p&gt;

&lt;p&gt;You ask "is this list safe to send to?" and the right answer involves: verify each email, then for each invalid → check if the domain is disposable, for each catch-all → check sender reputation, for each unknown → DNSBL the MX, then compute aggregate spam-trap probability for the whole list, then the recommendation.&lt;/p&gt;

&lt;p&gt;A dashboard makes you click through that. The model orchestrates it as tool calls — and explains the verdict in plain English at the end.&lt;/p&gt;

&lt;p&gt;That's why I exposed it as MCP rather than just a REST API + dashboard. The API is also there, but the MCP layer is what makes it agent-ready.&lt;/p&gt;

&lt;p&gt;The 12 MCP tools map to the pipeline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;deliveriq_verify_email&lt;/code&gt; — runs all 5 stages on a single address&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;deliveriq_batch_verify&lt;/code&gt; — same, async, up to 100K addresses per job&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;deliveriq_blacklist_check&lt;/code&gt; — Stage 4.2 only&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;deliveriq_infrastructure_check&lt;/code&gt; — Stage 4.6 only&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;deliveriq_spam_trap_analysis&lt;/code&gt; — Stage 5.1 only&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;deliveriq_domain_intel&lt;/code&gt; — Stages 4 + 5.2 (composite)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;deliveriq_find_email&lt;/code&gt; — pattern-based discovery&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;deliveriq_org_intel&lt;/code&gt; — cached organization patterns&lt;/li&gt;
&lt;li&gt;(+ batch status, batch download, list jobs, check credits)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When Claude is asked "audit this list before we send", it composes calls across these tools rather than stuffing 21 questions into a single prompt.&lt;/p&gt;




&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Add to ~/Library/Application Support/Claude/claude_desktop_config.json&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"mcpServers"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"deliveriq"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"command"&lt;/span&gt;: &lt;span class="s2"&gt;"npx"&lt;/span&gt;,
      &lt;span class="s2"&gt;"args"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;, &lt;span class="s2"&gt;"@deliveriq/mcp"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,
      &lt;span class="s2"&gt;"env"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"DELIVERIQ_API_KEY"&lt;/span&gt;: &lt;span class="s2"&gt;"lc_your_key"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Free tier with no credit card at &lt;a href="https://min8t.com/deliveriq" rel="noopener noreferrer"&gt;https://min8t.com/deliveriq&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;npm: &lt;a href="https://www.npmjs.com/package/@deliveriq/mcp" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@deliveriq/mcp&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/Davison-Francis/min8t-sdks" rel="noopener noreferrer"&gt;https://github.com/Davison-Francis/min8t-sdks&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Glama: &lt;a href="https://glama.ai/mcp/servers/Davison-Francis/min8t-sdks" rel="noopener noreferrer"&gt;https://glama.ai/mcp/servers/Davison-Francis/min8t-sdks&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Closing thoughts
&lt;/h2&gt;

&lt;p&gt;Two things I learned building this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SMTP catch-all servers are why "valid email" is unprovable.&lt;/strong&gt; A server that returns &lt;code&gt;250 OK&lt;/code&gt; to every &lt;code&gt;RCPT TO&lt;/code&gt; is correct from a protocol standpoint — RFC 5321 doesn't require accuracy. So you can never be 100% certain &lt;code&gt;hello@catch-all-domain.com&lt;/code&gt; reaches a real person. The best you can do is detect the catch-all behavior and weight it accordingly. Stage 5's "Risky" category is mostly catch-all results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MCP is genuinely better than a dashboard for this workflow.&lt;/strong&gt; Composing 6–8 tools in sequence to debug "why isn't this email getting through?" is the kind of agent-task that's incredibly tedious in a dashboard but feels natural when an LLM is driving it.&lt;/p&gt;

&lt;p&gt;What MCP tools are you missing for email or deliverability work? Curious what others would add to a pipeline like this.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>javascript</category>
      <category>mcp</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
