Skip to content

Native binary

Every release publishes one self-contained binary per platform: Linux, macOS and Windows, on amd64 and arm64, built by GoReleaser with CGO_ENABLED=0 and -trimpath as position-independent executables with the version and commit stamped in. No runtime, no shared libraries, no container; on Linux the binary still needs the glibc dynamic loader, so use the Docker image on musl systems such as Alpine. This page covers the two install scripts, downloading by hand, checking what you downloaded, where to keep the file, and how to replace it later.

Both scripts do the same four things: detect the platform, download the matching asset from the latest GitHub Release, verify its SHA-256 against the checksums.txt of that same release, and copy it to a per-user directory. Verification fails closed: a mismatch, a missing entry, or a checksums.txt that cannot be fetched aborts the install rather than skipping the check. Because checksums.txt comes from the same release as the binary, both scripts also check a signature when they can: with cosign installed they verify checksums.txt.sigstore.json against this repository’s release workflow, pinned to the version being installed, and they ask the gh CLI for the build-provenance attestation whenever that verified nothing. A verifier that ran and said no aborts; a verifier that could not run only warns, unless REQUIRE_SIGNATURE=1 makes that fatal too.

Terminal window
curl -fsSL https://raw.githubusercontent.com/jmrplens/gitlab-mcp-server/main/scripts/install.sh | sh

The script needs curl or wget, plus sha256sum or shasum. It maps x86_64 to amd64 and aarch64 to arm64, refuses any other OS or architecture (on Windows it points you at the PowerShell installer), and installs the file with mode 0755 as $HOME/.local/bin/gitlab-mcp-server. An existing binary at that path is removed first, so re-running over a server that is still running does not fail with “Text file busy”. If $HOME/.local/bin is not on your PATH, the script says so and prints the export PATH=... line to add.

Before downloading anything it resolves latest to the tag it currently names, so the signature check below is pinned to the version being installed. BusyBox’s wget, the one Alpine ships, cannot follow that redirect; the script says so and accepts any release tag of the repository instead.

VariableDefaultEffect
INSTALL_DIR$HOME/.local/binTarget directory
VERSIONlatestRelease tag to install, for example v3.2.0
REPOjmrplens/gitlab-mcp-serverGitHub repository to download from
REQUIRE_SIGNATUREunset1 aborts unless a signature or attestation verifies
ALLOW_UNVERIFIEDunset1 skips verification entirely, checksum and signature alike, at your own risk (the way out on a system with no SHA-256 tool)
FETCH_ATTEMPTS3Tries per download before giving up
FETCH_RETRY_DELAY2Seconds between those tries

Setting both REQUIRE_SIGNATURE=1 and ALLOW_UNVERIFIED=1 is refused rather than resolved in either direction: they ask for opposite things. Variables go in front of sh, since that is the process running the script:

Terminal window
curl -fsSL https://raw.githubusercontent.com/jmrplens/gitlab-mcp-server/main/scripts/install.sh | VERSION=v3.2.0 INSTALL_DIR=/usr/local/bin sh

Each download is tried up to FETCH_ATTEMPTS times, FETCH_RETRY_DELAY seconds apart, when it gets no usable answer. A 404 or 410 is not retried: the server has already said the asset is not there.

With cosign installed, the scripts verify checksums.txt.sigstore.json against the release workflow at the tag being installed. The gh CLI is asked whenever that verified nothing, not only when cosign is missing, because the build-provenance attestation lives in GitHub’s own store and is still checkable when the bundle is the asset that went missing. Since gh has to ask GitHub, the scripts first check that it can (gh auth status): a gh that cannot reach GitHub has produced no verdict and only warns, while a gh that reached GitHub and rejected the binary aborts.

OutcomeResult
cosign or gh verifiesInstall proceeds
cosign rejects checksums.txtAbort
gh reaches GitHub and finds no valid attestationAbort
cosign is installed, the release serves no checksums.txt.sigstore.json (a 404), and nothing else verifiedAbort
The signature bundle could not be fetched at all, after the retriesWarning; REQUIRE_SIGNATURE=1 aborts
gh is installed but cannot reach GitHubWarning; REQUIRE_SIGNATURE=1 aborts
Neither cosign nor gh is installedWarning; REQUIRE_SIGNATURE=1 aborts

