Cutting a release
SkillDocs & knowledgeLets your agent run the full release process for FastMCP, from changelog notes to tagging and publishing.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Cutting a release skill
About this capability
Cut a FastMCP release end to end. Use when the maintainer says "cut a release", "prep a patch", "ship 4.x.y", or asks what a release would contain. Covers the notes preview, the title pun, the docs changelog PR that must land before the tag, the tag itself, the publish fan-out, and verifying gofastm
What this skill tells your AI
The instructions your AI receives, as published by prefecthq/fastmcp in .claude/skills/release/SKILL.md and read by ahel’s review.
A release is a tag on the branch that owns the line: <branch> below is main for
the current major and release/3.x for 3.x maintenance. Every step runs against that
branch, including the docs PR, so a maintenance entry lands in the maintenance
branch's own changelog above its newest 3.x entry. The tag triggers Publish fastmcp-slim to PyPI;
its success fans out to fastmcp-tasks, fastmcp-remote, and fastmcp, and the
fastmcp publish job opens the published-docs PR that makes gofastmcp.com reflect
the release. Two hand-maintained docs files mirror every release and must be on the
tagged commit, so the docs PR always lands first. PyPI releases are immutable; a bad
one gets a follow-up patch, never a re-tag.
Procedure
-
Preview what's in it.
git fetch origin && git log v<prev>..origin/<branch> --format='%h %s' gh api -X POST repos/PrefectHQ/fastmcp/releases/generate-notes \ -f tag_name=v<new> -f target_commitish=<branch> -f previous_tag_name=v<prev> --jq .bodyRead the two most recent releases for voice:
gh release list -L 5, thengh release view <tag>on each. -
Propose titles. Titles are
v<version>: <pun>, pun on the release's main theme from the release preview. Check prior titles withgh release list --repo PrefectHQ/fastmcp --limit 60to avoid repeats and keep the options on brand: two to four words, a familiar phrase bent toward the theme ("Cache Me If You Can", "Trust, but Proxy", "Come Back Any Time"), and a running family for a major's prereleases ("Fourst Contact", "Group Effourt"). Offer several; the maintainer picks. Draft the handwritten notes at the same time: one or two sentences for a patch, narrative prose for a point release. Get sign-off on both before anything is pushed. -
Write the notes file outside the repository (a temp directory), intro only. The release title is the heading, so the file has none.
-
Docs entries. Branch
docs/changelog-<new>fromorigin/<branch>and insert both blocks (the last argument is the branch the notes are generated against):uv run .claude/skills/release/scripts/changelog_entry.py v<new> v<prev> "<pun>" /tmp/notes.md <branch>Validate, and repeat until it reports no errors; a parse error names the file and line:col:
cd docs && npx --yes mint@latest broken-links(
@lateston purpose: the hosted Mintlify build is always current.) Commit, push, open the PR titleddocs: add v<new> changelog entrieswith base<branch>and a one-line body, and merge it. Branch policy needs--adminon a maintainer-driven release. The docs PR itself appears in the GitHub notes but not in the mirror; that is expected. -
Tag, immediately after the docs PR merges. Re-run the step 1 preview first; if
<branch>moved, delete the entry blocks from both docs files, re-run the helper (it refuses to insert a label that already exists), and land that as a follow-up docs PR before tagging.gh release create v<new> --target <branch> --title "v<new>: <pun>" \ --generate-notes --notes-start-tag v<prev> --notes-file /tmp/notes.mdv<prev>is the last stable tag on that branch. The compare link at the bottom of the created release must readv<prev>...v<new>. -
Watch the fan-out. The slim run is keyed to the tag; the others are
workflow_runevents on the default branch, so select them by workflow name. The slim run appears within a minute of the tag; thefastmcprun is queued the moment slim succeeds, so watch slim to completion first and the newestfastmcprun is the right one.gh run watch $(gh run list --event release --branch v<new> --limit 1 --json databaseId --jq '.[0].databaseId') --exit-status gh run watch $(gh run list --workflow "Publish fastmcp to PyPI" --limit 1 --json databaseId --jq '.[0].databaseId') --exit-statusConfirm each package with
curl -fsS https://pypi.org/pypi/<pkg>/<new>/json | jq .info.version; the unversioned endpoint lags a few minutes. -
Publish docs (
mainreleases only; maintenance releases skip this step and leave gofastmcp.com on the current major). When thefastmcprun finishes, its last job has openedPublish FastMCP v<new> docsagainstpublished-docs:gh pr list --base published-docs --state open --json number,title gh pr merge <n> --merge --adminMerging pushes
published-docs, which runs theDeploy docsworkflow: it asks Mintlify's admin API for a deployment of the tip and waits for the verdict. That run is the signal that the site changed; the merge alone proves nothing.gh run watch $(gh run list --workflow "Deploy docs" --branch published-docs --limit 1 --json databaseId --jq '.[0].databaseId') --exit-statusA red run prints Mintlify's summary and logs; fix the cause on
mainand publish again. If Mintlify's queue is backed up (status.mintlify.com), the run waits up to 40 minutes; re-run it withgh workflow run deploy-docs.yml --ref published-docsonce their incident clears.Verify through the markdown endpoints, which bypass the HTML edge cache:
curl -fsS https://gofastmcp.com/changelog.md | grep -c "<pun>", and the same forupdates.mdand any page the release touched.
Publishing docs by hand
Used when a deploy failed and was fixed on main, or for a docs change outside a
release. The publication commit must match main's tree exactly.
git fetch origin main published-docs
git checkout -b publish-docs-<topic> origin/published-docs
git read-tree -m -u origin/main && git commit -m "docs: publish main @ $(git rev-parse --short origin/main)"
git diff --quiet HEAD origin/main && echo "tree matches main"
git push -u origin publish-docs-<topic>
gh pr create --base published-docs --title "docs: publish main to published-docs (<what>)" --body "<one line>"
gh pr merge <n> --merge --admin
Then watch the Deploy docs run and verify the endpoints as in step 7.
Template: notes file for a patch
`ClientGroup` now reference-counts its context the way `Client` does, so entering a
connected group from a nested block or a concurrent task reuses the existing
connections instead of raising.
Gotchas
--generate-notescopies PR titles verbatim; a title containing<1,{, or}breaks MDX.scripts/changelog_entry.pywraps those in backticks; the validator in step 4 catches anything it misses.- Mintlify's GitHub App also deploys on its own, but it only rebuilds files
changed since the last commit it recorded, and it records commits it failed on
or skipped. Only the
Deploy docsrun's verdict counts; its API-triggered deployment is what brings a page a skipped deploy left stale back in line. - Without
--notes-start-tag, a prerelease tag becomes the changelog start and the PR list is silently truncated. - Maintenance releases publish packages and notes but never repoint
published-docs; their changelog entries go on the maintenance branch under the matching major section.
Check before finishing
Each of these prints a count above zero:
curl -fsS https://pypi.org/pypi/fastmcp/<new>/json | grep -c '"version"',
curl -fsS https://gofastmcp.com/changelog.md | grep -c "<pun>",
curl -fsS https://gofastmcp.com/updates.md | grep -c "<pun>".
Signals
- GitHub stars
- 28k
- Forks
- 2k
- Last commit
- Sep 2026
- Hacker News mentions
- 20
Advanced
- Catalog kind
- skill
- Gateway key
release-prefecthq- Source
- github.com/prefecthq/fastmcp