feat: Claude Desktop extension (.mcpb) build and release pipeline - #224
Conversation
Package the server as a one-click MCPB desktop extension for Claude Desktop (macOS + Windows): - mcpb/manifest.json: MCPB v0.3 manifest (binary server type, win32 platform override, user_config with keychain-backed token, dynamic surface default, AUTO_UPDATE=false) + 512x512 icon from the SVG logo - .goreleaser.yml: darwin universal (fat) binary via universal_binaries (replace: false keeps per-arch assets that go-selfupdate matches) - scripts/build-mcpb.sh: assembles the bundle from GoReleaser artifacts and packs it with the pinned @anthropic-ai/mcpb CLI - release workflow: builds and uploads gitlab-mcp-server.mcpb as a release asset; update-server-json-sha.sh stamps the manifest version alongside server.json and plugin.json - Makefile: mcpb (local cross-compile + lipo + pack) and check-mcpb (manifest validation) targets - PRIVACY.md: privacy policy required for the Anthropic connectors directory submission, linked from README and the manifest - README: Claude Desktop row in the one-click install table + privacy section; docs/guides/claude-desktop-extension.md how-to Verified: manifest validates with mcpb CLI 2.1.2, goreleaser check passes, make mcpb produces a 39 MB bundle whose darwin binary boots over stdio with the manifest env pattern and serves the dynamic surface (gitlab_find_action / gitlab_execute_action). Claude-Session: https://claude.ai/code/session_01LJ6D4L6rnms9GqMqqc2tFb
Reviewer's GuideAdds a complete build and release pipeline to package the GitLab MCP server as a Claude Desktop (.mcpb) extension, including a versioned MCPB manifest, local/CI build scripts, GoReleaser universal binary config, README/docs updates, and a privacy policy for directory submission. File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Warning Review limit reached
Next review available in: 52 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThis PR adds Claude Desktop extension (MCPB) support: a new manifest and build script, Makefile targets, GoReleaser universal binary config, release workflow integration to build/upload the artifact, version-sync updates, and accompanying README/guide/privacy documentation. ChangesClaude Desktop Extension (MCPB)
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant CI as release.yml
participant UpdateScript as update-server-json-sha.sh
participant BuildScript as build-mcpb.sh
participant GoReleaser
participant GitHubRelease as GitHub Release
CI->>UpdateScript: run version sync
UpdateScript->>UpdateScript: update mcpb/manifest.json version
CI->>BuildScript: build-mcpb.sh <tag_version>
BuildScript->>GoReleaser: locate universal macOS + Windows binaries
BuildScript->>BuildScript: assemble bundle, stamp manifest, pack .mcpb
CI->>GitHubRelease: upload gitlab-mcp-server.mcpb
Possibly related PRs
Suggested labels: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Hey - I've found 3 issues, and left some high level feedback:
- The MCPB CLI version is pinned separately in the Makefile (
MCPB_CLI_VERSION) and inscripts/build-mcpb.sh(MCPB_VERSION); consider centralizing this into a single definition to avoid version drift between validation and packing. scripts/build-mcpb.shlocates binaries withfindpatterns like*darwin_all*and*windows_amd64*, which couples the bundle build to GoReleaser’s directory naming; if possible, tighten this to the specific GoReleaser IDs/paths to make it more robust against future config changes.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The MCPB CLI version is pinned separately in the Makefile (`MCPB_CLI_VERSION`) and in `scripts/build-mcpb.sh` (`MCPB_VERSION`); consider centralizing this into a single definition to avoid version drift between validation and packing.
- `scripts/build-mcpb.sh` locates binaries with `find` patterns like `*darwin_all*` and `*windows_amd64*`, which couples the bundle build to GoReleaser’s directory naming; if possible, tighten this to the specific GoReleaser IDs/paths to make it more robust against future config changes.
## Individual Comments
### Comment 1
<location path="Makefile" line_range="691" />
<code_context>
scripts/check-openplugin.sh
+# Pin the MCPB packer CLI for supply-chain integrity (also pinned in scripts/build-mcpb.sh).
+MCPB_CLI_VERSION := 2.1.2
+
+## check-mcpb: validate the Claude Desktop extension manifest (mcpb/manifest.json).
</code_context>
<issue_to_address>
**suggestion (bug_risk):** The MCPB CLI version is now pinned in two places (Makefile and scripts/build-mcpb.sh), which can easily drift out of sync.
Because MCPB_CLI_VERSION here and MCPB_VERSION in scripts/build-mcpb.sh are independent, they can diverge, causing local `make check-mcpb` and release bundling to use different MCPB versions. Please centralize this version (e.g., via a shared env file, a Make variable passed into the script, or generating the script from a template) so it’s defined in only one place.
Suggested implementation:
```
# Pin the MCPB packer CLI for supply-chain integrity.
# This is the single source of truth; scripts/build-mcpb.sh reads MCPB_CLI_VERSION from the environment.
MCPB_CLI_VERSION := 2.1.2
```
```
mcpb:
@command -v lipo >/dev/null || { echo "ERROR: lipo is required (macOS Xcode CLT)"; exit 1; }
@set -e; \
VER=$$(tr -d '[:space:]' < VERSION); \
MCPB_CLI_VERSION=$(MCPB_CLI_VERSION) scripts/build-mcpb.sh "$$VER"; \
```
To fully implement the centralization and avoid drift, you should also:
1. Update `scripts/build-mcpb.sh` to remove its internal `MCPB_VERSION` (or similarly named) constant and instead read the CLI version from the environment, e.g.:
```sh
: "${MCPB_CLI_VERSION:?MCPB_CLI_VERSION must be set}"
npx --yes @anthropic-ai/mcpb@"${MCPB_CLI_VERSION}" ...
```
2. Ensure that everywhere `scripts/build-mcpb.sh` is invoked (if there are other callers besides the `mcpb` Make target), it either:
- relies on the Makefile `mcpb` target, or
- explicitly sets `MCPB_CLI_VERSION` in the environment to keep the single source of truth.
3. Optionally add a brief comment at the top of `scripts/build-mcpb.sh` noting that `MCPB_CLI_VERSION` is expected to be provided by the Makefile so future changes don’t reintroduce a second version pin.
</issue_to_address>
### Comment 2
<location path="scripts/build-mcpb.sh" line_range="47-49" />
<code_context>
+# Locate the GoReleaser artifacts. Binary paths live in per-target build
+# directories (dist/<id>_<goos>_<goarch>[_<goamd64>]/); the darwin universal
+# binary comes from the universal_binaries step (goarch "all").
+find_binary() {
+ local pattern="$1" name="$2" found
+ found=$(find "$DIST_DIR" -type f -path "$pattern" -name "$name" | head -n1)
+ if [[ -z "$found" ]]; then
+ echo "ERROR: no $name matching $pattern under $DIST_DIR — run GoReleaser first" >&2
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Binary discovery via `find ... | head -n1` is somewhat brittle and can pick an unintended artifact if multiple paths match.
Because this uses `head -n1`, the chosen binary depends on filesystem ordering; leftover or additional GoReleaser artifacts could cause the wrong file to be selected. Prefer matching the exact expected GoReleaser directory structure (e.g., `gitlab-mcp-server_darwin_all/gitlab-mcp-server`, `gitlab-mcp-server_windows_amd64/gitlab-mcp-server.exe`), or at least verify that exactly one match exists and fail if there are zero or multiple matches, to avoid silently bundling an unintended binary.
Suggested implementation:
```
# Locate the GoReleaser artifacts. Binary paths live in per-target build
# directories (dist/<id>_<goos>_<goarch>[_<goamd64>]/); the darwin universal
# binary comes from the universal_binaries step (goarch "all").
find_binary() {
local pattern="$1" name="$2"
local matches
mapfile -t matches < <(find "$DIST_DIR" -type f -path "$pattern" -name "$name")
if (( ${#matches[@]} == 0 )); then
echo "ERROR: no $name matching $pattern under $DIST_DIR — run GoReleaser first" >&2
exit 1
elif (( ${#matches[@]} > 1 )); then
echo "ERROR: multiple $name files matching $pattern under $DIST_DIR; refusing to pick one automatically" >&2
printf 'Matches:\n' >&2
printf ' %s\n' "${matches[@]}" >&2
exit 1
fi
echo "${matches[0]}"
}
# These patterns are intended to match the exact GoReleaser output directories:
# dist/gitlab-mcp-server_darwin_all/gitlab-mcp-server
# dist/gitlab-mcp-server_windows_amd64/gitlab-mcp-server.exe
DARWIN_BIN=$(find_binary "$DIST_DIR/gitlab-mcp-server_darwin_all/*" "gitlab-mcp-server")
WINDOWS_BIN=$(find_binary "$DIST_DIR/gitlab-mcp-server_windows_amd64/*" "gitlab-mcp-server.exe")
```
If your GoReleaser IDs or output directory names differ from `gitlab-mcp-server_darwin_all` and `gitlab-mcp-server_windows_amd64`, adjust the two `find_binary` call patterns accordingly to match the actual `dist/<id>_<goos>_<goarch>` layout produced by your `goreleaser.yaml`. If additional platforms (e.g., `linux_amd64`) are bundled, add corresponding `find_binary` invocations using similarly strict patterns so they also benefit from the single-match validation.
</issue_to_address>
### Comment 3
<location path="docs/guides/claude-desktop-extension.md" line_range="58" />
<code_context>
+
+## Privacy and directory submission
+
+The manifest's `privacy_policies` points to [PRIVACY.md](../../PRIVACY.md) and
+the [GitLab Privacy Statement](https://about.gitlab.com/privacy/). Directory
+submissions for desktop extensions go through Anthropic's
</code_context>
<issue_to_address>
**nitpick (typo):** Minor subject–verb agreement tweak in the "privacy_policies" sentence.
Because the field name is plural, “The manifest’s `privacy_policies` points to…” reads a bit off. Consider “The `privacy_policies` field points to…” or “The `privacy_policies` entries point to…” for clearer subject–verb agreement.
```suggestion
The `privacy_policies` field points to [PRIVACY.md](../../PRIVACY.md) and
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| scripts/check-openplugin.sh | ||
|
|
||
| # Pin the MCPB packer CLI for supply-chain integrity (also pinned in scripts/build-mcpb.sh). | ||
| MCPB_CLI_VERSION := 2.1.2 |
There was a problem hiding this comment.
suggestion (bug_risk): The MCPB CLI version is now pinned in two places (Makefile and scripts/build-mcpb.sh), which can easily drift out of sync.
Because MCPB_CLI_VERSION here and MCPB_VERSION in scripts/build-mcpb.sh are independent, they can diverge, causing local make check-mcpb and release bundling to use different MCPB versions. Please centralize this version (e.g., via a shared env file, a Make variable passed into the script, or generating the script from a template) so it’s defined in only one place.
Suggested implementation:
# Pin the MCPB packer CLI for supply-chain integrity.
# This is the single source of truth; scripts/build-mcpb.sh reads MCPB_CLI_VERSION from the environment.
MCPB_CLI_VERSION := 2.1.2
mcpb:
@command -v lipo >/dev/null || { echo "ERROR: lipo is required (macOS Xcode CLT)"; exit 1; }
@set -e; \
VER=$$(tr -d '[:space:]' < VERSION); \
MCPB_CLI_VERSION=$(MCPB_CLI_VERSION) scripts/build-mcpb.sh "$$VER"; \
To fully implement the centralization and avoid drift, you should also:
- Update
scripts/build-mcpb.shto remove its internalMCPB_VERSION(or similarly named) constant and instead read the CLI version from the environment, e.g.:: "${MCPB_CLI_VERSION:?MCPB_CLI_VERSION must be set}" npx --yes @anthropic-ai/mcpb@"${MCPB_CLI_VERSION}" ...
- Ensure that everywhere
scripts/build-mcpb.shis invoked (if there are other callers besides themcpbMake target), it either:- relies on the Makefile
mcpbtarget, or - explicitly sets
MCPB_CLI_VERSIONin the environment to keep the single source of truth.
- relies on the Makefile
- Optionally add a brief comment at the top of
scripts/build-mcpb.shnoting thatMCPB_CLI_VERSIONis expected to be provided by the Makefile so future changes don’t reintroduce a second version pin.
| find_binary() { | ||
| local pattern="$1" name="$2" found | ||
| found=$(find "$DIST_DIR" -type f -path "$pattern" -name "$name" | head -n1) |
There was a problem hiding this comment.
suggestion (bug_risk): Binary discovery via find ... | head -n1 is somewhat brittle and can pick an unintended artifact if multiple paths match.
Because this uses head -n1, the chosen binary depends on filesystem ordering; leftover or additional GoReleaser artifacts could cause the wrong file to be selected. Prefer matching the exact expected GoReleaser directory structure (e.g., gitlab-mcp-server_darwin_all/gitlab-mcp-server, gitlab-mcp-server_windows_amd64/gitlab-mcp-server.exe), or at least verify that exactly one match exists and fail if there are zero or multiple matches, to avoid silently bundling an unintended binary.
Suggested implementation:
# Locate the GoReleaser artifacts. Binary paths live in per-target build
# directories (dist/<id>_<goos>_<goarch>[_<goamd64>]/); the darwin universal
# binary comes from the universal_binaries step (goarch "all").
find_binary() {
local pattern="$1" name="$2"
local matches
mapfile -t matches < <(find "$DIST_DIR" -type f -path "$pattern" -name "$name")
if (( ${#matches[@]} == 0 )); then
echo "ERROR: no $name matching $pattern under $DIST_DIR — run GoReleaser first" >&2
exit 1
elif (( ${#matches[@]} > 1 )); then
echo "ERROR: multiple $name files matching $pattern under $DIST_DIR; refusing to pick one automatically" >&2
printf 'Matches:\n' >&2
printf ' %s\n' "${matches[@]}" >&2
exit 1
fi
echo "${matches[0]}"
}
# These patterns are intended to match the exact GoReleaser output directories:
# dist/gitlab-mcp-server_darwin_all/gitlab-mcp-server
# dist/gitlab-mcp-server_windows_amd64/gitlab-mcp-server.exe
DARWIN_BIN=$(find_binary "$DIST_DIR/gitlab-mcp-server_darwin_all/*" "gitlab-mcp-server")
WINDOWS_BIN=$(find_binary "$DIST_DIR/gitlab-mcp-server_windows_amd64/*" "gitlab-mcp-server.exe")
If your GoReleaser IDs or output directory names differ from gitlab-mcp-server_darwin_all and gitlab-mcp-server_windows_amd64, adjust the two find_binary call patterns accordingly to match the actual dist/<id>_<goos>_<goarch> layout produced by your goreleaser.yaml. If additional platforms (e.g., linux_amd64) are bundled, add corresponding find_binary invocations using similarly strict patterns so they also benefit from the single-match validation.
|
|
||
| ## Privacy and directory submission | ||
|
|
||
| The manifest's `privacy_policies` points to [PRIVACY.md](../../PRIVACY.md) and |
There was a problem hiding this comment.
nitpick (typo): Minor subject–verb agreement tweak in the "privacy_policies" sentence.
Because the field name is plural, “The manifest’s privacy_policies points to…” reads a bit off. Consider “The privacy_policies field points to…” or “The privacy_policies entries point to…” for clearer subject–verb agreement.
| The manifest's `privacy_policies` points to [PRIVACY.md](../../PRIVACY.md) and | |
| The `privacy_policies` field points to [PRIVACY.md](../../PRIVACY.md) and |
There was a problem hiding this comment.
Pull request overview
Adds first-class packaging and release support for distributing gitlab-mcp-server as a Claude Desktop MCPB (.mcpb) desktop extension (macOS universal + Windows), including manifest/versioning automation and user-facing documentation needed for directory submission.
Changes:
- Introduces the MCPB bundle (manifest + icon) and a build script to assemble/pack a
.mcpbfrom GoReleaser artifacts. - Extends release automation to build/upload the
.mcpbasset and to version-stampmcpb/manifest.jsonalongside existing manifests. - Updates documentation/README to cover Claude Desktop installation and adds a new
PRIVACY.mdpolicy referenced by the manifest.
Reviewed changes
Copilot reviewed 10 out of 11 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/update-server-json-sha.sh | Also stamps MCPB manifest version during releases. |
| scripts/build-mcpb.sh | New script to assemble and pack gitlab-mcp-server.mcpb from dist artifacts. |
| README.md | Adds Claude Desktop download row and links to privacy policy. |
| PRIVACY.md | New privacy policy for connector submission requirements. |
| mcpb/manifest.json | New MCPB v0.3 manifest for Claude Desktop (binary server + user_config). |
| Makefile | Adds mcpb/check-mcpb targets and pins MCPB CLI version. |
| docs/guides/README.md | Adds guide index entry for the Claude Desktop extension. |
| docs/guides/claude-desktop-extension.md | New how-to guide for installing/building the .mcpb extension. |
| .goreleaser.yml | Adds macOS universal binary output for the extension entry point. |
| .github/workflows/release.yml | Builds/uploads .mcpb on release and commits MCPB manifest version bumps. |
| mcpb/icon.png | Adds 512×512 icon for Claude Desktop extension listing. |
| find_binary() { | ||
| local pattern="$1" name="$2" found | ||
| found=$(find "$DIST_DIR" -type f -path "$pattern" -name "$name" | head -n1) | ||
| if [[ -z "$found" ]]; then | ||
| echo "ERROR: no $name matching $pattern under $DIST_DIR — run GoReleaser first" >&2 | ||
| exit 1 | ||
| fi | ||
| echo "$found" | ||
| } |
| # 6. Update MCPB (Claude Desktop extension) manifest version (if present) | ||
| MCPB_JSON="mcpb/manifest.json" | ||
| if [[ -f "$MCPB_JSON" ]]; then | ||
| jq --arg v "$VERSION" '.version = $v' "$MCPB_JSON" > tmp.$$.json && mv tmp.$$.json "$MCPB_JSON" |
| - **GitHub (auto-update only).** When the auto-update feature is enabled | ||
| (`AUTO_UPDATE=true`, the default for standalone binaries), the server | ||
| periodically checks GitHub Releases on this repository for new versions and | ||
| downloads signed binaries from there. No personal data is sent — it is a | ||
| standard HTTPS request to `api.github.com`, subject to the | ||
| [GitHub Privacy Statement](https://docs.github.com/en/site-policy/privacy-policies/github-general-privacy-statement). | ||
| The Claude Desktop extension (`.mcpb`) ships with auto-update **disabled**; | ||
| updates arrive through new extension versions instead. |
There was a problem hiding this comment.
Code Review
This pull request introduces support for building and packaging the Claude Desktop extension (.mcpb) for the GitLab MCP Server. It adds configuration for macOS universal binaries in GoReleaser, new Makefile targets, a build script (scripts/build-mcpb.sh), a manifest file (mcpb/manifest.json), a privacy policy (PRIVACY.md), and comprehensive documentation. Feedback on the changes suggests two improvements in scripts/build-mcpb.sh: first, replacing head -n1 in the pipeline to prevent potential SIGPIPE failures when set -o pipefail is active, and second, adding a pre-execution check to verify that npx is installed.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| find_binary() { | ||
| local pattern="$1" name="$2" found | ||
| found=$(find "$DIST_DIR" -type f -path "$pattern" -name "$name" | head -n1) | ||
| if [[ -z "$found" ]]; then | ||
| echo "ERROR: no $name matching $pattern under $DIST_DIR — run GoReleaser first" >&2 | ||
| exit 1 | ||
| fi | ||
| echo "$found" | ||
| } |
There was a problem hiding this comment.
Using head -n1 in a pipeline when set -o pipefail is active can cause the script to fail with exit code 141 (SIGPIPE) if find produces multiple matches and exits after head closes the pipe. To prevent this, we can safely extract the first match using Bash parameter expansion instead of a pipeline.
| find_binary() { | |
| local pattern="$1" name="$2" found | |
| found=$(find "$DIST_DIR" -type f -path "$pattern" -name "$name" | head -n1) | |
| if [[ -z "$found" ]]; then | |
| echo "ERROR: no $name matching $pattern under $DIST_DIR — run GoReleaser first" >&2 | |
| exit 1 | |
| fi | |
| echo "$found" | |
| } | |
| find_binary() { | |
| local pattern="$1" name="$2" found | |
| found=$(find "$DIST_DIR" -type f -path "$pattern" -name "$name" 2>/dev/null) | |
| found="${found%%$'\\n'*}" | |
| if [[ -z "$found" ]]; then | |
| echo "ERROR: no $name matching $pattern under $DIST_DIR — run GoReleaser first" >&2 | |
| exit 1 | |
| fi | |
| echo "$found" | |
| } |
| if ! command -v jq &> /dev/null; then | ||
| echo "ERROR: jq is required but not installed" >&2 | ||
| exit 1 | ||
| fi |
There was a problem hiding this comment.
The script uses npx to pack the bundle, but it does not verify if npx is installed before running. Adding a check for npx alongside jq provides a friendlier error message if Node.js/npm is missing.
| if ! command -v jq &> /dev/null; then | |
| echo "ERROR: jq is required but not installed" >&2 | |
| exit 1 | |
| fi | |
| if ! command -v jq &> /dev/null; then | |
| echo "ERROR: jq is required but not installed" >&2 | |
| exit 1 | |
| fi | |
| if ! command -v npx &> /dev/null; then | |
| echo "ERROR: npx is required but not installed (Node.js/npm)" >&2 | |
| exit 1 | |
| fi |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/release.yml:
- Around line 112-121: The Claude Desktop extension packaging step in the
release workflow can fail the entire job even though it is non-essential. Update
the build-and-upload block that runs build-mcpb.sh, sha256sum, and gh release
upload so transient failures do not block later release steps, either by making
this step non-fatal or by adding retry/guarding around the npx/build/upload
portion. Keep the core release flow intact so the "Publish to MCP Registry" and
manifest-commit steps still run when this packaging step hits an intermittent
error.
In `@Makefile`:
- Around line 697-711: The mcpb target body is too long and should be moved into
a script to satisfy checkmake. Extract the cross-compile and lipo packaging
steps from the mcpb recipe into a new helper script (for example, a local build
script alongside scripts/build-mcpb.sh), then have the mcpb target simply invoke
that script and the existing packaging script. Keep the version reading and
artifact paths behavior the same, and use the mcpb target plus
scripts/build-mcpb.sh as the main symbols to locate the flow.
In `@mcpb/manifest.json`:
- Line 3: Update the manifest schema version in mcpb/manifest.json from
manifest_version 0.3 to 0.4. This is a simple version bump in the manifest JSON,
and the key to change is manifest_version so the file matches the newer MCPB
CLI-supported schema.
In `@README.md`:
- Around line 67-74: The README setup guidance conflates Docker/stdio clients
with the Claude Desktop extension. Update the install instructions around the
Claude Desktop row and the self-managed GitLab note so Claude Desktop users are
told to configure the required gitlab_url in the extension’s settings UI, while
the GITLAB_URL env var is mentioned only for Docker/stdio MCP clients. Use the
existing Claude Desktop, settings UI, and GITLAB_URL references to split the
guidance clearly.
In `@scripts/build-mcpb.sh`:
- Around line 47-58: The find_binary helper is currently selecting the first
match from find, which can silently pick the wrong GoReleaser artifact when
multiple binaries exist under DIST_DIR. Update find_binary in the build-mcpb.sh
script to detect ambiguous results for the gitlab-mcp-server and
gitlab-mcp-server.exe lookups, and fail with a clear error if more than one
match is found, or otherwise ensure dist/ is cleaned before resolving DARWIN_BIN
and WINDOWS_BIN.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 6942e432-4003-44f8-a605-0ea9e17d2712
⛔ Files ignored due to path filters (1)
mcpb/icon.pngis excluded by!**/*.png
📒 Files selected for processing (10)
.github/workflows/release.yml.goreleaser.ymlMakefilePRIVACY.mdREADME.mddocs/guides/README.mddocs/guides/claude-desktop-extension.mdmcpb/manifest.jsonscripts/build-mcpb.shscripts/update-server-json-sha.sh
| - name: Build and upload Claude Desktop extension (.mcpb) | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| run: | | ||
| set -euo pipefail | ||
| VERSION="${GITHUB_REF#refs/tags/v}" | ||
| bash scripts/build-mcpb.sh "${VERSION}" | ||
| sha256sum dist/gitlab-mcp-server.mcpb | ||
| gh release upload "v${VERSION}" dist/gitlab-mcp-server.mcpb --clobber | ||
|
|
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Non-essential MCPB step can block the rest of the release job.
This step has no retry and no continue-on-error, unlike the curl calls elsewhere in this job that use --retry 3 --retry-connrefused (Lines 135-136). A transient npx fetch failure or gh release upload hiccup here will abort the job before "Publish to MCP Registry" and the manifest-commit step run, even though the core GoReleaser release already succeeded — turning a packaging nicety into a release blocker.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yml around lines 112 - 121, The Claude Desktop
extension packaging step in the release workflow can fail the entire job even
though it is non-essential. Update the build-and-upload block that runs
build-mcpb.sh, sha256sum, and gh release upload so transient failures do not
block later release steps, either by making this step non-fatal or by adding
retry/guarding around the npx/build/upload portion. Keep the core release flow
intact so the "Publish to MCP Registry" and manifest-commit steps still run when
this packaging step hits an intermittent error.
| ## mcpb: build the Claude Desktop extension bundle (dist/gitlab-mcp-server.mcpb). | ||
| ## Cross-compiles the darwin universal binary (lipo) and the windows/amd64 binary, | ||
| ## then assembles and packs the bundle with scripts/build-mcpb.sh. | ||
| mcpb: | ||
| @command -v lipo >/dev/null || { echo "ERROR: lipo is required (macOS Xcode CLT)"; exit 1; } | ||
| @set -e; \ | ||
| VER=$$(tr -d '[:space:]' < VERSION); \ | ||
| rm -rf dist/local_darwin_arm64 dist/local_darwin_amd64 dist/local_darwin_all dist/local_windows_amd64; \ | ||
| mkdir -p dist/local_darwin_arm64 dist/local_darwin_amd64 dist/local_darwin_all dist/local_windows_amd64; \ | ||
| CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -trimpath -ldflags "-s -w -X main.version=$$VER" -o dist/local_darwin_arm64/gitlab-mcp-server ./cmd/server; \ | ||
| CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -trimpath -ldflags "-s -w -X main.version=$$VER" -o dist/local_darwin_amd64/gitlab-mcp-server ./cmd/server; \ | ||
| lipo -create -output dist/local_darwin_all/gitlab-mcp-server dist/local_darwin_arm64/gitlab-mcp-server dist/local_darwin_amd64/gitlab-mcp-server; \ | ||
| CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -trimpath -ldflags "-s -w -X main.version=$$VER" -o dist/local_windows_amd64/gitlab-mcp-server.exe ./cmd/server; \ | ||
| bash scripts/build-mcpb.sh "$$VER" | ||
|
|
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
Extract mcpb target body into a script.
Static analysis (checkmake) flags this target's body length. Given the project already externalizes MCPB packaging logic into scripts/build-mcpb.sh, consider moving the cross-compile + lipo steps into a small script (e.g. scripts/build-mcpb-local.sh) invoked by this target, consistent with the pattern used elsewhere in this PR.
🧰 Tools
🪛 checkmake (0.3.2)
[warning] 700-700: Target body for "mcpb" exceeds allowed length of 5 lines (10).
(maxbodylength)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Makefile` around lines 697 - 711, The mcpb target body is too long and should
be moved into a script to satisfy checkmake. Extract the cross-compile and lipo
packaging steps from the mcpb recipe into a new helper script (for example, a
local build script alongside scripts/build-mcpb.sh), then have the mcpb target
simply invoke that script and the existing packaging script. Keep the version
reading and artifact paths behavior the same, and use the mcpb target plus
scripts/build-mcpb.sh as the main symbols to locate the flow.
Source: Linters/SAST tools
| @@ -0,0 +1,131 @@ | |||
| { | |||
| "$schema": "https://raw.githubusercontent.com/modelcontextprotocol/mcpb/main/dist/mcpb-manifest.schema.json", | |||
| "manifest_version": "0.3", | |||
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
What manifest_version values does @anthropic-ai/mcpb 2.1.2 CLI support and validate?
💡 Result:
The @anthropic-ai/mcpb CLI tool version 2.1.2 supports and validates manifest_version values "0.1", "0.2", and "0.3" [1][2]. The tool includes versioned schemas for these manifest versions, which are used to validate the structure of the manifest.json file [1][2]. The package explicitly exports these schemas, including a union schema capable of validating manifests from any of these supported versions (0.1, 0.2, and 0.3) [2]. When using the mcpb validate command, the CLI checks the provided manifest against these schema definitions to ensure it conforms to the required specification [3]. While "0.3" is commonly cited in documentation as a current version [4][5], the CLI architecture maintains backward compatibility by including schemas for the older versions [1][2].
Citations:
- 1: https://cdn.jsdelivr.net/npm/@anthropic-ai/mcpb@2.1.2/dist/schemas/index.d.ts
- 2: feat: export versioned schemas in package.json modelcontextprotocol/mcpb#138
- 3: https://github.com/modelcontextprotocol/mcpb/blob/main/CLI.md
- 4: https://github.com/anthropics/mcpb/blob/main/MANIFEST.md
- 5: https://github.com/modelcontextprotocol/mcpb/blob/main/MANIFEST.md
🌐 Web query:
Current MCPB manifest schema version and whether @anthropic-ai/mcpb 2.1.2 supports manifest_version 0.4
💡 Result:
The current MCPB manifest schema version is 0.4 [1][2][3]. Yes, the @anthropic-ai/mcpb package version 2.1.2 supports manifest_version 0.4 [2]. The package includes specific Zod and JSON schemas for version 0.4, as well as an "any" union schema that validates manifests across supported versions [4][2]. Documentation also explicitly notes that the 0.4 manifest version introduces the "uv" runtime type [2][5][6].
Citations:
- 1: https://github.com/modelcontextprotocol/mcpb/tree/main/schemas
- 2: https://registry.npmjs.org/@anthropic-ai/mcpb
- 3: https://github.com/HK-hub/AgentSkills/blob/main/build-mcpb/references/manifest-schema.md
- 4: feat: export versioned schemas in package.json modelcontextprotocol/mcpb#138
- 5: https://github.com/anthropics/mcpb?tab=readme-ov-file
- 6: https://github.com/anthropics/mcpb/
Bump mcpb/manifest.json to manifest_version: "0.4"
The pinned MCPB CLI already supports the 0.4 schema, so keeping 0.3 leaves this manifest on an older version.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@mcpb/manifest.json` at line 3, Update the manifest schema version in
mcpb/manifest.json from manifest_version 0.3 to 0.4. This is a simple version
bump in the manifest JSON, and the key to change is manifest_version so the file
matches the newer MCPB CLI-supported schema.
| <tr> | ||
| <td><b>Claude Desktop</b></td> | ||
| <td><a href="https://github.com/jmrplens/gitlab-mcp-server/releases/latest/download/gitlab-mcp-server.mcpb"><img alt="Download .mcpb extension" src="https://img.shields.io/badge/Download-.mcpb_extension-d97757?style=flat-square&logo=claude&logoColor=white" /></a></td> | ||
| <td>settings UI (keychain)</td> | ||
| </tr> | ||
| </table> | ||
|
|
||
| Each button registers the **Docker**-based server (auto-pulls the image on first run; you need [Docker](https://www.docker.com/) installed). Need a token? [Create a Personal Access Token](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html) with the **`api`** scope. Self-managed GitLab? Add a `GITLAB_URL` env var in your client's MCP config after install. | ||
| Each button registers the **Docker**-based server (auto-pulls the image on first run; you need [Docker](https://www.docker.com/) installed). The **Claude Desktop** row instead downloads a native [.mcpb desktop extension](docs/guides/claude-desktop-extension.md) (macOS universal + Windows, no Docker) — open it with Claude Desktop and fill in the settings. Need a token? [Create a Personal Access Token](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html) with the **`api`** scope. Self-managed GitLab? Add a `GITLAB_URL` env var in your client's MCP config after install. |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Clarify the self-managed GitLab instruction for Claude Desktop.
The Claude Desktop extension uses the settings UI (gitlab_url is a required user_config field), so the GITLAB_URL env-var advice only applies to the Docker/stdio clients. Split this into client-specific guidance to avoid sending Claude Desktop users down the wrong setup path.
Proposed fix
-Each button registers the **Docker**-based server (auto-pulls the image on first run; you need [Docker](https://www.docker.com/) installed). The **Claude Desktop** row instead downloads a native [.mcpb desktop extension](docs/guides/claude-desktop-extension.md) (macOS universal + Windows, no Docker) — open it with Claude Desktop and fill in the settings. Need a token? [Create a Personal Access Token](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html) with the **`api`** scope. Self-managed GitLab? Add a `GITLAB_URL` env var in your client's MCP config after install.
+Each button registers the **Docker**-based server (auto-pulls the image on first run; you need [Docker](https://www.docker.com/) installed). The **Claude Desktop** row instead downloads a native [.mcpb desktop extension](docs/guides/claude-desktop-extension.md) (macOS universal + Windows, no Docker) — open it with Claude Desktop and fill in the settings.
+Need a token? [Create a Personal Access Token](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html) with the **`api`** scope.
+For the Docker-based options, add `GITLAB_URL` in your client's MCP config after install. For Claude Desktop, set the URL in the extension settings UI.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <tr> | |
| <td><b>Claude Desktop</b></td> | |
| <td><a href="https://github.com/jmrplens/gitlab-mcp-server/releases/latest/download/gitlab-mcp-server.mcpb"><img alt="Download .mcpb extension" src="https://img.shields.io/badge/Download-.mcpb_extension-d97757?style=flat-square&logo=claude&logoColor=white" /></a></td> | |
| <td>settings UI (keychain)</td> | |
| </tr> | |
| </table> | |
| Each button registers the **Docker**-based server (auto-pulls the image on first run; you need [Docker](https://www.docker.com/) installed). Need a token? [Create a Personal Access Token](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html) with the **`api`** scope. Self-managed GitLab? Add a `GITLAB_URL` env var in your client's MCP config after install. | |
| Each button registers the **Docker**-based server (auto-pulls the image on first run; you need [Docker](https://www.docker.com/) installed). The **Claude Desktop** row instead downloads a native [.mcpb desktop extension](docs/guides/claude-desktop-extension.md) (macOS universal + Windows, no Docker) — open it with Claude Desktop and fill in the settings. Need a token? [Create a Personal Access Token](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html) with the **`api`** scope. Self-managed GitLab? Add a `GITLAB_URL` env var in your client's MCP config after install. | |
| <tr> | |
| <td><b>Claude Desktop</b></td> | |
| <td><a href="https://github.com/jmrplens/gitlab-mcp-server/releases/latest/download/gitlab-mcp-server.mcpb"><img alt="Download .mcpb extension" src="https://img.shields.io/badge/Download-.mcpb_extension-d97757?style=flat-square&logo=claude&logoColor=white" /></a></td> | |
| <td>settings UI (keychain)</td> | |
| </tr> | |
| </table> | |
| Each button registers the **Docker**-based server (auto-pulls the image on first run; you need [Docker](https://www.docker.com/) installed). The **Claude Desktop** row instead downloads a native [.mcpb desktop extension](docs/guides/claude-desktop-extension.md) (macOS universal + Windows, no Docker) — open it with Claude Desktop and fill in the settings. | |
| Need a token? [Create a Personal Access Token](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html) with the **`api`** scope. | |
| For the Docker-based options, add `GITLAB_URL` in your client's MCP config after install. For Claude Desktop, set the URL in the extension settings UI. |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@README.md` around lines 67 - 74, The README setup guidance conflates
Docker/stdio clients with the Claude Desktop extension. Update the install
instructions around the Claude Desktop row and the self-managed GitLab note so
Claude Desktop users are told to configure the required gitlab_url in the
extension’s settings UI, while the GITLAB_URL env var is mentioned only for
Docker/stdio MCP clients. Use the existing Claude Desktop, settings UI, and
GITLAB_URL references to split the guidance clearly.
| find_binary() { | ||
| local pattern="$1" name="$2" found | ||
| found=$(find "$DIST_DIR" -type f -path "$pattern" -name "$name" | head -n1) | ||
| if [[ -z "$found" ]]; then | ||
| echo "ERROR: no $name matching $pattern under $DIST_DIR — run GoReleaser first" >&2 | ||
| exit 1 | ||
| fi | ||
| echo "$found" | ||
| } | ||
|
|
||
| DARWIN_BIN=$(find_binary "*darwin_all*" "gitlab-mcp-server") | ||
| WINDOWS_BIN=$(find_binary "*windows_amd64*" "gitlab-mcp-server.exe") |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== scripts/build-mcpb.sh ==\n'
cat -n scripts/build-mcpb.sh | sed -n '1,220p'
printf '\n== Makefile targets mentioning mcpb or dist/local_ ==\n'
rg -n "mcpb|dist/local_|goreleaser|DIST_DIR" Makefile scripts -S
printf '\n== Candidate build scripts and cleanup references ==\n'
rg -n "rm -rf .*dist|clean.*dist|dist/" -S . --glob '!**/node_modules/**'Repository: jmrplens/gitlab-mcp-server
Length of output: 22047
Fail on ambiguous binary matches. find_binary takes the first find result, so leftover GoReleaser artifacts alongside the local dist/local_* build can make the bundle pick an arbitrary darwin/windows binary. Reject multiple matches or clean dist/ before selecting.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/build-mcpb.sh` around lines 47 - 58, The find_binary helper is
currently selecting the first match from find, which can silently pick the wrong
GoReleaser artifact when multiple binaries exist under DIST_DIR. Update
find_binary in the build-mcpb.sh script to detect ambiguous results for the
gitlab-mcp-server and gitlab-mcp-server.exe lookups, and fail with a clear error
if more than one match is found, or otherwise ensure dist/ is cleaned before
resolving DARWIN_BIN and WINDOWS_BIN.
|
Bumps `VERSION` to **2.5.0** — first release carrying the Claude Desktop extension (`.mcpb`) pipeline (#224). - `VERSION` 2.4.1 → 2.5.0 (minor: new feature) - `CLAUDE.md`: version table + project tree (adds `mcpb/`) - Regenerated: README stats, `llms.txt` / `llms-full.txt`, site stats, testing docs (`make update-all`) After merging, tagging the squash commit as `v2.5.0` triggers the release pipeline (E2E gate → GoReleaser incl. darwin universal binary → `.mcpb` asset → Docker → MCP Registry → manifest commit-back). Release-pipeline changes were verified locally end to end: GoReleaser snapshot → `scripts/build-mcpb.sh` → stamped bundle unpacked, fat binary (x86_64+arm64) smoke-tested over stdio. https://claude.ai/code/session_01LJ6D4L6rnms9GqMqqc2tFb ## Summary by Sourcery Bump the project version to 2.5.0 and regenerate codebase and testing statistics for the release. Documentation: - Update CLAUDE.md with the 2.5.0 version and document the new mcpb/ Claude Desktop extension assets directory. - Refresh README codebase metrics, hall-of-fame stats, and assorted project statistics to reflect the latest state. Tests: - Regenerate testing documentation metrics and coverage tables to reflect the current test counts and distribution. Chores: - Update the VERSION file and associated llms/site stats artifacts in preparation for the 2.5.0 release pipeline.



Packages the server as a one-click MCPB desktop extension for Claude Desktop (macOS + Windows), as groundwork for submitting to the Anthropic connectors directory.
What's included
mcpb/manifest.json— MCPB v0.3 manifest:binaryserver type, darwin universal entry point with awin32platform override,user_config(URL, keychain-backed token, tool surface, tier, read-only, safe mode, TLS skip),dynamicsurface default,AUTO_UPDATE=falseinside the extension. Validates clean withmcpbCLI 2.1.2.mcpb/icon.png— 512×512 render ofsite/src/assets/logo-light.svg(recommended size for Claude Desktop).universal_binariesblock producing a darwin fat binary (arm64+amd64);replace: falsekeeps the per-arch assets thatgo-selfupdatematches by exact name.scripts/build-mcpb.sh— assembles the bundle from GoReleaser artifacts and packs it with the pinned@anthropic-ai/mcpbCLI.gitlab-mcp-server.mcpbas a release asset;update-server-json-sha.shnow stamps the MCPB manifest version alongsideserver.json/plugin.json(same commit-back flow).make mcpb(local cross-compile +lipo+ pack) andmake check-mcpb(manifest validation).PRIVACY.md— privacy policy required by the directory submission, linked from the README and the manifest'sprivacy_policies.docs/guides/claude-desktop-extension.md.Verification
npx @anthropic-ai/mcpb@2.1.2 validate mcpb/manifest.json→ schema + icon passgoreleaser check→ config validmake mcpb→ 39 MB bundle;lipo -infoconfirms x86_64+arm64; the bundled darwin binary boots over stdio with the manifest's exact env pattern and servesgitlab_find_action/gitlab_execute_actionmarkdownlint-cli2+format_md_tables --checkgreenThe
.mcpbasset will first appear on the next release; the README download button points atreleases/latest/download/gitlab-mcp-server.mcpb.https://claude.ai/code/session_01LJ6D4L6rnms9GqMqqc2tFb
Summary by Sourcery
Add a Claude Desktop (.mcpb) extension distribution for the GitLab MCP server, including local build tooling, release packaging, and documentation and privacy policy updates required for directory submission.
New Features:
Enhancements:
Build:
CI:
Documentation:
Chores:
Summary by CodeRabbit
New Features
.mcpbrelease artifact.Bug Fixes