Skip to content

Commit 281c334

Browse files
Add API documentation with GitHub Pages deployment
This adds automated deployment of a versioned API documentation landing page using GitHub Pages. The landing page links to documentation hosted on RubyDoc.info, the de facto standard documentation site for the Ruby ecosystem. The landing page will update to reflect new releases. Changes ------- 1. **Documentation generation script** (see `bin/generate-gh-pages.sh`) Copies and commits documentation files to the gh-pages branch. The script uses git worktrees to isolate the process from the main workspace. The script: - Creates the gh-pages branch as an orphan branch if it doesn't exist - Parses semantic versions from tag names, ignoring arbitrary prefixes (e.g., tags `1.2.3`, `v1.2.3`, and `release-1.2.3` all create `v1.2.3`) - Determines the latest version using semantic version sorting - Copies static Jekyll template files from `docs/` to the gh-pages root (for the latest version only) - Generates `docs/_data/versions.yml` with a list of all versions for the Jekyll templates to consume 2. **Jekyll template files** (see `docs/` directory) - `docs/_config.yml` - Jekyll configuration - `docs/index.md` - Landing page template that links to all versions on RubyDoc.info based on generated `docs/_data/versions.yml` - `docs/latest/index.html` - Redirect page template that redirects to the latest documentation on RubyDoc.info 3. **GitHub Actions workflow** (see `.github/workflows/release.yml`) Added a `publish_gh_pages` job that runs after the `publish_gem` job on release events. The job invokes the generation script with the release tag name, then pushes the updated gh-pages branch to deploy the documentation. 4. **CI validation** (see `.github/workflows/ci.yml`) Added a `yard` job that runs YARD validation with `--no-output`. This ensures YARD can successfully parse the codebase (without generating output), catching documentation issues early in CI. 5. **YARD dependencies** (see `Gemfile`) Added `yard` and `yard-sorbet` gems as development dependencies to support documentation validation in CI. YARD documentation is automatically generated and hosted by RubyDoc.info when the gem is published to RubyGems.org. 6. **Documentation links** (see `README.md` and `mcp.gemspec`) Added links to the published API documentation on RubyDoc.info in the README and gemspec metadata. Documentation URL ----------------- Custom documentation will be available at: https://modelcontextprotocol.github.io/ruby-sdk/ RubyDoc.info documentation will be accessible at: - https://rubydoc.info/gems/mcp (latest version) - https://rubydoc.info/gems/mcp/1.2.3 (specific versions) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 400a56e commit 281c334

File tree

9 files changed

+202
-0
lines changed

9 files changed

+202
-0
lines changed

.github/workflows/ci.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,14 @@ jobs:
3131
ruby-version: 3.2 # Specify the oldest supported Ruby version.
3232
bundler-cache: true
3333
- run: bundle exec rake rubocop
34+
35+
yard:
36+
runs-on: ubuntu-latest
37+
name: YARD Documentation
38+
steps:
39+
- uses: actions/checkout@v5
40+
- uses: ruby/setup-ruby@v1
41+
with:
42+
ruby-version: 3.2 # Specify the oldest supported Ruby version.
43+
bundler-cache: true
44+
- run: bundle exec yard --no-output

.github/workflows/release.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,35 @@ jobs:
2323
bundler-cache: true
2424
ruby-version: 3.4
2525
- uses: rubygems/release-gem@v1
26+
27+
publish_gh_pages:
28+
if: github.repository_owner == 'modelcontextprotocol'
29+
name: Publish Documentation to GitHub Pages
30+
runs-on: ubuntu-latest
31+
needs: [publish_gem]
32+
33+
permissions:
34+
contents: write
35+
36+
steps:
37+
- uses: actions/checkout@v5
38+
with:
39+
fetch-depth: 0 # Fetch all history for all branches and tags
40+
41+
- name: Configure Git
42+
run: |
43+
git config --global user.name "github-actions[bot]"
44+
git config --global user.email "github-actions[bot]@users.noreply.github.com"
45+
46+
- name: Get version tag
47+
id: version
48+
run: |
49+
git fetch --tags
50+
TAG=$(git describe --tags --exact-match HEAD)
51+
echo "tag=${TAG}" >> $GITHUB_OUTPUT
52+
53+
- name: Generate GitHub Pages
54+
run: ./bin/generate-gh-pages.sh ${{ steps.version.outputs.tag }}
55+
56+
- name: Push to gh-pages
57+
run: git push origin gh-pages

Gemfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ gem "activesupport"
1717
gem "debug"
1818
gem "rake", "~> 13.0"
1919
gem "sorbet-static-and-runtime"
20+
gem "yard", "~> 0.9"
21+
gem "yard-sorbet", "~> 0.9"
2022

2123
group :test do
2224
gem "faraday", ">= 2.0"

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -932,3 +932,8 @@ The client provides a wrapper class for tools returned by the server:
932932
- `MCP::Client::Tool` - Represents a single tool with its metadata
933933

934934
This class provides easy access to tool properties like name, description, input schema, and output schema.
935+
936+
## Documentation
937+
938+
- [SDK API documentation](https://rubydoc.info/gems/mcp)
939+
- [Model Context Protocol documentation](https://modelcontextprotocol.io)

bin/generate-gh-pages.sh

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
#!/bin/bash
2+
set -e
3+
4+
# Generates versioned documentation links and commits to gh-pages branch
5+
#
6+
# PURPOSE:
7+
# This script generates a landing page with links to API documentation on
8+
# RubyDoc.info for a specific version tag. This script is invoked by the
9+
# publish-gh-pages job in the GitHub Actions workflow
10+
# (.github/workflows/release.yml) when a release is published.
11+
#
12+
# HOW IT WORKS:
13+
# - Creates isolated git worktrees for the specified tag and gh-pages branch
14+
# - Copies static Jekyll template files from docs/
15+
# - Generates _data/versions.yml with list of versions
16+
# - Commits changes to gh-pages (does not push automatically)
17+
#
18+
# WORKFLOW:
19+
# 1. Run this script with a tag name: `generate-gh-pages.sh v1.2.3`
20+
# 2. Script generates docs and commits to local gh-pages branch
21+
# 3. Push gh-pages branch to deploy: `git push origin gh-pages`
22+
23+
# Parse semantic version from tag name (ignoring arbitrary prefixes)
24+
if [[ "${1}" =~ ([0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?)$ ]]; then
25+
VERSION="v${BASH_REMATCH[1]}"
26+
else
27+
echo "Error: Must specify a tag name that contains a valid semantic version"
28+
echo "Usage: ${0} <tag-name>"
29+
echo "Examples:"
30+
echo " ${0} 1.2.3"
31+
echo " ${0} v2.0.0-rc.1"
32+
exit 1
33+
fi
34+
35+
TAG_NAME="${1}"
36+
REPO_ROOT="$(git rev-parse --show-toplevel)"
37+
38+
echo "Generating documentation for tag: ${TAG_NAME}"
39+
40+
# Create temporary directories for both worktrees
41+
WORKTREE_DIR=$(mktemp -d)
42+
GHPAGES_WORKTREE_DIR=$(mktemp -d)
43+
44+
# Set up trap to clean up both worktrees on exit
45+
trap 'git worktree remove --force "${WORKTREE_DIR}" 2>/dev/null || true; \
46+
git worktree remove --force "${GHPAGES_WORKTREE_DIR}" 2>/dev/null || true' EXIT
47+
48+
echo "Creating worktree for ${TAG_NAME}..."
49+
git worktree add --quiet "${WORKTREE_DIR}" "${TAG_NAME}"
50+
51+
# Check if gh-pages branch exists
52+
if git show-ref --verify --quiet refs/heads/gh-pages; then
53+
echo "Creating worktree for existing gh-pages branch..."
54+
git worktree add --quiet "${GHPAGES_WORKTREE_DIR}" gh-pages
55+
elif git ls-remote --exit-code --heads origin gh-pages > /dev/null 2>&1; then
56+
echo "Creating worktree for gh-pages branch from remote..."
57+
git worktree add --quiet "${GHPAGES_WORKTREE_DIR}" -b gh-pages origin/gh-pages
58+
else
59+
echo "Creating worktree for new orphan gh-pages branch..."
60+
git worktree add --quiet --detach "${GHPAGES_WORKTREE_DIR}"
61+
git -C "${GHPAGES_WORKTREE_DIR}" checkout --orphan gh-pages
62+
git -C "${GHPAGES_WORKTREE_DIR}" rm -rf . > /dev/null 2>&1 || true
63+
fi
64+
65+
# Change to gh-pages worktree
66+
cd "${GHPAGES_WORKTREE_DIR}"
67+
68+
# Determine if this tag is the latest version
69+
echo "Determining if ${VERSION} is the latest version..."
70+
71+
# Get all existing version tags from the repository (reverse sorted, newest first)
72+
ALL_VERSIONS=$(
73+
git -C "${REPO_ROOT}" tag --list | \
74+
sed -nE 's/^[^0-9]*([0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?)$/v\1/p' | \
75+
sort -Vr
76+
)
77+
78+
# Get the latest version from all version tags
79+
LATEST_VERSION=$(echo "${ALL_VERSIONS}" | head -n 1)
80+
81+
if [ "${VERSION}" = "${LATEST_VERSION}" ]; then
82+
echo "${VERSION} is the latest version"
83+
else
84+
echo "${VERSION} is not the latest version (latest is ${LATEST_VERSION})"
85+
fi
86+
87+
# Update custom documentation for latest version
88+
if [ "${VERSION}" = "${LATEST_VERSION}" ]; then
89+
echo "Updating custom documentation..."
90+
91+
# Clean up old custom docs from gh-pages root
92+
echo "Cleaning gh-pages root..."
93+
git ls-tree --name-only HEAD | xargs -r git rm -rf
94+
95+
# Copy custom docs from docs/ directory
96+
echo "Copying custom docs from ${WORKTREE_DIR}/docs/..."
97+
cp -r "${WORKTREE_DIR}/docs/." "${GHPAGES_WORKTREE_DIR}/"
98+
fi
99+
100+
# Generate version data for Jekyll
101+
echo "Generating _data/versions.yml..."
102+
mkdir -p _data
103+
echo "${ALL_VERSIONS}" | sed 's/^v/- /' > _data/versions.yml
104+
105+
# Stage all changes
106+
git add .
107+
108+
# Commit if there are changes
109+
if git diff --staged --quiet; then
110+
echo "No changes to commit"
111+
else
112+
echo "Committing documentation for ${VERSION}..."
113+
git commit -m "Add ${VERSION} docs"
114+
115+
echo "Documentation committed to gh-pages branch!"
116+
echo "Push to remote to deploy to GitHub Pages"
117+
fi
118+
119+
echo "Done!"

docs/_config.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Use package name as site title
2+
title: "MCP Ruby SDK"
3+
4+
# Include generated files and directories which may start with underscores
5+
include:
6+
- "_*"

docs/index.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
# Empty Jekyll front matter to enable Liquid templating (see {{ ... }} below)
3+
---
4+
5+
{% for version in site.data.versions -%}
6+
- [v{{ version }}](https://rubydoc.info/gems/mcp/{{ version }})
7+
{% endfor %}

docs/latest/index.html

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
# Empty Jekyll front matter to enable Liquid templating (see {{ ... }} below)
3+
---
4+
5+
<!DOCTYPE html>
6+
<html>
7+
<head>
8+
<meta charset="utf-8">
9+
<title>Redirecting to latest documentation...</title>
10+
<meta http-equiv="refresh" content="0; url=https://rubydoc.info/gems/mcp">
11+
<link rel="canonical" href="https://rubydoc.info/gems/mcp">
12+
</head>
13+
<body>
14+
<p>Redirecting to <a href="https://rubydoc.info/gems/mcp">latest documentation</a>...</p>
15+
<script>
16+
window.location.href = "https://rubydoc.info/gems/mcp";
17+
</script>
18+
</body>
19+
</html>

mcp.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
2020
spec.metadata["homepage_uri"] = spec.homepage
2121
spec.metadata["source_code_uri"] = spec.homepage
2222
spec.metadata["bug_tracker_uri"] = "#{spec.homepage}/issues"
23+
spec.metadata["documentation_uri"] = "https://rubydoc.info/gems/mcp"
2324

2425
spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do
2526
%x(git ls-files -z).split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }

0 commit comments

Comments
 (0)