Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,16 @@ jobs:
- name: Update server.json with SHA256 hashes
run: bash scripts/update-server-json-sha.sh dist/checksums.txt "${GITHUB_REF#refs/tags/v}"

- 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

Comment on lines +112 to +121

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 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.

- name: Publish to MCP Registry
env:
# Pin to a specific version of the mcp-publisher tool for supply-chain integrity.
Expand Down Expand Up @@ -182,6 +192,7 @@ jobs:

git add server.json
[ -f .plugin/plugin.json ] && git add .plugin/plugin.json
[ -f mcpb/manifest.json ] && git add mcpb/manifest.json

if git diff --cached --quiet; then
echo "::notice::No manifest changes to commit"
Expand All @@ -190,8 +201,9 @@ jobs:

git commit -m "chore: update manifests for v${VERSION}

Updates server.json (MCP Registry) and .plugin/plugin.json
(Open Plugins) with version ${VERSION} and pinned SHA256 hashes."
Updates server.json (MCP Registry), .plugin/plugin.json
(Open Plugins), and mcpb/manifest.json (Claude Desktop
extension) with version ${VERSION} and pinned SHA256 hashes."
git push origin main

docker:
Expand Down
12 changes: 12 additions & 0 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ builds:
# Reproducible builds: stamp binary mtime from commit timestamp.
mod_timestamp: "{{ .CommitTimestamp }}"

# macOS universal (fat) binary for the Claude Desktop extension (.mcpb):
# platform_overrides in the MCPB manifest key by OS only (darwin/win32), not
# by architecture, so the darwin entry point must run on both arm64 and amd64.
# replace: false keeps the per-arch darwin assets that go-selfupdate matches
# by exact name.
universal_binaries:
- id: gitlab-mcp-server-universal
ids: [gitlab-mcp-server]
name_template: gitlab-mcp-server
replace: false
mod_timestamp: "{{ .CommitTimestamp }}"

archives:
- id: binaries
formats: [ binary ]
Expand Down
24 changes: 23 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
audit-struct-completeness audit-action-coverage audit-metadata-completeness audit-1to1 audit-1to1-validate-docs audit-edition-tier \
audit-discovery audit-discovery-check audit-e2e-gaps \
audit-doc-coverage audit-doc-coverage-check \
gen-action-catalog-manifest check-action-catalog-manifest gen-llms check-llms check-server-json check-openplugin gen-readme gen-footprint check-footprint gen-stats check-stats gen-site-stats check-site-stats gen-testing-docs update-all \
gen-action-catalog-manifest check-action-catalog-manifest gen-llms check-llms check-server-json check-openplugin check-mcpb mcpb gen-readme gen-footprint check-footprint gen-stats check-stats gen-site-stats check-site-stats gen-testing-docs update-all \
docs-local-go \
docker-build docker-push docker-run \
fly-check fly-deploy fly-deploy-release fly-status fly-logs fly-ssh fly-restart \
Expand Down Expand Up @@ -687,6 +687,28 @@ check-server-json:
check-openplugin:
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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.:
    : "${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.


## check-mcpb: validate the Claude Desktop extension manifest (mcpb/manifest.json).
check-mcpb:
npx --yes @anthropic-ai/mcpb@$(MCPB_CLI_VERSION) validate mcpb/manifest.json

## 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"

Comment on lines +697 to +711

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 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

## gen-readme: regenerate all managed README.md sections (token footprint + stats).
gen-readme: gen-footprint gen-stats

Expand Down
66 changes: 66 additions & 0 deletions PRIVACY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Privacy Policy

Last updated: 2026-07-07

**gitlab-mcp-server** is a local Model Context Protocol (MCP) server. It runs
entirely on your machine and acts as a bridge between your MCP client (Claude
Desktop, Claude Code, Cursor, VS Code, …) and the GitLab instance you
configure. This policy describes what data the server handles and where it
goes.

## What we collect

**Nothing.** The server has no telemetry, no analytics, no crash reporting,
and no backend of its own. The maintainer never receives, stores, or has
access to any of your data, credentials, or usage information.

## Data flows

