How to automate publishing with GitHub Actions
This guide explains how to set up GitHub Actions to automatically publish your MCP server to the MCP Registry whenever you push a new version tag.
Step 1: Create a Workflow File
In your server project directory, create a .github/workflows/publish-mcp.yml
file. Here is an example for npm-based local server, but the MCP Registry publishing
steps are the same for all package types:
name: Publish to MCP Registry
on:
push:
tags: ["v*"] # Triggers on version tags like v1.0.0
jobs:
publish:
runs-on: ubuntu-latest
permissions:
id-token: write # Required for OIDC authentication
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v5
### Publish underlying npm package:
- name: Set up Node.js
uses: actions/setup-node@v5
with:
node-version: "lts/*"
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm run test --if-present
- name: Build package
run: npm run build --if-present
- name: Publish package to npm
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
### Publish MCP server:
- 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
- name: Authenticate to MCP Registry
run: ./mcp-publisher login github-oidc
# Optional:
# - name: Set version in server.json
# run: |
# VERSION=${GITHUB_REF#refs/tags/v}
# jq --arg v "$VERSION" '.version = $v' server.json > server.tmp && mv server.tmp server.json
- name: Publish server to MCP Registry
run: ./mcp-publisher publish
name: Publish to MCP Registry
on:
push:
tags: ["v*"] # Triggers on version tags like v1.0.0
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v5
### Publish underlying npm package:
- name: Set up Node.js
uses: actions/setup-node@v5
with:
node-version: "lts/*"
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm run test --if-present
- name: Build package
run: npm run build --if-present
- name: Publish package to npm
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
### Publish MCP server:
- 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
- name: Authenticate to MCP Registry
run: ./mcp-publisher login github --token ${{ secrets.MCP_GITHUB_TOKEN }}
# Optional:
# - name: Set version in server.json
# run: |
# VERSION=${GITHUB_REF#refs/tags/v}
# jq --arg v "$VERSION" '.version = $v' server.json > server.tmp && mv server.tmp server.json
- name: Publish server to MCP Registry
run: ./mcp-publisher publish
name: Publish to MCP Registry
on:
push:
tags: ["v*"] # Triggers on version tags like v1.0.0
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v5
### Publish underlying npm package:
- name: Set up Node.js
uses: actions/setup-node@v5
with:
node-version: "lts/*"
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm run test --if-present
- name: Build package
run: npm run build --if-present
- name: Publish package to npm
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
### Publish MCP server:
- 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
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# TODO: Replace `example.com` with your domain name
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- name: Authenticate to MCP Registry
run: ./mcp-publisher login dns --domain example.com --private-key ${{ secrets.MCP_PRIVATE_KEY }}
# Optional:
# - name: Set version in server.json
# run: |
# VERSION=${GITHUB_REF#refs/tags/v}
# jq --arg v "$VERSION" '.version = $v' server.json > server.tmp && mv server.tmp server.json
- name: Publish server to MCP Registry
run: ./mcp-publisher publish
Step 2: Add Secrets
You may need to add a secret to the repository depending on which authentication method you choose:
- GitHub OIDC Authentication: No dedicated secret necessary.
- GitHub PAT Authentication: Add a
MCP_GITHUB_TOKENsecret with a GitHub Personal Access Token (PAT) that hasread:organdread:userscopes. - DNS Authentication: Add a
MCP_PRIVATE_KEYsecret with your Ed25519 private key.
You may also need to add secrets for your package registry. For example, the workflow
above needs an NPM_TOKEN secret with your npm token.
For information about how to add secrets to a repository, see Using secrets in GitHub Actions.
Step 3: Tag and Release
Create and push a version tag to trigger the workflow:
git tag v1.0.0 git push origin v1.0.0
The workflow will run tests, build the package, publish the package to npm, and publish the server to the MCP Registry.
Troubleshooting
| Error Message | Action |
|---|---|
| "Authentication failed" | Ensure id-token: write permission is set for OIDC, or check secrets. |
| "Package validation failed" | Verify your package successfully published to the package registry (e.g., npm, PyPI), and that your package has the necessary verification information. |
Source: https://modelcontextprotocol.io/registry/github-actions.md