The fourth row is why a machine with cosign cannot be walked past by deleting one release asset: every release since signing began publishes the bundle, so its absence is not an old release but the one file whoever replaced the binary and checksums.txt together would also have to remove. A download that never came back is a different fact and is reported as one: it says nothing about whether the release is signed, so it warns rather than claiming the release is unsigned.

The latest release carries these assets:

PlatformAsset
Linux (x86_64)gitlab-mcp-server-linux-amd64
Linux (ARM64)gitlab-mcp-server-linux-arm64
macOS (universal)gitlab-mcp-server-darwin-all
macOS (Intel)gitlab-mcp-server-darwin-amd64
macOS (Apple Silicon)gitlab-mcp-server-darwin-arm64
Windows (x86_64)gitlab-mcp-server-windows-amd64.exe
Windows (ARM64)gitlab-mcp-server-windows-arm64.exe
Integritychecksums.txt (the binaries, their SBOMs and the notices), checksums.txt.sigstore.json, and an SPDX SBOM per binary, <asset>.sbom.json
LicensesTHIRD_PARTY_NOTICES, from the first release after 3.1.0
Claude Desktopgitlab-mcp-server-darwin.mcpb, -windows.mcpb or -linux.mcpb (one per operating system, from the first release after 3.1.0), and gitlab-mcp-server.mcpb for all three (every release); see Claude Desktop

gitlab-mcp-server-darwin-all is a universal binary (arm64 and amd64 in one file), produced for the macOS Claude Desktop extension because its manifest can only pick a file per operating system, not per architecture. On macOS you can use it or the single-architecture asset; all three are listed in checksums.txt.

Two URL forms cover every asset: releases/latest/download/<asset> always resolves to the newest release, and releases/download/v<version>/<asset> pins one.

Terminal window
# Latest release
curl -fsSLO https://github.com/jmrplens/gitlab-mcp-server/releases/latest/download/gitlab-mcp-server-linux-amd64
# A pinned version
curl -fsSLO https://github.com/jmrplens/gitlab-mcp-server/releases/download/v3.2.0/gitlab-mcp-server-linux-amd64

The download arrives without the execute bit. Set it, then move the file to a directory on your PATH, dropping the platform suffix so the command is gitlab-mcp-server like on every other channel:

Terminal window
chmod +x gitlab-mcp-server-*
sudo mv gitlab-mcp-server-linux-amd64 /usr/local/bin/gitlab-mcp-server

Without sudo, ~/.local/bin/gitlab-mcp-server works just as well; it is where the install script puts it.

On Windows, rename the file to gitlab-mcp-server.exe and place it in a folder that is on your PATH, or reference it by its full path from the client configuration.

The install scripts already compare the download with checksums.txt, and check the signature on it when cosign or gh is installed. Done by hand, that check plus the signature on the checksums file takes two commands. checksums.txt lists the SHA-256 of the seven binaries, of their SBOMs and, from the first release after 3.1.0, of THIRD_PARTY_NOTICES, and checksums.txt.sigstore.json is a keyless Cosign signature of that file, made by the release workflow under GitHub’s OIDC identity, so there is no key to distribute.

  1. Download checksums.txt and checksums.txt.sigstore.json from the same release as the binary.

  2. Verify the signature. The identity is the release workflow at the tag you downloaded:

    Terminal window
    cosign verify-blob \
    --bundle checksums.txt.sigstore.json \
    --certificate-identity "https://github.com/jmrplens/gitlab-mcp-server/.github/workflows/release.yml@refs/tags/v3.2.0" \
    --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
    checksums.txt

    --certificate-identity-regexp "^https://github.com/jmrplens/gitlab-mcp-server/" in place of the exact identity accepts any tag signed by this repository’s workflows. Either way the expected output is Verified OK.

  3. Check the binary against the signed list:

    Terminal window
    # Linux
    sha256sum --check --ignore-missing checksums.txt
    # macOS (shasum has no --ignore-missing; filter the relevant line first)
    grep "$(ls gitlab-mcp-server-*)" checksums.txt | shasum -a 256 -c
    Terminal window
    # Windows: the two hex strings must match (case aside)
    (Get-FileHash .\gitlab-mcp-server-windows-amd64.exe -Algorithm SHA256).Hash
    Select-String gitlab-mcp-server-windows-amd64.exe .\checksums.txt

    Expected output on Linux and macOS: gitlab-mcp-server-linux-amd64: OK, or the name of your asset.