- **Your GitLab instance.** Every tool call results in requests to the GitLab
URL you configure (`GITLAB_URL`), authenticated with your Personal Access
Token (`GITLAB_TOKEN`). Data returned by GitLab (projects, issues, merge
requests, pipeline logs, …) is passed directly to your MCP client and is
never sent anywhere else. GitLab's handling of that data is governed by the
[GitLab Privacy Statement](https://about.gitlab.com/privacy/) (for
GitLab.com) or by your organization's own policies (for self-managed
instances).
- **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.
Comment on lines +27 to +34

There are no other network destinations.

## Credentials

Your GitLab Personal Access Token is provided by you through environment
variables or your MCP client's configuration UI. Claude Desktop stores
extension secrets in the operating system keychain. The server keeps the
token in process memory only, uses it solely to authenticate requests to your
configured GitLab instance, and never logs it.

## Local storage and logs

The server writes logs to standard error only (collected, if at all, by your
MCP client). It does not create databases, caches, or files with your GitLab
data. In HTTP mode, token identities are cached in memory for the configured
TTL and are never persisted to disk.

## Data retention and sharing

The server retains nothing after it exits and shares data with no third
parties beyond the GitLab instance you explicitly configure.

## Changes

Changes to this policy are published in this file and noted in release
changelogs.

## Contact

