NuGet and dnx
GitLab MCP Server is published on NuGet.org as
gitlab-mcp-server.
It is a .NET tool in the layout the .NET 10 SDK introduced for tools that
ship a platform-specific executable: the entry point is the same native Go
binary every other channel ships, dotnet tool install and dnx pick the
package for your operating system and architecture, and no .NET code runs
once the server is up.
What you get
Section titled “What you get”Seven packages, published together at every release, each with a SLSA build provenance attestation made before it was pushed (from the first release after 3.1.0):
gitlab-mcp-serveris the pointer package. It declares thegitlab-mcp-servercommand and names, for each runtime identifier, the package that carries it. It also holds the package README and an.mcp/server.json, which is what NuGet.org renders its install snippet from.gitlab-mcp-server.<rid>packages carry the binary itself, one per runtime identifier:linux-x64,linux-arm64,osx-x64,osx-arm64,win-x64andwin-arm64. The SDK installs exactly one of them, the one matching your machine, and runs the binary directly (Runner="executable"in the tool manifest, so there is no .NET host in between).
| Platform | Runtime identifier |
|---|---|
| Linux x64 (glibc) | linux-x64 |
| Linux arm64 (glibc) | linux-arm64 |
| macOS Intel | osx-x64 |
| macOS Apple Silicon | osx-arm64 |
| Windows x64 | win-x64 |
| Windows ARM64 | win-arm64 |
Requirements: the .NET 10 SDK or newer. The runtime-identifier layout is
tool manifest version 2, which older SDKs do not read, and dnx itself
arrived with .NET 10. On Linux the binaries need glibc; on musl systems such
as Alpine, use the Docker image
instead.
Install
Section titled “Install”Nothing to install: dnx downloads the two packages into the NuGet cache
on the first run and executes the tool. Clients can launch it this way
directly.
dnx gitlab-mcp-serverTwo things about dnx that are easy to trip over:
- It parses its own options anywhere on the line. Arguments meant for
the server go after
--:dnx gitlab-mcp-server -- --versionprints the server’s version, whilednx gitlab-mcp-server --versionis read asdnx’s own--version <VERSION>option and printsdnx’s usage. - It asks nothing when its standard input is not a terminal. Microsoft’s
documentation shows
dnx <package> --yesto skip the “download this tool?” prompt; an MCP client connects pipes, so the prompt never appears and the flag is unnecessary (the .NET 10.0.400 and 10.0.401 SDKs accept it all the same, so a client template that adds it does no harm). In a terminal, the first run asks once.
dotnet tool install -g gitlab-mcp-serverThe SDK places a gitlab-mcp-server shim in ~/.dotnet/tools (a symlink
into the tool store on Linux and macOS), which the SDK installer already put
on your PATH.
Check the install:
gitlab-mcp-server --version# gitlab-mcp-server <version> (commit: ...)With dnx no command lands on your PATH; dnx gitlab-mcp-server -- --version
runs the same check. Started in a terminal without both GITLAB_URL and
GITLAB_TOKEN set, the binary prints what it is and the two values it
needs, then waits for Enter. That is the first-run screen, not an error: an
MCP client connects pipes rather than a terminal and never sees it. There is
no setup wizard; configuration lives in your client’s JSON, below.
Configure your client
Section titled “Configure your client”The portable form needs no install at all. Point the client at dnx:
{ "mcpServers": { "gitlab": { "command": "dnx", "args": ["gitlab-mcp-server"], "env": { "GITLAB_URL": "https://gitlab.com", "GITLAB_TOKEN": "glpat-xxxxxxxxxxxxxxxxxxxx" } } }}After dotnet tool install -g, use the command instead:
{ "mcpServers": { "gitlab": { "command": "gitlab-mcp-server", "env": { "GITLAB_TOKEN": "glpat-xxxxxxxxxxxxxxxxxxxx" } } }}GITLAB_TOKEN is the only required value: a personal access token with the
api scope (read_api also works; the server detects it at startup and serves a read-only surface).
GITLAB_URL defaults to https://gitlab.com; add it to env only for a
self-managed instance. A client that does not inherit your shell’s PATH may
need the full path to the command; which gitlab-mcp-server (or
where gitlab-mcp-server on Windows) prints it. To pin a release rather
than follow the newest one with dnx, name it in the argument:
"gitlab-mcp-server@<version>", with a version NuGet.org lists. Pinning
does not let dnx start without NuGet.org (see
Upgrade and uninstall).
One caution for a machine on which the .NET CLI has never run: its first-use
welcome text goes to standard output, which is the stream the MCP protocol
runs on. Run dotnet --version once in a terminal before the client’s first
launch, or add "DOTNET_NOLOGO": "1" to the env block.
Per-client file locations and shapes (VS Code uses servers with
"type": "stdio", Zed uses context_servers) are in the
Quick Start’s client tabs.
Verify what you run
Section titled “Verify what you run”The binary the SDK runs is the release asset byte for byte, so the build provenance attestation GitHub holds for that asset verifies it with the GitHub CLI, wherever it was installed from:
# dnx: the NuGet package cache (<rid> and <version> as in the table above)gh attestation verify ~/.nuget/packages/gitlab-mcp-server.<rid>/<version>/tools/any/<rid>/gitlab-mcp-server \ -R jmrplens/gitlab-mcp-server --signer-workflow jmrplens/gitlab-mcp-server/.github/workflows/release.yml
# dotnet tool install -g: on Linux and macOS the shim links into the tool storegh attestation verify "$(readlink -f "$(command -v gitlab-mcp-server)")" \ -R jmrplens/gitlab-mcp-server --signer-workflow jmrplens/gitlab-mcp-server/.github/workflows/release.yml--signer-workflow holds the attestation to the release workflow, since
-R alone accepts one any workflow of the repository minted; add
--source-ref refs/tags/v<version> to hold it to the release you run.
On Windows the cache is under %USERPROFILE%\.nuget\packages and the file
is gitlab-mcp-server.exe. dnx gitlab-mcp-server -- --version prints the
version you run. Both paths were checked on 3.1.0 against the release’s
signed checksums.txt.
From the first release after 3.1.0 the seven packages are attested too, as
they were before NuGet.org added the repository signature
(.signature.p7s) it puts on every package it serves. Remove that entry
from a downloaded copy and verify what is left:
v=<version>curl -sSLO "https://api.nuget.org/v3-flatcontainer/gitlab-mcp-server/$v/gitlab-mcp-server.$v.nupkg"zip -q -d "gitlab-mcp-server.$v.nupkg" .signature.p7sgh attestation verify "gitlab-mcp-server.$v.nupkg" -R jmrplens/gitlab-mcp-server \ --signer-workflow jmrplens/gitlab-mcp-server/.github/workflows/release.yml --source-ref "refs/tags/v$v"The same works for each gitlab-mcp-server.<rid> package. Info-ZIP’s
zip -d (zip 3.0) leaves every other byte where it was, which is NuGet’s
own definition of the unsigned package: on all seven 3.1.0 packages it gives
back exactly what a rebuild from the tag produces. A tool that rewrites the
archive gives other bytes, and gh then finds nothing. Rebuilding from the
tag with scripts/build_nuget.py and the signed binaries matches byte for
byte only on a Python linked against stock zlib: zlib-ng, which is what
zlib is on Fedora 40 and later, compresses the same input to different
bytes, so a rebuild there differs without saying anything about the
published packages.
Upgrade and uninstall
Section titled “Upgrade and uninstall”The server never checks for updates and never replaces its own binary; the SDK owns it here, as each channel owns what it installed. Every release moves the pointer and its six runtime packages together at one version, so a pointer never resolves a binary from another release.
dnx resolves the version against NuGet.org on every launch, so an
unpinned dnx gitlab-mcp-server follows each release as it is published,
and the download is skipped when the resolved version is already in the
NuGet cache. To stay on one release, name it:
dnx gitlab-mcp-server@<version> (@latest is not a NuGet version and is
refused). The other side of that coin: every launch contacts NuGet.org,
pinned or not, cached or not. Measured on the 10.0.401 SDK with 3.1.0
already in the cache and no network, both dnx gitlab-mcp-server@3.1.0 and
dnx gitlab-mcp-server exit with
Resource temporarily unavailable (api.nuget.org:443), while the command
dotnet tool install -g had placed starts. A machine that is offline at
times, or a host that cannot reach NuGet.org at all, is served by
dotnet tool install (with --source <dir> pointing at downloaded
packages where the feed is out of reach).
Nothing was installed, so there is nothing to uninstall beyond the entry in
your client’s configuration (and the cached packages under ~/.nuget, if
you want them gone).
dotnet tool update -g gitlab-mcp-serverdotnet tool uninstall -g gitlab-mcp-serverOther channels are compared in the installation overview.