If either step fails, do not run the file. The full walkthrough, including installing Cosign, is under Verifying release integrity.

Releases are published only once every asset is attached and attested, and the npm, PyPI and NuGet packages and the Homebrew formula are built from those published assets after their signature has been checked; How a release reaches every channel has the order.

  • Per user, no sudo: ~/.local/bin on Linux and macOS (the script’s default), %LOCALAPPDATA%\Programs\gitlab-mcp-server on Windows (the PowerShell script’s default).
  • System-wide: /usr/local/bin on Linux and macOS.

Clients started from the desktop rather than from a shell do not always inherit your shell’s PATH. If a client reports that it cannot find gitlab-mcp-server, put the absolute path in its configuration instead of the bare name.

Terminal window
gitlab-mcp-server --version

prints gitlab-mcp-server 3.2.0 (commit: <commit>) for the current release. Starting the binary with no arguments in a terminal, or double-clicking it on Windows, without both GITLAB_URL and GITLAB_TOKEN set prints a short first-run screen naming the two values it needs and waits for Enter; an MCP client never sees it, because it talks to the server over pipes. There is no setup wizard.

The minimal stdio entry is the path to the binary and the token. Add GITLAB_URL to env only for a self-managed instance:

{
"mcpServers": {
"gitlab": {
"command": "/path/to/gitlab-mcp-server",
"env": {
"GITLAB_TOKEN": "glpat-xxxxxxxxxxxxxxxxxxxx"
}
}
}
}

With Claude Code the registration itself is one command that names no token, claude mcp add gitlab -- gitlab-mcp-server, because the server reads ~/.gitlab-mcp-server.env for what it does not already have in its environment:

Terminal window
# Prompts for the token without echoing it, so it stays out of shell history
printf 'GitLab token: ' && read -rs t && printf 'GITLAB_TOKEN=%s\n' "$t" > ~/.gitlab-mcp-server.env && unset t && echo
chmod 600 ~/.gitlab-mcp-server.env
claude mcp add gitlab -- gitlab-mcp-server

Add GITLAB_URL=https://gitlab.example.com to the same file for self-managed GitLab. The per-client tabs, including VS Code’s servers map with a masked token prompt, are on the Quick Start.

The binary never updates itself. To upgrade, run the install script again, which replaces the file in place after verifying the new checksum, or download the newer asset and overwrite the old one. VERSION=v3.2.0 (or $env:VERSION on Windows) pins a specific release when you need to stay put.

To uninstall, delete the file: rm ~/.local/bin/gitlab-mcp-server (or wherever you placed it) on Linux and macOS; on Windows, delete %LOCALAPPDATA%\Programs\gitlab-mcp-server and remove that folder from your user PATH, which the script appended and never removes. Then drop the gitlab entry from your client’s configuration.

The source builds with the Go toolchain the go.mod file names (1.27 today) and nothing else:

Terminal window
git clone https://github.com/jmrplens/gitlab-mcp-server.git
cd gitlab-mcp-server
make build

make build writes dist/gitlab-mcp-server (.exe on Windows) for the machine it runs on, and make build-all cross-compiles the six release targets into the same directory. A binary built this way carries no signature and no provenance attestation, since those are made by the release workflow. The development guide covers the tests, the analysis gates and the rest of the contributor workflow.