modelcontextprotocol/servers

5 workflows · maturity 33% · 3 patterns · GitHub ↗

Security 0/100

Practices

✓ Matrix○ Permissions○ Security scan✓ AI review○ Cache○ Concurrency○ Reusable workflows

Detected patterns

Security dimensions

permissions
0
security scan
0
supply chain
0
secret handling
0
harden runner
0

Workflows (5)

claude AI .github/workflows/claude.yml
Triggers
issue_comment, pull_request_review_comment, issues, pull_request_review
Runs on
ubuntu-latest
Jobs
claude
Actions
anthropics/claude-code-action
View raw YAML
name: Claude Code

on:
  issue_comment:
    types: [created]
  pull_request_review_comment:
    types: [created]
  issues:
    types: [opened, assigned]
  pull_request_review:
    types: [submitted]

jobs:
  claude:
    if: |
      (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
      (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
      (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
      (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: read
      issues: read
      id-token: write
      actions: read
    steps:
      - name: Checkout repository
        uses: actions/checkout@v6
        with:
          fetch-depth: 1

      - name: Run Claude Code
        id: claude
        uses: anthropics/claude-code-action@v1
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}

          # Allow Claude to read CI results on PRs
          additional_permissions: |
            actions: read

          # Trigger when assigned to an issue
          assignee_trigger: "claude"

          claude_args: |
            --mcp-config .mcp.json
            --allowedTools "Bash,mcp__mcp-docs,WebFetch"
            --append-system-prompt "If posting a comment to GitHub, give a concise summary of the comment at the top and put all the details in a <details> block. When working on MCP-related code or reviewing MCP-related changes, use the mcp-docs MCP server to look up the latest protocol documentation. For schema details, reference https://github.com/modelcontextprotocol/modelcontextprotocol/tree/main/schema which contains versioned schemas in JSON (schema.json) and TypeScript (schema.ts) formats."
python matrix .github/workflows/python.yml
Triggers
push, pull_request, release
Runs on
ubuntu-latest, ubuntu-latest, ubuntu-latest, ubuntu-latest
Jobs
detect-packages, test, build, publish
Matrix
package→ ${{ fromJson(needs.detect-packages.outputs.packages) }}
Actions
astral-sh/setup-uv, astral-sh/setup-uv, pypa/gh-action-pypi-publish
Commands
  • PACKAGES=$(find . -name pyproject.toml -exec dirname {} \; | sed 's/^\.\///' | jq -R -s -c 'split("\n")[:-1]') echo "packages=$PACKAGES" >> $GITHUB_OUTPUT
  • uv sync --frozen --all-extras --dev
  • if [ -d "tests" ] || [ -d "test" ] || grep -q "pytest" pyproject.toml; then echo "has-tests=true" >> $GITHUB_OUTPUT else echo "has-tests=false" >> $GITHUB_OUTPUT fi
  • uv run pytest
  • uv sync --locked --all-extras --dev
  • uv run --frozen pyright
  • uv build
View raw YAML
name: Python

on:
  push:
    branches:
      - main
  pull_request:
  release:
    types: [published]

jobs:
  detect-packages:
    runs-on: ubuntu-latest
    outputs:
      packages: ${{ steps.find-packages.outputs.packages }}
    steps:
      - uses: actions/checkout@v6

      - name: Find Python packages
        id: find-packages
        working-directory: src
        run: |
          PACKAGES=$(find . -name pyproject.toml -exec dirname {} \; | sed 's/^\.\///' | jq -R -s -c 'split("\n")[:-1]')
          echo "packages=$PACKAGES" >> $GITHUB_OUTPUT

  test:
    needs: [detect-packages]
    strategy:
      matrix:
        package: ${{ fromJson(needs.detect-packages.outputs.packages) }}
    name: Test ${{ matrix.package }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - name: Install uv
        uses: astral-sh/setup-uv@v3

      - name: Set up Python
        uses: actions/setup-python@v6
        with:
          python-version-file: "src/${{ matrix.package }}/.python-version"

      - name: Install dependencies
        working-directory: src/${{ matrix.package }}
        run: uv sync --frozen --all-extras --dev

      - name: Check if tests exist
        id: check-tests
        working-directory: src/${{ matrix.package }}
        run: |
          if [ -d "tests" ] || [ -d "test" ] || grep -q "pytest" pyproject.toml; then
            echo "has-tests=true" >> $GITHUB_OUTPUT
          else
            echo "has-tests=false" >> $GITHUB_OUTPUT
          fi

      - name: Run tests
        if: steps.check-tests.outputs.has-tests == 'true'
        working-directory: src/${{ matrix.package }}
        run: uv run pytest

  build:
    needs: [detect-packages, test]
    strategy:
      matrix:
        package: ${{ fromJson(needs.detect-packages.outputs.packages) }}
    name: Build ${{ matrix.package }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - name: Install uv
        uses: astral-sh/setup-uv@v3

      - name: Set up Python
        uses: actions/setup-python@v6
        with:
          python-version-file: "src/${{ matrix.package }}/.python-version"

      - name: Install dependencies
        working-directory: src/${{ matrix.package }}
        run: uv sync --locked --all-extras --dev

      - name: Run pyright
        working-directory: src/${{ matrix.package }}
        run: uv run --frozen pyright

      - name: Build package
        working-directory: src/${{ matrix.package }}
        run: uv build

      - name: Upload artifacts
        uses: actions/upload-artifact@v6
        with:
          name: dist-${{ matrix.package }}
          path: src/${{ matrix.package }}/dist/

  publish:
    runs-on: ubuntu-latest
    needs: [build, detect-packages]
    if: github.event_name == 'release'

    strategy:
      matrix:
        package: ${{ fromJson(needs.detect-packages.outputs.packages) }}
    name: Publish ${{ matrix.package }}

    environment: release
    permissions:
      id-token: write # Required for trusted publishing

    steps:
      - name: Download artifacts
        uses: actions/download-artifact@v7
        with:
          name: dist-${{ matrix.package }}
          path: dist/

      - name: Publish package to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1
readme-pr-check .github/workflows/readme-pr-check.yml
Triggers
pull_request, issue_comment
Runs on
ubuntu-latest, ubuntu-latest
Jobs
check-readme-only, handle-confirmation
View raw YAML
name: README PR Check

on:
  pull_request:
    types: [opened]
    paths:
      - 'README.md'
  issue_comment:
    types: [created]

jobs:
  check-readme-only:
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - name: Check files and comment if README-only
        uses: actions/github-script@v8
        with:
          script: |
            const { owner, repo } = context.repo;
            const prNumber = context.payload.pull_request.number;

            const { data: files } = await github.rest.pulls.listFiles({ owner, repo, pull_number: prNumber });

            if (files.length !== 1 || files[0].filename !== 'README.md') {
              console.log('PR modifies files other than README, skipping');
              return;
            }

            // Check if we've already commented
            const { data: comments } = await github.rest.issues.listComments({ owner, repo, issue_number: prNumber });
            if (comments.some(c => c.user.login === 'github-actions[bot]' && c.body.includes('no longer accepting PRs to add new servers'))) {
              console.log('Already commented on this PR, skipping');
              return;
            }

            await github.rest.issues.addLabels({ owner, repo, issue_number: prNumber, labels: ['readme: pending'] });

            await github.rest.issues.createComment({
              owner,
              repo,
              issue_number: prNumber,
              body: [
                'Thanks for your contribution!',
                '',
                '**We are no longer accepting PRs to add new servers to the README.** The server lists are deprecated and will eventually be removed entirely, replaced by the registry.',
                '',
                '👉 **To add a new MCP server:** Please publish it to the [MCP Server Registry](https://github.com/modelcontextprotocol/registry) instead. You can browse published servers at [registry.modelcontextprotocol.io](https://registry.modelcontextprotocol.io/).',
                '',
                '👉 **If this PR updates or removes an existing entry:** We do still accept these changes. Please reply with `/i-promise-this-is-not-a-new-server` to continue.',
                '',
                'If this PR is adding a new server, please close it and submit to the registry instead.',
              ].join('\n'),
            });

  handle-confirmation:
    if: github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(github.event.comment.body, '/i-promise-this-is-not-a-new-server')
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
    steps:
      - name: Swap labels and minimize comments
        uses: actions/github-script@v8
        with:
          script: |
            const { owner, repo } = context.repo;
            const prNumber = context.payload.issue.number;

            // Check if pending label exists
            const { data: labels } = await github.rest.issues.listLabelsOnIssue({ owner, repo, issue_number: prNumber });
            if (!labels.some(l => l.name === 'readme: pending')) {
              console.log('No pending label found, skipping');
              return;
            }

            // Swap labels
            try {
              await github.rest.issues.removeLabel({ owner, repo, issue_number: prNumber, name: 'readme: pending' });
            } catch (e) {}
            await github.rest.issues.addLabels({ owner, repo, issue_number: prNumber, labels: ['readme: ready for review'] });

            // Find the bot's original comment
            const { data: comments } = await github.rest.issues.listComments({ owner, repo, issue_number: prNumber });
            const botComment = comments.find(c =>
              c.user.login === 'github-actions[bot]' &&
              c.body.includes('no longer accepting PRs to add new servers')
            );

            // Minimize both comments via GraphQL
            const minimizeComment = async (nodeId) => {
              await github.graphql(`
                mutation($id: ID!) {
                  minimizeComment(input: {subjectId: $id, classifier: RESOLVED}) {
                    minimizedComment { isMinimized }
                  }
                }
              `, { id: nodeId });
            };

            if (botComment) {
              await minimizeComment(botComment.node_id);
            }

            // Only minimize user's comment if it's just the command
            const userComment = context.payload.comment.body.trim();
            if (userComment === '/i-promise-this-is-not-a-new-server') {
              await minimizeComment(context.payload.comment.node_id);
            }
release matrix .github/workflows/release.yml
Triggers
workflow_dispatch, schedule
Runs on
ubuntu-latest, ubuntu-latest, ubuntu-latest, ubuntu-latest, ubuntu-latest
Jobs
create-metadata, update-packages, publish-pypi, publish-npm, create-release
Matrix
package→ ${{ fromJson(needs.create-metadata.outputs.npm_packages) }}, ${{ fromJson(needs.create-metadata.outputs.pypi_packages) }}
Actions
astral-sh/setup-uv, astral-sh/setup-uv, astral-sh/setup-uv, pypa/gh-action-pypi-publish
Commands
  • HASH=$(git rev-list --tags --max-count=1 || echo "HEAD~1") echo "hash=${HASH}" >> $GITHUB_OUTPUT echo "Using last release hash: ${HASH}"
  • VERSION=$(uv run --script scripts/release.py generate-version) echo "version $VERSION" echo "version=$VERSION" >> $GITHUB_OUTPUT
  • HASH="${{ steps.last-release.outputs.hash }}" uv run --script scripts/release.py generate-notes --directory src/ $HASH > RELEASE_NOTES.md cat RELEASE_NOTES.md
  • HASH="${{ steps.last-release.outputs.hash }}" PYPI=$(uv run --script scripts/release.py generate-matrix --pypi --directory src $HASH) echo "pypi_packages $PYPI" echo "pypi_packages=$PYPI" >> $GITHUB_OUTPUT
  • HASH="${{ steps.last-release.outputs.hash }}" NPM=$(uv run --script scripts/release.py generate-matrix --npm --directory src $HASH) echo "npm_packages $NPM" echo "npm_packages=$NPM" >> $GITHUB_OUTPUT
  • HASH="${{ needs.create-metadata.outputs.hash }}" uv run --script scripts/release.py update-packages --directory src/ $HASH
  • git config --global user.name "GitHub Actions" git config --global user.email "actions@github.com"
  • VERSION="${{ needs.create-metadata.outputs.version }}" git add -u if git diff-index --quiet HEAD; then echo "changes_made=false" >> $GITHUB_OUTPUT else git commit -m 'Automatic update of packages' git tag -a "$VERSION" -m "Release $VERSION" git push origin "$VERSION" echo "changes_made=true" >> $GITHUB_OUTPUT fi
View raw YAML
name: Automatic Release Creation

on:
  workflow_dispatch:
  schedule:
    - cron: '0 10 * * *'

jobs:
  create-metadata:
    runs-on: ubuntu-latest
    if: github.repository_owner == 'modelcontextprotocol'
    outputs:
      hash: ${{ steps.last-release.outputs.hash }}
      version: ${{ steps.create-version.outputs.version}}
      npm_packages: ${{ steps.create-npm-packages.outputs.npm_packages}}
      pypi_packages: ${{ steps.create-pypi-packages.outputs.pypi_packages}}
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 0

      - name: Get last release hash
        id: last-release
        run: |
          HASH=$(git rev-list --tags --max-count=1 || echo "HEAD~1")
          echo "hash=${HASH}" >> $GITHUB_OUTPUT
          echo "Using last release hash: ${HASH}"

      - name: Install uv
        uses: astral-sh/setup-uv@v5

      - name: Create version name
        id: create-version
        run: |
          VERSION=$(uv run --script scripts/release.py generate-version)
          echo "version $VERSION"
          echo "version=$VERSION" >> $GITHUB_OUTPUT

      - name: Create notes
        run: |
          HASH="${{ steps.last-release.outputs.hash }}"
          uv run --script scripts/release.py generate-notes --directory src/ $HASH > RELEASE_NOTES.md
          cat RELEASE_NOTES.md

      - name: Release notes
        uses: actions/upload-artifact@v6
        with:
          name: release-notes
          path: RELEASE_NOTES.md

      - name: Create python matrix
        id: create-pypi-packages
        run: |
          HASH="${{ steps.last-release.outputs.hash }}"
          PYPI=$(uv run --script scripts/release.py generate-matrix --pypi --directory src $HASH)
          echo "pypi_packages $PYPI"
          echo "pypi_packages=$PYPI" >> $GITHUB_OUTPUT

      - name: Create npm matrix
        id: create-npm-packages
        run: |
          HASH="${{ steps.last-release.outputs.hash }}"
          NPM=$(uv run --script scripts/release.py generate-matrix --npm --directory src $HASH)
          echo "npm_packages $NPM"
          echo "npm_packages=$NPM" >> $GITHUB_OUTPUT

  update-packages:
    needs: [create-metadata]
    if: ${{ needs.create-metadata.outputs.npm_packages != '[]' || needs.create-metadata.outputs.pypi_packages != '[]' }}
    runs-on: ubuntu-latest
    environment: release
    permissions:
      contents: write
    outputs:
      changes_made: ${{ steps.commit.outputs.changes_made }}
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 0

      - name: Install uv
        uses: astral-sh/setup-uv@v5

      - name: Update packages
        run: |
          HASH="${{ needs.create-metadata.outputs.hash }}"
          uv run --script scripts/release.py update-packages --directory src/ $HASH

      - name: Configure git
        run: |
          git config --global user.name "GitHub Actions"
          git config --global user.email "actions@github.com"

      - name: Commit changes
        id: commit
        run: |
          VERSION="${{ needs.create-metadata.outputs.version }}"
          git add -u
          if git diff-index --quiet HEAD; then
            echo "changes_made=false" >> $GITHUB_OUTPUT
          else
            git commit -m 'Automatic update of packages'
            git tag -a "$VERSION" -m "Release $VERSION"
            git push origin "$VERSION"
            echo "changes_made=true" >> $GITHUB_OUTPUT
          fi

  publish-pypi:
    needs: [update-packages, create-metadata]
    if: ${{ needs.create-metadata.outputs.pypi_packages != '[]' && needs.create-metadata.outputs.pypi_packages != '' }}
    strategy:
      fail-fast: false
      matrix:
        package: ${{ fromJson(needs.create-metadata.outputs.pypi_packages) }}
    name: Build ${{ matrix.package }}
    environment: release
    permissions:
      id-token: write # Required for trusted publishing
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
        with:
          ref: ${{ needs.create-metadata.outputs.version }}

      - name: Install uv
        uses: astral-sh/setup-uv@v5

      - name: Set up Python
        uses: actions/setup-python@v6
        with:
          python-version-file: "src/${{ matrix.package }}/.python-version"

      - name: Install dependencies
        working-directory: src/${{ matrix.package }}
        run: uv sync --frozen --all-extras --dev

      - name: Run pyright
        working-directory: src/${{ matrix.package }}
        run: uv run --frozen pyright

      - name: Build package
        working-directory: src/${{ matrix.package }}
        run: uv build

      - name: Publish package to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          packages-dir: src/${{ matrix.package }}/dist

  publish-npm:
    needs: [update-packages, create-metadata]
    if: ${{ needs.create-metadata.outputs.npm_packages != '[]' && needs.create-metadata.outputs.npm_packages != '' }}
    strategy:
      fail-fast: false
      matrix:
        package: ${{ fromJson(needs.create-metadata.outputs.npm_packages) }}
    name: Build ${{ matrix.package }}
    environment: release
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
        with:
          ref: ${{ needs.create-metadata.outputs.version }}

      - uses: actions/setup-node@v6
        with:
          node-version: 22
          cache: npm
          registry-url: 'https://registry.npmjs.org'

      - name: Install dependencies
        working-directory: src/${{ matrix.package }}
        run: npm ci

      - name: Check if version exists on npm
        working-directory: src/${{ matrix.package }}
        run: |
          VERSION=$(jq -r .version package.json)
          if npm view --json | jq -e --arg version "$VERSION" '[.[]][0].versions | contains([$version])'; then
            echo "Version $VERSION already exists on npm"
            exit 1
          fi
          echo "Version $VERSION is new, proceeding with publish"

      - name: Build package
        working-directory: src/${{ matrix.package }}
        run: npm run build

      - name: Publish package
        working-directory: src/${{ matrix.package }}
        run: |
          npm publish --access public
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

  create-release:
    needs: [update-packages, create-metadata, publish-pypi, publish-npm]
    if: |
      always() &&
      needs.update-packages.outputs.changes_made == 'true' &&
      (needs.publish-pypi.result == 'success' || needs.publish-npm.result == 'success')
    runs-on: ubuntu-latest
    environment: release
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v6

      - name: Download release notes
        uses: actions/download-artifact@v7
        with:
          name: release-notes

      - name: Create release
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN}}
        run: |
          VERSION="${{ needs.create-metadata.outputs.version }}"
          gh release create "$VERSION" \
            --title "Release $VERSION" \
            --notes-file RELEASE_NOTES.md

typescript matrix .github/workflows/typescript.yml
Triggers
push, pull_request, release
Runs on
ubuntu-latest, ubuntu-latest, ubuntu-latest, ubuntu-latest
Jobs
detect-packages, test, build, publish
Matrix
package→ ${{ fromJson(needs.detect-packages.outputs.packages) }}
Commands
  • PACKAGES=$(find . -name package.json -not -path "*/node_modules/*" -exec dirname {} \; | sed 's/^\.\///' | jq -R -s -c 'split("\n")[:-1]') echo "packages=$PACKAGES" >> $GITHUB_OUTPUT
  • npm ci
  • npm test --if-present
  • npm ci
  • npm run build
  • npm ci
  • npm publish --access public
View raw YAML
name: TypeScript

on:
  push:
    branches:
      - main
  pull_request:
  release:
    types: [published]

jobs:
  detect-packages:
    runs-on: ubuntu-latest
    outputs:
      packages: ${{ steps.find-packages.outputs.packages }}
    steps:
      - uses: actions/checkout@v6
      - name: Find JS packages
        id: find-packages
        working-directory: src
        run: |
          PACKAGES=$(find . -name package.json -not -path "*/node_modules/*" -exec dirname {} \; | sed 's/^\.\///' | jq -R -s -c 'split("\n")[:-1]')
          echo "packages=$PACKAGES" >> $GITHUB_OUTPUT

  test:
    needs: [detect-packages]
    strategy:
      matrix:
        package: ${{ fromJson(needs.detect-packages.outputs.packages) }}
    name: Test ${{ matrix.package }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - uses: actions/setup-node@v6
        with:
          node-version: 22
          cache: npm

      - name: Install dependencies
        working-directory: src/${{ matrix.package }}
        run: npm ci

      - name: Run tests
        working-directory: src/${{ matrix.package }}
        run: npm test --if-present

  build:
    needs: [detect-packages, test]
    strategy:
      matrix:
        package: ${{ fromJson(needs.detect-packages.outputs.packages) }}
    name: Build ${{ matrix.package }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - uses: actions/setup-node@v6
        with:
          node-version: 22
          cache: npm

      - name: Install dependencies
        working-directory: src/${{ matrix.package }}
        run: npm ci

      - name: Build package
        working-directory: src/${{ matrix.package }}
        run: npm run build

  publish:
    runs-on: ubuntu-latest
    needs: [build, detect-packages]
    if: github.event_name == 'release'
    environment: release

    strategy:
      matrix:
        package: ${{ fromJson(needs.detect-packages.outputs.packages) }}
    name: Publish ${{ matrix.package }}

    permissions:
      contents: read
      id-token: write

    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-node@v6
        with:
          node-version: 22
          cache: npm
          registry-url: "https://registry.npmjs.org"

      - name: Install dependencies
        working-directory: src/${{ matrix.package }}
        run: npm ci

      - name: Publish package
        working-directory: src/${{ matrix.package }}
        run: npm publish --access public
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}