From 4b2fa962d4334917452a5c02a48f397143e4fb4e Mon Sep 17 00:00:00 2001 From: Matt Rossman <22670878+mattrossman@users.noreply.github.com> Date: Tue, 9 Jun 2026 09:43:03 -0400 Subject: [PATCH 1/8] ci: add force_publish input to release workflow for retrying stuck publishes --- .github/workflows/release.yml | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7fe6d37..33fe552 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,6 +5,11 @@ on: branches: - main workflow_dispatch: + inputs: + force_publish: + description: 'Skip release-please and publish current package.json versions' + type: boolean + default: false permissions: contents: write @@ -32,33 +37,36 @@ jobs: with: token: ${{ steps.generate-token.outputs.token }} - # Everything below only runs when a release PR is merged. - # Shared setup steps gate on releases_created (true if any package released). + # Everything below runs when a release PR is merged, or when + # force_publish is manually enabled for retrying current package versions. + # Shared setup steps gate on releases_created (true if any package released) + # or force_publish. # Per-package publish steps gate on --release_created so only the - # released package(s) are published each run. + # released package(s) are published each run. force_publish runs all + # publish steps; already-published npm versions are skipped below. # Ref: https://github.com/googleapis/release-please-action#path-outputs - name: Checkout repository uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 - if: ${{ steps.release.outputs.releases_created }} + if: ${{ steps.release.outputs.releases_created || inputs.force_publish == true }} - name: Setup tools uses: jdx/mise-action@5228313ee0372e111a38da051671ca30fc5a96db # v3.6.3 - if: ${{ steps.release.outputs.releases_created }} + if: ${{ steps.release.outputs.releases_created || inputs.force_publish == true }} with: install: true cache: true - name: Install dependencies - if: ${{ steps.release.outputs.releases_created }} + if: ${{ steps.release.outputs.releases_created || inputs.force_publish == true }} run: pnpm install --frozen-lockfile - name: Build packages - if: ${{ steps.release.outputs.releases_created }} + if: ${{ steps.release.outputs.releases_created || inputs.force_publish == true }} run: pnpm --filter @supabase/mcp-utils --filter @supabase/mcp-server-supabase --filter @supabase/mcp-server-postgrest build - name: Publish @supabase/mcp-utils to npm - if: ${{ steps.release.outputs['packages/mcp-utils--release_created'] }} + if: ${{ steps.release.outputs['packages/mcp-utils--release_created'] || inputs.force_publish == true }} working-directory: packages/mcp-utils run: | set -euo pipefail @@ -78,7 +86,7 @@ jobs: pnpm publish --no-git-checks --ignore-scripts --access public --provenance - name: Publish @supabase/mcp-server-supabase to npm - if: ${{ steps.release.outputs['packages/mcp-server-supabase--release_created'] }} + if: ${{ steps.release.outputs['packages/mcp-server-supabase--release_created'] || inputs.force_publish == true }} working-directory: packages/mcp-server-supabase run: | set -euo pipefail @@ -98,7 +106,7 @@ jobs: pnpm publish --no-git-checks --ignore-scripts --access public --provenance - name: Publish @supabase/mcp-server-postgrest to npm - if: ${{ steps.release.outputs['packages/mcp-server-postgrest--release_created'] }} + if: ${{ steps.release.outputs['packages/mcp-server-postgrest--release_created'] || inputs.force_publish == true }} working-directory: packages/mcp-server-postgrest run: | set -euo pipefail @@ -118,13 +126,13 @@ jobs: pnpm publish --no-git-checks --ignore-scripts --access public --provenance - name: Authenticate to MCP registry - if: ${{ steps.release.outputs['packages/mcp-server-supabase--release_created'] }} + if: ${{ steps.release.outputs['packages/mcp-server-supabase--release_created'] || inputs.force_publish == true }} working-directory: packages/mcp-server-supabase env: DOMAIN_VERIFICATION_KEY: ${{ secrets.DOMAIN_VERIFICATION_KEY }} run: pnpm registry:login - name: Publish to MCP registry - if: ${{ steps.release.outputs['packages/mcp-server-supabase--release_created'] }} + if: ${{ steps.release.outputs['packages/mcp-server-supabase--release_created'] || inputs.force_publish == true }} working-directory: packages/mcp-server-supabase run: pnpm registry:publish From 5715ca0895902a8f42dd3ff355ae98bb727ed240 Mon Sep 17 00:00:00 2001 From: Matt Rossman <22670878+mattrossman@users.noreply.github.com> Date: Tue, 9 Jun 2026 09:49:43 -0400 Subject: [PATCH 2/8] chore: generate mgmt-api type --- .../mcp-server-supabase/src/management-api/types.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/mcp-server-supabase/src/management-api/types.ts b/packages/mcp-server-supabase/src/management-api/types.ts index 54b591d..d6f7111 100644 --- a/packages/mcp-server-supabase/src/management-api/types.ts +++ b/packages/mcp-server-supabase/src/management-api/types.ts @@ -217,7 +217,10 @@ export interface paths { }; get?: never; put?: never; - /** [Beta] Exchange auth code for user's access and refresh token */ + /** + * [Beta] Exchange auth code for user's access and refresh token + * @description Supports `authorization_code`, `refresh_token`, and `urn:ietf:params:oauth:grant-type:jwt-bearer` grant types. The `jwt-bearer` grant type (IDJAG — identity-directed JWT assertion) is in beta and available on Team and Enterprise plans only. + */ post: operations["v1-exchange-oauth-token"]; delete?: never; options?: never; @@ -2396,7 +2399,7 @@ export interface components { * } */ OAuthTokenBody: { /** @enum {string} */ - grant_type?: "authorization_code" | "refresh_token"; + grant_type?: "authorization_code" | "refresh_token" | "urn:ietf:params:oauth:grant-type:jwt-bearer"; /** Format: uuid */ client_id?: string; client_secret?: string; @@ -2404,6 +2407,8 @@ export interface components { code_verifier?: string; redirect_uri?: string; refresh_token?: string; + /** @description IDJAG assertion JWT for grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer. Beta - available on Team and Enterprise plans only. */ + assertion?: string; /** * Format: uri * @description Resource indicator for MCP (Model Context Protocol) clients @@ -2413,7 +2418,8 @@ export interface components { }; OAuthTokenResponse: { access_token: string; - refresh_token: string; + /** @description The `urn:ietf:params:oauth:grant-type:jwt-bearer` grant type issues access tokens only, no refresh token is returned and the token cannot be revoked via `/v1/oauth/revoke`. */ + refresh_token?: string; expires_in: number; /** @enum {string} */ token_type: "Bearer"; From fca43e3e2b885208f6977e045164f52409d5ecf5 Mon Sep 17 00:00:00 2001 From: Matt Rossman <22670878+mattrossman@users.noreply.github.com> Date: Tue, 9 Jun 2026 10:39:49 -0400 Subject: [PATCH 3/8] ci: skip MCP registry publish when version already exists --- .github/workflows/release.yml | 41 ++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 33fe552..f132fae 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -43,7 +43,8 @@ jobs: # or force_publish. # Per-package publish steps gate on --release_created so only the # released package(s) are published each run. force_publish runs all - # publish steps; already-published npm versions are skipped below. + # publish steps; already-published npm versions and MCP registry + # versions are skipped below. # Ref: https://github.com/googleapis/release-please-action#path-outputs - name: Checkout repository @@ -125,14 +126,48 @@ jobs: rm -f "$NPM_VIEW_STDERR" pnpm publish --no-git-checks --ignore-scripts --access public --provenance - - name: Authenticate to MCP registry + # The MCP registry rejects re-publishing an existing version, so (unlike + # npm) it isn't idempotent on its own. Probe the registry first and gate + # both login and publish on the result so force_publish stays re-runnable. + - name: Check MCP registry version + id: mcp-registry-check if: ${{ steps.release.outputs['packages/mcp-server-supabase--release_created'] || inputs.force_publish == true }} working-directory: packages/mcp-server-supabase + run: | + set -euo pipefail + ALREADY_PUBLISHED=$(node -e ' + const { name, version } = require("./server.json"); + const url = `https://registry.modelcontextprotocol.io/v0/servers?search=${encodeURIComponent(name)}&version=${encodeURIComponent(version)}`; + fetch(url) + .then((res) => { + if (!res.ok) throw new Error(`Registry query failed: ${res.status} ${res.statusText}`); + return res.json(); + }) + .then((data) => { + const exists = (data.servers ?? []).some( + (entry) => entry.server?.name === name && entry.server?.version === version + ); + process.stdout.write(exists ? "true" : "false"); + }) + .catch((err) => { + console.error(err.message); + process.exit(1); + }); + ') + echo "already_published=$ALREADY_PUBLISHED" >> "$GITHUB_OUTPUT" + if [ "$ALREADY_PUBLISHED" = "true" ]; then + VERSION=$(node -p "require('./server.json').version") + echo "MCP registry version $VERSION already published, skipping." + fi + + - name: Authenticate to MCP registry + if: ${{ steps.mcp-registry-check.outputs.already_published == 'false' }} + working-directory: packages/mcp-server-supabase env: DOMAIN_VERIFICATION_KEY: ${{ secrets.DOMAIN_VERIFICATION_KEY }} run: pnpm registry:login - name: Publish to MCP registry - if: ${{ steps.release.outputs['packages/mcp-server-supabase--release_created'] || inputs.force_publish == true }} + if: ${{ steps.mcp-registry-check.outputs.already_published == 'false' }} working-directory: packages/mcp-server-supabase run: pnpm registry:publish From 5aa6c892044c668e697471f4f7ece09dd9239411 Mon Sep 17 00:00:00 2001 From: Matt Rossman <22670878+mattrossman@users.noreply.github.com> Date: Tue, 9 Jun 2026 10:41:10 -0400 Subject: [PATCH 4/8] docs: shrink comment --- .github/workflows/release.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f132fae..a7cafc5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -126,9 +126,8 @@ jobs: rm -f "$NPM_VIEW_STDERR" pnpm publish --no-git-checks --ignore-scripts --access public --provenance - # The MCP registry rejects re-publishing an existing version, so (unlike - # npm) it isn't idempotent on its own. Probe the registry first and gate - # both login and publish on the result so force_publish stays re-runnable. + # The MCP registry rejects re-publishing an existing version. + # Probe the registry first and gate both login and publish on the result. - name: Check MCP registry version id: mcp-registry-check if: ${{ steps.release.outputs['packages/mcp-server-supabase--release_created'] || inputs.force_publish == true }} From 99bd6aef544bad60b22b3d4e6563b03d3206a625 Mon Sep 17 00:00:00 2001 From: Matt Rossman <22670878+mattrossman@users.noreply.github.com> Date: Tue, 9 Jun 2026 15:53:31 -0400 Subject: [PATCH 5/8] ci: require release ref for force publish --- .github/workflows/release.yml | 28 ++++++++++++++++++++++++++++ CONTRIBUTING.md | 2 ++ 2 files changed, 30 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a7cafc5..0ecc0af 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,6 +10,10 @@ on: description: 'Skip release-please and publish current package.json versions' type: boolean default: false + release_ref: + description: 'Required with force_publish. Release tag or commit SHA to publish from.' + type: string + default: '' permissions: contents: write @@ -37,6 +41,28 @@ jobs: with: token: ${{ steps.generate-token.outputs.token }} + - name: Validate force publish inputs + if: ${{ inputs.force_publish == true }} + env: + RELEASE_REF: ${{ inputs.release_ref }} + REPO_URL: ${{ github.server_url }}/${{ github.repository }} + run: | + set -euo pipefail + if [ -z "$RELEASE_REF" ]; then + echo "release_ref is required when force_publish is enabled." + echo "Use the release tag or commit SHA from the failed publish." + exit 1 + fi + if [[ "$RELEASE_REF" =~ ^[0-9a-fA-F]{40}$ ]]; then + exit 0 + fi + RELEASE_TAG="${RELEASE_REF#refs/tags/}" + if git ls-remote --exit-code --tags "$REPO_URL" "$RELEASE_TAG" >/dev/null; then + exit 0 + fi + echo "release_ref must be an existing tag or full commit SHA." + exit 1 + # Everything below runs when a release PR is merged, or when # force_publish is manually enabled for retrying current package versions. # Shared setup steps gate on releases_created (true if any package released) @@ -50,6 +76,8 @@ jobs: - name: Checkout repository uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 if: ${{ steps.release.outputs.releases_created || inputs.force_publish == true }} + with: + ref: ${{ inputs.force_publish == true && inputs.release_ref || github.ref }} - name: Setup tools uses: jdx/mise-action@5228313ee0372e111a38da051671ca30fc5a96db # v3.6.3 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bb7198d..1123537 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -54,6 +54,8 @@ Most contributors don't need to do anything beyond merging the release PR and up If the release PR gets into a bad state, close it and manually re-run the workflow from the [Actions tab](https://github.com/supabase/mcp/actions/workflows/release.yml) → **Run workflow**. release-please will recreate the PR from scratch. +If the workflow creates GitHub releases and tags but fails before publishing to npm or the MCP registry, re-run the workflow with `force_publish` enabled and set `release_ref` to the release tag or commit SHA from the failed workflow run. + ## Manual MCP registry publish (optional) This is only needed if the automated publish failed or needs to be re-run manually. The MCP registry stores metadata about the server (defined in `packages/mcp-server-supabase/server.json`) — it does not host the server itself. From c0cd101ab7b63cc35ec96bd0e37782b3b62bceae Mon Sep 17 00:00:00 2001 From: Matt Rossman <22670878+mattrossman@users.noreply.github.com> Date: Tue, 9 Jun 2026 16:20:43 -0400 Subject: [PATCH 6/8] ci: validate force publish refs --- .github/workflows/release.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0ecc0af..ca2271d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -45,7 +45,7 @@ jobs: if: ${{ inputs.force_publish == true }} env: RELEASE_REF: ${{ inputs.release_ref }} - REPO_URL: ${{ github.server_url }}/${{ github.repository }} + GH_TOKEN: ${{ github.token }} run: | set -euo pipefail if [ -z "$RELEASE_REF" ]; then @@ -54,10 +54,11 @@ jobs: exit 1 fi if [[ "$RELEASE_REF" =~ ^[0-9a-fA-F]{40}$ ]]; then - exit 0 + COMMIT_REF="$RELEASE_REF" + else + COMMIT_REF="tags/${RELEASE_REF#refs/tags/}" fi - RELEASE_TAG="${RELEASE_REF#refs/tags/}" - if git ls-remote --exit-code --tags "$REPO_URL" "$RELEASE_TAG" >/dev/null; then + if gh api --silent "repos/$GITHUB_REPOSITORY/commits/$COMMIT_REF"; then exit 0 fi echo "release_ref must be an existing tag or full commit SHA." From ff8ceb4f30fcbcffa8a6368e550f779974ba5079 Mon Sep 17 00:00:00 2001 From: Matt Rossman <22670878+mattrossman@users.noreply.github.com> Date: Tue, 9 Jun 2026 23:01:11 -0400 Subject: [PATCH 7/8] ci: simplify force publish ref selection --- .github/workflows/release.yml | 27 ++++++--------------------- CONTRIBUTING.md | 2 +- 2 files changed, 7 insertions(+), 22 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ca2271d..fb893f7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,10 +10,6 @@ on: description: 'Skip release-please and publish current package.json versions' type: boolean default: false - release_ref: - description: 'Required with force_publish. Release tag or commit SHA to publish from.' - type: string - default: '' permissions: contents: write @@ -41,27 +37,18 @@ jobs: with: token: ${{ steps.generate-token.outputs.token }} - - name: Validate force publish inputs + - name: Validate force publish ref if: ${{ inputs.force_publish == true }} env: - RELEASE_REF: ${{ inputs.release_ref }} - GH_TOKEN: ${{ github.token }} + REF_TYPE: ${{ github.ref_type }} + REF_NAME: ${{ github.ref_name }} run: | set -euo pipefail - if [ -z "$RELEASE_REF" ]; then - echo "release_ref is required when force_publish is enabled." - echo "Use the release tag or commit SHA from the failed publish." - exit 1 - fi - if [[ "$RELEASE_REF" =~ ^[0-9a-fA-F]{40}$ ]]; then - COMMIT_REF="$RELEASE_REF" - else - COMMIT_REF="tags/${RELEASE_REF#refs/tags/}" - fi - if gh api --silent "repos/$GITHUB_REPOSITORY/commits/$COMMIT_REF"; then + if [ "$REF_TYPE" = "tag" ]; then + echo "Force publishing from release tag $REF_NAME." exit 0 fi - echo "release_ref must be an existing tag or full commit SHA." + echo "force_publish must be run from the release tag selected in the workflow ref picker." exit 1 # Everything below runs when a release PR is merged, or when @@ -77,8 +64,6 @@ jobs: - name: Checkout repository uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 if: ${{ steps.release.outputs.releases_created || inputs.force_publish == true }} - with: - ref: ${{ inputs.force_publish == true && inputs.release_ref || github.ref }} - name: Setup tools uses: jdx/mise-action@5228313ee0372e111a38da051671ca30fc5a96db # v3.6.3 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1123537..2115b88 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -54,7 +54,7 @@ Most contributors don't need to do anything beyond merging the release PR and up If the release PR gets into a bad state, close it and manually re-run the workflow from the [Actions tab](https://github.com/supabase/mcp/actions/workflows/release.yml) → **Run workflow**. release-please will recreate the PR from scratch. -If the workflow creates GitHub releases and tags but fails before publishing to npm or the MCP registry, re-run the workflow with `force_publish` enabled and set `release_ref` to the release tag or commit SHA from the failed workflow run. +If the workflow creates GitHub releases and tags but fails before publishing to npm or the MCP registry, re-run the workflow from one of the release tags created by the failed workflow run and enable `force_publish`. ## Manual MCP registry publish (optional) From 924148be60de1ca14fab1e21885ad16b1bc99bf4 Mon Sep 17 00:00:00 2001 From: Matt Rossman <22670878+mattrossman@users.noreply.github.com> Date: Tue, 9 Jun 2026 23:08:57 -0400 Subject: [PATCH 8/8] ci: validate force publish release tags --- .github/workflows/release.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fb893f7..2498028 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -40,15 +40,20 @@ jobs: - name: Validate force publish ref if: ${{ inputs.force_publish == true }} env: + GH_TOKEN: ${{ github.token }} REF_TYPE: ${{ github.ref_type }} REF_NAME: ${{ github.ref_name }} run: | set -euo pipefail - if [ "$REF_TYPE" = "tag" ]; then + if [ "$REF_TYPE" != "tag" ]; then + echo "Select a release tag instead of a branch when running force_publish." + exit 1 + fi + if gh api --silent "repos/$GITHUB_REPOSITORY/releases/tags/$REF_NAME"; then echo "Force publishing from release tag $REF_NAME." exit 0 fi - echo "force_publish must be run from the release tag selected in the workflow ref picker." + echo "Selected tag $REF_NAME is not associated with a GitHub Release." exit 1 # Everything below runs when a release PR is merged, or when