MCP RegistryへGitHub Actionsで自動公開する手順
MCP Registryへのサーバー公開をGitHub Actionsで自動化する手順を、OIDC・GitHub PAT・DNSの3認証方式の使い分けとタグpush運用まで確認します。
MCP Registryへのサーバー公開は、手元でmcp-publisherコマンドを実行する方法に加えて、GitHubのバージョンタグpushをきっかけに自動実行することもできます。公式が用意するワークフローテンプレートはOIDC・GitHub PAT・DNSの3つの認証方式に対応し、npmなど下流のパッケージレジストリへの公開とMCP Registryへの公開を1つのワークフローにまとめられます。この記事では、ワークフローファイルの作成から認証方式の選び方、タグpushでの実行までを順に確認します。
自動化の前に確認しておく前提
GitHub Actionsで公開を自動化する前に、次の3点がそろっている必要があります。
- MCPの仕様に沿って実装したサーバーと、公開先のパッケージレジストリ(npm・PyPI・Dockerなど)のアカウント
- サーバーのメタデータを記述した
server.json - パッケージレジストリ側の公開トークン(npmなら
NPM_TOKEN)
公式のサンプルはnpmパッケージを例にしていますが、MCP Registryへの公開手順そのものはパッケージの種類によらず共通です。npm・PyPI・Dockerのどれであっても、「パッケージ本体をレジストリへ公開する」→「mcp-publisherでMCP Registryへメタデータを公開する」という2段構成は変わりません。
自動化する動機はシンプルです。手元での公開はmcp-publisher loginとmcp-publisher publishを毎回自分の端末で実行する必要があり、コマンドのインストール自体を忘れる・実行し忘れる・複数人がいるプロジェクトで誰か1人しか公開できないといった属人化が起きやすくなります。タグpushをトリガーにしてしまえば、パッケージ本体の公開とMCP Registryへのメタデータ公開が1つのイベントで完結し、誰がリリース作業をしても同じ手順が走ります。
もう1つ、ワークフローを組む前に決めておくべきことがあります。それは認証方式です。認証方式によってサーバー名の名前空間の形式が変わるため、後から変更するとサーバー名を作り直すことになります。
| 認証方式 | サーバー名の名前空間 | 具体例 |
|---|---|---|
| GitHubベース | サーバー名の名前空間io.github.ユーザー名またはOrg名/* | 具体例io.github.alice/weather-server |
| ドメインベース | サーバー名の名前空間逆引きドメイン.*/* | 具体例io.modelcontextprotocol/everything |
GitHub Actionsから公開するなら、GitHubベースの認証(OIDCまたはPAT)を選ぶのが自然です。独自ドメインの名前空間で公開したい場合だけ、DNS認証(ドメインベース)を選びます。
ステップ1: ワークフローファイルを作る
サーバーのリポジトリに.github/workflows/publish-mcp.ymlを作成します。バージョンタグのpushをトリガーにし、パッケージの公開とMCP Registryへの公開を1つのジョブにまとめます。OIDC認証を使う場合の骨格は次のとおりです。
name: Publish to MCP Registry
on:
push:
tags: ["v*"]
jobs:
publish:
runs-on: ubuntu-latest
permissions:
id-token: write # OIDC認証に必須
contents: read
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: "lts/*"
- run: npm ci
- run: npm run build --if-present
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Install mcp-publisher
run: |
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
- run: ./mcp-publisher login github-oidc
- run: ./mcp-publisher publishpermissions.id-token: writeはOIDC認証だけに必要な設定です。GitHub PATやDNS認証を使う場合は不要になる代わりに、次のステップで扱うシークレットの用意が要ります。認証部分だけを差し替えると、3方式は次のようになります。
# OIDC
- run: ./mcp-publisher login github-oidc
# GitHub PAT
- run: ./mcp-publisher login github --token ${{ secrets.MCP_GITHUB_TOKEN }}
# DNS
- run: ./mcp-publisher login dns --domain example.com --private-key ${{ secrets.MCP_PRIVATE_KEY }}ワークフローの他の部分(チェックアウト・ビルド・npm publish・mcp-publisherのインストール)は3方式とも共通です。違うのはこの1行だけです。
バージョン番号をタグからserver.jsonへ反映したいときは、publishステップの前に次のようなコマンドを挟めます。
VERSION=${GITHUB_REF#refs/tags/v}
jq --arg v "$VERSION" '.version = $v' server.json > server.tmp && mv server.tmp server.jsonMCP Registryはバージョンごとに一意な文字列を要求するため、server.jsonのバージョン更新を忘れたまま同じタグ形式でpushを繰り返すと、2回目以降のpublishはバージョン重複でエラーになります。サーバーのメタデータを更新したいときも、既存バージョンを上書きするのではなく、常に新しいバージョン文字列を持つserver.jsonを送り直す仕組みです。
もう1つ、公開時に独自のメタデータを埋め込みたい場合は、_metaの"io.modelcontextprotocol.registry/publisher-provided"キー配下にJSONを入れておくと、公開後もそのまま保持されます。CIのコミットSHAやビルド日時のようなCI固有の情報を残しておきたいときに使えます。ただしこのフィールドには4KB(4096バイト)というサイズ上限があり、超過するとpublish自体が失敗します。
ステップ2: 認証方式を選び、シークレットを設定する
公式テンプレートの3方式にHTTPを加えた4通りは、必要なシークレットと向いている状況がそれぞれ違います。
| 認証方式 | 追加で必要なシークレット | 向いているケース |
|---|---|---|
| OIDC(推奨) | 追加で必要なシークレットなし | 向いているケースGitHub Actionsから公開する場合の既定選択 |
| GitHub PAT | 追加で必要なシークレットMCP_GITHUB_TOKEN(read:org・read:userスコープ) | 向いているケースOIDCを使えないCI環境からの移行時 |
| DNS | 追加で必要なシークレットMCP_PRIVATE_KEY(Ed25519などの秘密鍵) | 向いているケース独自ドメインの名前空間で公開したい場合 |
| HTTP | 追加で必要なシークレット秘密鍵(DNSと同じ鍵形式) | 向いているケースドメイン配下に/.well-known/mcp-registry-authファイルを置ける場合 |
OIDC認証は追加のシークレットが要らない分、鍵の漏洩リスクそのものがなくなります。公式もOIDCを推奨方式として案内しています。GitHub PATを使う場合はread:orgとread:userスコープを持つPersonal Access Tokenを発行し、MCP_GITHUB_TOKENという名前でリポジトリシークレットに登録します。DNS認証はEd25519やECDSA P-384の鍵ペアを生成し、公開鍵をDNSのTXTレコードに登録したうえで、秘密鍵をMCP_PRIVATE_KEYとして登録する流れです。
HTTP認証はDNS認証と同じ鍵ペアの仕組みを使いますが、TXTレコードの代わりにhttps://ドメイン/.well-known/mcp-registry-authというファイルを公開して所有権を証明します。名前空間の形式(com.example.*/*)もDNS認証と同じです。ただし公式のGitHub Actionsテンプレートには、OIDC・GitHub PAT・DNSの3方式しか用意されていません。HTTP認証をワークフローに組み込みたい場合は、./mcp-publisher login http --domain example.com --private-key ${{ secrets.MCP_PRIVATE_KEY }}のようなステップを自分で追加する必要があります(example.com部分は自分のドメインに置き換えます)。
どの方式を選んでも、パッケージレジストリ側のトークン(NPM_TOKENなど)は別途必要です。パッケージレジストリの認証とMCP Registryの認証は別物で、片方だけ設定して残りを忘れるミスが起きやすいところです。
ステップ3: バージョンタグをpushして実行する
ワークフローとシークレットの準備ができたら、バージョンタグを作成してpushします。
git tag v1.0.0
git push origin v1.0.0タグのpushをきっかけに、テストの実行・ビルド・パッケージレジストリへの公開・MCP Registryへの公開が順番に走ります。トリガーをブランチへのpushではなくタグのpushにしているのは、開発中のコミットのたびに公開が走ってしまうのを防ぐためです。バージョンタグはリリースの意思を明示する行為なので、公開のトリガーとして自然に対応します。初回はいきなり本番用のタグでは実行せず、v0.0.1-testのような検証用タグで一度ワークフロー全体を通しておくと、後述のつまずきに引っかかったときの被害を小さくできます。テスト用のバージョンも公開後は削除できない点は変わらないため、検証用タグの命名は本番のバージョン体系と混同しない形にしておくと安全です。
よくあるつまずき
公式が案内しているエラーへの対処は次の2つです。
| エラーメッセージ | 対処 |
|---|---|
| "Authentication failed" | 対処OIDCならid-token: write権限の設定漏れを確認する。PAT・DNSならシークレットの値とスコープを確認する |
| "Package validation failed" | 対処パッケージレジストリへの公開自体が成功しているか、パッケージ側に必要な検証情報が含まれているかを確認する |
"Package validation failed"の中身は、パッケージの種類ごとに用意された所有権確認の仕組みが満たされていないことがほとんどです。server.jsonに書いたサーバー名と、パッケージ側に埋め込む識別情報が一致していないと失敗します。
パッケージ種別(registryType) | 所有権確認の方法 |
|---|---|
| npm | 所有権確認の方法package.jsonのmcpNameフィールドがserver.jsonのサーバー名と一致すること |
| PyPI | 所有権確認の方法READMEにmcp-name: サーバー名という文字列が含まれること(コメント内でも可) |
| NuGet | 所有権確認の方法READMEに同様のmcp-name: サーバー名文字列が含まれること |
| Docker/OCI | 所有権確認の方法イメージにio.modelcontextprotocol.server.nameアノテーションを付け、サーバー名と一致させること |
| MCPB | 所有権確認の方法配布URLに"mcp"の文字列を含み、server.jsonに成果物のSHA-256(fileSha256)を記載すること |
npmパッケージで自動公開を組む場合、npm publish自体は成功していてもpackage.jsonにmcpNameを書き忘れているだけで"Package validation failed"になります。ワークフローのビルドステップでpackage.jsonとserver.jsonのサーバー名が一致しているかを事前にチェックしておくと、この種の失敗を公開前に検出できます。
これに加えて、実際の運用で見落としやすい点が2つあります。1つは、上のCalloutで触れた不変性です。タグpushのたびに自動実行されるワークフローは、テストが通っていない壊れたビルドを誤って本番タグとしてpushしてしまうリスクと隣り合わせです。壊れたバージョンを消す手段がない以上、公開ステップの手前にテストとビルドのステップを必ず挟み、if-present任せにせず自分のプロジェクトで実際に走ることを確認しておく価値があります。
もう1つは、GitHub PAT認証のスコープ不足です。read:orgとread:userの両方のスコープを付けたPATが必要で、片方だけでは認証に失敗することがあります。
まとめ
GitHub Actionsを使ったMCP Registryへの公開は、①認証方式(OIDC・PAT・DNS)の選択がサーバー名の名前空間を決め、②パッケージレジストリの認証とMCP Registryの認証を別々に用意する必要があり、③一度publishしたバージョンは削除できない、という3点を押さえれば設定自体は難しくありません。OIDC認証を選べば追加シークレットなしで運用できるため、GitHub Actionsから公開するなら基本の選択肢になります。手元でのpublish前確認にはMCP Inspectorの使い方が使え、MCPそのものの仕組みはMCPとはで解説しています。なお、MCP Registryへの公開とClaude製品への表示は別の仕組みで、Claude.ai・Claude Code・Coworkに表示させたい場合はAnthropic Connectors Directoryとはで扱う提出ポータルからの申請が別途必要です。