If you've ever wanted to turn an email you wrote into a published story on your newslode — a monthly investor update, a field note, a newsletter you already sent — this skill does the conversion for you, with Claude doing the cleanup.
distro-email-ingest takes an email out of your Gmail, strips the chrome (greetings, signatures, tracking links, reply chains), restructures it into semantic markdown, and files it as a draft on whatever Distro newslode you point it at. It also handles the awkward case of inline images, which Gmail's API doesn't expose directly — by walking you through a one-line local curl upload.
The editorial default is faithful restructure: preserve your exact wording, just clean up the email noise. You can override with "light edit" or "full rewrite" if you want Claude to be more active.
What you'll need
To actually use this skill (after installing it), your Claude needs two connectors turned on:
Distro Publisher MCP — to create the draft on your newslode
Gmail connector — to read the email
If you're missing either, Claude will tell you when you try to use the skill. Both are one-time setups in your Claude settings.
What it does
Finds the email in your Gmail by subject, sender, or date hint
Strips email chrome (greetings, sign-offs, signature blocks, "view in browser," reply chains, tracking URLs)
Preserves lists, links, blockquotes, and all substantive prose in the original wording
Publishes as a draft on the newslode of your choice, with the email subject as the title and the send date as the editorial display date
Optionally attaches an inline image from the email as the story's featured image (via a short local
curlstep you run yourself)Returns a Tiptap editor link so you can review before publishing live
Install the skill
Ask Claude:
"Please fetch the distro-email-ingest skill from Distro Skills and give me a
.skillfile I can install."
Claude will pull this story, extract the SKILL.md from the fenced block below, and package it as distro-email-ingest.skill. The file will appear in your chat with a Save skill button — click it to install directly. That registers the skill with your Claude app so it shows up in your skills list and triggers on matching requests.
Requires the Distro Reader or Distro Publisher MCP connected so Claude can pull the story. While Distro Skills is private, this works for account holders with access. Public read access is coming.
Claude Code users: you have a direct path — ask Claude to drop the unzipped folder into ~/.claude/skills/distro-email-ingest/ and restart the session. No packaging step needed.
No Distro MCP connected? Copy the SKILL.md contents from the fenced block below into a file called SKILL.md inside a folder called distro-email-ingest, zip it (folder as zip root), and upload through Settings → Capabilities → Skills → Upload skill.
The skill source
This is the canonical SKILL.md:
---
name: distro-email-ingest
description: Use this skill when the user asks to ingest, import, publish, or draft an email from their Gmail as a story on a Distro publication (newslode). Triggers on requests like "post this email to my newslode", "publish the [sender/subject] email as a draft on Distro", "ingest this Gmail message to [newslode name]", or any request that pairs a Gmail message with a Distro publishing action. Handles email chrome stripping, faithful-by-default restructuring, the Gmail-inline-image limitation (which requires a one-line local curl from the user), and the full S3 presigned-upload-to-featured-image pipeline.
---
# Distro email-ingest skill
Turn an email from the user's Gmail into a clean draft story on a Distro newslode — preserving the author's voice, stripping email chrome, and (optionally) attaching an inline image as the story's featured image.
## Default editorial stance
**Faithful restructure is the default.** Preserve the sender's exact wording. Strip email chrome (greetings, signatures, footers, quoted reply chains, tracking links). Restructure into semantic paragraphs/lists/blockquotes. Do **not** rewrite, tighten, or "improve" the prose.
Only deviate when the user explicitly asks for:
- **Light edit** — fix awkward transitions, tighten the lede, keep voice
- **Full rewrite** — treat the email as notes/reporting and write a new story
If the user doesn't specify, do faithful restructure and don't ask.
## Workflow
### Step 1 — Identify the email and destination
Ask the user (if not already supplied):
- Which email? (subject, sender, or date hint — anything that narrows a search)
- Which newslode? (name or publication ID)
Use `Gmail:search_threads` with a targeted `query:` string (e.g., `subject:("February investor update") from:[email protected]`). If multiple matches come back, list them with dates and ask which one. Then call `Gmail:get_thread` with `messageFormat: "FULL_CONTENT"` to get the `plaintextBody`.
If the user names a newslode by title rather than ID, call `Distro:distro_publication_list` to resolve it. (`publication_list` is the only tool that surfaces private newslodes, so use it rather than `publication_discover`.)
### Step 2 — Extract and restructure
Work from `plaintextBody`. Strip:
- Subject-line prefixes: "Re:", "Fwd:", "[Newsletter Name]"
- Greetings ("Hi all,", "Hello Brad,", "Dear investors,")
- Sign-offs if they're generic ("Thanks,", "Best,", "Cheers,")
- Signature blocks containing role/company/email/phone
- "View in browser", "Unsubscribe", tracking pixels and URLs
- Reply chains (everything after `On [date] [sender] wrote:`)
- Forwarded-message headers
Preserve:
- All substantive paragraphs in original wording
- Numbered and bulleted lists as proper markdown lists
- Links — but drop tracking/click-wrapper URLs; keep canonical destination URLs
- Blockquotes for any clearly quoted material
- The author's sign-off/signature **only if the user asks** — by default, strip it along with the signature block
**Handle inline image references.** Gmail inserts placeholders like `[image: some-filename.png]` where inline images were embedded. Leave a clearly-marked italicized placeholder in the markdown at that exact position:
```
*[Image to add in editor: some-filename.png]*
```
This will be cleaned up in Step 4 if the user attaches an image, or left for them to handle in the Tiptap editor.
### Step 3 — Publish as draft
Call `Distro:distro_content_publish` with:
- `publicationId`: resolved from Step 1
- `title`: the email subject, with email prefixes stripped
- `content`: the restructured markdown body
- `contentFormat: "markdown"` — **explicit, not auto-detected**
- `preview`: the first substantive sentence of the body (not the greeting), trimmed to roughly 200 chars
- `author`: the email sender's name (if known) or the authenticated user's default
- `displayDate`: the original email send date in ISO 8601 — so if/when published, the editorial date reflects when it was written
- `status: "draft"` — **always draft by default for email ingest**; users review before going live
Capture the returned `contentId` — you'll need it for image attachment and for giving the user an edit link.
### Step 4 — Handle the inline image (if any)
If the email had an inline image reference, ask the user whether they want to attach it as the story's featured image. If yes, follow the image-upload pipeline below. If no, leave the placeholder in the body and tell them they can add the image in Tiptap.
#### Image-upload pipeline
Gmail MCP does **not** expose message attachments or inline image bytes, and the bash-tool network allowlist in this environment does **not** include `distro-app-storage.s3.amazonaws.com`. So the user has to do one manual step: a local `curl` PUT from their own machine. Design the interaction to be as painless as possible.
1. **Tell the user what to do, in this order:**
> Save the image from the email to your **Downloads folder**. Then tell me the exact filename (including the extension).
Wait for the filename. Don't guess it.
2. **Generate the presigned upload URL** via `Distro:distro_media_upload_url`:
- `fileName`: a kebab-case identifier relevant to the story (e.g., `feb-investor-update-hero`) — **without** extension
- `mimeType`: match the actual file. If filename ends `.jpg`/`.jpeg` → `image/jpeg`; `.png` → `image/png`; `.gif` → `image/gif`; `.webp` → `image/webp`. **Note:** files named `.png` are sometimes actually JPEGs (screen grabs on macOS often do this). If the user has uploaded the file to `/mnt/user-data/uploads/` you can run `file <path>` to verify — declare the real MIME type, not the filename-implied one.
- `newsId`: the `contentId` from Step 3
The response gives you a `uploadUrl` (5-minute expiry) and an `assetUrl` (the final S3 URL).
3. **Hand the user a ready-to-paste curl command.** Format it so they can copy once and run — no placeholders to fill in:
```
curl -X PUT \
-H "Content-Type: <actual-mime-type>" \
--upload-file "$HOME/Downloads/<exact-filename-user-gave-you>" \
"<the-uploadUrl-with-all-query-params>"
```
Tell them:
- A successful upload returns nothing (empty body) — no news is good news
- The URL expires in 5 minutes, so run it now; if they get an `AccessDenied` or `expired` error, you'll regenerate instantly
- If their filename has spaces, the `$HOME/Downloads/...` wrapping in quotes handles it
4. **Wait for confirmation.** When they say it worked (or show you a clean terminal), call `Distro:distro_document_create`:
- `src`: the `assetUrl` from Step 2
- `newsId`: the `contentId` from Step 3
- `name`: `<kebab-case-id>.<actual-ext>` — the extension should match the real MIME type, not the user's filename
- `caption`: a short caption drawn from the email context (what the image shows, who made it, when) — keep it factual
This attaches the image as the story's **featured image**, which displays at the top of the rendered story.
5. **Clean up the placeholder.** Since the image now displays as the featured image at the top, the inline `*[Image to add in editor: ...]*` placeholder in the body is redundant. Ask the user if they want to remove it. If yes, call `Distro:distro_content_update` with the updated markdown body and `contentFormat: "markdown"`.
### Step 5 — Return the edit link
Always end with the Tiptap editor URL so the user can review:
```
https://app.distro.media/stories/<contentId>
```
Tell them succinctly what was done (faithful restructure, any image handling, draft status) and let them take it from there.
## Distro tool quirks to remember
- **Both** `distro_content_publish` and `distro_content_update` now accept `contentFormat: "markdown"`. Prefer markdown over HTML — it's cleaner to generate, easier for the user to eyeball, and the server handles the Tiptap ProseMirror conversion. This is a change from older guidance that said updates needed HTML.
- `distro_publication_list` is the only tool that surfaces private newslodes. Use it to resolve newslode names to IDs.
- `distro_media_upload_url` presigned URLs expire in **5 minutes**. Generate one immediately before handing it to the user, not in advance.
- `distro_document_create` with `newsId` attaches as the **featured image** (the story's hero). With `publicationId`, it sets the publication logo. These are mutually exclusive uses.
- If you associate a second image with the same `newsId` via `distro_document_create`, the existing featured image is **replaced**, not added. There's no multi-image-per-story API here — inline body images have to be handled in Tiptap directly.
## Environment limitations to remember
- **Gmail MCP doesn't expose attachments.** `get_thread` returns `plaintextBody` with `[image: filename]` placeholders where inline images were embedded, but not the image bytes. The user has to download manually.
- **Bash-tool network allowlist blocks S3.** `distro-app-storage.s3.amazonaws.com` is not reachable from the Claude sandbox, so the `curl` PUT has to run on the user's own machine. This is a hard constraint — don't try to work around it with base64, sidecar scripts, or other cleverness; they'll all hit the same wall.
- **File-extension sniffing.** Screen grabs exported from macOS are often JPEGs named `.png`. If the user has already uploaded the file to `/mnt/user-data/uploads/`, run `file <path>` to check the real format and declare the MIME type accurately — S3 validates it.
## Examples
### Example 1 — minimal: user names an email and a newslode
**User:** "Ingest my February investor update into Distro Newslode as a draft."
**Claude:**
1. `Gmail:search_threads` with `subject:"investor update" from:me` (or similar)
2. `Gmail:get_thread` on the matching thread
3. `Distro:distro_publication_list` → resolve "Distro Newslode" to its pub ID
4. Extract plaintext body, strip chrome, restructure as markdown preserving voice
5. `Distro:distro_content_publish` with `contentFormat: "markdown"`, `status: "draft"`, `displayDate` = email send date
6. Ask about the inline image (if any)
7. Return edit link
### Example 2 — user wants featured image attached
After draft is created and user confirms they want the image attached:
**Claude:** "Save the image from the email to your Downloads folder, then tell me the exact filename."
**User:** "it's `hero-shot.jpeg`"
**Claude:** [generates presigned URL, hands over ready-to-paste curl]
**User:** [runs curl, confirms]
**Claude:** [calls `distro_document_create`, optionally cleans up placeholder in body, returns edit link]
### Example 3 — user wants a light edit rather than faithful
**User:** "Ingest my Feb investor update — light edit — and draft it on Liqquidity's Newslode."
**Claude:** Same workflow, but in Step 2 the restructuring includes tightening awkward transitions and sharpening the lede while preserving voice. Do not silently rewrite — keep changes minimal and preserve distinctive phrasing.
## When not to use this skill
- **Link-style posts** (user shares a URL, not an email) → use `distro-link-post` instead.
- **Longform original essays** the user wrote directly (not via email) → just use `distro_content_publish` directly; no ingest pipeline needed.
- **Forwarded newsletters that are really link collections** → these are usually better handled as multiple link-posts (one per link) via `distro-link-post`, not as a single ingested story.
- **Wire-service / Distrowire posts** — different formatting requirements.
When to use
You wrote something in email that deserves a second life as a published story (investor updates, field notes, newsletters)
You want the editorial date to match when you wrote the email, not when you republished it
You have inline images in the email you'd like as the story's featured image
When not to use
You just have a URL to share → use distro-link-post instead
You're writing original longform directly on Distro → use
distro_content_publishstraight through, no ingest pipeline neededYou're sending to Distrowire (corporate wire service) → different formatting conventions
Feedback
This is v1.0.0. Edge cases or suggestions → reply or ping Brad.