registry/docs/guides/publishing/github-actions.md
adam jones f975e68cf2
Clean up schema by simplifying version structure (#379)
## Summary

Simplified the schema structure by:
- Replacing `version_detail.version` with direct `version` field  
- Removing unused `release_date` field from seed data
- Updating all API types, models, and tests to match new structure
- Maintaining backwards compatibility in API responses

This change makes the schema more intuitive and reduces nesting depth.
2025-09-08 14:32:59 -07:00

8.3 KiB

Automate Publishing with GitHub Actions

Set up automated MCP server publishing using GitHub Actions.

What You'll Learn

By the end of this tutorial, you'll have:

  • A GitHub Actions workflow that automatically publishes your server
  • Understanding of GitHub OIDC authentication
  • Knowledge of best practices for automated publishing
  • Working examples for Node.js, Python, and Docker projects

Prerequisites

  • Already published your server manually (complete Publishing Tutorial first)
  • GitHub repository with your MCP server code
  • Basic understanding of CI/CD concepts
  • 20-30 minutes

GitHub Actions Setup

Step 1: Create Workflow File

Create .github/workflows/publish-mcp.yml in your repository:

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@v4
      
      - name: Setup Node.js  # Adjust for your language
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          registry-url: 'https://registry.npmjs.org'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run tests
        run: npm test
      
      - name: Build package  
        run: npm run build
      
      - name: Publish to npm
        run: npm publish
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
      
      - name: Install MCP Publisher
        run: |
          # Build publisher from source (requires Go)
          git clone https://github.com/modelcontextprotocol/registry publisher-repo
          cd publisher-repo
          make publisher
          cp cmd/publisher/bin/mcp-publisher ../mcp-publisher
          cd ..
          chmod +x mcp-publisher
      
      - name: Login to MCP Registry
        run: ./mcp-publisher login github-oidc
      
      - name: Publish to MCP Registry
        run: ./mcp-publisher publish

Step 2: Configure Secrets

For npm publishing, add your NPM token to repository secrets:

  1. Go to repository Settings → Secrets and variables → Actions
  2. Add NPM_TOKEN with your npm access token

Note: GitHub OIDC authentication requires no additional secrets for MCP Registry publishing.

Step 3: Tag and Release

Create a version tag to trigger the workflow:

git tag v1.0.0
git push origin v1.0.0

The workflow will:

  1. Run tests
  2. Build your package
  3. Publish to npm
  4. Automatically authenticate with the MCP Registry
  5. Publish updated server.json

Authentication Methods by CI Platform

- name: Login to MCP Registry
  run: mcp-publisher login github-oidc

Advantages:

  • No secrets to manage
  • Automatically scoped to your repository
  • Most secure option

GitHub Actions - Personal Access Token

- name: Login to MCP Registry
  run: mcp-publisher login github --token ${{ secrets.GITHUB_TOKEN }}
  env:
    GITHUB_TOKEN: ${{ secrets.MCP_GITHUB_TOKEN }}

Add MCP_GITHUB_TOKEN secret with a GitHub PAT that has repo access.

DNS Authentication (Any CI)

For custom domain namespaces (com.yourcompany/*):

- name: Login to MCP Registry
  run: |
    echo "${{ secrets.MCP_PRIVATE_KEY }}" > key.pem
    mcp-publisher login dns --domain yourcompany.com --private-key-file key.pem

Add your Ed25519 private key as MCP_PRIVATE_KEY secret.

Language-Specific Examples

Python Project

name: Publish Python MCP Server

on:
  push:
    tags: ['v*']

jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'
      
      - name: Install Poetry
        run: pipx install poetry
      
      - name: Build package
        run: poetry build
      
      - name: Publish to PyPI
        run: poetry publish
        env:
          POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_TOKEN }}
      
      - name: Install MCP Publisher
        run: |
          git clone https://github.com/modelcontextprotocol/registry publisher-repo
          cd publisher-repo && make publisher && cd ..
          cp publisher-repo/cmd/publisher/bin/mcp-publisher mcp-publisher
      
      - name: Publish to MCP Registry
        run: |
          ./mcp-publisher login github-oidc
          ./mcp-publisher publish

Docker Project

name: Publish Docker MCP Server

on:
  push:
    tags: ['v*']

jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Docker Buildx
        uses: docker/setup-buildx-action@v3
      
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}
      
      - name: Extract version
        id: version
        run: echo "version=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
      
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: yourname/your-server:${{ steps.version.outputs.version }}
          labels: |
            io.modelcontextprotocol.server.name=io.github.yourname/your-server
      
      - name: Install MCP Publisher
        run: |
          git clone https://github.com/modelcontextprotocol/registry publisher-repo
          cd publisher-repo && make publisher && cd ..
          cp publisher-repo/cmd/publisher/bin/mcp-publisher mcp-publisher
      
      - name: Publish to MCP Registry
        run: |
          ./mcp-publisher login github-oidc
          ./mcp-publisher publish

Best Practices

1. Version Alignment

Keep your package version and server.json version in sync:

- name: Update server.json version
  run: |
    VERSION=${GITHUB_REF#refs/tags/v}
    jq --arg version "$VERSION" '.version = $version' server.json > tmp.json
    mv tmp.json server.json

2. Conditional Publishing

Only publish to registry after package publishing succeeds:

- name: Publish to npm
  run: npm publish
  id: npm-publish

- name: Publish to MCP Registry
  if: steps.npm-publish.outcome == 'success'
  run: ./mcp-publisher publish

3. Test Before Publishing

Include validation in your workflow:

- name: Validate server.json
  run: |
    # Validate against schema
    curl -s https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json | \
    jq . > schema.json
    npx ajv-cli validate -s schema.json -d server.json
    
    # Test with publisher
    ./mcp-publisher init --validate-only

4. Notification Setup

Get notified of publishing status:

- name: Notify on success
  if: success()
  run: |
    curl -X POST "${{ secrets.WEBHOOK_URL }}" \
         -H "Content-Type: application/json" \
         -d '{"text": "✅ MCP server ${{ github.repository }} published successfully"}'

- name: Notify on failure  
  if: failure()
  run: |
    curl -X POST "${{ secrets.WEBHOOK_URL }}" \
         -H "Content-Type: application/json" \
         -d '{"text": "❌ MCP server ${{ github.repository }} publishing failed"}'

Troubleshooting

"Publisher binary not found" - Ensure you download the correct binary for your CI platform (linux/mac/windows).

"Authentication failed" - For GitHub OIDC, verify id-token: write permission is set. For other methods, check secret configuration.

"Package validation failed" - Ensure your package was published successfully before MCP Registry publishing runs.

"Version already exists" - Each server.json version must be unique. Consider using build numbers: 1.0.0-build.123.

What You've Accomplished

You now have automated MCP server publishing that:

  • Triggers on version tags
  • Runs tests before publishing
  • Publishes to package registry first
  • Automatically publishes to MCP Registry
  • Handles authentication securely
  • Provides failure notifications

Your MCP server publishing is now fully automated - just tag a release and everything happens automatically!