Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 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
98 changes: 98 additions & 0 deletions .github/workflows/api-docs-backfill.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


name: "API Reference Backfill"
on:
workflow_dispatch:
inputs:
package:
description: "Package to backfill"
type: choice
options: [core, tbadk, tbgenkit]
required: true
version:
description: "Version tag to build, e.g. v1.0.0"
type: string
required: true

concurrency:
group: api-docs-deploy
cancel-in-progress: false

jobs:
backfill:
# Never run on forks; docs deploy only from the upstream repository.
if: github.repository == 'googleapis/mcp-toolbox-sdk-go'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
# Build from main so the archived page carries the current version picker;
# only the package's source comes from the tag (overlaid below) so
# gomarkdoc documents that version's API.
- name: Checkout main (current layout + scripts)
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
ref: main
submodules: recursive

- name: Checkout tagged package source
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
ref: refs/tags/${{ inputs.package }}/${{ inputs.version }}
path: tagged_src
sparse-checkout: ${{ inputs.package }}

- name: Overlay tagged source onto main
env:
PKG: ${{ inputs.package }}
run: |
rm -rf "$PKG"
mv "tagged_src/$PKG" "$PKG"

- name: Set up Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
with:
go-version: '1.25.0'

- name: Setup Hugo
uses: peaceiris/actions-hugo@75d2e84710de30f6ff7268e08f310b60ef14033f # v3
with:
hugo-version: '0.152.2'
extended: true

- name: Install PostCSS Dependencies
run: |
cd docs-site
npm install postcss postcss-cli autoprefixer

- name: Build docs
env:
PKG: ${{ inputs.package }}
VER: ${{ inputs.version }}
# Deploys only run upstream (see job-level guard), so always use the
# production domain.
BASE_URL: https://go.mcp-toolbox.dev/
run: |
chmod +x scripts/generate-api-docs.sh
./scripts/generate-api-docs.sh "$PKG" "$VER" "$BASE_URL"

- name: Deploy to gh-pages
uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs-site/public
keep_files: true
cname: ${{ github.repository == 'googleapis/mcp-toolbox-sdk-go' && 'go.mcp-toolbox.dev' || '' }}
61 changes: 38 additions & 23 deletions .github/workflows/api-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,18 @@
name: "API Reference Deployment"
on:
push:
branches: [ 'main', 'ref-docs' ]
branches: [ 'main' ]
tags: [ '**' ]
workflow_dispatch:

concurrency:
group: api-docs-deploy
cancel-in-progress: false

jobs:
deploy:
# Never run on forks; docs deploy only from the upstream repository.
if: github.repository == 'googleapis/mcp-toolbox-sdk-go'
runs-on: ubuntu-latest
permissions:
contents: write
Expand All @@ -48,32 +54,41 @@ jobs:
cd docs-site
npm install postcss postcss-cli autoprefixer

