Skip to content

Script per-server release notes with PRs and a reporter Thanks section (release:notes) #5056

Description

@cliffhall

Problem

Our release notes don't say what changed or who helped. The current latest Release, 2026.8.31, reads in full:

# Release : v2026.8.31

## Updated packages
- @modelcontextprotocol/server-filesystem@2026.8.31
- @modelcontextprotocol/server-memory@2026.8.31
- @modelcontextprotocol/server-sequential-thinking@2026.8.31
- @modelcontextprotocol/server-everything@2026.8.31

It has no PRs, no issues and no contributors. A user of one server can't tell what changed in it, and GitHub's Contributors strip stays empty because nobody is @-mentioned.

The release skill's step 5a (.claude/skills/release/SKILL.md, from #4873) improves on this with a hand-run shell recipe. It builds a Packages table, GitHub's generated What's Changed list and one repo-wide ## Thanks for helping us improve section. That still has two gaps:

  1. It is one flat list for the whole repo. We publish only the servers that changed, each at its own version, so a reader of a release wants to know what changed in the server they use. A flat What's Changed list mixes git fixes, everything fixes and CI chores together, and a single Thanks section doesn't say which server a reporter's issue improved.
  2. It is an untested shell recipe pasted into a skill. The inspector replaced the same kind of recipe with a tested script (Script the release notes (generated list, ledger, known issues, reporter thanks) and release creation in the release skill inspector#2550, implemented in feat(scripts): script the release notes and Release creation (release:notes) inspector#2591) after finding that a recipe like this can fail silently. For example, a failed permission lookup can read as "community member", or a rate-limited call can drop a PR.

We now follow the same issue-only contribution model as the inspector (docs/contribution-model.md): outside PRs are closed, and the work is done by maintainers from community-filed issues. PR authors are therefore almost always maintainers, and the people to credit are the issue reporters, per server.

Change

Add scripts/release-notes.mjs (npm run release:notes), modeled on the inspector's helper and adapted to a multi-package release, and use it in the release skill's step 5.

Notes layout

## Packages
| Package | Version | Registry | In this release |
| … one row per package, as step 5a builds today … |

## @modelcontextprotocol/server-git 2.0.0      ← one section per PUBLISHED package
### What's Changed
* git: git_show prints Python reprs … by @maintainer in #NNNN
* …
### Thanks for helping us improve
This release addresses issues reported by these community members. Thank you for taking the time to file them:
* @reporter (#4998, #4997)

## @modelcontextprotocol/server-everything 2.0.0
### What's Changed
…
### Thanks for helping us improve
…

## Repository                                    ← PRs that touch no server (CI, docs, skills)
### What's Changed
…

Release ledger: [<merge branch>](<ledger url>)

## Known issues                                   ← only when passed in
…

Assembling it

  1. Packages table. Port step 5a's table (the manifest from scripts/release-manifest.mjs, checked against the npm and PyPI registries). An UNKNOWN registry answer aborts the run.
  2. The PR list comes from POST repos/{owner}/{repo}/releases/generate-notes, from the previous published Release to the merge commit ($MERGE_SHA). Use the same previous-tag rule as step 5a: ask GitHub, and never sort the mixed CalVer, milestone and per-package tags.
  3. Bucket each PR by server using the files it changes: src/<dir>/ maps to that server's package. A PR that touches several servers is listed under each one, and a PR that touches none goes under Repository.
  4. Emit a section only for a package this release publishes (the Packages table's published rows). Any PR left over, such as one touching a server whose version didn't change, goes under Repository, so no PR from the generated list is dropped.
  5. Thanks, per section. For every PR in a section, take the issues it closes (closingIssuesReferences, paginated, plus Closes/Fixes/Resolves #N in the body) and credit each issue's author. Exclude maintainers (admin/maintain/write), bots, deleted accounts, PR numbers and other repos' issues. List one line per person, most issues first, as * @user (#1, #2). Leave the subsection out when nobody remains. A reporter who filed issues against two servers appears in both sections, each listing only that server's issues.
  6. Outside-PR credit. When the repo closes an outside PR and files an issue for the fix, that issue's body credits the PR's author, and step 5a currently adds those people by hand. Settle on a machine-readable credit line for those issues (for example, Reported-by: @user or Credit: @user) and have the script credit it, or keep this as a documented manual step. Decide which in the PR.
  7. The ledger line and ## Known issues come from arguments (--ledger-url, --merge-branch, repeatable --known-issue "<markdown>"), since what counts as a known issue is a maintainer's judgment.

Modes and safety (as in the inspector helper)

  • The default run is a preview that prints the notes and creates nothing. --draft creates a draft Release. Publishing stays a deliberate maintainer act, because publishing triggers release.yml. The helper must never publish implicitly, and if it offers --publish at all, that flag must be explicit.
  • Create the Release with --target "$MERGE_SHA" (the SHA, not main) and the milestone tag vX.Y.Z, matching step 5b.
  • Fail fast. Check every gh/git call. A non-zero exit, GraphQL errors, a missing node or an unknown permission value throws, so the run aborts before anything is created.

Acceptance

  • scripts/release-notes.mjs with a sibling scripts/release-notes.test.mjs covering per-server bucketing (one server, several servers, none), sections only for published packages, the Repository catch-all dropping no PR, per-section Thanks with every exclusion, keyword parsing, pagination, and a failed lookup at each stage aborting with nothing created
  • npm run release:notes added to package.json, and the scripts/ listings (README, AGENTS.md, the project-structure skill) updated
  • The release skill's step 5a uses the helper (preview, then --draft) in place of the shell recipe, and step 5b publishes the draft
  • A preview run for the v2.0.0 release (First semver release: TypeScript servers at 1.0.0 and Python servers at the next CalVer, the frozen legacy-era servers #4968) produces per-server sections whose Thanks lists match the milestone's closed community-filed issues

Activity

  1. added this to the v2.0.0 milestone on Oct 5, 2026
  2. added
    choreMaintenance: deps, build tooling, CI, cleanup — no user-facing behavior change
    on Oct 5, 2026
  3. self-assigned this
    on Oct 7, 2026
  4. added a commit that references this issue on Oct 11, 2026
    b1df103
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

choreMaintenance: deps, build tooling, CI, cleanup — no user-facing behavior changev2

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions