Skip to content

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

Terminal window
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:

Terminal window
export GITLAB_TOKEN=glpat-xxxx
claude mcp add gitlab --transport stdio \
-- docker run -i --rm -e GITLAB_TOKEN ghcr.io/jmrplens/gitlab-mcp-server:latest

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

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.

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:

Terminal window
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.com

A 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:

Terminal window
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

An 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:

Terminal window
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.

  • latest follows the newest release; 2.7.5 and 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:8eec1825b266712cd544bf1b2144e55c1eb711b4540def40e963a664c4e97168 is the form the MCP Registry’s server.json uses for the current release.
  • ghcr.io/jmrplens/gitlab-mcp-server and docker.io/jmrplens/gitlab-mcp-server carry the same images: the 2.7.5 tag resolves to the same digest on both. The documentation uses ghcr.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.

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.

The server never updates itself, and inside a container it should not: pull the newer image and start a fresh container from it.

Terminal window
docker pull ghcr.io/jmrplens/gitlab-mcp-server:latest

A 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:

Terminal window
docker rmi ghcr.io/jmrplens/gitlab-mcp-server:latest

and drop the gitlab entry from your client’s configuration. These are the standard Docker commands; nothing about the image changes them.