pyproject.toml holds the only version number anyone edits. scripts/sync_version.py copies it
into server.json (the top-level version and every packages[] entry with registryType: pypi),
gemini-extension.json, manifest.json (the Claude Desktop extension), and diffbot_mcp/__init__.py. CI fails a pull request whose copies drift.
Pushing a vX.Y.Z tag runs .github/workflows/release.yml, which
publishes to PyPI, then to the MCP Registry, then cuts a GitHub release. Nothing publishes without a
tag.
uv version --bump patch --frozen # or: uv version 0.3.0 --frozen
python3 scripts/sync_version.py # stamp the new version into the other files
git switch -c release/0.2.3 && git commit -am "Release 0.2.3" && git push -u origin HEAD--frozen keeps uv version from locking and syncing on the side; without it, uv writes a uv.lock
and a .venv into the checkout, and this project deliberately ships neither.
Open the pull request, let Version check and the other checks pass, and merge. Merging also runs
Deploy to Production, which redeploys the hosted https://mcp.diffbot.com/mcp remote; that is
unrelated to the tag below, which is what publishes the package. Tag the merge commit on main:
git switch main && git pull
git tag v0.2.3
git push origin v0.2.3Watch the Release run. In order it:
- verify — the tag equals
v+ thepyproject.tomlversion, and the version copies are in sync. - build —
uv buildproduces the sdist and wheel,twine check --strictpasses, and the package description still carries the<!-- mcp-name: io.github.diffbot/diffbot-mcp -->comment. - publish-pypi — uploads to PyPI through Trusted Publishing (no GitHub environment).
- publish-mcp-registry — waits for the new version to appear on PyPI, then publishes
server.json. - github-release — creates the release with generated notes and the built files attached.
- The Claude Desktop extension workflow (
.github/workflows/mcpb.yml) packs the.mcpbbundle and attaches it to that same release.
The tag is what makes the version public, so a mistake is cheap to avoid and expensive to undo: PyPI will not accept the same version twice, and neither will the registry. If a tag was wrong, bump to the next patch version and tag again.
Run Release by hand (Actions → Release → Run workflow) on any branch. workflow_dispatch runs
only verify and build; the three publishing jobs are gated on a tag push and will not run.
PyPI has the version at that point, so only the registry needs retrying. Run Publish to MCP
Registry by hand (Actions → Publish to MCP Registry → Run workflow) and pick the vX.Y.Z tag as the
ref. It runs the same steps as the release path — it is the same workflow, called directly rather than
from release.yml. Common causes:
- PyPI had not yet served the new version. The workflow waits up to 10 minutes; a rerun usually passes.
- The registry rejects a version it already has, which means the publish actually succeeded.
PyPI trusted publisher. On pypi.org → project
diffbot-mcp → Manage → Publishing, add a GitHub publisher:
| field | value |
|---|---|
| Owner | diffbot |
| Repository | diffbot-mcp |
| Workflow | release.yml |
| Environment | (leave blank) |
No API token goes anywhere; the upload authenticates with the workflow's OIDC identity.
No GitHub environment. The publish job uses no environment, so leave PyPI's Environment field
blank. The publisher is then bound only to release.yml in this repo, and the job only runs on v*
tag pushes. If an admin later creates a pypi environment (Settings → Environments, restricted to
v* tags, optionally with required reviewers), add environment: pypi to the publish-pypi job and
set the same name on the PyPI publisher.
The MCP Registry needs no setup: mcp-publisher login github-oidc exchanges the workflow's OIDC token
for publish rights to io.github.diffbot/*, based on the repository owner in the token.