diff --git a/.github/workflows/plugin-version.yml b/.github/workflows/plugin-version.yml new file mode 100644 index 0000000..ede00a8 --- /dev/null +++ b/.github/workflows/plugin-version.yml @@ -0,0 +1,48 @@ +name: plugin version + +# Claude Code caches an installed plugin at a path keyed by its manifest version +# (~/.claude/plugins/cache///). If a change ships +# without bumping that version, the cache key is unchanged, the extracted copy is +# never refreshed, and users keep running the old code no matter how many times +# they update or reinstall. The change is invisible rather than broken, which is +# the worst way to fail, so this gate makes it loud. +on: + pull_request: + paths: + - "harbor-mcp/**" + - ".github/workflows/plugin-version.yml" + +jobs: + version-bumped: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Require a version bump when plugin files change + env: + BASE: ${{ github.event.pull_request.base.sha }} + MANIFEST: harbor-mcp/.claude-plugin/plugin.json + run: | + set -euo pipefail + + # Only the shipped plugin payload matters. Tests and evals are not + # copied into the plugin cache, so changing them cannot strand a user. + if ! git diff --name-only "$BASE"...HEAD -- harbor-mcp \ + ':(exclude)harbor-mcp/tests/**' \ + ':(exclude)harbor-mcp/evals/**' | grep -q .; then + echo "No shipped plugin files changed; version bump not required." + exit 0 + fi + + read_version() { python3 -c "import json,sys;print(json.load(sys.stdin)['version'])"; } + base_version="$(git show "$BASE:$MANIFEST" | read_version)" + head_version="$(read_version < "$MANIFEST")" + + echo "base=$base_version head=$head_version" + if [ "$base_version" = "$head_version" ]; then + echo "::error file=$MANIFEST::Shipped plugin files changed but version is still $head_version. Bump it, or installed copies will never pick this up." + exit 1 + fi + echo "Version bumped $base_version -> $head_version." diff --git a/README.md b/README.md index 2c0b2c4..406ea09 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,10 @@ This repo is also a plugin marketplace. Run these as two separate commands, not Plugins require [`uv`](https://docs.astral.sh/uv/) on your PATH. The first launch builds the server's environment, so give it a moment before the tools appear. See each server's README for credentials. +### Shipping a plugin change + +**Bump `version` in the plugin's `.claude-plugin/plugin.json` in the same PR.** Claude Code extracts an installed plugin to a cache path keyed by that version, so if it does not change, the cache is never refreshed and users keep running the old code however many times they update or reinstall. The change is invisible rather than broken. The `plugin version` workflow enforces this. + ## Design These servers follow Honeycomb's [MCP, easy as 1-2-3](https://www.honeycomb.io/blog/mcp-easy-as-1-2-3) guidance: a few curated tools built around real questions rather than raw API endpoints, responses shaped for a model instead of a UI, and typed schemas that steer the model toward valid calls. diff --git a/harbor-mcp/.claude-plugin/plugin.json b/harbor-mcp/.claude-plugin/plugin.json index b0b0c0d..f947c17 100644 --- a/harbor-mcp/.claude-plugin/plugin.json +++ b/harbor-mcp/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "harbor-mcp", - "version": "0.1.0", + "version": "0.2.0", "description": "MCP server for the Harbor hub: inspect evaluation jobs, trials, and uploads; publish tasks and datasets when writes are enabled.", "author": { "name": "Walker Hughes" diff --git a/harbor-mcp/scripts/start-server.sh b/harbor-mcp/scripts/start-server.sh index 5ab1fc0..5d4d3c2 100755 --- a/harbor-mcp/scripts/start-server.sh +++ b/harbor-mcp/scripts/start-server.sh @@ -42,6 +42,12 @@ if ! UV="$(find_uv)"; then exit 127 fi +# Drop any inherited VIRTUAL_ENV. uv already ignores a mismatched one, but it +# warns loudly on stderr about "does not match the project environment path", +# which reads like the cause of a failure and has already sent one debugging +# session down the wrong path. +unset VIRTUAL_ENV + # First launch builds the environment from the checked-in uv.lock, which takes a # few seconds; later launches reuse it. exec "$UV" run --project "$ROOT" harbor-mcp