Publish Your First Skill

Ship a skill from your laptop to your AI Coach catalog in five steps. Free or paid (one-time fee, 60/40 split). Anyone with the AI Coach MCP server can then discover, install, and run your skill.

What gets published

A skill is a gzipped tarball with a fixed layout. v1 enforces markdown only — no executable scripts, no compiled binaries. Anything outside the allowlist is rejected at finalize.

bundle.tar.gz
├── SKILL.md             # required, YAML frontmatter + body
├── LICENSE*             # optional
├── NOTICE*              # optional
├── references/          # optional, .md only
│   └── playbook.md
└── assets/              # optional, .json .txt .md .png .svg only
    └── config.json

Bundle is capped at 5 MB compressed, 20 MB decompressed. The SHA-256 of the tarball is your content hash — it's shown to buyers and printed in the install command.

SKILL.md frontmatter contract

name must match the skill's slug (kebab-case, 1–64 chars). description is the one-liner shown in search results — max 200 chars.

---
name: code-review-coach
description: Senior-eng code review with focus on readability + bugs
---

# Code Review Coach

Walk through the diff like a senior reviewer would...

Prerequisites

  1. Sign in to AI Coach and enable publisher mode (one POST to /api/publisher/become or the toggle on your account page).
  2. Mint an API key with the publisher:write scope. Key management →
  3. Your publisher slug is derived from your GitHub username. Reserved handles (aicoach, anthropic, trademarks, etc.) are blocked — contact support if affected.

Step 1 — Create the skill record

Starts in status: 'draft', visibility: 'private'. Goes 'published' automatically when your first version finalizes.

curl -X POST https://aicoach.pw/api/publisher/skills \
  -H "Authorization: Bearer mcp_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "code-review-coach",
    "displayName": "Code Review Coach",
    "descriptionShort": "Senior-eng code review with focus on readability + bugs",
    "visibility": "private",
    "pricingModel": "free"
  }'

Response includes skill.id — capture it as $SKILL_ID for the next steps.

Step 2 — Build + hash the bundle

tar -czf bundle.tar.gz SKILL.md references/ assets/
shasum -a 256 bundle.tar.gz

Step 3 — Reserve a version

Semver is enforced: every new version must be strictly greater than the last. You're committing to the SHA-256 here — the actual upload in step 4 has to match exactly.

curl -X POST https://aicoach.pw/api/publisher/skills/$SKILL_ID/versions \
  -H "Authorization: Bearer mcp_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "semver": "0.1.0",
    "sha256": "abc123…",
    "sizeBytes": 4096,
    "changelog": "Initial release"
  }'

Step 4 — Upload the bundle

PUT the raw tar.gz body to the uploadUrl returned by step 3. We re-hash on receipt and reject any mismatch.

tar -czf bundle.tar.gz SKILL.md references/ assets/
SHA=$(shasum -a 256 bundle.tar.gz | cut -d' ' -f1)
SIZE=$(stat -f%z bundle.tar.gz)   # macOS; use stat -c%s on Linux

curl -X PUT https://aicoach.pw/api/publisher/skills/$SKILL_ID/versions/0.1.0/bundle \
  -H "Authorization: Bearer mcp_xxx" \
  -H "Content-Type: application/gzip" \
  --data-binary @bundle.tar.gz

Step 5 — Finalize + publish

Finalize runs the server-side validator: gzip integrity, file allowlist, SKILL.md frontmatter shape, slug match. On success the version flips to 'finalized' and the skill's latestVersionId pointer moves. On failure you get a structured report — fix the bundle, re-upload to the same version, finalize again. Failed finalizes don't burn the version slot.

curl -X POST https://aicoach.pw/api/publisher/skills/$SKILL_ID/versions/0.1.0/finalize \
  -H "Authorization: Bearer mcp_xxx"

Make it discoverable

Drafts and private skills are visible only to you. Flip to 'unlisted' for share-by-link, 'public' for the marketplace + MCP search.

curl -X PATCH https://aicoach.pw/api/publisher/skills/$SKILL_ID \
  -H "Authorization: Bearer mcp_xxx" \
  -d '{ "visibility": "public" }'

Charge for it

Switch pricingModel to 'one_time' with a priceCents between 100 ($1.00) and 100,000 ($1000.00). Buyers pay once via Stripe Checkout; you keep 60%, the platform keeps 40%. USD only in v1; Stripe handles FX for non-USD cards.

curl -X PATCH https://aicoach.pw/api/publisher/skills/$SKILL_ID \
  -H "Authorization: Bearer mcp_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "pricingModel": "one_time",
    "priceCents": 999,
    "visibility": "public"
  }'

Earnings accumulate in your publisher wallet. Payouts in v1 are manual (Stripe Connect is on the v2 roadmap) — contact support to request a payout once the balance crosses your threshold.

Publishing updates

Repeat steps 2–5 with a bumped semver. Old versions stay downloadable for buyers who pinned to them — never republish under the same number. Soft-deprecate an old version with DELETE /api/publisher/skills/$SKILL_ID/versions/$SEMVER; existing buyers keep access, new buyers get the next live version.

Common errors

  • must_bump_version — your semver isn't strictly greater than the last finalized one.
  • hash_mismatch — the SHA-256 you committed in step 3 doesn't match the bytes you PUT in step 4. Re-hash and try again.
  • disallowed_path — bundle contains a file outside the markdown-only allowlist (e.g. scripts/, .sh, .js).
  • frontmatter_name_mismatch — your SKILL.md's name: doesn't equal your skill slug.
  • too_large — bundle exceeds 5 MB. Trim assets or split into multiple skills.
  • reserved_publisher_slug — your GitHub handle clashes with a reserved name. Contact support.

Next