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
22 changes: 22 additions & 0 deletions .github/actions/install-mcp-publisher/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Install mcp-publisher
description: Download a pinned release of mcp-publisher from the official MCP registry repo

inputs:
version:
description: "mcp-publisher release tag (e.g. v1.5.0). Bump when a new release is needed."
required: false
default: v1.5.0

runs:
using: composite
steps:
- name: Download and extract mcp-publisher
shell: bash
env:
VERSION: ${{ inputs.version }}
run: |
os=$(uname -s | tr '[:upper:]' '[:lower:]')
arch=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
curl -fsSL "https://github.com/modelcontextprotocol/registry/releases/download/${VERSION}/mcp-publisher_${os}_${arch}.tar.gz" \
| tar xz mcp-publisher
./mcp-publisher --version
8 changes: 8 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ jobs:
- name: Verify server.json matches pyproject.toml
run: python scripts/sync-server-json.py --check

validate-server-json:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/install-mcp-publisher
- name: Validate server.json against registry schema
run: ./mcp-publisher validate server.json

typecheck:
runs-on: ubuntu-latest
steps:
Expand Down
45 changes: 45 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,51 @@ jobs:
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1

publish-registry:
# Publish (or update) the server entry in the official MCP registry.
# Runs after publish-pypi because the registry validates that the
# referenced PyPI package+version exists before accepting the entry.
needs: publish-pypi
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4

- uses: ./.github/actions/install-mcp-publisher

- name: Authenticate to MCP registry (GitHub OIDC)
run: ./mcp-publisher login github-oidc

- name: Publish server.json to MCP registry
env:
TAG: ${{ github.ref_name }}
run: |
# Idempotent: the registry rejects re-publishing an existing
# version (see docs/modelcontextprotocol-io/versioning.mdx:
# "The version string MUST be unique for each publication").
# The match string is anchored to the upstream error constant
# `ErrInvalidVersion` in internal/database/database.go:
# "invalid version: cannot publish duplicate version"
# (verified against tag v1.5.0). The publisher surfaces this as
# part of the 400 response body from POST /v0/publish. If the
# upstream error text changes, this step will fall through to
# `exit $status` and fail loudly — preferable to silently
# swallowing a real publish error.
set +e
output=$(./mcp-publisher publish 2>&1)
status=$?
echo "$output"
if [ $status -eq 0 ]; then
exit 0
fi
if echo "$output" | grep -qF 'cannot publish duplicate version'; then
echo "::warning::Version $TAG already exists in MCP registry — treating as no-op"
exit 0
fi
exit $status

github-release:
needs: publish-pypi
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Added

- **Auto-publish to MCP registry on release** (#27) — adds a `publish-registry` job to `.github/workflows/publish.yml` that runs after `publish-pypi` and publishes `server.json` to `registry.modelcontextprotocol.io` via [`mcp-publisher`](https://github.com/modelcontextprotocol/registry). Uses GitHub OIDC authentication (`permissions: id-token: write`) so no long-lived registry token is needed. The step is idempotent — if the tag's workflow is re-run after a successful registry publish, the duplicate-version error (registry requires every version string to be unique per [its versioning docs](https://github.com/modelcontextprotocol/registry/blob/main/docs/modelcontextprotocol-io/versioning.mdx)) is caught and downgraded to a `::warning::` rather than failing the release. Also adds a `validate-server-json` job to `.github/workflows/ci.yml` that runs `mcp-publisher validate server.json` on every PR so schema breakage is caught before a tag push, complementing the existing `version-sync` check which enforces alignment with `pyproject.toml`.
- **vdsm CI workflow** (#24) — adds `.github/workflows/vdsm.yml` that runs the 47 vdsm integration tests on every PR using GitHub Actions' `ubuntu-24.04` runner with `/dev/kvm` access. Golden image is cached via `actions/cache@v4`, keyed on DSM version + hash of the setup scripts (`scripts/vdsm_setup.py`, `tests/vdsm/setup_dsm.py`, `tests/vdsm/config.py`, `tests/vdsm/golden_image.py`, `tests/vdsm/container.py`). Cache miss path invokes `scripts/vdsm_setup.py --yes` to build a fresh golden image. The new workflow is independent from `ci.yml` so a vdsm flake never blocks unit-test merges, and starts with `continue-on-error: true` until it has a track record of stability. Also adds a `--yes/-y` flag to `vdsm_setup.py` for non-interactive CI use.

### Fixed
Expand Down
11 changes: 11 additions & 0 deletions docs/specs/project-scaffolding-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,17 @@ uvx --from git+https://github.com/cmeans/mcp-synology mcp-synology serve

Once stable enough for versioned releases: `uvx mcp-synology serve`

### Automated release pipeline

Tagging `v*` triggers `.github/workflows/publish.yml`, which runs four jobs:

1. `build` — runs the unit test suite and builds sdist + wheel.
2. `publish-pypi` — uploads the built distributions to PyPI via Trusted Publishing (OIDC, no long-lived token).
3. `publish-registry` — installs [`mcp-publisher`](https://github.com/modelcontextprotocol/registry), authenticates via GitHub OIDC (`id-token: write`), and publishes `server.json` to `registry.modelcontextprotocol.io`. Idempotent on re-run: the registry's "version already exists" error is caught and downgraded to a workflow warning.
4. `github-release` — extracts the release notes for the tag from `CHANGELOG.md` (via `awk` on the `## <version>` heading) and either creates a new GitHub Release or updates the existing one (`gh release create` / `gh release edit`).

`server.json` is validated on every PR by the `validate-server-json` job in `ci.yml`, which runs `mcp-publisher validate` against the current schema. The `version-sync` job in the same workflow enforces that `server.json`'s version fields stay aligned with `[project].version` in `pyproject.toml` via `scripts/sync-server-json.py --check`.

### Future: Docker

```dockerfile
Expand Down
Loading