diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9a9b5930..c11d5efb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,3 +31,14 @@ jobs: ruby-version: 3.2 # Specify the oldest supported Ruby version. bundler-cache: true - run: bundle exec rake rubocop + + yard: + runs-on: ubuntu-latest + name: YARD Documentation + steps: + - uses: actions/checkout@v5 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.2 # Specify the oldest supported Ruby version. + bundler-cache: true + - run: bundle exec yard --no-output diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 142cba43..14d70127 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,3 +23,35 @@ jobs: bundler-cache: true ruby-version: 3.4 - uses: rubygems/release-gem@v1 + + publish_gh_pages: + if: github.repository_owner == 'modelcontextprotocol' + name: Publish Documentation to GitHub Pages + runs-on: ubuntu-latest + needs: [publish_gem] + + permissions: + contents: write + + steps: + - uses: actions/checkout@v5 + with: + fetch-depth: 0 # Fetch all history for all branches and tags + + - name: Configure Git + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + + - name: Get version tag + id: version + run: | + git fetch --tags + TAG=$(git describe --tags --exact-match HEAD) + echo "tag=${TAG}" >> $GITHUB_OUTPUT + + - name: Generate GitHub Pages + run: ./bin/generate-gh-pages.sh ${{ steps.version.outputs.tag }} + + - name: Push to gh-pages + run: git push origin gh-pages diff --git a/Gemfile b/Gemfile index e841d58f..547ccd0d 100644 --- a/Gemfile +++ b/Gemfile @@ -17,6 +17,8 @@ gem "activesupport" gem "debug" gem "rake", "~> 13.0" gem "sorbet-static-and-runtime" +gem "yard", "~> 0.9" +gem "yard-sorbet", "~> 0.9" group :test do gem "faraday", ">= 2.0" diff --git a/README.md b/README.md index 64617bf5..583d6d59 100644 --- a/README.md +++ b/README.md @@ -932,3 +932,8 @@ The client provides a wrapper class for tools returned by the server: - `MCP::Client::Tool` - Represents a single tool with its metadata This class provides easy access to tool properties like name, description, input schema, and output schema. + +## Documentation + +- [SDK API documentation](https://rubydoc.info/gems/mcp) +- [Model Context Protocol documentation](https://modelcontextprotocol.io) diff --git a/bin/generate-gh-pages.sh b/bin/generate-gh-pages.sh new file mode 100755 index 00000000..f40d1773 --- /dev/null +++ b/bin/generate-gh-pages.sh @@ -0,0 +1,119 @@ +#!/bin/bash +set -e + +# Generates versioned documentation links and commits to gh-pages branch +# +# PURPOSE: +# This script generates a landing page with links to API documentation on +# RubyDoc.info for a specific version tag. This script is invoked by the +# publish-gh-pages job in the GitHub Actions workflow +# (.github/workflows/release.yml) when a release is published. +# +# HOW IT WORKS: +# - Creates isolated git worktrees for the specified tag and gh-pages branch +# - Copies static Jekyll template files from docs/ +# - Generates _data/versions.yml with list of versions +# - Commits changes to gh-pages (does not push automatically) +# +# WORKFLOW: +# 1. Run this script with a tag name: `generate-gh-pages.sh v1.2.3` +# 2. Script generates docs and commits to local gh-pages branch +# 3. Push gh-pages branch to deploy: `git push origin gh-pages` + +# Parse semantic version from tag name (ignoring arbitrary prefixes) +if [[ "${1}" =~ ([0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?)$ ]]; then + VERSION="v${BASH_REMATCH[1]}" +else + echo "Error: Must specify a tag name that contains a valid semantic version" + echo "Usage: ${0} " + echo "Examples:" + echo " ${0} 1.2.3" + echo " ${0} v2.0.0-rc.1" + exit 1 +fi + +TAG_NAME="${1}" +REPO_ROOT="$(git rev-parse --show-toplevel)" + +echo "Generating documentation for tag: ${TAG_NAME}" + +# Create temporary directories for both worktrees +WORKTREE_DIR=$(mktemp -d) +GHPAGES_WORKTREE_DIR=$(mktemp -d) + +# Set up trap to clean up both worktrees on exit +trap 'git worktree remove --force "${WORKTREE_DIR}" 2>/dev/null || true; \ + git worktree remove --force "${GHPAGES_WORKTREE_DIR}" 2>/dev/null || true' EXIT + +echo "Creating worktree for ${TAG_NAME}..." +git worktree add --quiet "${WORKTREE_DIR}" "${TAG_NAME}" + +# Check if gh-pages branch exists +if git show-ref --verify --quiet refs/heads/gh-pages; then + echo "Creating worktree for existing gh-pages branch..." + git worktree add --quiet "${GHPAGES_WORKTREE_DIR}" gh-pages +elif git ls-remote --exit-code --heads origin gh-pages > /dev/null 2>&1; then + echo "Creating worktree for gh-pages branch from remote..." + git worktree add --quiet "${GHPAGES_WORKTREE_DIR}" -b gh-pages origin/gh-pages +else + echo "Creating worktree for new orphan gh-pages branch..." + git worktree add --quiet --detach "${GHPAGES_WORKTREE_DIR}" + git -C "${GHPAGES_WORKTREE_DIR}" checkout --orphan gh-pages + git -C "${GHPAGES_WORKTREE_DIR}" rm -rf . > /dev/null 2>&1 || true +fi + +# Change to gh-pages worktree +cd "${GHPAGES_WORKTREE_DIR}" + +# Determine if this tag is the latest version +echo "Determining if ${VERSION} is the latest version..." + +# Get all existing version tags from the repository (reverse sorted, newest first) +ALL_VERSIONS=$( + git -C "${REPO_ROOT}" tag --list | \ + sed -nE 's/^[^0-9]*([0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?)$/v\1/p' | \ + sort -Vr +) + +# Get the latest version from all version tags +LATEST_VERSION=$(echo "${ALL_VERSIONS}" | head -n 1) + +if [ "${VERSION}" = "${LATEST_VERSION}" ]; then + echo "${VERSION} is the latest version" +else + echo "${VERSION} is not the latest version (latest is ${LATEST_VERSION})" +fi + +# Update custom documentation for latest version +if [ "${VERSION}" = "${LATEST_VERSION}" ]; then + echo "Updating custom documentation..." + + # Clean up old custom docs from gh-pages root + echo "Cleaning gh-pages root..." + git ls-tree --name-only HEAD | xargs -r git rm -rf + + # Copy custom docs from docs/ directory + echo "Copying custom docs from ${WORKTREE_DIR}/docs/..." + cp -r "${WORKTREE_DIR}/docs/." "${GHPAGES_WORKTREE_DIR}/" +fi + +# Generate version data for Jekyll +echo "Generating _data/versions.yml..." +mkdir -p _data +echo "${ALL_VERSIONS}" | sed 's/^v/- /' > _data/versions.yml + +# Stage all changes +git add . + +# Commit if there are changes +if git diff --staged --quiet; then + echo "No changes to commit" +else + echo "Committing documentation for ${VERSION}..." + git commit -m "Add ${VERSION} docs" + + echo "Documentation committed to gh-pages branch!" + echo "Push to remote to deploy to GitHub Pages" +fi + +echo "Done!" diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 00000000..3b4ca1fc --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1,6 @@ +# Use package name as site title +title: "MCP Ruby SDK" + +# Include generated files and directories which may start with underscores +include: + - "_*" diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..305c3a11 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,7 @@ +--- +# Empty Jekyll front matter to enable Liquid templating (see {{ ... }} below) +--- + +{% for version in site.data.versions -%} +- [v{{ version }}](https://rubydoc.info/gems/mcp/{{ version }}) +{% endfor %} diff --git a/docs/latest/index.html b/docs/latest/index.html new file mode 100644 index 00000000..86653740 --- /dev/null +++ b/docs/latest/index.html @@ -0,0 +1,19 @@ +--- +# Empty Jekyll front matter to enable Liquid templating (see {{ ... }} below) +--- + + + + + + Redirecting to latest documentation... + + + + +

Redirecting to latest documentation...

+ + + diff --git a/mcp.gemspec b/mcp.gemspec index 57cd60ab..eb87aeb5 100644 --- a/mcp.gemspec +++ b/mcp.gemspec @@ -20,6 +20,7 @@ Gem::Specification.new do |spec| spec.metadata["homepage_uri"] = spec.homepage spec.metadata["source_code_uri"] = spec.homepage spec.metadata["bug_tracker_uri"] = "#{spec.homepage}/issues" + spec.metadata["documentation_uri"] = "https://rubydoc.info/gems/mcp" spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do %x(git ls-files -z).split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }