Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 40 additions & 21 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,17 +135,26 @@ jobs:
if [ -n "$EXISTING_PR" ]; then
echo "PR #$EXISTING_PR already exists, updated branch"
else
# Build file list dynamically
FILE_LIST=""
for registry_dir in pkg/catalog/*/; do
registry_name=$(basename "$registry_dir")
[ -d "$registry_dir/data" ] || continue
FILE_LIST="${FILE_LIST}- \`pkg/catalog/${registry_name}/data/registry.json\` — ${registry_name} legacy format
"
FILE_LIST="${FILE_LIST}- \`pkg/catalog/${registry_name}/data/official-registry.json\` — ${registry_name} upstream MCP format
"
done

gh pr create \
--title "chore: update catalog data files for v${VERSION}" \
--body "$(cat <<'BODY'
--body "$(cat <<EOF
Automated PR to update the embedded catalog data files.

Files updated:
- `pkg/catalog/toolhive/data/registry.json` — ToolHive legacy format
- `pkg/catalog/toolhive/data/official-registry.json` — Upstream MCP format

${FILE_LIST}
When merged, a release will be created automatically.
BODY
EOF
)" \
--head "$BRANCH"

Expand Down Expand Up @@ -182,25 +191,35 @@ jobs:
- name: Generate PR comment
id: comment
run: |
CONTAINER_COUNT=$(jq '.servers | length' build/toolhive/registry.json)
REMOTE_COUNT=$(jq '.remote_servers | length // 0' build/toolhive/registry.json)
TOTAL=$((CONTAINER_COUNT + REMOTE_COUNT))
SKILL_COUNT=$(jq '.data.skills | length // 0' build/toolhive/official-registry.json)
SIZE=$(du -h build/toolhive/registry.json | cut -f1)
LAST_UPDATED=$(jq -r '.last_updated' build/toolhive/registry.json)

{
echo "body<<EOF"
echo "## Registry Build Preview"
echo ""
echo "Registry built successfully!"
echo "All registries built successfully!"
echo ""
echo "- **Total Servers**: $TOTAL"
echo " - Container-based: $CONTAINER_COUNT"
echo " - Remote: $REMOTE_COUNT"
echo "- **Skills**: $SKILL_COUNT"
echo "- **File Size**: $SIZE"
echo "- **Last Updated**: $LAST_UPDATED"

for registry_dir in build/*/; do
registry_name=$(basename "$registry_dir")
[ -f "$registry_dir/registry.json" ] || continue

CONTAINER_COUNT=$(jq '.servers | length' "$registry_dir/registry.json")
REMOTE_COUNT=$(jq '.remote_servers | length // 0' "$registry_dir/registry.json")
TOTAL=$((CONTAINER_COUNT + REMOTE_COUNT))
SKILL_COUNT=$(jq '.data.skills | length // 0' "$registry_dir/official-registry.json")
SIZE=$(du -h "$registry_dir/registry.json" | cut -f1)
LAST_UPDATED=$(jq -r '.last_updated' "$registry_dir/registry.json")

echo "### Registry: \`$registry_name\`"
echo ""
echo "- **Total Servers**: $TOTAL"
echo " - Container-based: $CONTAINER_COUNT"
echo " - Remote: $REMOTE_COUNT"
echo "- **Skills**: $SKILL_COUNT"
echo "- **File Size**: $SIZE"
echo "- **Last Updated**: $LAST_UPDATED"
echo ""
done

echo "EOF"
} >> $GITHUB_OUTPUT

Expand Down Expand Up @@ -230,6 +249,6 @@ jobs:
with:
name: pr-registry-json
path: |
build/toolhive/registry.json
build/toolhive/official-registry.json
build/*/registry.json
build/*/official-registry.json
retention-days: 7
139 changes: 79 additions & 60 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,75 +26,94 @@ jobs:
echo "version=0.$(date +'%Y%m%d').0" >> $GITHUB_OUTPUT
echo "date=$(date +'%Y.%m.%d')" >> $GITHUB_OUTPUT

