Docker
The image is published as ghcr.io/jmrplens/gitlab-mcp-server and mirrored as docker.io/jmrplens/gitlab-mcp-server on Docker Hub, with a <version> tag and latest on both registries, for linux/amd64 and linux/arm64. Every release builds it with provenance and SBOM attestations and signs it by digest with Cosign. Inside: an Alpine base with CA certificates and time zone data, the binary at /usr/local/bin/gitlab-mcp-server, a non-root appuser (UID 10001), port 8080 exposed, and a HEALTHCHECK that runs gitlab-mcp-server --probe, which asks the running server’s /health wherever it listens (the 2.7.5 image runs wget against port 8080 instead).
Stdio: the image as a local MCP server
Section titled “Stdio: the image as a local MCP server”docker run -i --rm -e GITLAB_TOKEN ghcr.io/jmrplens/gitlab-mcp-server:latest-e GITLAB_TOKEN with no value forwards the variable from the environment the client gives docker, so the token lives in the client’s env block rather than on the command line. Docker pulls the image on the first run. The client entry that launches this command is under Configure your client below; with Claude Code the registration is the same command line, and it carries no token either:
export GITLAB_TOKEN=glpat-xxxxclaude mcp add gitlab --transport stdio \ -- docker run -i --rm -e GITLAB_TOKEN ghcr.io/jmrplens/gitlab-mcp-server:latestClaude Code hands its own environment to docker, which is where -e GITLAB_TOKEN picks the value up, so export it in the shell you launch the client from (your shell profile makes that durable) or add it to the entry’s env block afterwards. A container has its own filesystem and never reads ~/.gitlab-mcp-server.env, which is how every other channel supplies the token; forwarding is the container’s equivalent.
The one-click buttons for VS Code, Cursor, LM Studio and Kiro on the Quick Start register exactly this entry, and the Agent Plugins manifest runs it too.
Environment variables
Section titled “Environment variables”In stdio mode every setting is an environment variable, and each one you use needs its own -e NAME so Docker copies it into the container. The list the bundled plugin configuration forwards is a good template: GITLAB_URL, GITLAB_TOKEN, GITLAB_MCP_SKIP_TLS_VERIFY, GITLAB_MCP_TOOL_SURFACE, GITLAB_MCP_CAPABILITY_SURFACE, GITLAB_MCP_META_PARAM_SCHEMA, GITLAB_MCP_TIER, GITLAB_MCP_READ_ONLY, GITLAB_MCP_SAFE_MODE, GITLAB_MCP_EMBEDDED_RESOURCES, GITLAB_MCP_EXCLUDE_TOOLS, GITLAB_MCP_IGNORE_SCOPES, GITLAB_MCP_UPLOAD_MAX_FILE_SIZE, GITLAB_MCP_ALLOWED_IMPORT_DIRS, GITLAB_MCP_RATE_LIMIT_RPS, GITLAB_MCP_RATE_LIMIT_BURST, GITLAB_MCP_CLIENT_COMPAT and GITLAB_MCP_LOG_LEVEL. What each one does is on the Configuration page.
HTTP: the image as a service
Section titled “HTTP: the image as a service”Started without -i, the container has no stdin to speak on and listens on port 8080, but it will not start until you say which GitLab it serves:
docker run --rm -p 8080:8080 ghcr.io/jmrplens/gitlab-mcp-server:latest \ --http --http-addr=0.0.0.0:8080 --gitlab-url=https://gitlab.comA deployment that names no instance would make requests to whatever host a caller puts in the GITLAB-URL header, carrying whatever token that caller supplied, so it is refused at startup. --allow-any-gitlab-url accepts that for a single-user local deployment and warns; on a listener anyone else can reach, publish the instances instead. Each client sends its own GitLab token on every request, and the server stores none of them. To harden the container the way the reference deployment does:
docker run -d \ --name gitlab-mcp \ --read-only \ --tmpfs /tmp:rw,size=64m \ --cap-drop=ALL \ --security-opt=no-new-privileges:true \ -p 8080:8080 \ ghcr.io/jmrplens/gitlab-mcp-server:latest \ --http \ --http-addr=0.0.0.0:8080 \ --gitlab-url=https://gitlab.com# Publish each instance; the GITLAB-URL header then selects among them and is# required, since choosing for the caller would send their token elsewheredocker run -d \ --name gitlab-mcp \ --read-only \ --tmpfs /tmp:rw,size=64m \ --cap-drop=ALL \ --security-opt=no-new-privileges:true \ -p 8080:8080 \ ghcr.io/jmrplens/gitlab-mcp-server:latest \ --http \ --http-addr=0.0.0.0:8080 \ --gitlab-url=https://gitlab.com \ --gitlab-url=https://gitlab.example.comAn HTTP client then points at http://localhost:8080/mcp with type: "http" and its own token in a PRIVATE-TOKEN header (the legacy authentication mode, which is the default), and http://localhost:8080/health answers without a credential:
curl -X POST http://localhost:8080/mcp \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -H "PRIVATE-TOKEN: glpat-your-token" \ -d '{"jsonrpc":"2.0","method":"tools/list","id":1}'In HTTP mode configuration comes from flags rather than variables (--tool-surface, --tier, --read-only, --safe-mode, --skip-tls-verify, and so on), which is what the repository’s docker-compose.yml passes on its command line. OAuth mode, rate limiting, TLS on the listener, sizing and the client configuration for this transport are on the HTTP Server page.
Tags and registries
Section titled “Tags and registries”latestfollows the newest release;2.7.5and every other version tag stays put. Pin a version tag in production, or pin the digest for full immutability:ghcr.io/jmrplens/gitlab-mcp-server:2.7.5@sha256:8eec1825b266712cd544bf1b2144e55c1eb711b4540def40e963a664c4e97168is the form the MCP Registry’sserver.jsonuses for the current release.ghcr.io/jmrplens/gitlab-mcp-serveranddocker.io/jmrplens/gitlab-mcp-servercarry the same images: the2.7.5tag resolves to the same digest on both. The documentation usesghcr.io; use the Docker Hub name if that registry is the one your network allows.- The image carries the label
io.modelcontextprotocol.server.name=io.github.jmrplens/gitlab-mcp-server, which is how the MCP Registry validates its ownership.
Configure your client
Section titled “Configure your client”The stdio entry for a desktop client is the docker run command from above, split into command and args, with the token in env:
{ "mcpServers": { "gitlab": { "command": "docker", "args": ["run", "-i", "--rm", "-e", "GITLAB_TOKEN", "ghcr.io/jmrplens/gitlab-mcp-server:latest"], "env": { "GITLAB_TOKEN": "glpat-xxxxxxxxxxxxxxxxxxxx" } } }}For a self-managed instance add "-e", "GITLAB_URL" to args and "GITLAB_URL": "https://gitlab.example.com" to env: the variable has to be both set and forwarded. The per-client tabs on the Quick Start show where this JSON goes for each client; swap their command and env for the ones above. VS Code uses a servers map with "type": "stdio" instead of mcpServers.
Upgrade and uninstall
Section titled “Upgrade and uninstall”The server never updates itself, and inside a container it should not: pull the newer image and start a fresh container from it.
docker pull ghcr.io/jmrplens/gitlab-mcp-server:latestA stdio client that runs docker run --rm picks up the pulled image on its next launch. For an HTTP service, recreate the container (docker compose pull && docker compose up -d with the reference Compose file, or docker rm -f gitlab-mcp and the docker run above). With a version tag, change the tag instead and pull that one. To uninstall, stop any container, then remove the image:
docker rmi ghcr.io/jmrplens/gitlab-mcp-server:latestand drop the gitlab entry from your client’s configuration. These are the standard Docker commands; nothing about the image changes them.