From 412b0109543116eae88090142b2d7e0d3682a68f Mon Sep 17 00:00:00 2001 From: Lloyd Watkin Date: Sat, 19 Sep 2026 10:03:20 +0100 Subject: [PATCH 1/2] Attach the Claude Desktop bundle to published releases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bundle was only ever a CI build artifact, so installing it meant digging through the Actions tab for a green run on the right commit. The release workflow now packs it and uploads activeadmin-mcp-X.Y.Z.mcpb as an asset on the release that produced it. The release tag becomes the source of truth for the bundle version too: the workflow writes it into mcpb/manifest.json and mcpb/package.json before packing and commits the bump back alongside version.rb. The manual bump the README asked for had already been missed once — the bundle sat at 0.0.3 against a 0.0.4 gem — and Claude Desktop reads the manifest version to detect upgrades, so a stale one means colleagues are never offered the update. The proxy tests and the pack run before the gem is pushed, so a broken bundle stops the release rather than trailing a published gem, and the upload uses --clobber so re-running the workflow on a release replaces the asset instead of failing. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/release.yml | 53 +++++++++++++++++++++++++++-------- CHANGELOG.md | 10 +++++++ README.md | 24 +++++++++------- RELEASING.md | 26 +++++++++++++---- 4 files changed, 86 insertions(+), 27 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4838af8..afdbe5b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,11 +1,13 @@ name: Release # Publishes the gem to RubyGems.org via Trusted Publishing (OIDC) whenever a -# GitHub Release is published. The release tag (e.g. v0.1.1) is the source of -# truth for the version: the workflow writes it into lib/activeadmin_mcp/version.rb, -# builds and publishes the gem, then commits the version bump back to the -# default branch. No API key is stored — RubyGems verifies this workflow via -# OIDC. See RELEASING.md for the one-time RubyGems setup. +# GitHub Release is published, and attaches the Claude Desktop bundle to the +# release. The release tag (e.g. v0.1.1) is the source of truth for the +# version: the workflow writes it into lib/activeadmin_mcp/version.rb, +# mcpb/manifest.json and mcpb/package.json, publishes the gem, uploads +# activeadmin-mcp-.mcpb as a release asset, then commits the version +# bumps back to the default branch. No API key is stored — RubyGems verifies +# this workflow via OIDC. See RELEASING.md for the one-time RubyGems setup. on: release: @@ -19,7 +21,7 @@ jobs: runs-on: ubuntu-latest environment: rubygems permissions: - contents: write # commit the version bump back to the default branch + contents: write # upload the release asset, commit the version bumps back id-token: write # exchange the OIDC token with RubyGems steps: @@ -45,15 +47,35 @@ jobs: ruby-version: "4.0.7" bundler-cache: true - - name: Write version file + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version: "22" + + - name: Write version files run: | version="${{ steps.version.outputs.version }}" sed -i -E "s/VERSION = \".*\"/VERSION = \"${version}\"/" lib/activeadmin_mcp/version.rb grep -q "VERSION = \"${version}\"" lib/activeadmin_mcp/version.rb + # The leading quote in the pattern keeps this off "manifest_version". + for json in mcpb/manifest.json mcpb/package.json; do + sed -i -E "s/^(\s*)\"version\": \".*\",$/\\1\"version\": \"${version}\",/" "$json" + grep -q "\"version\": \"${version}\"," "$json" + done - name: Run specs run: bundle exec rspec + - name: Run proxy tests + run: npm test + working-directory: mcpb + + - name: Pack the bundle + run: | + version="${{ steps.version.outputs.version }}" + npx --yes @anthropic-ai/mcpb pack . "../activeadmin-mcp-${version}.mcpb" + working-directory: mcpb + - name: Configure RubyGems credentials (OIDC) uses: rubygems/configure-rubygems-credentials@v2.1.0 @@ -63,15 +85,24 @@ jobs: gem build activeadmin_mcp.gemspec gem push "activeadmin_mcp-${version}.gem" - - name: Commit version bump to default branch + - name: Attach the bundle to the release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + version="${{ steps.version.outputs.version }}" + gh release upload "${{ github.event.release.tag_name }}" \ + "activeadmin-mcp-${version}.mcpb" --clobber + + - name: Commit version bumps to default branch run: | version="${{ steps.version.outputs.version }}" - if git diff --quiet -- lib/activeadmin_mcp/version.rb; then - echo "version.rb already at ${version}; nothing to commit." + versioned_files="lib/activeadmin_mcp/version.rb mcpb/manifest.json mcpb/package.json" + if git diff --quiet -- $versioned_files; then + echo "version files already at ${version}; nothing to commit." else git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add lib/activeadmin_mcp/version.rb + git add $versioned_files git commit -m "Bump version to ${version}" git push origin "HEAD:${{ github.event.repository.default_branch }}" fi diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c18156..a7a3b3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,8 +36,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 underlying exception message is written to the log instead of being sent to the MCP client, where it could disclose SQL, table names or file paths. +- Publishing a GitHub Release now attaches the Claude Desktop bundle to it as + `activeadmin-mcp-X.Y.Z.mcpb`, so installing the bundle no longer means + digging a build artifact out of the Actions tab. + ### Changed +- The release tag is now the source of truth for the bundle's version too: the + release workflow writes it into `mcpb/manifest.json` and `mcpb/package.json` + before packing, and commits the bump back alongside `version.rb`. The gem and + the bundle can no longer drift apart, and nobody has to remember the manual + bump the README used to ask for. + - **Breaking:** the minimum supported Ruby is now 4.0 and the minimum Rails is 7.2, and ActiveAdmin is constrained to `~> 3.5`. Applications outside those must stay on the previous release until they upgrade. Rails 7.2 is the oldest diff --git a/README.md b/README.md index 7eb1a6d..6f04a97 100644 --- a/README.md +++ b/README.md @@ -324,16 +324,20 @@ npx @anthropic-ai/mcpb pack . ../activeadmin-mcp.mcpb ``` That writes `activeadmin-mcp.mcpb` (a zip of `manifest.json`, `package.json` and -`server/index.js`) to the repository root, ready to distribute. Bump `version` -in **both** `mcpb/manifest.json` and `mcpb/package.json` before packing a -release — Claude Desktop uses the manifest version to detect upgrades. - -CI packs the bundle on every push and attaches it as a build artifact, so you -can also download a build from the Actions tab rather than packing it yourself. - -Distribute the file however suits you: an internal file share, a GitHub release -asset, or an S3 bucket. Anyone with the file can install it, but it is inert -without a token. +`server/index.js`) to the repository root. You do not bump the bundle's +`version` by hand: publishing a GitHub Release writes the release tag into +`mcpb/manifest.json` and `mcpb/package.json`, packs the bundle from that, and +attaches `activeadmin-mcp-.mcpb` to the release — so the bundle +version Claude Desktop uses to detect upgrades always matches the gem version. +See [RELEASING.md](RELEASING.md). + +So the bundle for any released version is on that release's page, and CI packs +the bundle on every push and attaches it as a build artifact if you want an +unreleased build from the Actions tab. + +Distribute the file however suits you — pointing colleagues at the release +asset, an internal file share, or an S3 bucket. Anyone with the file can +install it, but it is inert without a token. ### Installing diff --git a/RELEASING.md b/RELEASING.md index 3208af2..ba607f5 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -8,12 +8,21 @@ Releases are driven by **GitHub Releases**. Publishing a release with a `vX.Y.Z` tag triggers `.github/workflows/release.yml`, which: 1. Derives the version from the release tag. -2. Writes it into `lib/activeadmin_mcp/version.rb`. -3. Runs the specs, then builds and publishes the gem to RubyGems via OIDC. -4. Commits the version bump back to the default branch. +2. Writes it into `lib/activeadmin_mcp/version.rb`, `mcpb/manifest.json` and + `mcpb/package.json`. +3. Runs the specs and the MCPB proxy tests, and packs the Claude Desktop bundle. +4. Builds and publishes the gem to RubyGems via OIDC. +5. Uploads `activeadmin-mcp-X.Y.Z.mcpb` as an asset on the release. +6. Commits the version bumps back to the default branch. The release tag is the single source of truth for the version — you do not edit -`version.rb` by hand. +`version.rb` or the bundle's `version` fields by hand, and the gem and the +bundle always carry the same version. + +The bundle is packed before the gem is pushed, so a broken bundle stops the +release rather than following a published gem. Uploading the asset uses +`--clobber`, so re-running the workflow on the same release replaces the asset +rather than failing. ## One-time setup (RubyGems side) @@ -48,8 +57,10 @@ regular trusted publisher automatically — no further RubyGems setup is needed. notes, and click **Publish release**. Publishing the release triggers `.github/workflows/release.yml`, which bumps -`version.rb` to match the tag, runs the specs, publishes the gem to RubyGems via -OIDC, and commits the version bump back to `main`. +`version.rb` and the bundle manifests to match the tag, runs the specs, +publishes the gem to RubyGems via OIDC, attaches the +`activeadmin-mcp-X.Y.Z.mcpb` bundle to the release, and commits the version +bumps back to `main`. > **Branch protection:** the workflow pushes the version-bump commit to the > default branch using the built-in `GITHUB_TOKEN`. If `main` requires pull @@ -65,5 +76,8 @@ To verify the packaged gem without publishing: bundle exec rake build # writes pkg/activeadmin_mcp-.gem ``` +To verify the Claude Desktop bundle without publishing, see +[Building the bundle](README.md#building-the-bundle) in the README. + Do **not** run `rake release` locally — publishing happens only through the tagged CI workflow. From cbaad52863e2af8b68b3e0aa0e0d53000b99e8dc Mon Sep 17 00:00:00 2001 From: Lloyd Watkin Date: Sat, 19 Sep 2026 10:05:35 +0100 Subject: [PATCH 2/2] Run the bundle jobs on Node 24 Node 22 (Jod) has moved to maintenance. Node 24 (Krypton) is the current LTS, so CI and the release workflow both pack the bundle on it. The proxy tests pass unchanged on 24.21.0. The bundle's own floor stays at Node 18: that governs the machines colleagues install on, not the machine that packs the file. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bf603b1..917ccde 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: - name: Set up Node uses: actions/setup-node@v4 with: - node-version: "22" + node-version: "24" - name: Run proxy tests run: npm test diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index afdbe5b..a73543c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -50,7 +50,7 @@ jobs: - name: Set up Node uses: actions/setup-node@v4 with: - node-version: "22" + node-version: "24" - name: Write version files run: |