| 1 | name: MCP Registry Release |
| 2 | |
| 3 | on: |
| 4 | workflow_dispatch: |
| 5 | inputs: |
| 6 | tag: |
| 7 | description: "Release tag (e.g. v0.4.1)" |
| 8 | required: true |
| 9 | |
| 10 | # OIDC is how we authenticate to the MCP Registry: the registry trusts a GitHub |
| 11 | # Actions token issued for this repository, so no long-lived secret is needed. |
| 12 | permissions: |
| 13 | id-token: write |
| 14 | contents: read |
| 15 | |
| 16 | jobs: |
| 17 | publish: |
| 18 | name: Publish server.json to the MCP Registry |
| 19 | runs-on: ubuntu-latest |
| 20 | steps: |
| 21 | # Unlike the other release workflows, this one builds nothing from source: |
| 22 | # it uploads metadata. So it checks out the branch it was dispatched on |
| 23 | # rather than the tag — `server.json` may have landed after the tag was |
| 24 | # cut — and uses the tag input only to pick the version to publish. |
| 25 | - name: Checkout |
| 26 | uses: actions/checkout@v6 |
| 27 | |
| 28 | - name: Set the release version |
| 29 | shell: bash |
| 30 | run: | |
| 31 | release_version="${{ github.event.inputs.tag }}" |
| 32 | release_version="${release_version#v}" |
| 33 | |
| 34 | echo "RELEASE_VERSION=${release_version}" >> "$GITHUB_ENV" |
| 35 | |
| 36 | - name: Check the server name is consistent across packages |
| 37 | shell: bash |
| 38 | run: | |
| 39 | server_name="$(jq -r '.name' server.json)" |
| 40 | echo "Server name: ${server_name}" |
| 41 | |
| 42 | # npm proves ownership through `mcpName` in the published package.json, |
| 43 | # rendered at release time from this template. |
| 44 | grep -q "\"mcpName\": \"${server_name}\"" npm/package-main.json.tmpl |
| 45 | |
| 46 | # NuGet proves ownership through an `mcp-name:` marker in the package README. |
| 47 | grep -q "mcp-name: ${server_name}" nuget/onde-cli/README.md |
| 48 | |
| 49 | - name: Set the version in server.json |
| 50 | shell: bash |
| 51 | run: | |
| 52 | jq --arg v "${RELEASE_VERSION}" \ |
| 53 | '.version = $v | .packages |= map(.version = $v)' \ |
| 54 | server.json > server.tmp |
| 55 | mv server.tmp server.json |
| 56 | cat server.json |
| 57 | |
| 58 | - name: Verify the published packages carry the ownership marker |
| 59 | shell: bash |
| 60 | run: | |
| 61 | # The registry stores metadata only, and validates it by fetching each |
| 62 | # listed package and looking for a marker naming this server. Checking |
| 63 | # the sources isn't enough: a package published before the marker |
| 64 | # landed will still be missing it. Both comparisons are case-sensitive. |
| 65 | server_name="$(jq -r '.name' server.json)" |
| 66 | |
| 67 | # npm is queryable within seconds of publishing, but the npm release |
| 68 | # workflow may still be building its platform packages when this runs. |
| 69 | for attempt in $(seq 1 20); do |
| 70 | if npm view "@ondeinference/cli@${RELEASE_VERSION}" version; then |
| 71 | break |
| 72 | fi |
| 73 | if [ "${attempt}" -eq 20 ]; then |
| 74 | echo "@ondeinference/cli@${RELEASE_VERSION} is still not on npm after 10 minutes." >&2 |
| 75 | exit 1 |
| 76 | fi |
| 77 | echo "Waiting for @ondeinference/cli@${RELEASE_VERSION} on npm (attempt ${attempt}/20)..." |
| 78 | sleep 30 |
| 79 | done |
| 80 | |
| 81 | npm_mcp_name="$(npm view "@ondeinference/cli@${RELEASE_VERSION}" mcpName)" |
| 82 | if [ "${npm_mcp_name}" != "${server_name}" ]; then |
| 83 | echo "@ondeinference/cli@${RELEASE_VERSION} declares mcpName '${npm_mcp_name}', expected '${server_name}'." >&2 |
| 84 | echo "npm versions are immutable — cut a new release with the marker in place." >&2 |
| 85 | exit 1 |
| 86 | fi |
| 87 | |
| 88 | # nuget.org validates a package before it reaches the flat container, |
| 89 | # so a version pushed minutes ago is not queryable yet. Poll instead |
| 90 | # of failing the release on that race. |
| 91 | for attempt in $(seq 1 20); do |
| 92 | nuget_versions="$(curl -fsSL "https://api.nuget.org/v3-flatcontainer/onde.cli/index.json")" |
| 93 | if echo "${nuget_versions}" | jq -e --arg v "${RELEASE_VERSION}" '.versions | index($v)' >/dev/null; then |
| 94 | echo "Onde.Cli ${RELEASE_VERSION} is indexed." |
| 95 | break |
| 96 | fi |
| 97 | if [ "${attempt}" -eq 20 ]; then |
| 98 | echo "Onde.Cli ${RELEASE_VERSION} is still not indexed on nuget.org after 10 minutes." >&2 |
| 99 | exit 1 |
| 100 | fi |
| 101 | echo "Waiting for nuget.org to index ${RELEASE_VERSION} (attempt ${attempt}/20)..." |
| 102 | sleep 30 |
| 103 | done |
| 104 | |
| 105 | curl -fsSL -o package.nupkg \ |
| 106 | "https://api.nuget.org/v3-flatcontainer/onde.cli/${RELEASE_VERSION}/onde.cli.${RELEASE_VERSION}.nupkg" |
| 107 | if ! unzip -p package.nupkg README.md | grep -q "mcp-name: ${server_name}"; then |
| 108 | echo "Onde.Cli ${RELEASE_VERSION} README is missing the 'mcp-name: ${server_name}' marker." >&2 |
| 109 | exit 1 |
| 110 | fi |
| 111 | rm package.nupkg |
| 112 | |
| 113 | - name: Install mcp-publisher |
| 114 | shell: bash |
| 115 | run: | |
| 116 | curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher |
| 117 | |
| 118 | - name: Authenticate to the MCP Registry |
| 119 | shell: bash |
| 120 | run: ./mcp-publisher login github-oidc |
| 121 | |
| 122 | - name: Publish |
| 123 | shell: bash |
| 124 | run: ./mcp-publisher publish |
| 125 | |
| 126 | - name: Verify the server is listed |
| 127 | shell: bash |
| 128 | run: | |
| 129 | server_name="$(jq -r '.name' server.json)" |
| 130 | curl -fsSL "https://registry.modelcontextprotocol.io/v0.1/servers?search=${server_name}" | jq . |