Skip to content

chore(deps): update ghcr.io/stacklok/dockyard/npx/brightdata-mcp docker tag to v2.8.4 #681

chore(deps): update ghcr.io/stacklok/dockyard/npx/brightdata-mcp docker tag to v2.8.4

chore(deps): update ghcr.io/stacklok/dockyard/npx/brightdata-mcp docker tag to v2.8.4 #681

Workflow file for this run

name: Update MCP Server Tool Lists
on:
pull_request:
paths:
- 'registry/**/spec.yaml'
- 'registry/**/spec.yml'
workflow_dispatch:
inputs:
server:
description: 'Specific server to update (leave empty for all changed)'
required: false
type: string
permissions:
contents: write
pull-requests: write
jobs:
# Security check for fork PRs - prevents untrusted code execution with write permissions
security-check:
name: Security Check for Fork PRs
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
outputs:
is-fork: ${{ steps.check-fork.outputs.is-fork }}
has-label: ${{ steps.check-label.outputs.has-label }}
should-run: ${{ steps.check-fork.outputs.should-run }}
steps:
- name: Check if PR is from a fork
id: check-fork
run: |
# Get PR details from GitHub API
PR_DATA=$(gh pr view ${{ github.event.pull_request.number }} --json isCrossRepository,labels --repo ${{ github.repository }})
IS_FORK=$(echo "$PR_DATA" | jq -r '.isCrossRepository')
echo "is-fork=$IS_FORK" >> $GITHUB_OUTPUT
if [ "$IS_FORK" = "false" ]; then
echo "PR is from the same repository - workflow will run automatically"
echo "should-run=true" >> $GITHUB_OUTPUT
else
echo "PR is from a fork - checking for 'safe-to-update' label"
# Check if PR has the safe-to-update label
HAS_LABEL=$(echo "$PR_DATA" | jq -r '.labels | map(select(.name == "safe-to-update")) | length > 0')
if [ "$HAS_LABEL" = "true" ]; then
echo "PR has 'safe-to-update' label - workflow will run"
echo "should-run=true" >> $GITHUB_OUTPUT
else
echo "PR does not have 'safe-to-update' label - workflow will be skipped"
echo "should-run=false" >> $GITHUB_OUTPUT
fi
fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Check label status
id: check-label
if: steps.check-fork.outputs.is-fork == 'true'
run: |
PR_DATA=$(gh pr view ${{ github.event.pull_request.number }} --json labels --repo ${{ github.repository }})
HAS_LABEL=$(echo "$PR_DATA" | jq -r '.labels | map(select(.name == "safe-to-update")) | length > 0')
echo "has-label=$HAS_LABEL" >> $GITHUB_OUTPUT
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Post instructions for fork PRs without label
if: steps.check-fork.outputs.is-fork == 'true' && steps.check-fork.outputs.should-run == 'false'
uses: peter-evans/create-or-update-comment@v5
with:
issue-number: ${{ github.event.pull_request.number }}
body: |
## Security Notice: Fork PR Tool Update Workflow
This PR is from a forked repository. For security reasons, the automatic tool list update workflow requires a maintainer to add the `safe-to-update` label before it will run.
A maintainer will review this PR and add the label if appropriate. The workflow will then automatically update the tool lists.
---
**Why is this needed?** This workflow executes code and connects to MCP servers specified in spec files. To prevent potential security issues, we require manual verification for fork PRs.
detect-changes:
name: Detect Changed Specs
runs-on: ubuntu-latest
needs: [security-check]
if: |
always() &&
(github.event_name == 'workflow_dispatch' ||
(github.event_name == 'pull_request' && needs.security-check.outputs.should-run == 'true'))
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
has-changes: ${{ steps.set-matrix.outputs.has-changes }}
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Get changed files
id: changed-files
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
# For PRs, get changed files between base and head
BASE_SHA="${{ github.event.pull_request.base.sha }}"
HEAD_SHA="${{ github.event.pull_request.head.sha }}"
# Get changed spec files
CHANGED_FILES=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA" | grep -E '^registry/.*/spec\.ya?ml$' || true)
else
# For workflow_dispatch, get files changed in the last commit or all files if no recent changes
CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD | grep -E '^registry/.*/spec\.ya?ml$' || true)
# If no files in last commit, we'll handle this in the matrix step
fi
# Convert to JSON array
if [ -z "$CHANGED_FILES" ]; then
echo "all_changed_files=[]" >> $GITHUB_OUTPUT
else
# Convert newline-separated list to JSON array
JSON_ARRAY=$(echo "$CHANGED_FILES" | jq -R -s -c 'split("\n") | map(select(length > 0))')
echo "all_changed_files=$JSON_ARRAY" >> $GITHUB_OUTPUT
fi
- name: Set matrix for changed specs
id: set-matrix
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ -n "${{ inputs.server }}" ]; then
# Manual run with specific server
SPEC_FILE="registry/${{ inputs.server }}/spec.yaml"
if [ ! -f "$SPEC_FILE" ]; then
SPEC_FILE="registry/${{ inputs.server }}/spec.yml"
fi
if [ -f "$SPEC_FILE" ]; then
echo "matrix={\"spec\":[\"$SPEC_FILE\"]}" >> $GITHUB_OUTPUT
echo "has-changes=true" >> $GITHUB_OUTPUT
else
echo "Error: Server ${{ inputs.server }} not found"
echo "has-changes=false" >> $GITHUB_OUTPUT
exit 1
fi
elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
# Manual run without specific server - process all spec files
echo "Manual run without specific server - processing all spec files"
ALL_SPECS=$(find registry -name "spec.yaml" -o -name "spec.yml" | sort)
if [ -z "$ALL_SPECS" ]; then
echo "No spec files found"
echo "has-changes=false" >> $GITHUB_OUTPUT
else
# Convert to JSON array
JSON_ARRAY=$(echo "$ALL_SPECS" | jq -R -s -c 'split("\n") | map(select(length > 0))')
MATRIX=$(echo "$JSON_ARRAY" | jq -c '{spec: .}')
echo "matrix=$MATRIX" >> $GITHUB_OUTPUT
echo "has-changes=true" >> $GITHUB_OUTPUT
# Log the files that will be processed
echo "Files to process:"
echo "$JSON_ARRAY" | jq -r '.[]'
fi
else
# PR - use changed files
CHANGED_FILES='${{ steps.changed-files.outputs.all_changed_files }}'
if [ "$CHANGED_FILES" = "[]" ]; then
echo "No spec files changed"
echo "has-changes=false" >> $GITHUB_OUTPUT
else
# Convert the JSON array to matrix format
MATRIX=$(echo "$CHANGED_FILES" | jq -c '{spec: .}')
echo "matrix=$MATRIX" >> $GITHUB_OUTPUT
echo "has-changes=true" >> $GITHUB_OUTPUT
# Log the files that will be processed
echo "Files to process:"
echo "$CHANGED_FILES" | jq -r '.[]'
fi
fi
update-tools:
name: Update Tools for ${{ matrix.spec }}
needs: detect-changes
if: needs.detect-changes.outputs.has-changes == 'true'
runs-on: ubuntu-latest
strategy:
matrix: ${{ fromJson(needs.detect-changes.outputs.matrix) }}
fail-fast: false
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
ref: ${{ github.head_ref || github.ref }}
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: 'go.mod'
cache: true
- name: Install ToolHive
uses: StacklokLabs/toolhive-actions/install@v0
with:
version: 'v0.2.9'
- name: Build update-tools
run: |
echo "Building update-tools..."
go build -o update-tools ./cmd/update-tools
- name: Extract server name
id: server-info
run: |
SERVER_DIR=$(dirname "${{ matrix.spec }}")
SERVER_NAME=$(basename "$SERVER_DIR")
echo "server-name=$SERVER_NAME" >> $GITHUB_OUTPUT
echo "Processing server: $SERVER_NAME"
- name: Update tool list
id: update
run: |
echo "Updating tools for ${{ steps.server-info.outputs.server-name }}..."
# Run the update tool
if ./update-tools "${{ matrix.spec }}" -v; then
echo "update-status=success" >> $GITHUB_OUTPUT
# Check if file was modified
if git diff --quiet "${{ matrix.spec }}"; then
echo "No changes needed for ${{ matrix.spec }}"
echo "changed=false" >> $GITHUB_OUTPUT
else
echo "Tools updated for ${{ matrix.spec }}"
echo "changed=true" >> $GITHUB_OUTPUT
# Get the diff for the comment
DIFF=$(git diff "${{ matrix.spec }}" | head -100)
echo "diff<<EOF" >> $GITHUB_OUTPUT
echo "$DIFF" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
fi
else
echo "update-status=failed" >> $GITHUB_OUTPUT
echo "changed=false" >> $GITHUB_OUTPUT
# Check if warning was added
if git diff "${{ matrix.spec }}" | grep -q "WARNING"; then
echo "Warning comment added to spec file"
echo "warning-added=true" >> $GITHUB_OUTPUT
else
echo "warning-added=false" >> $GITHUB_OUTPUT
fi
fi
- name: Prepare changes for commit
if: (steps.update.outputs.changed == 'true' || steps.update.outputs.warning-added == 'true') && github.event_name == 'pull_request'
run: |
# Create artifact directory with both metadata and the actual changed file
SERVER_NAME="${{ steps.server-info.outputs.server-name }}"
mkdir -p /tmp/commit-info
if [ "${{ steps.update.outputs.changed }}" = "true" ]; then
echo "update" > "/tmp/commit-info/${SERVER_NAME}.type"
echo "$SERVER_NAME" > "/tmp/commit-info/${SERVER_NAME}.name"
echo "${{ matrix.spec }}" > "/tmp/commit-info/${SERVER_NAME}.spec"
else
echo "warning" > "/tmp/commit-info/${SERVER_NAME}.type"
echo "$SERVER_NAME" > "/tmp/commit-info/${SERVER_NAME}.name"
echo "${{ matrix.spec }}" > "/tmp/commit-info/${SERVER_NAME}.spec"
fi
# Copy the modified spec file to the artifact directory
cp "${{ matrix.spec }}" "/tmp/commit-info/${SERVER_NAME}.spec.yaml"
- name: Upload commit info
if: (steps.update.outputs.changed == 'true' || steps.update.outputs.warning-added == 'true') && github.event_name == 'pull_request'
uses: actions/upload-artifact@v6
with:
name: commit-info-${{ steps.server-info.outputs.server-name }}
path: /tmp/commit-info/
- name: Output diff for workflow dispatch
if: github.event_name == 'workflow_dispatch' && (steps.update.outputs.changed == 'true' || steps.update.outputs.warning-added == 'true')
run: |
SERVER_NAME="${{ steps.server-info.outputs.server-name }}"
echo "## 🔧 Tool List Update for $SERVER_NAME" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ steps.update.outputs.changed }}" = "true" ]; then
echo "✅ **Status**: Successfully updated tool list" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Changes Made:" >> $GITHUB_STEP_SUMMARY
echo '```diff' >> $GITHUB_STEP_SUMMARY
git diff "${{ matrix.spec }}" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "⚠️ **Note**: Changes were not committed because this is a workflow dispatch run. To apply these changes:" >> $GITHUB_STEP_SUMMARY
echo "1. Create a new branch" >> $GITHUB_STEP_SUMMARY
echo "2. Apply the changes shown above" >> $GITHUB_STEP_SUMMARY
echo "3. Open a pull request" >> $GITHUB_STEP_SUMMARY
else
echo "❌ **Status**: Tool list update failed, warning added" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Warning Added:" >> $GITHUB_STEP_SUMMARY
echo '```diff' >> $GITHUB_STEP_SUMMARY
git diff "${{ matrix.spec }}" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Server**: $SERVER_NAME" >> $GITHUB_STEP_SUMMARY
echo "**Spec File**: \`${{ matrix.spec }}\`" >> $GITHUB_STEP_SUMMARY
echo "**Triggered by**: @${{ github.actor }}" >> $GITHUB_STEP_SUMMARY
commit-all-changes:
name: Commit All Tool Updates
needs: update-tools
if: always() && needs.detect-changes.outputs.has-changes == 'true' && github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
ref: ${{ github.head_ref || github.ref }}
- name: Download all commit info artifacts
uses: actions/download-artifact@v7
with:
path: /tmp/artifacts/
pattern: commit-info-*
merge-multiple: true
- name: Check if any changes were made
id: check_changes
run: |
if [ -d "/tmp/artifacts" ] && [ "$(ls -A /tmp/artifacts 2>/dev/null)" ]; then
echo "changes=true" >> $GITHUB_OUTPUT
echo "Found commit info files:"
ls -la /tmp/artifacts/
else
echo "changes=false" >> $GITHUB_OUTPUT
echo "No changes to commit"
fi
- name: Commit and push all changes
if: steps.check_changes.outputs.changes == 'true'
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
# Pull any remote changes first to avoid conflicts
git pull origin ${{ github.head_ref || github.ref_name }} --rebase
# Collect all the server names that were updated
UPDATED_SERVERS=""
WARNING_SERVERS=""
for file in /tmp/artifacts/*.type; do
if [ -f "$file" ]; then
SERVER_NAME=$(basename "$file" .type)
TYPE=$(cat "$file")
if [ "$TYPE" = "update" ]; then
UPDATED_SERVERS="$UPDATED_SERVERS $SERVER_NAME"
else
WARNING_SERVERS="$WARNING_SERVERS $SERVER_NAME"
fi
# Restore the modified spec file from the artifact
SPEC_FILE=$(cat "/tmp/artifacts/${SERVER_NAME}.spec")
if [ -f "/tmp/artifacts/${SERVER_NAME}.spec.yaml" ]; then
cp "/tmp/artifacts/${SERVER_NAME}.spec.yaml" "$SPEC_FILE"
git add "$SPEC_FILE"
else
echo "Warning: Modified spec file not found in artifacts for $SERVER_NAME"
fi
fi
done
# Create commit message
COMMIT_MSG="chore: update tool lists for MCP servers"
if [ -n "$UPDATED_SERVERS" ]; then
COMMIT_MSG="$COMMIT_MSG\n\nUpdated servers:$(echo $UPDATED_SERVERS | sed 's/ /\\n- /g' | sed 's/^/\\n- /')"
fi
if [ -n "$WARNING_SERVERS" ]; then
COMMIT_MSG="$COMMIT_MSG\n\nWarning added for servers:$(echo $WARNING_SERVERS | sed 's/ /\\n- /g' | sed 's/^/\\n- /')"
fi
COMMIT_MSG="$COMMIT_MSG\n\nAutomatically updated using 'thv mcp list' command.\n\nCo-authored-by: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>"
# Check if there are actually any changes to commit
if ! git diff --cached --quiet; then
git commit -m "$COMMIT_MSG"
git push
echo "✅ Successfully committed and pushed all tool updates"
else
echo "ℹ️ No changes to commit (files may have been identical)"
fi
comment-summary:
name: Post Summary Comment
needs: [detect-changes, update-tools]
if: always() && needs.detect-changes.outputs.has-changes == 'true'
runs-on: ubuntu-latest
steps:
- name: Find existing comment
if: github.event_name == 'pull_request'
uses: peter-evans/find-comment@v4
id: find-comment
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
body-includes: '## 🔧 MCP Server Tool List Updates'
- name: Download all commit info artifacts
uses: actions/download-artifact@v7
with:
path: /tmp/comment-artifacts/
pattern: commit-info-*
merge-multiple: true
- name: Generate summary from artifacts
id: generate-summary
run: |
SUMMARY=""
if [ -d "/tmp/comment-artifacts" ] && [ "$(ls -A /tmp/comment-artifacts 2>/dev/null)" ]; then
for file in /tmp/comment-artifacts/*.type; do
if [ -f "$file" ]; then
SERVER_NAME=$(basename "$file" .type)
TYPE=$(cat "$file")
if [ "$TYPE" = "update" ]; then
SUMMARY="$SUMMARY| $SERVER_NAME | ✅ Updated | Tool list refreshed |\n"
else
SUMMARY="$SUMMARY| $SERVER_NAME | ⚠️ Warning | Could not fetch tools, added warning comment |\n"
fi
fi
done
fi
if [ -z "$SUMMARY" ]; then
SUMMARY="| _No changes detected_ | | |"
fi
echo "summary<<EOF" >> $GITHUB_OUTPUT
echo -e "$SUMMARY" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Create or update comment
if: github.event_name == 'pull_request'
uses: peter-evans/create-or-update-comment@v5
with:
comment-id: ${{ steps.find-comment.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
body: |
## 🔧 MCP Server Tool List Updates
The tool lists for modified MCP server specs have been automatically updated using `thv mcp list`.
### Summary
| Server | Status | Details |
|--------|--------|---------|
${{ steps.generate-summary.outputs.summary }}
---
_This comment is automatically generated and will be updated as the workflow progresses._
edit-mode: replace
validate-after-update:
name: Validate Updated Specs
needs: update-tools
if: always()
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
ref: ${{ github.head_ref || github.ref }}
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: 'go.mod'
cache: true
- name: Build registry-builder
run: go build -o registry-builder ./cmd/registry-builder
- name: Validate all specs
run: |
echo "Validating all registry entries after updates..."
./registry-builder validate -v