- name: Collect registry stats
id: stats
- name: Generate release body
id: release_body
run: |
{
echo "body<<EOF"
echo "## ToolHive Catalog Release"
echo ""
echo "**Date**: ${{ steps.metadata.outputs.date }}"
echo ""

for registry_dir in pkg/catalog/*/; do
registry_name=$(basename "$registry_dir")
data_file="$registry_dir/data/registry.json"
[ -f "$data_file" ] || continue

CONTAINER_COUNT=$(jq '.servers | length' "$data_file")
REMOTE_COUNT=$(jq '.remote_servers | length // 0' "$data_file")
TOTAL=$((CONTAINER_COUNT + REMOTE_COUNT))
LAST_UPDATED=$(jq -r '.last_updated' "$data_file")

upstream_file="$registry_dir/data/official-registry.json"
if [ -f "$upstream_file" ]; then
SKILL_COUNT=$(jq '.data.skills | length // 0' "$upstream_file")
else
SKILL_COUNT=0
fi

echo "### Registry: \`$registry_name\`"
echo ""
echo "**Last Updated**: $LAST_UPDATED"
echo ""
echo "| Category | Count |"
echo "|----------|-------|"
echo "| **Total Servers** | $TOTAL |"
echo "| **Container-based** | $CONTAINER_COUNT |"
echo "| **Remote** | $REMOTE_COUNT |"
echo "| **Skills** | $SKILL_COUNT |"
echo ""
echo "\`\`\`sh"
echo "go get github.com/stacklok/toolhive-catalog/pkg/catalog/${registry_name}@latest"
echo "\`\`\`"
echo ""
echo "\`\`\`go"
echo "import \"github.com/stacklok/toolhive-catalog/pkg/catalog/${registry_name}\""
echo ""
echo "// ToolHive legacy format"
echo "data := ${registry_name}.Legacy()"
echo ""
echo "// Upstream MCP format"
echo "data := ${registry_name}.Upstream()"
echo "\`\`\`"
echo ""
done

echo "---"
echo "*This is an automated release triggered by a catalog data update.*"
echo "EOF"
} >> $GITHUB_OUTPUT

- name: Collect release artifacts
id: artifacts
run: |
ARTIFACT_LIST=""
for registry_dir in pkg/catalog/*/; do
registry_name=$(basename "$registry_dir")
data_file="$registry_dir/data/registry.json"
[ -f "$data_file" ] || continue

CONTAINER_COUNT=$(jq '.servers | length' "$data_file")
REMOTE_COUNT=$(jq '.remote_servers | length // 0' "$data_file")
TOTAL=$((CONTAINER_COUNT + REMOTE_COUNT))
LAST_UPDATED=$(jq -r '.last_updated' "$data_file")

# Count skills from the upstream format
upstream_file="$registry_dir/data/official-registry.json"
if [ -f "$upstream_file" ]; then
SKILL_COUNT=$(jq '.data.skills | length // 0' "$upstream_file")
else
SKILL_COUNT=0
fi

echo "${registry_name}_total=$TOTAL" >> $GITHUB_OUTPUT
echo "${registry_name}_container=$CONTAINER_COUNT" >> $GITHUB_OUTPUT
echo "${registry_name}_remote=$REMOTE_COUNT" >> $GITHUB_OUTPUT
echo "${registry_name}_skills=$SKILL_COUNT" >> $GITHUB_OUTPUT
echo "${registry_name}_last_updated=$LAST_UPDATED" >> $GITHUB_OUTPUT
[ -d "$registry_dir/data" ] || continue
for f in "$registry_dir"/data/*.json; do
[ -f "$f" ] || continue
if [ -n "$ARTIFACT_LIST" ]; then
ARTIFACT_LIST="${ARTIFACT_LIST}
${f}"
else
ARTIFACT_LIST="$f"
fi
done
done
{
echo "files<<EOF"
echo "$ARTIFACT_LIST"
echo "EOF"
} >> $GITHUB_OUTPUT

- name: Create or update release
uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # v1
with:
tag: v${{ steps.metadata.outputs.version }}
name: Catalog v${{ steps.metadata.outputs.date }}
body: |
## ToolHive Catalog Release

**Date**: ${{ steps.metadata.outputs.date }}
**Last Updated**: ${{ steps.stats.outputs.toolhive_last_updated }}

### Statistics

| Category | Count |
|----------|-------|
| **Total Servers** | ${{ steps.stats.outputs.toolhive_total }} |
| **Container-based** | ${{ steps.stats.outputs.toolhive_container }} |
| **Remote** | ${{ steps.stats.outputs.toolhive_remote }} |
| **Skills** | ${{ steps.stats.outputs.toolhive_skills }} |

### Usage

```sh
go get github.com/stacklok/toolhive-catalog/pkg/catalog/toolhive@latest
```

```go
import "github.com/stacklok/toolhive-catalog/pkg/catalog/toolhive"

// ToolHive legacy format
data := toolhive.Legacy()

// Upstream MCP format
data := toolhive.Upstream()
```

---
*This is an automated release triggered by a catalog data update.*
artifacts: |
pkg/catalog/toolhive/data/registry.json
pkg/catalog/toolhive/data/official-registry.json
body: ${{ steps.release_body.outputs.body }}
artifacts: ${{ steps.artifacts.outputs.files }}
allowUpdates: true
makeLatest: true
env:
Expand Down
30 changes: 30 additions & 0 deletions pkg/catalog/official/catalog.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Package official provides the pre-built official vendors registry as embedded bytes.
//
// This registry contains MCP servers and skills from official vendors,
// companies, and widely popular community projects.
//
// The data files are updated daily by the build workflow and committed to the
// repository, so the embedded content reflects the last daily build.
//
// Two formats are available via [Legacy] and [Upstream]:
// - Legacy: ToolHive's own registry format (registry.json)
// - Upstream: The upstream MCP official registry format (official-registry.json)
package official

import _ "embed"

//go:embed data/registry.json
var legacyRegistry []byte

//go:embed data/official-registry.json
var upstreamRegistry []byte

// Legacy returns the registry in ToolHive's legacy format.
func Legacy() []byte {
return legacyRegistry
}

// Upstream returns the registry in the upstream MCP official format.
func Upstream() []byte {
return upstreamRegistry
}
Loading