Skip to content

chore(deps): update ghcr.io/stackloklabs/toolhive-doc-mcp docker tag to v0.0.6 #1095

chore(deps): update ghcr.io/stackloklabs/toolhive-doc-mcp docker tag to v0.0.6

chore(deps): update ghcr.io/stackloklabs/toolhive-doc-mcp docker tag to v0.0.6 #1095

name: Build and Publish Registry
on:
push:
branches:
- main
pull_request:
branches:
- main
schedule:
# Run daily at 00:00 UTC to catch any updates
- cron: '0 0 * * *'
workflow_dispatch:
permissions:
contents: write
pull-requests: read
jobs:
lint:
name: Lint Go Code
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: 'go.mod'
cache: true
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v9
with:
version: latest
args: --timeout=5m
validate-and-test:
name: Validate and Test
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: 'go.mod'
cache: true
- name: Install dependencies
run: go mod download
- name: Run tests
run: go test -v -race -coverprofile=coverage.out ./...
- name: Upload coverage reports
uses: codecov/codecov-action@v5
with:
file: ./coverage.out
flags: unittests
name: codecov-umbrella
continue-on-error: true
- name: Install Task
uses: arduino/setup-task@v2
- name: Validate registry entries
run: task validate
build-and-release:
name: Build and Release Registry
runs-on: ubuntu-latest
needs: [lint, validate-and-test]
if: github.ref == 'refs/heads/main' && (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: 'go.mod'
cache: true
- name: Install Task
uses: arduino/setup-task@v2
- name: Build registry files (both formats)
run: |
mkdir -p dist
task build:registry
cp build/registry.json dist/registry.json
cp build/official-registry.json dist/official-registry.json
CONTAINER_COUNT=$(jq '.servers | length' dist/registry.json)
REMOTE_COUNT=$(jq '.remote_servers | length // 0' dist/registry.json)
TOTAL_COUNT=$((CONTAINER_COUNT + REMOTE_COUNT))
echo "Registry built successfully with $TOTAL_COUNT entries ($CONTAINER_COUNT container-based, $REMOTE_COUNT remote)"
echo "Both ToolHive and official MCP formats generated"
- name: Validate JSON files
run: |
echo "Validating ToolHive format..."
# Validate JSON structure
jq empty dist/registry.json
echo "βœ… Valid JSON"
# Check required fields
jq -e '.last_updated' dist/registry.json > /dev/null
echo "βœ… Has last_updated field"
jq -e '.servers' dist/registry.json > /dev/null
echo "βœ… Has servers field"
# Check for remote_servers field (may be empty)
jq -e 'has("remote_servers")' dist/registry.json > /dev/null && echo "βœ… Has remote_servers field" || echo "⚠️ No remote_servers field"
jq -e '."$schema"' dist/registry.json > /dev/null
echo "βœ… Has schema field"
echo ""
echo "Validating Official MCP format..."
# Validate official registry JSON structure
jq empty dist/official-registry.json
echo "βœ… Valid JSON"
# Check required fields for official format
jq -e '.version' dist/official-registry.json > /dev/null
echo "βœ… Has version field"
jq -e '.meta.last_updated' dist/official-registry.json > /dev/null
echo "βœ… Has meta.last_updated field"
jq -e '.data.servers' dist/official-registry.json > /dev/null
echo "βœ… Has data.servers field"
# Check that servers have the flattened structure with _meta
SERVER_COUNT=$(jq '.data.servers | length' dist/official-registry.json)
if [ "$SERVER_COUNT" -gt 0 ]; then
jq -e '.data.servers[0].name' dist/official-registry.json > /dev/null
echo "βœ… Servers have name field"
jq -e '.data.servers[0]._meta."io.modelcontextprotocol.registry/publisher-provided"' dist/official-registry.json > /dev/null
echo "βœ… Servers have _meta with publisher-provided extensions"
fi
- name: Generate metadata
id: metadata
run: |
# Generate version based on date and time
VERSION=$(date +'%Y.%m.%d')
TIMESTAMP=$(date +'%Y%m%d-%H%M%S')
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "timestamp=$TIMESTAMP" >> $GITHUB_OUTPUT
# Count servers by status and tier (including both container and remote servers)
CONTAINER_COUNT=$(jq '.servers | length' dist/registry.json)
REMOTE_COUNT=$(jq '.remote_servers | length // 0' dist/registry.json)
TOTAL=$((CONTAINER_COUNT + REMOTE_COUNT))
# Count by status (combine container and remote servers)
ACTIVE_CONTAINER=$(jq '[.servers[] | select(.status == "Active")] | length' dist/registry.json)
ACTIVE_REMOTE=$(jq '[(.remote_servers // {})[] | select(.status == "Active")] | length' dist/registry.json)
ACTIVE=$((ACTIVE_CONTAINER + ACTIVE_REMOTE))
BETA_CONTAINER=$(jq '[.servers[] | select(.status == "Beta")] | length' dist/registry.json)
BETA_REMOTE=$(jq '[(.remote_servers // {})[] | select(.status == "Beta")] | length' dist/registry.json)
BETA=$((BETA_CONTAINER + BETA_REMOTE))
DEPRECATED_CONTAINER=$(jq '[.servers[] | select(.status == "Deprecated")] | length' dist/registry.json)
DEPRECATED_REMOTE=$(jq '[(.remote_servers // {})[] | select(.status == "Deprecated")] | length' dist/registry.json)
DEPRECATED=$((DEPRECATED_CONTAINER + DEPRECATED_REMOTE))
# Count by tier (combine container and remote servers)
OFFICIAL_CONTAINER=$(jq '[.servers[] | select(.tier == "Official")] | length' dist/registry.json)
OFFICIAL_REMOTE=$(jq '[(.remote_servers // {})[] | select(.tier == "Official")] | length' dist/registry.json)
OFFICIAL=$((OFFICIAL_CONTAINER + OFFICIAL_REMOTE))
PARTNER_CONTAINER=$(jq '[.servers[] | select(.tier == "Partner")] | length' dist/registry.json)
PARTNER_REMOTE=$(jq '[(.remote_servers // {})[] | select(.tier == "Partner")] | length' dist/registry.json)
PARTNER=$((PARTNER_CONTAINER + PARTNER_REMOTE))
COMMUNITY_CONTAINER=$(jq '[.servers[] | select(.tier == "Community")] | length' dist/registry.json)
COMMUNITY_REMOTE=$(jq '[(.remote_servers // {})[] | select(.tier == "Community")] | length' dist/registry.json)
COMMUNITY=$((COMMUNITY_CONTAINER + COMMUNITY_REMOTE))
echo "total=$TOTAL" >> $GITHUB_OUTPUT
echo "container_count=$CONTAINER_COUNT" >> $GITHUB_OUTPUT
echo "remote_count=$REMOTE_COUNT" >> $GITHUB_OUTPUT
echo "active=$ACTIVE" >> $GITHUB_OUTPUT
echo "beta=$BETA" >> $GITHUB_OUTPUT
echo "deprecated=$DEPRECATED" >> $GITHUB_OUTPUT
echo "official=$OFFICIAL" >> $GITHUB_OUTPUT
echo "partner=$PARTNER" >> $GITHUB_OUTPUT
echo "community=$COMMUNITY" >> $GITHUB_OUTPUT
- name: Create checksums
run: |
cd dist
sha256sum registry.json > registry.json.sha256
md5sum registry.json > registry.json.md5
sha256sum official-registry.json > official-registry.json.sha256
md5sum official-registry.json > official-registry.json.md5
- name: Create tarball
run: |
cd dist
tar -czf registry-${{ steps.metadata.outputs.version }}.tar.gz \
registry.json registry.json.sha256 registry.json.md5 \
official-registry.json official-registry.json.sha256 official-registry.json.md5
tar -tzf registry-${{ steps.metadata.outputs.version }}.tar.gz
- name: Check if release exists
id: check_release
run: |
if gh release view "v${{ steps.metadata.outputs.version }}" >/dev/null 2>&1; then
echo "exists=true" >> $GITHUB_OUTPUT
echo "Release v${{ steps.metadata.outputs.version }} already exists"
else
echo "exists=false" >> $GITHUB_OUTPUT
echo "Release v${{ steps.metadata.outputs.version }} does not exist"
fi
env:
GH_TOKEN: ${{ github.token }}
- name: Get changes since last release
id: changes
if: steps.check_release.outputs.exists == 'false'
run: |
# Get the last release tag
LAST_TAG=$(gh release list --limit 1 --json tagName -q '.[0].tagName' || echo "")
if [ -z "$LAST_TAG" ]; then
echo "No previous release found"
CHANGES="Initial release"
else
echo "Last release: $LAST_TAG"
# Get commit messages since last release
CHANGES=$(git log --pretty=format:"- %s" $LAST_TAG..HEAD --grep="^feat\|^fix\|^docs\|^chore" | head -20)
if [ -z "$CHANGES" ]; then
CHANGES="- Minor updates and maintenance"
fi
fi
# Write to file to preserve formatting
echo "$CHANGES" > changes.txt
env:
GH_TOKEN: ${{ github.token }}
- name: Create Release
if: steps.check_release.outputs.exists == 'false'
uses: ncipollo/release-action@v1
with:
tag: v${{ steps.metadata.outputs.version }}
name: Registry v${{ steps.metadata.outputs.version }}
body: |
## πŸ“¦ ToolHive Registry Snapshot
**Date**: ${{ steps.metadata.outputs.version }}
**Build**: ${{ steps.metadata.outputs.timestamp }}
### πŸ“Š Statistics
| Category | Count |
|----------|-------|
| **Total Servers** | ${{ steps.metadata.outputs.total }} |
| **Container-based** | ${{ steps.metadata.outputs.container_count }} |
| **Remote** | ${{ steps.metadata.outputs.remote_count }} |
| **Active** | ${{ steps.metadata.outputs.active }} |
| **Beta** | ${{ steps.metadata.outputs.beta }} |
| **Deprecated** | ${{ steps.metadata.outputs.deprecated }} |
| Tier | Count |
|------|-------|
| **Official** | ${{ steps.metadata.outputs.official }} |
| **Partner** | ${{ steps.metadata.outputs.partner }} |
| **Community** | ${{ steps.metadata.outputs.community }} |
### πŸ“₯ Download Options
**Individual Files:**
- **registry.json** - ToolHive format registry file
- **official-registry.json** - Official MCP format registry file
**Archives:**
- **registry-${{ steps.metadata.outputs.version }}.tar.gz** - Complete archive with both formats and checksums
### πŸ”— Direct URLs
**ToolHive Format:**
- Latest: `https://github.com/stacklok/toolhive-registry/releases/latest/download/registry.json`
- This version: `https://github.com/stacklok/toolhive-registry/releases/download/v${{ steps.metadata.outputs.version }}/registry.json`
**Official MCP Format:**
- Latest: `https://github.com/stacklok/toolhive-registry/releases/latest/download/official-registry.json`
- This version: `https://github.com/stacklok/toolhive-registry/releases/download/v${{ steps.metadata.outputs.version }}/official-registry.json`
### πŸ“ Recent Changes
${{ steps.changes.outputs.changes }}
---
*This is an automated release generated from the main branch.*
artifacts: |
dist/registry.json
dist/registry.json.sha256
dist/registry.json.md5
dist/official-registry.json
dist/official-registry.json.sha256
dist/official-registry.json.md5
dist/registry-${{ steps.metadata.outputs.version }}.tar.gz
makeLatest: true
artifactErrorsFailBuild: true
- name: Update existing release
if: steps.check_release.outputs.exists == 'true'
run: |
echo "Updating existing release v${{ steps.metadata.outputs.version }}"
# Delete old assets
gh release delete-asset "v${{ steps.metadata.outputs.version }}" registry.json --yes || true
gh release delete-asset "v${{ steps.metadata.outputs.version }}" registry.json.sha256 --yes || true
gh release delete-asset "v${{ steps.metadata.outputs.version }}" registry.json.md5 --yes || true
gh release delete-asset "v${{ steps.metadata.outputs.version }}" official-registry.json --yes || true
gh release delete-asset "v${{ steps.metadata.outputs.version }}" official-registry.json.sha256 --yes || true
gh release delete-asset "v${{ steps.metadata.outputs.version }}" official-registry.json.md5 --yes || true
gh release delete-asset "v${{ steps.metadata.outputs.version }}" "registry-${{ steps.metadata.outputs.version }}.tar.gz" --yes || true
# Upload new assets
gh release upload "v${{ steps.metadata.outputs.version }}" \
dist/registry.json \
dist/registry.json.sha256 \
dist/registry.json.md5 \
dist/official-registry.json \
dist/official-registry.json.sha256 \
dist/official-registry.json.md5 \
"dist/registry-${{ steps.metadata.outputs.version }}.tar.gz" \
--clobber
echo "βœ… Release updated successfully"
env:
GH_TOKEN: ${{ github.token }}
build-pr:
name: Build PR Preview
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- name: Checkout code
uses: actions/checkout@v6
- 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: Build registry.json
run: |
mkdir -p build
./registry-builder build -v
- name: Generate PR comment
run: |
CONTAINER_COUNT=$(jq '.servers | length' build/registry.json)
REMOTE_COUNT=$(jq '.remote_servers | length // 0' build/registry.json)
TOTAL=$((CONTAINER_COUNT + REMOTE_COUNT))
SIZE=$(du -h build/registry.json | cut -f1)
echo "## πŸ“¦ Registry Build Preview" > pr-comment.md
echo "" >> pr-comment.md
echo "βœ… Registry built successfully!" >> pr-comment.md
echo "" >> pr-comment.md
echo "- **Total Servers**: $TOTAL" >> pr-comment.md
echo " - Container-based: $CONTAINER_COUNT" >> pr-comment.md
echo " - Remote: $REMOTE_COUNT" >> pr-comment.md
echo "- **File Size**: $SIZE" >> pr-comment.md
echo "- **Last Updated**: $(jq -r '.last_updated' build/registry.json)" >> pr-comment.md
echo "" >> pr-comment.md
echo "The registry.json will be published when this PR is merged." >> pr-comment.md
- name: Upload PR artifact
uses: actions/upload-artifact@v5
with:
name: pr-registry-json
path: build/registry.json
retention-days: 7