diff --git a/.github/workflows/update-mcp-registry.yml b/.github/workflows/update-mcp-registry.yml
new file mode 100644
index 0000000..1de6234
--- /dev/null
+++ b/.github/workflows/update-mcp-registry.yml
@@ -0,0 +1,145 @@
+name: Update MCP Registry
+
+on:
+ workflow_run:
+ workflows: ["Build and Push Docker Image"]
+ types:
+ - completed
+ branches:
+ - main
+
+jobs:
+ check-and-update-registry:
+ # Only run on tag pushes and if the docker workflow succeeded
+ if: |
+ github.event.workflow_run.conclusion == 'success' &&
+ github.event.workflow_run.event == 'push' &&
+ startsWith(github.event.workflow_run.head_branch, 'v')
+
+ runs-on: ubuntu-latest
+ permissions:
+ id-token: write # Required for GitHub OIDC authentication with MCP Registry
+ contents: read
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.workflow_run.head_branch }}
+
+ - name: Wait for PyPI release to complete
+ uses: fountainhead/action-wait-for-check@v1.2.0
+ id: wait-for-pypi
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ checkName: "Build and publish Python distributions to PyPI"
+ ref: ${{ github.event.workflow_run.head_sha }}
+ timeoutSeconds: 600
+ intervalSeconds: 10
+
+ - name: Check PyPI release status
+ if: steps.wait-for-pypi.outputs.conclusion != 'success'
+ run: |
+ echo "PyPI release did not succeed. Status: ${{ steps.wait-for-pypi.outputs.conclusion }}"
+ exit 1
+
+ - name: Validate version consistency
+ id: version
+ run: |
+ # Read versions from files
+ ROOT_VERSION=$(jq -r '.version' server.json)
+ # Only get versions from packages that have a version field (exclude null/empty)
+ PACKAGE_VERSIONS=$(jq -r '.packages[] | select(.version != null) | .version' server.json | sort -u)
+ PYPROJECT_VERSION=$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/')
+ TAG_VERSION=${GITHUB_REF_NAME#v}
+
+ echo "Version Check:"
+ echo " server.json root: $ROOT_VERSION"
+ echo " server.json packages:"
+ jq -r '.packages[] |
+ if .version != null then
+ " - \(.registryType):\(.identifier) = \(.version)"
+ else
+ " - \(.registryType):\(.identifier) (no version field)"
+ end' server.json
+ echo " pyproject.toml: $PYPROJECT_VERSION"
+ echo " Git tag: $TAG_VERSION"
+ echo ""
+
+ # Check if all package versions match root version (only for packages with version field)
+ MISMATCH=false
+ if [ -n "$PACKAGE_VERSIONS" ]; then
+ while IFS= read -r pkg_ver; do
+ if [ "$pkg_ver" != "$ROOT_VERSION" ]; then
+ echo "ERROR: Package version ($pkg_ver) doesn't match root version ($ROOT_VERSION)"
+ MISMATCH=true
+ fi
+ done <<< "$PACKAGE_VERSIONS"
+ fi
+
+ if [ "$MISMATCH" = true ]; then
+ echo ""
+ echo "Fix: Update server.json so all package versions match the root version"
+ exit 1
+ fi
+
+ # Check if root version matches tag
+ if [ "$ROOT_VERSION" != "$TAG_VERSION" ]; then
+ echo "ERROR: server.json version ($ROOT_VERSION) doesn't match tag ($TAG_VERSION)"
+ echo "Fix: Update server.json root version to match the tag"
+ exit 1
+ fi
+
+ # Warn if pyproject.toml doesn't match
+ if [ "$PYPROJECT_VERSION" != "$TAG_VERSION" ]; then
+ echo "WARNING: pyproject.toml version ($PYPROJECT_VERSION) doesn't match tag ($TAG_VERSION)"
+ echo "This may cause PyPI issues"
+ fi
+
+ # Check Docker image tags in OCI identifiers
+ echo ""
+ echo "Checking Docker image tags..."
+ OCI_PACKAGES=$(jq -r '.packages[] | select(.registryType == "oci") | .identifier' server.json)
+ if [ -n "$OCI_PACKAGES" ]; then
+ OCI_TAG_MISMATCH=false
+ while IFS= read -r oci_id; do
+ # Extract tag from identifier (everything after last :)
+ IMAGE_TAG="${oci_id##*:}"
+ echo " - Image: $oci_id"
+ echo " Tag: $IMAGE_TAG"
+
+ if [ "$IMAGE_TAG" != "$ROOT_VERSION" ]; then
+ echo " ERROR: Docker image tag ($IMAGE_TAG) doesn't match version ($ROOT_VERSION)"
+ OCI_TAG_MISMATCH=true
+ else
+ echo " Tag matches"
+ fi
+ done <<< "$OCI_PACKAGES"
+
+ if [ "$OCI_TAG_MISMATCH" = true ]; then
+ echo ""
+ echo "Fix: Update Docker image tags in server.json to match the version"
+ exit 1
+ fi
+ fi
+
+ echo ""
+ echo "All version checks passed!"
+ echo "version=$ROOT_VERSION" >> $GITHUB_OUTPUT
+
+ - name: Install MCP Publisher
+ run: |
+ curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher
+
+ - name: Login to MCP Registry
+ run: ./mcp-publisher login github-oidc
+
+ - name: Publish to MCP Registry
+ run: ./mcp-publisher publish
+
+ - name: Notify completion
+ if: success()
+ run: |
+ echo "MCP Registry updated successfully for version ${{ steps.version.outputs.version }}"
+ echo "Docker image: docker.io/couchbaseecosystem/mcp-server-couchbase:${{ steps.version.outputs.version }}"
+ echo "PyPI package: couchbase-mcp-server==${{ steps.version.outputs.version }}"
diff --git a/README.md b/README.md
index 9fae243..5c1b6bb 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,8 @@ An [MCP](https://modelcontextprotocol.io/) server implementation of Couchbase th
+
+
## Features
- Get a list of all the buckets in the cluster
diff --git a/RELEASE.md b/RELEASE.md
new file mode 100644
index 0000000..151eca8
--- /dev/null
+++ b/RELEASE.md
@@ -0,0 +1,264 @@
+# Release Process
+
+This document describes how to create a new release of `mcp-server-couchbase`.
+
+## Release Process
+
+### Update Version Numbers
+
+**Option A: Use the helper script (Recommended):**
+
+```bash
+./scripts/update_version.sh 0.5.2
+```
+
+This automatically updates:
+
+- `pyproject.toml` version
+- `server.json` root version
+- `server.json` all package versions
+- `server.json` Docker image tags (OCI identifiers)
+- `uv.lock`
+
+**Option B: Manual update:**
+
+Update the version in all locations:
+
+1. **`pyproject.toml`:**
+
+ ```toml
+ version = "0.5.2"
+ ```
+
+2. **`server.json`** (root version):
+
+ ```json
+ {
+ "version": "0.5.2",
+ ...
+ }
+ ```
+
+3. **`server.json`** (each package version):
+
+ ```json
+ {
+ "packages": [
+ {
+ "version": "0.5.2",
+ ...
+ }
+ ]
+ }
+ ```
+
+4. **`server.json`** (Docker image tags in OCI packages):
+
+ ```json
+ {
+ "packages": [
+ {
+ "registryType": "oci",
+ "identifier": "docker.io/couchbaseecosystem/mcp-server-couchbase:0.5.2",
+ ...
+ }
+ ]
+ }
+ ```
+
+5. Update lock file:
+ ```bash
+ uv lock
+ ```
+
+> **Important:** All versions and Docker image tags must match the root version. The CI/CD pipeline validates this and will fail if versions are inconsistent.
+
+### 2. Validate Versions
+
+Before pushing, verify all versions match:
+
+```bash
+# Check versions
+echo "Checking version consistency..."
+echo "pyproject.toml: $(grep '^version = ' pyproject.toml)"
+echo "server.json root: $(jq -r '.version' server.json)"
+echo "server.json packages:"
+jq -r '.packages[] |
+ if .registryType == "oci" then
+ " - \(.registryType):\(.identifier) (tag: \(.identifier | split(":")[1]))"
+ else
+ " - \(.registryType):\(.identifier) (version: \(.version))"
+ end' server.json
+```
+
+**Expected output:**
+
+- All versions should be `0.5.2`
+- Docker image tag should be `:0.5.2`
+
+If versions don't match, run `./scripts/update_version.sh 0.5.2` again.
+
+### 3. Commit and Tag
+
+```bash
+git add pyproject.toml server.json uv.lock
+git commit -m "Bump version to 0.5.2"
+git tag v0.5.2
+git push origin main
+git push origin v0.5.2
+```
+
+> **Important:** Once you push the tag, **all workflows start immediately** and PyPI publishes within ~3 minutes. PyPI versions are **immutable** - if anything fails later, you'll need a new version number. There's no going back!
+
+### 4. Automated Pipeline
+
+Once the tag is pushed, three GitHub Actions workflows run **in parallel/sequence**:
+
+1. **PyPI Release**
+
+ - Builds Python package
+ - Publishes to PyPI as `couchbase-mcp-server`
+ - Creates GitHub Release with changelog
+
+2. **Docker Build**
+
+ - Builds multi-architecture images (amd64, arm64)
+ - Pushes to Docker Hub as `couchbaseecosystem/mcp-server-couchbase`
+ - Updates Docker Hub description
+
+3. **MCP Registry Update** (runs after Docker completes)
+ - Waits for both PyPI and Docker to complete
+ - Validates version consistency
+ - Publishes to MCP Registry
+
+> **Note:** Version validation happens in the MCP Registry workflow, which runs **after** PyPI and Docker have already published. This is why local validation (step 2) is critical!
+
+### 5. Verify Release
+
+Check that all three workflows succeeded:
+
+- https://github.com/Couchbase-Ecosystem/mcp-server-couchbase/actions
+
+Verify the release is available:
+
+- PyPI: https://pypi.org/project/couchbase-mcp-server/
+- Docker Hub: https://hub.docker.com/r/couchbaseecosystem/mcp-server-couchbase
+- MCP Registry
+
+## Release Candidates
+
+**Recommended for first-time releases or major changes.**
+
+Release candidates let you test the full release pipeline without committing to a final version number. If something fails, you can fix it and release the final version without version conflicts.
+
+**Create an RC release:**
+
+```bash
+# Update version to RC
+./scripts/update_version.sh 0.5.2rc1
+
+# Or manually update pyproject.toml and server.json (root + all packages)
+
+# Commit and tag
+git add pyproject.toml server.json uv.lock
+git commit -m "Bump version to 0.5.2rc1"
+git tag v0.5.2rc1
+git push origin main
+git push origin v0.5.2rc1
+```
+
+**What gets published:**
+
+- PyPI: `couchbase-mcp-server==0.5.2rc1`
+- Docker Hub: `couchbaseecosystem/mcp-server-couchbase:0.5.2rc1`
+- MCP Registry: version `0.5.2rc1`
+
+**If RC succeeds, release the final version:**
+
+```bash
+./scripts/update_version.sh 0.5.2
+git add pyproject.toml server.json uv.lock
+git commit -m "Bump version to 0.5.2"
+git tag v0.5.2
+git push origin main
+git push origin v0.5.2
+```
+
+**If RC fails:**
+
+- Fix the issues
+- Create `0.5.2rc2` and test again
+- No version conflicts since the final `0.5.2` wasn't published yet!
+
+## Troubleshooting
+
+### Release Failed
+
+**IMPORTANT:** Once PyPI publishes a version, it **cannot be reused**. PyPI versions are immutable.
+
+If a release fails after PyPI has published (e.g., Docker build fails, MCP Registry update fails):
+
+**Skip to next patch version:**
+
+```bash
+# If v0.5.2 was published but release incomplete
+./scripts/update_version.sh 0.5.3
+
+git add pyproject.toml server.json uv.lock
+git commit -m "Bump version to 0.5.3"
+git tag v0.5.3
+git push origin main
+git push origin v0.5.3
+```
+
+**Why this happens:**
+
+- PyPI, Docker, and MCP Registry workflows all start when you push the tag
+- Version validation only happens in the MCP Registry workflow (which runs last)
+- By that time, PyPI and Docker have already published
+- If validation or MCP Registry publish fails, you can't reuse the version number
+
+**Prevention:**
+
+- **Always test with RC releases first** (e.g., `0.5.2rc1`)
+- **Use the helper script** (`./scripts/update_version.sh`) to ensure all versions match
+- **Review changes carefully** before pushing the tag (`git diff`)
+- Verify all workflows succeeded before announcing release
+
+## How Versioning Works
+
+### Version Files
+
+All version numbers must be **manually synchronized** across:
+
+- **`pyproject.toml`**: Python package version
+- **`server.json` root `version`**: MCP Registry metadata version
+- **`server.json` package `version`**: Must match root version
+- **`server.json` OCI identifiers**: Docker image tags must match root version
+- **Git tag**: Must match all versions
+
+### Why All Versions Must Match
+
+The CI/CD pipeline validates version consistency and will **fail the build** if:
+
+- Package versions in `server.json` don't match the root version
+- Docker image tags in OCI identifiers don't match the root version
+- Root version in `server.json` doesn't match the git tag
+- (Warning only) `pyproject.toml` doesn't match the git tag
+
+This ensures:
+
+- No accidental version mismatches
+- Consistent versioning across PyPI, Docker, and MCP Registry
+- Valid JSON files that can be tested locally
+- Clear version history in git
+
+### Helper Script
+
+The `scripts/update_version.sh` script keeps all versions synchronized automatically:
+
+```bash
+./scripts/update_version.sh 0.5.2
+```
+
+This updates all three locations and runs `uv lock` in one command.
diff --git a/pyproject.toml b/pyproject.toml
index dd24dba..33f7749 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "couchbase-mcp-server"
-version = "0.5.1"
+version = "0.5.2rc1"
description = "Couchbase MCP Server - The Developer Data Platform for Critical Applications in Our AI World"
readme = "README.md"
requires-python = ">=3.10,<3.14"
diff --git a/scripts/update_version.sh b/scripts/update_version.sh
new file mode 100755
index 0000000..6cca4c6
--- /dev/null
+++ b/scripts/update_version.sh
@@ -0,0 +1,79 @@
+#!/bin/bash
+set -e
+
+# Simple script to update version in all necessary files
+# Usage: ./scripts/update_version.sh 0.5.2
+
+if [ -z "$1" ]; then
+ echo "Usage: $0 "
+ echo "Example: $0 0.5.2"
+ echo "Example: $0 0.5.2rc1"
+ exit 1
+fi
+
+NEW_VERSION="$1"
+
+echo "Updating version to $NEW_VERSION"
+echo ""
+
+# Update pyproject.toml
+echo "Updating pyproject.toml..."
+if [[ "$OSTYPE" == "darwin"* ]]; then
+ # macOS
+ sed -i '' "s/^version = \".*\"/version = \"$NEW_VERSION\"/" pyproject.toml
+else
+ # Linux
+ sed -i "s/^version = \".*\"/version = \"$NEW_VERSION\"/" pyproject.toml
+fi
+
+# Update server.json (root version, package versions, and Docker image tags)
+echo "Updating server.json..."
+if command -v jq &> /dev/null; then
+ # Use jq for safer JSON manipulation
+ jq --arg ver "$NEW_VERSION" '
+ .version = $ver |
+ .packages = [
+ .packages[] |
+ if .registryType == "oci" then
+ # Update Docker image tag (everything after last :)
+ # OCI packages should NOT have a separate version field
+ .identifier = (.identifier | sub(":[^:]*$"; ":" + $ver))
+ else
+ # Update version field for non-OCI packages (e.g., PyPI)
+ .version = $ver
+ end
+ ]
+ ' server.json > server.json.tmp
+ mv server.json.tmp server.json
+else
+ echo "Error: jq is required but not installed"
+ echo "Install with: brew install jq (macOS) or apt install jq (Linux)"
+ exit 1
+fi
+
+# Update lock file
+echo "Updating uv.lock"
+uv lock
+
+echo ""
+echo "Version updated to $NEW_VERSION in:"
+echo " - pyproject.toml"
+echo " - server.json (root, packages, and Docker image tags)"
+echo " - uv.lock"
+echo ""
+echo "Verification:"
+echo " pyproject.toml: $(grep '^version = ' pyproject.toml)"
+echo " server.json root: $(jq -r '.version' server.json)"
+echo " server.json packages:"
+jq -r '.packages[] |
+ if .registryType == "oci" then
+ " - \(.registryType):\(.identifier) (tag: \(.identifier | split(":")[1]))"
+ else
+ " - \(.registryType):\(.identifier) (version: \(.version))"
+ end' server.json
+echo ""
+echo "Next steps:"
+echo " 1. Review changes: git diff"
+echo " 2. Commit: git add pyproject.toml server.json uv.lock && git commit -m 'Bump version to $NEW_VERSION'"
+echo " 3. Tag: git tag v$NEW_VERSION"
+echo " 4. Push: git push origin main && git push origin v$NEW_VERSION"
diff --git a/server.json b/server.json
index d33ca58..6c24a5c 100644
--- a/server.json
+++ b/server.json
@@ -6,12 +6,12 @@
"url": "https://github.com/Couchbase-Ecosystem/mcp-server-couchbase",
"source": "github"
},
- "version": "0.5.1",
+ "version": "0.5.2rc1",
"packages": [
{
"registryType": "pypi",
"identifier": "couchbase-mcp-server",
- "version": "0.5.1",
+ "version": "0.5.2rc1",
"transport": {
"type": "stdio"
},
@@ -172,7 +172,7 @@
},
{
"registryType": "oci",
- "identifier": "docker.io/couchbaseecosystem/mcp-server-couchbase:0.5.1",
+ "identifier": "docker.io/couchbaseecosystem/mcp-server-couchbase:0.5.2rc1",
"transport": {
"type": "stdio"
},
diff --git a/uv.lock b/uv.lock
index 20b547d..43d7022 100644
--- a/uv.lock
+++ b/uv.lock
@@ -168,7 +168,7 @@ wheels = [
[[package]]
name = "couchbase-mcp-server"
-version = "0.5.1"
+version = "0.5.2rc1"
source = { editable = "." }
dependencies = [
{ name = "click" },