A skill you've written is only useful to other people if they can get it. Emailing a SKILL.md around works once; a registry that both humans and agents can read from works every time. The problem is that those two audiences want opposite things from the same page — a person wants an intro that explains what the thing does, and an agent wants raw, unmangled YAML.
distro-skill-publish files a skill to a Distro newslode as one story that serves both. Human-readable intro, prerequisites, feature list and install steps at the top and bottom; the canonical SKILL.md embedded verbatim in a four-backtick fenced code block in the middle. People read around the fence, agents extract what's inside it.
The four-backtick fence is doing real work. An earlier design made the story body be the SKILL.md, which broke twice over: Distro's Tiptap converter mangled the YAML frontmatter, turning --- sentinels into * * *, and anyone browsing the newslode saw a wall of YAML instead of a readable page. Fencing the source protects it from the converter and keeps the rest of the story human. Four backticks rather than three because SKILL.md files routinely contain their own code blocks, which would otherwise close the wrapper early.
What you'll need
- The Distro Publisher MCP connected, for
distro_content_publish,distro_content_update, and the newslode tools. - A skill source: a draft in the conversation, a SKILL.md on disk, an uploaded
.skillpackage, or an existing story to fork.
What it does
- Drafts by default. Always files as a draft unless you explicitly say go live, so you can review in the editor first.
- Never guesses the destination. If you don't name a newslode, it asks rather than defaulting to one.
- Validates before publishing. Checks for frontmatter, a
name:, adescription:, and a body, and flags what's missing instead of silently publishing a broken skill. - Composes a real page. Intro, prerequisites, feature list, install instructions, the fenced source, when-to-use and when-not-to-use, and a feedback line.
- Writes install instructions that work. Leads with the
.skill+ Save skill path, which is the only reliable one-click install on Claude Desktop, and notes the direct filesystem path for Claude Code users. - Checks facts instead of asserting them. Newslode visibility is read from
isPublicviadistro_newslode_getrather than hardcoded — a lesson learned the hard way when stale boilerplate put a wrong sentence on a live page. - Updates in place. A new version of an existing skill edits the canonical story with
significantUpdate: truerather than creating a second one and fragmenting the registry. - Documents the extraction side. Ships the regex a future Claude needs to pull the SKILL.md back out, tolerant of both three- and four-backtick fences.
Install the skill
Ask Claude:
"Please fetch the distro-skill-publish 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-skill-publish.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.
Claude Code users: you have a direct path — ask Claude to drop the unzipped folder into ~/.claude/skills/distro-skill-publish/ 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-skill-publish, 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-skill-publish
description: Use this skill whenever the user asks to publish, post, file, register, or upload a Claude skill (a SKILL.md file, a .skill package, or a skill the user has written or is iterating on) to a Distro newslode. Triggers on requests like "publish this skill to [newslode]", "file the [name] skill on Distro", "post this SKILL.md to my skill registry newslode", or any request that pairs a skill source file with a Distro publishing action. Publishes a unified story that serves both humans (intro, features, install instructions) and agents (canonical SKILL.md in a fenced code block, retrievable via distro_content_to_markdown). Always drafts by default.
---
# Distro skill-publish skill
Publish a Claude skill to a Distro newslode as a draft story, structured so that both humans reading the newslode and future Claude instances retrieving it can get what they need from the same artifact.
## Core design principle
**One story per skill, serving two audiences.**
The story has a human-readable intro and install guide at the top and bottom, with the canonical `SKILL.md` embedded verbatim in a four-backtick fenced code block in the middle. Humans get context; agents extract the SKILL.md. The four-backtick fence is important because SKILL.md files commonly contain their own triple-backtick blocks — the outer four-backtick fence prevents ambiguous parsing.
This replaces an earlier "body IS the SKILL.md, no wrappers" approach, which had two problems: (1) the HTML round-trip through Distro's Tiptap converter mangled YAML frontmatter (`---` sentinels got converted to `* * *`); (2) humans browsing the newslode saw a wall of YAML and technical prose rather than a readable intro. Wrapping the SKILL.md in a fenced code block protects it from the converter and keeps the rest of the story human-friendly.
## Default behavior
- **Status:** Always `draft` by default. The user can override by explicitly saying "publish live" or "go live immediately." After the draft is filed, the user can review in the Tiptap editor and then either publish it themselves or ask this skill to flip the status.
- **Newslode:** The user must specify. Do not default to a newslode — ask if not provided.
## Workflow
### Step 1 — Locate the skill source
The skill source might come from several places. In order of likelihood:
1. **The current conversation** — the user has been drafting/iterating on a SKILL.md, and the latest version is visible in context. Use that.
2. **A file on disk** — e.g., `/mnt/skills/user/some-skill/SKILL.md` (if the skill is installed in the user's Claude Desktop, it's mirrored here read-only) or `/home/claude/some-skill/SKILL.md`. Read it.
3. **A file the user uploaded** — e.g., `/mnt/user-data/uploads/SKILL.md` or a `.skill` package. Read it.
4. **A previously published story on another Distro newslode** — the user wants to republish or fork. Fetch via `distro_content_to_markdown`, then extract the fenced SKILL.md block (see extraction note at the bottom of this file).
If it's ambiguous, ask which source to use. Do not guess.
### Step 2 — Validate the SKILL.md
Before publishing, do a structural sanity check:
- Must start with YAML frontmatter delimited by `---` lines
- Frontmatter must contain a `name:` field (kebab-case skill identifier)
- Frontmatter must contain a `description:` field (the triggering text)
- Body must exist after the frontmatter
Extract for later use:
- `name:` value from frontmatter → used in folder names, install instructions, and the canonical title
- `description:` value → can inform the preview text
If any required piece is missing, flag it to the user before publishing. Don't silently publish broken skills.
### Step 3 — Resolve the destination newslode
If the user named the newslode by title (e.g., "Distro Skills"), call `distro_newslode_list` to resolve it to a `publicationId`. (`distro_newslode_list` is the only tool that surfaces private newslodes; `distro_newslode_get` returns one newslode's detail including its `isPublic` flag.)
If the user gave an ID directly, use it.
If the user didn't specify a newslode at all, **ask**. Do not default to any particular newslode.
### Step 4 — Compose the story
The story has six sections in this order:
**1. Intro** — 2–3 paragraphs explaining what the skill does in plain language. Lead with the user problem, not the feature list. Name the skill in **bold** on first reference using its kebab-case identifier.
**2. What you'll need** — runtime prerequisites for *using* the skill (not installing it). If the skill calls MCP tools, name them — "Distro Publisher MCP" or "Gmail connector," using plain terms. Skip this section if the skill has no runtime prereqs beyond a vanilla Claude.
**3. What it does** — bulleted feature list, written for humans. Draw the material from the SKILL.md body but rewrite for reader-friendliness — the frontmatter `description:` field is keyword-optimized for triggering, not for reading.
**4. Install the skill** — use this exact structure:
> Ask Claude:
>
> > "Please fetch the [skill-name] skill from Distro Skills and give me a `.skill` file I can install."
>
> Claude will pull this story, extract the SKILL.md from the fenced block below, and package it as `[skill-name].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.*
>
> Note: if the destination newslode is private, add a sentence saying so. Do not assert that a newslode is private without checking — `distro_newslode_get` reports `isPublic`, and Distro Skills (pub 114) is public.
>
> **Claude Code users:** you have a direct path — ask Claude to drop the unzipped folder into `~/.claude/skills/[skill-name]/` 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 `[skill-name]`, zip it (folder as zip root), and upload through Settings → Capabilities → Skills → *Upload skill*.
**Important:** the ".skill + Save skill button" path is the cleanest one-click install on Claude Desktop and Claude.ai. Writes to `/mnt/skills/user/` from a chat sandbox do NOT register as installed skills — the file may appear in the mirror but won't be indexed by the app, won't show in the Settings UI, and won't trigger in future sessions. The `.skill` package flowing through `present_files` is what surfaces Claude Desktop's native "Save skill" affordance; that's the reliable install path for non-Code clients.
**5. The skill source** — heading says exactly "The skill source" so agents can locate it. Preceded by one sentence: "This is the canonical `SKILL.md`:". Then a four-backtick fenced code block (```` ```` ````) containing the raw SKILL.md verbatim, with `markdown` as the language hint. The extra backticks matter because SKILL.md files often contain their own triple-backtick code blocks (shell examples, code snippets) — a four-backtick outer fence prevents those from prematurely closing the wrapper.
Nothing else inside the fence. No commentary, no wrappers, no sentinels — just the raw SKILL.md. The fence itself is the wrapper.
Note: Distro's Tiptap converter may normalize a four-backtick fence down to three backticks if the inner content contains no triple-backtick blocks. This is safe — without inner triple-backticks, a three-backtick fence can't be prematurely closed. The extraction-side regex (documented below) tolerates both fence widths.
**6. When to use / When not to use** — short bullet lists, written for humans. Restates the scope from the SKILL.md body but in reader-friendly language.
**7. Feedback** — one line: "This is v<version>. Edge cases or suggestions → reply or ping [author]."
### Step 5 — Publish as draft
Call `distro_content_publish` with:
- `publicationId`: resolved from Step 3
- `title`: human-readable form of the skill name. Examples:
- `distro-email-ingest` → "Distro email ingest skill"
- `distro-link-post` → "Distro link post skill"
- `brads-voice` → "Brad's voice skill"
The word "skill" at the end aids feed readability. If the user gives a preferred title, use that.
- `content`: the full composed story from Step 4, as markdown
- `contentFormat: "markdown"` — explicit, not auto-detected
- `preview`: 150–250 chars. Reader-friendly summary of what the skill does. Do not lead with version numbers — lead with the user-facing value.
- `author`: the user's name or the authenticated default
- `status: "draft"` — always draft unless user explicitly says go live
Capture the returned `contentId` for the edit link.
### Step 6 — Return the edit link and next steps
Tell the user concisely:
- That the draft was filed (content ID, newslode name)
- The Tiptap editor URL: `https://app.distro.media/stories/<contentId>`
- That it's a draft — they can review before publishing, or tell you to flip status to live
## Updating an existing skill story
If the user asks to update a skill already published on a newslode (e.g., "bump distro-email-ingest to v1.1.0 with this change"):
1. Locate the existing story via `distro_content_list` filtered to the destination newslode. Match by title or by searching for the kebab-case `name:` in the fenced block.
2. Call `distro_content_update` with:
- `contentId`: the existing story's ID
- `content`: the updated full story body
- `contentFormat: "markdown"`
- `preview`: updated if needed
- `significantUpdate: true` — sets `contentUpdatedAt` so readers see the update timestamp
- Leave `status` unchanged unless the user asks to flip it
Do **not** create a new story for an updated version of the same skill — that fragments the registry. One canonical story per skill; update the body in place.
## Extracting the SKILL.md from a published story
If a future Claude is installing a skill from a Distro story (the "easy way" install path), the workflow is:
1. `distro_content_to_markdown` on the contentId → returns the full story body
2. Find the first outer fenced code block with `markdown` as the language hint. **The fence may be three or four backticks** — Distro's Tiptap converter normalizes fence length based on whether the inner content contains triple-backtick blocks. Use a regex that tolerates both.
3. Extract everything between that fence and the matching closing fence (same backtick count)
4. The extracted content is the canonical SKILL.md, ready to write to `~/.claude/skills/<name>/SKILL.md`
5. Parse the frontmatter `name:` field to determine the folder name
A resilient Python regex that works for both cases:
```python
re.compile(r"^(`{3,})(markdown)?\n(.*?)\n\1\s*quot;, re.DOTALL | re.MULTILINE)
```
The captured content (group 3) is the SKILL.md. The backreference `\1` ensures the closing fence has the same length as the opening fence.
## Distro tool quirks to remember
- `distro_content_publish` and `distro_content_update` both accept `contentFormat: "markdown"`. Always pass it explicitly rather than relying on `"auto"` — the YAML frontmatter inside the fenced block could confuse the auto-detector.
- `distro_newslode_list` is the only tool that surfaces private newslodes. Before writing anything about a newslode's visibility into the install instructions, check `isPublic` with `distro_newslode_get` rather than assuming.
- `distro_content_to_markdown` returns the story body as markdown. The four-backtick fence survives the HTML → markdown round-trip cleanly, but raw frontmatter at the top level of a story body does not — another reason the fence is essential.
- **Newly created publications may hit a permissions cache**: if the user just created a new newslode via `distro_newslode_create` and a subsequent publish to it fails with "API key does not have access to this publication," the fix is to disconnect/reconnect the Distro MCP to force a fresh permissions fetch. Start a new chat after reconnecting.
- **`distro_content_delete` had a server-side bug as of April 2026** (returning "Cannot read properties of undefined (reading 'id')"). If a delete fails, don't loop — flag to the user and leave the story as a draft.
## What Claude can and cannot do for skill installation
- **Can:** draft the SKILL.md, package it as a `.skill` file via `skill-creator`'s `package_skill.py`, and present it via `present_files`. Claude Desktop's UI recognizes `.skill` files in chat and surfaces a one-click **Save skill** button — that's the cleanest install path for non-Code clients.
- **Cannot:** write directly into the user's installed skills registry by dropping files into `/mnt/skills/user/`. That path looks writable from inside the sandbox, but writes land in a sandbox-local overlay and are NOT indexed by Claude Desktop's skill engine. The files won't show in Settings → Capabilities → Skills and won't trigger in future sessions.
- **Can (Claude Code only):** write directly to `~/.claude/skills/` on the user's actual filesystem. Claude Code's skill engine reads that path at session start, so a write + session restart is a real install.
In install instructions, lead with the `.skill` + Save skill path for Claude Desktop and Claude.ai users. Mention the direct filesystem path only for Claude Code. Do not claim any form of "automatic install" for Claude Desktop — the Save skill button is still a user click, even if it's a simpler click than going through Settings.
## Examples
### Example 1 — first publish of a new skill
**User:** "Publish the distro-email-ingest skill we just drafted to Distro Skills."
**Claude:**
1. Uses the SKILL.md draft from the current conversation context
2. Validates: has frontmatter, `name: distro-email-ingest`, has `description:`, has body ✓
3. Resolves "Distro Skills" → `publicationId: 114`
4. Composes the six-section story (intro, prereqs, features, install, source in four-backtick fence, when to use / not use, feedback)
5. Title: "Distro email ingest skill"; preview: short human summary of value
6. `distro_content_publish` with `contentFormat: "markdown"`, `status: "draft"`
7. Returns content ID, edit link, notes draft status
### Example 2 — user explicitly wants live
**User:** "File the distro-link-post skill on Distro Skills — publish it live, not draft."
**Claude:** Same workflow, but `status: "published"`. Confirms in the response that it's live.
### Example 3 — update existing skill
**User:** "Update the distro-email-ingest skill story on Distro Skills — here's v1.1.0 with the new image-upload tweaks."
**Claude:**
1. `distro_content_list` on pub 114, finds existing story
2. Composes the updated full story (intro, prereqs, etc. — not just a SKILL.md swap) with the new SKILL.md in the fenced block
3. `distro_content_update` with `significantUpdate: true`
4. Returns edit link; notes what changed
## When not to use this skill
- **Publishing a regular story** (article, essay, newsletter) to a newslode — just use `distro_content_publish` directly.
- **Publishing a link-style post** (a URL with a hot take) — use `distro-link-post` instead.
- **Publishing an ingested email as a story** — use `distro-email-ingest` instead.
- **Creating the newslode itself** — use `distro_newslode_create` directly; this skill only publishes stories into existing newslodes.
When to use
- You've written a skill and want it installable by other people, or by your own future sessions.
- You've revised a skill already on a newslode and want the canonical story updated in place.
- You want a skill registry that agents can read from as reliably as humans can.
When not to use
- A regular story — article, essay, newsletter — just use
distro_content_publishdirectly. - A link-style post with a hot take — use
distro-link-post. - An ingested email — use
distro-email-ingest. - Creating the newslode itself — use
distro_newslode_createdirectly.
Feedback
This is v1.1. Fixed in this version: four call sites referenced distro_publication_list and distro_publication_create, which do not exist — the real tools are distro_newslode_list and distro_newslode_create. Also removed hardcoded boilerplate claiming Distro Skills is private, replaced with a rule to check isPublic rather than assert it. Edge cases or suggestions → reply or ping Bradley Keoun.