Questions or concerns: [open an issue](https://github.com/jmrplens/gitlab-mcp-server/issues)
or email <jmrplens@gmail.com>.
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,14 @@ Pick one. Each path ends with you typing a prompt to your assistant.
<td><a href="https://kiro.dev/launch/mcp/add?name=gitlab&amp;config=%7B%22command%22%3A%22docker%22%2C%22args%22%3A%5B%22run%22%2C%22-i%22%2C%22--rm%22%2C%22-e%22%2C%22GITLAB_TOKEN%22%2C%22ghcr.io%2Fjmrplens%2Fgitlab-mcp-server%3Alatest%22%2C%22--http%3Dfalse%22%5D%2C%22env%22%3A%7B%22GITLAB_TOKEN%22%3A%22YOUR_GITLAB_TOKEN%22%7D%7D"><img alt="Add to Kiro" src="https://kiro.dev/images/add-to-kiro.svg" height="28" /></a></td>
<td>edit <code>YOUR_GITLAB_TOKEN</code></td>
</tr>
<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&amp;logo=claude&amp;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.
Comment on lines +67 to +74

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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.

Suggested change
<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&amp;logo=claude&amp;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&amp;logo=claude&amp;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.


### Claude Code (`claude mcp add`)

Expand Down Expand Up @@ -343,6 +348,14 @@ The published container image is `ghcr.io/jmrplens/gitlab-mcp-server:latest`. Se
| GitLab Client | `gitlab.com/gitlab-org/api/client-go/v2` v2.46.0 |
| Transport | stdio (default), HTTP (Streamable HTTP) |

## Privacy Policy

The server runs entirely on your machine and has **no telemetry, analytics, or
backend of its own** — data flows only between your MCP client and the GitLab
instance you configure (plus an optional signed-binary update check against
GitHub Releases). Your token is used solely to authenticate GitLab requests
and is never logged. Full details: [PRIVACY.md](PRIVACY.md).

## Contributing & Security

- **Contributing**: see [CONTRIBUTING.md](CONTRIBUTING.md) for development guidelines, branch naming, commit conventions, and the PR process.
Expand Down
19 changes: 10 additions & 9 deletions docs/guides/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,16 @@ over HTTP, wiring it into CI, keeping it current, or getting unstuck.

> **Diátaxis type**: How-to · **Audience**: 👤 Users & 🔧 operators

| Guide | What it helps you do |
| ----------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| [IDE Configuration](ide-configuration.md) | Configure the MCP server in VS Code, Cursor, JetBrains, and other clients (stdio, HTTP legacy, HTTP OAuth) |
| [HTTP Server Mode](http-server-mode.md) | Run the multi-user HTTP transport with a per-token+URL server pool |
| [OAuth App Setup](oauth-app-setup.md) | Create a GitLab OAuth application so MCP clients can authenticate |
| [CI/CD Usage](ci-cd.md) | Use the server inside CI/CD pipelines, with or without an LLM in the loop |
| [Auto-Update](auto-update.md) | Enable, configure, or disable the self-update mechanism |
| [Troubleshooting](troubleshooting.md) | Diagnose common connection, TLS, tool, and transport problems |
| [Examples](examples/README.md) | Walk through real-world, multi-step usage scenarios and skill templates |
| Guide | What it helps you do |
| ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| [IDE Configuration](ide-configuration.md) | Configure the MCP server in VS Code, Cursor, JetBrains, and other clients (stdio, HTTP legacy, HTTP OAuth) |
| [Claude Desktop Extension](claude-desktop-extension.md) | Install or build the one-click `.mcpb` desktop extension for Claude Desktop |
| [HTTP Server Mode](http-server-mode.md) | Run the multi-user HTTP transport with a per-token+URL server pool |
| [OAuth App Setup](oauth-app-setup.md) | Create a GitLab OAuth application so MCP clients can authenticate |
| [CI/CD Usage](ci-cd.md) | Use the server inside CI/CD pipelines, with or without an LLM in the loop |
| [Auto-Update](auto-update.md) | Enable, configure, or disable the self-update mechanism |
| [Troubleshooting](troubleshooting.md) | Diagnose common connection, TLS, tool, and transport problems |
| [Examples](examples/README.md) | Walk through real-world, multi-step usage scenarios and skill templates |

**Looking for something else?**
[Reference](../reference/README.md) for exact flags and variables ·
Expand Down
63 changes: 63 additions & 0 deletions docs/guides/claude-desktop-extension.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Claude Desktop Extension (.mcpb)

The server ships as a one-click [Desktop Extension](https://www.anthropic.com/engineering/desktop-extensions)
(MCPB bundle) for Claude Desktop on macOS and Windows. The bundle contains a
macOS universal binary (arm64 + amd64) and a Windows amd64 executable — no
Docker, Node.js, or Python required.

## Install

1. Download `gitlab-mcp-server.mcpb` from the
[latest release](https://github.com/jmrplens/gitlab-mcp-server/releases/latest).
2. Open the file with Claude Desktop (double-click, or drag it onto the
window). Claude shows an install dialog with the extension details.
3. Fill in the settings:

| Setting | Required | Default | Maps to |
| ---------------------------- | -------- | -------------------- | ------------------------ |
| GitLab URL | Yes | `https://gitlab.com` | `GITLAB_URL` |
| GitLab Personal Access Token | Yes | — | `GITLAB_TOKEN` |
| Tool surface | No | `dynamic` | `TOOL_SURFACE` |
| GitLab tier | No | auto-detect | `GITLAB_TIER` |
| Read-only mode | No | off | `GITLAB_READ_ONLY` |
| Safe mode | No | off | `GITLAB_SAFE_MODE` |
| Skip TLS verification | No | off | `GITLAB_SKIP_TLS_VERIFY` |

The token is stored in the operating system keychain by Claude Desktop.

Auto-update is disabled inside the extension (`AUTO_UPDATE=false`): updates
arrive as new extension versions rather than in-place binary swaps.

## Build locally

```bash
make mcpb # builds dist/gitlab-mcp-server.mcpb (requires macOS lipo + npx)
make check-mcpb # validates mcpb/manifest.json with the official CLI
```

`make mcpb` cross-compiles the darwin arm64/amd64 binaries, merges them with
`lipo`, cross-compiles the Windows amd64 binary, and packs everything with the
pinned `@anthropic-ai/mcpb` CLI via `scripts/build-mcpb.sh`.

In CI, the release workflow builds the bundle from the GoReleaser artifacts
(including the `universal_binaries` darwin build) and uploads it as a release
asset. The manifest version is stamped from the git tag by
`scripts/update-server-json-sha.sh`, the same flow that versions `server.json`.

## Files

| Path | Purpose |
| ----------------------- | ------------------------------------------------------------ |
| `mcpb/manifest.json` | MCPB manifest (source of truth; version stamped per release) |
| `mcpb/icon.png` | 512×512 icon rendered from `site/src/assets/logo-dark.svg` |
| `scripts/build-mcpb.sh` | Bundle assembly + `mcpb pack` |
| `PRIVACY.md` | Privacy policy referenced by the manifest |

## Privacy and directory submission

The manifest's `privacy_policies` points to [PRIVACY.md](../../PRIVACY.md) and

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
The manifest's `privacy_policies` points to [PRIVACY.md](../../PRIVACY.md) and
The `privacy_policies` field 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
[submission form](https://claude.com/docs/connectors/building/submission),
which requires the documentation URL, the privacy policy, the icon, and test
credentials.
Binary file added mcpb/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading