diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index df84276249..0c6f0910fa 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -2,11 +2,13 @@ name: Python on: push: - branches: - - main pull_request: - release: - types: [published] + +# Superseded runs on the same ref are cancelled (e.g. rapid amend-pushes), +# except on main, where every merge keeps its own CI run +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} jobs: detect-packages: @@ -95,27 +97,3 @@ jobs: with: name: dist-${{ matrix.package }} path: src/${{ matrix.package }}/dist/ - - publish: - runs-on: ubuntu-latest - needs: [build, detect-packages] - if: github.event_name == 'release' - - strategy: - matrix: - package: ${{ fromJson(needs.detect-packages.outputs.packages) }} - name: Publish ${{ matrix.package }} - - environment: release - permissions: - id-token: write # Required for trusted publishing - - steps: - - name: Download artifacts - uses: actions/download-artifact@v7 - with: - name: dist-${{ matrix.package }} - path: dist/ - - - name: Publish package to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a3ad5387d3..f02a26332b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,9 +1,7 @@ -name: Automatic Release Creation +name: Release on: workflow_dispatch: - schedule: - - cron: '0 10 * * *' jobs: create-metadata: @@ -138,6 +136,15 @@ jobs: working-directory: src/${{ matrix.package }} run: uv run --frozen pyright + - name: Run tests + working-directory: src/${{ matrix.package }} + run: | + if [ -d "tests" ] || [ -d "test" ]; then + uv run --frozen pytest + else + echo "No tests directory, skipping" + fi + - name: Build package working-directory: src/${{ matrix.package }} run: uv build @@ -146,6 +153,7 @@ jobs: uses: pypa/gh-action-pypi-publish@release/v1 with: packages-dir: src/${{ matrix.package }}/dist + skip-existing: true # re-runs tolerate already-uploaded files publish-npm: needs: [update-packages, create-metadata] @@ -156,6 +164,9 @@ jobs: package: ${{ fromJson(needs.create-metadata.outputs.npm_packages) }} name: Build ${{ matrix.package }} environment: release + permissions: + contents: read + id-token: write # Required for npm trusted publishing (OIDC) runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 @@ -168,6 +179,10 @@ jobs: cache: npm registry-url: 'https://registry.npmjs.org' + # OIDC trusted publishing requires npm >=11.5.1; Node 22's bundled npm is 10.x. + - name: Ensure npm CLI supports OIDC trusted publishing + run: npm install -g npm@^11.5.1 + - name: Install dependencies working-directory: src/${{ matrix.package }} run: npm ci @@ -182,16 +197,22 @@ jobs: fi echo "Version $VERSION is new, proceeding with publish" + - name: Run tests + working-directory: src/${{ matrix.package }} + run: npm test --if-present + - name: Build package working-directory: src/${{ matrix.package }} run: npm run build + # Authenticates via OIDC trusted publishing (no token) — each package's + # npm trusted publisher is bound to this workflow + the release environment - name: Publish package working-directory: src/${{ matrix.package }} run: | npm publish --access public env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + NPM_CONFIG_PROVENANCE: "true" create-release: needs: [update-packages, create-metadata, publish-pypi, publish-npm] diff --git a/.github/workflows/typescript.yml b/.github/workflows/typescript.yml index 4e29e524ad..8d1b81e6c2 100644 --- a/.github/workflows/typescript.yml +++ b/.github/workflows/typescript.yml @@ -2,11 +2,13 @@ name: TypeScript on: push: - branches: - - main pull_request: - release: - types: [published] + +# Superseded runs on the same ref are cancelled (e.g. rapid amend-pushes), +# except on main, where every merge keeps its own CI run +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} jobs: detect-packages: @@ -68,35 +70,3 @@ jobs: working-directory: src/${{ matrix.package }} run: npm run build - publish: - runs-on: ubuntu-latest - needs: [build, detect-packages] - if: github.event_name == 'release' - environment: release - - strategy: - matrix: - package: ${{ fromJson(needs.detect-packages.outputs.packages) }} - name: Publish ${{ matrix.package }} - - permissions: - contents: read - id-token: write - - steps: - - uses: actions/checkout@v6 - - uses: actions/setup-node@v6 - with: - node-version: 22 - cache: npm - registry-url: "https://registry.npmjs.org" - - - name: Install dependencies - working-directory: src/${{ matrix.package }} - run: npm ci - - - name: Publish package - working-directory: src/${{ matrix.package }} - run: npm publish --access public - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/README.md b/README.md index 1a6fcb70fa..fe5351a890 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,10 @@ See [ADDITIONAL.md](ADDITIONAL.md) for a curated list of frameworks and resource See [CONTRIBUTING.md](CONTRIBUTING.md) for information about contributing to this repository. +## 📦 Releasing + +See [RELEASING.md](RELEASING.md) for how packages are published (OIDC trusted publishing from CI — no registry tokens) and how to retry a failed publish. + ## 🔒 Security See [SECURITY.md](SECURITY.md) for reporting security vulnerabilities. diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 0000000000..fc7056c4d8 --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,49 @@ +# Releasing + +How the packages in this repository are published, and what to do when a publish fails. + +## How publishing works + +All packages publish exclusively from the [`release.yml`](.github/workflows/release.yml) GitHub Actions workflow, gated by the `release` environment (a required reviewer must approve each deployment). Releases are triggered deliberately by a maintainer via **workflow_dispatch** (Actions → Release → Run workflow, or `gh workflow run release.yml`) — there is no scheduled/automatic release. + +**Authentication is OIDC trusted publishing on both registries — there are no registry tokens.** + +- **npm** (TypeScript servers): each `@modelcontextprotocol/*` package is registered on npmjs.com with a [trusted publisher](https://docs.npmjs.com/trusted-publishers) bound to this repository, workflow filename `release.yml`, and environment `release` (the binding is case-sensitive). Packages publish with [provenance attestations](https://docs.npmjs.com/generating-provenance-statements). +- **PyPI** (Python servers): published via [PyPI trusted publishing](https://docs.pypi.org/trusted-publishers/) using `pypa/gh-action-pypi-publish`. + +A release run: + +1. **Detects changed packages** since the last release tag — a package counts as changed if any `.py`, `.ts`, or `.md` file in its directory changed (READMEs ship inside the published artifacts). +2. **Stamps versions and pushes the release tag** — versions are date-based (CalVer, e.g. `2026.7.4`). +3. **Publishes each changed package as an independent matrix job** (`fail-fast: false` — one package's failure never blocks another). Each job: checkout at the release tag → install → double-publish guard → **run the package's tests** (plus `pyright` for Python) → build → publish. The guard differs by registry: the npm job aborts before tests if the version already exists; for PyPI the skip happens at the publish step itself (`skip-existing` on the upload action). +4. **Creates the GitHub release** with generated notes. + +## When a publish fails + +A failed matrix leg means that one package didn't publish; everything that succeeded stays published. + +**Preferred: re-run the failed jobs on the same run.** + +```bash +gh run rerun --failed --repo modelcontextprotocol/servers +``` + +- A re-run is still a `release.yml` run in the `release` environment, so it satisfies the trusted-publisher binding. +- It re-runs only the failed legs, checked out at the original release tag — it publishes exactly the tagged code, and the double-publish guard keeps already-published packages safe. +- It needs a fresh `release` environment approval, and the run must be complete first (approve or reject any pending deployments). +- GitHub's re-run window is ~30 days from the original run, and re-runs execute the *original* workflow snapshot — workflow fixes on `main` don't apply to a re-run. + +**Otherwise: let the next release pick it up.** If the re-run window has closed (or the fix required a workflow change), the failed version simply never exists on that registry — that's benign; npm and PyPI version histories don't need to match. The package publishes at the next version, provided it has a qualifying change (`.py`, `.ts`, or `.md`) since the last release tag. + +**Never:** + +- Publish manually with an npm token or from a laptop — there are no registry tokens, and manual publishes would break the provenance/trust chain. +- Dispatch a fresh `release.yml` run expecting it to retry a failed version — versions are date-granular, so a same-day dispatch collides with the existing tag, and a later dispatch mints a *new* version. Neither retries the failed one. + +## Environment approvals + +The `release` environment's required-reviewer list is configured in the repository settings (Settings → Environments → `release`). Reviewer rights come only from that list — repository admin does not confer deployment approval. + +--- + +Planned changes to this process — semver via changesets for the TypeScript packages, publishing triggered by manually-created GitHub Releases — are tracked in [#4463](https://github.com/modelcontextprotocol/servers/issues/4463). This document will be updated when that work merges. diff --git a/scripts/release.py b/scripts/release.py index e4ce1274c3..b3dcf0c1e2 100755 --- a/scripts/release.py +++ b/scripts/release.py @@ -113,7 +113,9 @@ def has_changes(path: Path, git_hash: GitHash) -> bool: ) changed_files = [Path(f) for f in output.stdout.splitlines()] - relevant_files = [f for f in changed_files if f.suffix in [".py", ".ts"]] + # .md counts as a change: READMEs ship inside the published package + # (npm tarball / PyPI long_description) + relevant_files = [f for f in changed_files if f.suffix in [".py", ".ts", ".md"]] return len(relevant_files) >= 1 except subprocess.CalledProcessError: return False diff --git a/src/everything/README.md b/src/everything/README.md index 34a948a2fe..a5943c6fce 100644 --- a/src/everything/README.md +++ b/src/everything/README.md @@ -134,3 +134,7 @@ npx @modelcontextprotocol/server-everything sse ```shell npx @modelcontextprotocol/server-everything streamableHttp ``` + +## License + +This MCP server is licensed under the MIT License. This means you are free to use, modify, and distribute the software, subject to the terms and conditions of the MIT License. For more details, please see the LICENSE file in the project repository. diff --git a/src/filesystem/README.md b/src/filesystem/README.md index c099da1e8c..76cabd0cce 100644 --- a/src/filesystem/README.md +++ b/src/filesystem/README.md @@ -2,6 +2,8 @@ Node.js server implementing Model Context Protocol (MCP) for filesystem operations. +Published on npm as [`@modelcontextprotocol/server-filesystem`](https://www.npmjs.com/package/@modelcontextprotocol/server-filesystem). + ## Features - Read/write files diff --git a/src/memory/README.md b/src/memory/README.md index e9fb37e241..0f294231a9 100644 --- a/src/memory/README.md +++ b/src/memory/README.md @@ -2,6 +2,8 @@ A basic implementation of persistent memory using a local knowledge graph. This lets Claude remember information about the user across chats. +Published on npm as [`@modelcontextprotocol/server-memory`](https://www.npmjs.com/package/@modelcontextprotocol/server-memory). + ## Core Concepts ### Entities diff --git a/src/sequentialthinking/README.md b/src/sequentialthinking/README.md index 45d2c30f75..b6b853aa56 100644 --- a/src/sequentialthinking/README.md +++ b/src/sequentialthinking/README.md @@ -2,6 +2,8 @@ An MCP server implementation that provides a tool for dynamic and reflective problem-solving through a structured thinking process. +Published on npm as [`@modelcontextprotocol/server-sequential-thinking`](https://www.npmjs.com/package/@modelcontextprotocol/server-sequential-thinking). + ## Features - Break down complex problems into manageable steps