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.
Install script
Section titled “Install script”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.
curl -fsSL https://raw.githubusercontent.com/jmrplens/gitlab-mcp-server/main/scripts/install.sh | shThe 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.
| Variable | Default | Effect |
|---|---|---|
INSTALL_DIR | $HOME/.local/bin | Target directory |
VERSION | latest | Release tag to install, for example v3.2.0 |
REPO | jmrplens/gitlab-mcp-server | GitHub repository to download from |
REQUIRE_SIGNATURE | unset | 1 aborts unless a signature or attestation verifies |
ALLOW_UNVERIFIED | unset | 1 skips verification entirely, checksum and signature alike, at your own risk (the way out on a system with no SHA-256 tool) |
FETCH_ATTEMPTS | 3 | Tries per download before giving up |
FETCH_RETRY_DELAY | 2 | Seconds 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:
curl -fsSL https://raw.githubusercontent.com/jmrplens/gitlab-mcp-server/main/scripts/install.sh | VERSION=v3.2.0 INSTALL_DIR=/usr/local/bin shirm https://raw.githubusercontent.com/jmrplens/gitlab-mcp-server/main/scripts/install.ps1 | iexThe script reads the real machine architecture (PROCESSOR_ARCHITEW6432 first, so a 32-bit PowerShell on 64-bit Windows still picks the right build), downloads gitlab-mcp-server-windows-amd64.exe or gitlab-mcp-server-windows-arm64.exe, verifies it against checksums.txt with Get-FileHash and its signature by the same rules as the shell script (below), and installs it as %LOCALAPPDATA%\Programs\gitlab-mcp-server\gitlab-mcp-server.exe. It then appends that folder to your user PATH if it is not there yet. No 32-bit x86 build is published, so the script stops on that architecture.
The same overrides exist as environment variables (VERSION, INSTALL_DIR, REPO, REQUIRE_SIGNATURE, ALLOW_UNVERIFIED, FETCH_ATTEMPTS, FETCH_RETRY_DELAY, with the same defaults and the same refusal of the contradictory pair) and, when you save the script and run it as a file, the first three as parameters (-Version, -InstallDir, -Repo). When piping into iex, set the variables first:
$env:VERSION = 'v3.2.0'irm https://raw.githubusercontent.com/jmrplens/gitlab-mcp-server/main/scripts/install.ps1 | iexWhat the signature check decides
Section titled “What the signature check decides”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.
| Outcome | Result |
|---|---|
cosign or gh verifies | Install proceeds |
cosign rejects checksums.txt | Abort |
gh reaches GitHub and finds no valid attestation | Abort |
cosign is installed, the release serves no checksums.txt.sigstore.json (a 404), and nothing else verified | Abort |
| The signature bundle could not be fetched at all, after the retries | Warning; REQUIRE_SIGNATURE=1 aborts |
gh is installed but cannot reach GitHub | Warning; REQUIRE_SIGNATURE=1 aborts |
Neither cosign nor gh is installed | Warning; 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.
Download by hand
Section titled “Download by hand”The latest release carries these assets:
| Platform | Asset |
|---|---|
| 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 |
| Integrity | checksums.txt (the binaries, their SBOMs and the notices), checksums.txt.sigstore.json, and an SPDX SBOM per binary, <asset>.sbom.json |
| Licenses | THIRD_PARTY_NOTICES, from the first release after 3.1.0 |
| Claude Desktop | gitlab-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.
# Latest releasecurl -fsSLO https://github.com/jmrplens/gitlab-mcp-server/releases/latest/download/gitlab-mcp-server-linux-amd64
# A pinned versioncurl -fsSLO https://github.com/jmrplens/gitlab-mcp-server/releases/download/v3.2.0/gitlab-mcp-server-linux-amd64Make it executable (Linux/macOS)
Section titled “Make it executable (Linux/macOS)”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:
chmod +x gitlab-mcp-server-*sudo mv gitlab-mcp-server-linux-amd64 /usr/local/bin/gitlab-mcp-serverWithout 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.
Verify what you downloaded
Section titled “Verify what you downloaded”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.
-
Download
checksums.txtandchecksums.txt.sigstore.jsonfrom the same release as the binary. -
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 isVerified OK. -
Check the binary against the signed list:
Terminal window # Linuxsha256sum --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 -cTerminal window # Windows: the two hex strings must match (case aside)(Get-FileHash .\gitlab-mcp-server-windows-amd64.exe -Algorithm SHA256).HashSelect-String gitlab-mcp-server-windows-amd64.exe .\checksums.txtExpected 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.
Where to put it
Section titled “Where to put it”- Per user, no
sudo:~/.local/binon Linux and macOS (the script’s default),%LOCALAPPDATA%\Programs\gitlab-mcp-serveron Windows (the PowerShell script’s default). - System-wide:
/usr/local/binon 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.
Check it runs
Section titled “Check it runs”gitlab-mcp-server --versionprints 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.
Configure your client
Section titled “Configure your client”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:
# Prompts for the token without echoing it, so it stays out of shell historyprintf 'GitLab token: ' && read -rs t && printf 'GITLAB_TOKEN=%s\n' "$t" > ~/.gitlab-mcp-server.env && unset t && echochmod 600 ~/.gitlab-mcp-server.envclaude mcp add gitlab -- gitlab-mcp-serverAdd 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.
Upgrade and uninstall
Section titled “Upgrade and uninstall”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.
Build from source
Section titled “Build from source”The source builds with the Go toolchain the go.mod file names (1.27 today) and nothing else:
git clone https://github.com/jmrplens/gitlab-mcp-server.gitcd gitlab-mcp-servermake buildmake 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.