- name: Build API Reference
- name: Resolve build target
id: resolve
run: |
# Route the git ref to the package(s) and version to build.
# A per-package tag builds that one package; pushes to main (and manual
# dispatch) build all three as "dev". Any other tag is skipped.
REF="${GITHUB_REF}"
case "$REF" in
refs/tags/core/v*) echo "packages=core" >> "$GITHUB_OUTPUT"; echo "version=${REF#refs/tags/core/}" >> "$GITHUB_OUTPUT" ;;
refs/tags/tbadk/v*) echo "packages=tbadk" >> "$GITHUB_OUTPUT"; echo "version=${REF#refs/tags/tbadk/}" >> "$GITHUB_OUTPUT" ;;
refs/tags/tbgenkit/v*) echo "packages=tbgenkit" >> "$GITHUB_OUTPUT"; echo "version=${REF#refs/tags/tbgenkit/}" >> "$GITHUB_OUTPUT" ;;
refs/tags/*) echo "packages=" >> "$GITHUB_OUTPUT"; echo "version=" >> "$GITHUB_OUTPUT" ;;
*) echo "packages=core tbadk tbgenkit" >> "$GITHUB_OUTPUT"; echo "version=dev" >> "$GITHUB_OUTPUT" ;;
esac

# Deploys only run upstream (see job-level guard), so always use the
# production domain.
echo "BASE_URL=https://go.mcp-toolbox.dev/" >> "$GITHUB_ENV"

- name: Build per-package docs
if: steps.resolve.outputs.packages != ''
run: |
chmod +x scripts/generate-api-docs.sh

# If the Action is running in the official upstream repository,
# strictly route all assets and links to the production custom domain.
if [[ "${{ github.repository }}" == "googleapis/mcp-toolbox-sdk-go" ]]; then
BASE_URL="https://go.mcp-toolbox.dev/"

# If the Action is running in an external contributor's fork,
# dynamically fallback to their personal GitHub Pages URL.
# This ensures external contributors can successfully build and preview
# docsite changes on their own forks without encountering broken links.
else
BASE_URL="https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/"
fi

if [[ $GITHUB_REF == refs/tags/* ]]; then
VERSION=${GITHUB_REF#refs/tags/}
else
VERSION="main"
fi

./scripts/generate-api-docs.sh "$VERSION" "$BASE_URL"
for PKG in ${{ steps.resolve.outputs.packages }}; do
./scripts/generate-api-docs.sh "$PKG" "${{ steps.resolve.outputs.version }}" "$BASE_URL"
done

- name: Build root page
if: steps.resolve.outputs.packages != '' && startsWith(github.ref, 'refs/tags/')
run: |
chmod +x scripts/generate-root.sh
./scripts/generate-root.sh "$BASE_URL"

- name: Deploy to gh-pages
if: steps.resolve.outputs.packages != ''
uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
Expand Down
54 changes: 54 additions & 0 deletions DEVELOPER.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,60 @@ This project uses `golangci-lint`.
Releases are managed by **Release Please**.
* Each module (`core`, `tbadk`, `tbgenkit`) is released independently.
* Tags will be in the format `module/vX.Y.Z` (e.g., `core/v0.6.0`).
* Before releasing, add a `[[params.versions.<pkg>]]` block for the new version to
`docs-site/hugo.toml` so it appears in the API reference version picker. See
[Adding a version to the picker](#adding-a-version-to-the-picker).

## API Reference Documentation

The API reference is published to [go.mcp-toolbox.dev](https://go.mcp-toolbox.dev).
It is generated with [`gomarkdoc`](https://github.com/princjef/gomarkdoc) and
rendered by [Hugo](https://gohugo.io/) + [Docsy](https://www.docsy.dev/) from the
`docs-site/` directory. Docs are built **per package, per version** and served at
`/<package>/<version>/` (e.g. `/core/v1.0.0/`), with a `/<package>/latest/`
redirect to the newest release.

### Workflows

The `api-docs.yml` workflow deploys to the `gh-pages` branch. It runs only on
the upstream repository and uses the `api-docs-deploy` concurrency group, so it
never races another deploy.

The automatic flow is as follows:
* Push to `main` (or manual dispatch) → builds all three packages as `dev`.
* Push of a per-package tag `<pkg>/vX.Y.Z` → builds that one version **and**
rebuilds the root README landing page.
* Other tags are skipped.

### Adding a version to the picker

Before each **new release**, add a `[[params.versions.<pkg>]]` block for the version
to `docs-site/hugo.toml` (newest first). On a successful release, the tag is created
automatically and triggers the `api-docs.yml` workflow, which builds and deploys
that version.

### Backfilling old docs

The **`api-docs-backfill.yml`** (API Reference Backfill) workflow runs on demand and
builds **one historical version per run**. Use it to build any version whose docs
are missing or whose deployment failed.

Trigger it from the Actions tab, or with:

```bash
gh workflow run api-docs-backfill.yml -f package=core -f version=v1.0.0
```

### Building locally

```bash
# Build a single package/version (base URL must end in a slash).
./scripts/generate-api-docs.sh core dev http://localhost:8080/

# Serve the output.
(cd docs-site/public && python3 -m http.server 8080)
# → http://localhost:8080/core/dev/
```

## Further Information

Expand Down
49 changes: 38 additions & 11 deletions docs-site/hugo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,33 @@
baseURL = "PLACEHOLDER_BASE_URL"
title = "MCP Toolbox Go API"

[params]
offlineSearch = true
copyright = "2026 Google LLC"

# Hand-edited version lists, one per package. Newest first — the order is
# mirrored into the dropdown. Add a block before tagging a release, and only
# list versions whose /<pkg>/<version>/ docs already exist (or will after the
# backfill run), or the dropdown link 404s.
[params.versions]
[[params.versions.core]]
version = "dev"
url = "https://go.mcp-toolbox.dev/core/dev/"
[[params.versions.core]]
version = "v1.0.0"
url = "https://go.mcp-toolbox.dev/core/v1.0.0/"
[[params.versions.tbadk]]
version = "dev"
url = "https://go.mcp-toolbox.dev/tbadk/dev/"
[[params.versions.tbadk]]
version = "v0.8.0"
url = "https://go.mcp-toolbox.dev/tbadk/v0.8.0/"
[[params.versions.tbgenkit]]
version = "dev"
url = "https://go.mcp-toolbox.dev/tbgenkit/dev/"
[[params.versions.tbgenkit]]
version = "v0.7.0"
url = "https://go.mcp-toolbox.dev/tbgenkit/v0.7.0/"

[markup.goldmark.renderer]
unsafe = true

Expand Down Expand Up @@ -32,19 +55,23 @@ title = "MCP Toolbox Go API"
pre = "<i class='fab fa-github'></i>"

[outputFormats]
[outputFormats.LLMS]
mediaType = "text/plain"
baseName = "llms"
isPlainText = true
root = true
[outputFormats.LLMS-FULL]
mediaType = "text/plain"
baseName = "llms-full"
# Dropdown fragment, fetched at runtime by the navbar version selector so
# frozen old version pages show newly-added versions without a rebuild.
[outputFormats.releases]
baseName = "releases"
isPlainText = true
root = true
mediaType = "text/releases"
# Per-package "latest" redirect, hoisted to /<pkg>/latest/index.html.
[outputFormats.latest]
baseName = "latest"
mediaType = "text/html"
isHTML = true

[mediaTypes."text/releases"]
suffixes = ["releases"]

[outputs]
home = ["HTML", "RSS", "LLMS", "LLMS-FULL", "JSON"]
home = ["HTML", "JSON", "releases", "latest"] # JSON kept for offline search

[module]
[[module.imports]]
Expand Down
11 changes: 11 additions & 0 deletions docs-site/layouts/_default/home.latest.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{{- $pkg := .Site.Params.package | default "" -}}
{{- $target := printf "/%s/dev/" $pkg -}}
{{- with index .Site.Params.versions $pkg }}
{{- range . }}{{ if ne .version "dev" }}{{ $target = .url }}{{ break }}{{ end }}{{ end }}
{{- end -}}
<!DOCTYPE html>
<html><head><meta charset="utf-8">
<meta http-equiv="refresh" content="0; url={{ $target }}">
<link rel="canonical" href="{{ $target }}">
</head><body><a href="{{ $target }}">Redirecting…</a>
<script>location.replace('{{ $target }}');</script></body></html>
7 changes: 7 additions & 0 deletions docs-site/layouts/_default/home.releases.releases
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{{- $pkg := .Site.Params.package | default "" -}}
<a class="dropdown-item" href="/{{ $pkg }}/latest/">latest</a>
{{- with index .Site.Params.versions $pkg }}
{{- range . }}
<a class="dropdown-item" href="{{ .url }}">{{ .version }}</a>
{{- end }}
{{- end -}}
1 change: 1 addition & 0 deletions docs-site/layouts/_partials/hooks/head-end.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<script src='{{ "js/w3.js" | relURL }}'></script>
14 changes: 14 additions & 0 deletions docs-site/layouts/_partials/navbar-version-selector.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{{ if .Site.Params.versions -}}
{{ $pkg := .Site.Params.package | default "" -}}
<div class="dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button"
data-bs-toggle="dropdown" aria-expanded="false">
{{ .Site.Params.version | default "Versions" }}
</a>
<div class="dropdown-menu"
w3-include-html="/{{ $pkg }}/releases.releases"
w3-include-html-default='<a class="dropdown-item" href="/{{ $pkg }}/dev/">dev</a>'>
</div>
<script>w3.includeHTML();</script>
</div>
{{ end -}}
34 changes: 34 additions & 0 deletions docs-site/static/js/w3.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/* Trimmed from W3.JS 1.04 (w3schools.com): only w3.includeHTML, plus a
w3-include-html-default fallback rendered on a 404. Used by the navbar
version selector to fetch /<pkg>/releases.releases at runtime. */
"use strict";
var w3 = {};
w3.includeHTML = function (cb) {
var z, i, elmnt, file, xhttp;
z = document.getElementsByTagName("*");
for (i = 0; i < z.length; i++) {
elmnt = z[i];
file = elmnt.getAttribute("w3-include-html");
if (file) {
xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (this.readyState == 4) {
if (this.status == 200) { elmnt.innerHTML = this.responseText; }
if (this.status == 404) {
if (elmnt.getAttribute("w3-include-html-default")) {
elmnt.innerHTML = elmnt.getAttribute("w3-include-html-default");
} else {
elmnt.innerHTML = "Page not found.";
}
}
elmnt.removeAttribute("w3-include-html");
w3.includeHTML(cb);
}
};
xhttp.open("GET", file, true);
xhttp.send();
return;
}
}
if (cb) cb();
};
Loading
Loading