Ir al contenido

Docker

La imagen se publica como ghcr.io/jmrplens/gitlab-mcp-server y se replica como docker.io/jmrplens/gitlab-mcp-server en Docker Hub, con una etiqueta <versión> y latest en ambos registros, para linux/amd64 y linux/arm64. Cada release la construye con atestaciones de procedencia y SBOM y la firma por digest con Cosign. Dentro: una base Alpine con certificados CA y datos de zona horaria, el binario en /usr/local/bin/gitlab-mcp-server, un usuario no-root appuser (UID 10001), el puerto 8080 expuesto y un HEALTHCHECK que ejecuta gitlab-mcp-server --probe, que pregunta por /health al servidor en marcha allá donde escuche (la imagen 2.7.5 lanza wget contra el puerto 8080 en su lugar).

El binario de la imagen se compila sin -trimpath, a diferencia de los binarios del release, así que su información de compilación registra el release con el que se compiló y el SBOM de la imagen lista el servidor con esa versión, con una URL de paquete versionada que un escáner de vulnerabilidades puede casar. Las imágenes hasta la 3.1.0 incluida lo listan sin versión. A partir del primer release posterior a 3.1.0, la imagen lleva además LICENSE y THIRD_PARTY_NOTICES en /usr/share/licenses/gitlab-mcp-server/: los textos de licencia, aviso y patente de todos los módulos que enlaza ese binario, generados durante la compilación de la imagen a partir de la lista de módulos del propio binario.

Ventana de terminal
docker run -i --rm -e GITLAB_TOKEN ghcr.io/jmrplens/gitlab-mcp-server:latest

-e GITLAB_TOKEN sin valor reenvía la variable desde el entorno que el cliente le da a docker, de modo que el token vive en el bloque env del cliente y no en la línea de comandos. Docker descarga la imagen en la primera ejecución. La entrada de cliente que lanza este comando está en Configura tu cliente más abajo; con Claude Code el registro es esa misma línea de comandos, y tampoco lleva token:

Ventana de terminal
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 le pasa su propio entorno a docker, que es de donde -e GITLAB_TOKEN toma el valor, así que expórtalo en la shell desde la que lanzas el cliente (tu perfil de shell lo hace duradero) o añádelo después al bloque env de la entrada. Un contenedor tiene su propio sistema de archivos y nunca lee ~/.gitlab-mcp-server.env, que es como el resto de canales aportan el token; el reenvío es el equivalente del contenedor.

Los botones de un clic para VS Code, Cursor, LM Studio y Kiro del Inicio rápido registran exactamente esta entrada, y el manifiesto de Plugins de agente también la ejecuta.

En modo stdio cada ajuste es una variable de entorno, y cada una que uses necesita su propio -e NOMBRE para que Docker la copie dentro del contenedor. La lista que reenvía la configuración del plugin incluido es una buena plantilla: 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 y GITLAB_MCP_LOG_LEVEL. Qué hace cada una está en la página de Configuración.

Arrancado sin -i, el contenedor no tiene una entrada estándar por la que hablar y escucha en el puerto 8080, pero no arranca hasta que le digas a qué GitLab sirve:

Ventana de terminal
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

Un despliegue que no nombra ninguna instancia haría peticiones al host que quien llama ponga en la cabecera GITLAB-URL, con el token que esa misma persona enviara, así que se rechaza al arrancar. --allow-any-gitlab-url lo acepta para un despliegue local de un único usuario y avisa, pero solo si --http-addr liga loopback o un socket unix, así que dentro de un contenedor que liga 0.0.0.0 no sirve: publica las instancias. Cada cliente envía además su propio token de GitLab en cada petición, y el servidor no guarda ninguno. Para endurecer el contenedor como hace el despliegue de referencia:

Ventana de terminal
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

Un cliente HTTP apunta entonces a http://localhost:8080/mcp con type: "http" y su propio token en una cabecera PRIVATE-TOKEN (el modo de autenticación legacy, que es el predeterminado), y http://localhost:8080/health responde sin credencial:

Ventana de terminal
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "PRIVATE-TOKEN: glpat-tu-token" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}'

En modo HTTP la configuración viene de flags y no de variables (--tool-surface, --tier, --read-only, --safe-mode, --skip-tls-verify, etcétera), que es lo que el docker-compose.yml del repositorio pasa en su línea command. El modo OAuth, el rate limiting, TLS en el propio listener, el dimensionado y la configuración de cliente para este transporte están en la página del Servidor HTTP.

  • latest sigue a la release más nueva; 2.7.5 y cualquier otra etiqueta de versión se quedan fijas. Fija una etiqueta de versión en producción, o fija el digest para una inmutabilidad total: ghcr.io/jmrplens/gitlab-mcp-server:2.7.5@sha256:8eec1825b266712cd544bf1b2144e55c1eb711b4540def40e963a664c4e97168 es la forma que usa el server.json del MCP Registry para la release actual.
  • ghcr.io/jmrplens/gitlab-mcp-server y docker.io/jmrplens/gitlab-mcp-server llevan las mismas imágenes: la etiqueta 2.7.5 resuelve al mismo digest en ambos. La documentación usa ghcr.io; usa el nombre de Docker Hub si ese es el registro que permite tu red.
  • La imagen lleva la etiqueta io.modelcontextprotocol.server.name=io.github.jmrplens/gitlab-mcp-server, que es como el MCP Registry valida su propiedad.

La entrada stdio para un cliente de escritorio es el comando docker run de arriba, repartido entre command y args, con el token en env:

{
"mcpServers": {
"gitlab": {
"command": "docker",
"args": ["run", "-i", "--rm", "-e", "GITLAB_TOKEN", "ghcr.io/jmrplens/gitlab-mcp-server:latest"],
"env": {
"GITLAB_TOKEN": "glpat-xxxxxxxxxxxxxxxxxxxx"
}
}
}
}

Para una instancia autogestionada añade "-e", "GITLAB_URL" a args y "GITLAB_URL": "https://gitlab.example.com" a env: la variable tiene que estar definida y reenviada a la vez. Las pestañas por cliente del Inicio rápido muestran dónde va este JSON en cada cliente; sustituye su command y su env por los de arriba. VS Code usa un mapa servers con "type": "stdio" en lugar de mcpServers.

El servidor nunca se actualiza a sí mismo, y dentro de un contenedor no debería: descarga la imagen más nueva y arranca un contenedor nuevo a partir de ella.

Ventana de terminal
docker pull ghcr.io/jmrplens/gitlab-mcp-server:latest

Un cliente stdio que ejecuta docker run --rm usa la imagen descargada en su siguiente arranque. Para un servicio HTTP, recrea el contenedor (docker compose pull && docker compose up -d con el archivo Compose de referencia, o docker rm -f gitlab-mcp y el docker run de arriba). Con una etiqueta de versión, cambia la etiqueta y descarga esa. Para desinstalar, para cualquier contenedor y elimina la imagen:

Ventana de terminal
docker rmi ghcr.io/jmrplens/gitlab-mcp-server:latest

y quita la entrada gitlab de la configuración de tu cliente. Son los comandos estándar de Docker; nada en la imagen los cambia.