gitlab-mcp-go
An HTTP MCP server that exposes a GitLab instance to Claude Code: read code, CI pipelines/jobs/logs, artifacts, and merge requests; perform guarded write operations.
Installation
Install the latest release with Go:
go install github.com/imunhatep/gitlab-mcp-go/cmd/gitlab-mcp@latest
Or download a prebuilt binary from the Releases
page — archives are published for darwin and linux on amd64 and arm64.
Each release also ships a checksums.txt for verification.
Container image
Every release publishes a multi-arch (linux/amd64, linux/arm64) image built
from the repo's Containerfile to GitHub Container Registry, tagged with the
release version (plus latest for non-prerelease tags):
export GITLAB_URL=https://gitlab.example.com
export GITLAB_TOKEN=glpat-xxxxxxxx
podman run --rm -d \
--name gitlab-mcp \
-e GITLAB_URL \
-e GITLAB_TOKEN \
-p 127.0.0.1:3030:3030 \
ghcr.io/imunhatep/gitlab-mcp-go:latest
-e GITLAB_URL / -e GITLAB_TOKEN pass the values through from the host
environment, so the token never appears in the command line or shell history.
The image listens on 0.0.0.0:3030 inside the container; publishing it as
127.0.0.1:3030 keeps it reachable from the host (and from Claude Code) while
staying off any external interface. Add -e GITLAB_MCP_READ_ONLY=true to drop
the write tools.
Configuration
Set GITLAB_URL and GITLAB_TOKEN in the environment, then run the server:
export GITLAB_URL=https://gitlab.example.com
export GITLAB_TOKEN=glpat-xxxxxxxx
make run # listens on 127.0.0.1:3030, MCP at /mcp
See gitlab-mcp serve --help for all flags.
Transports
The server speaks MCP over two transports on the same port:
Both share the same tool registry, so read-only gating and tool behavior are
identical regardless of which transport a client connects with. A /healthz
endpoint returns 200 ok for liveness checks.
Registering with Claude Code
Run the server, then add it as an MCP server. Streamable HTTP (recommended):
claude mcp add --transport http gitlab http://127.0.0.1:3030/mcp
Or, for a client using the legacy SSE transport:
claude mcp add --transport sse gitlab http://127.0.0.1:3030/sse
Project-scoped tools take a project argument — pass the repo's git remote
(git remote get-url origin) or a group/repo path. The server rejects any
project whose host differs from GITLAB_URL.
Flags
Logging
Every HTTP call, MCP request/response and GitLab API call is logged to stderr,
split by verbosity (--verbose/-v) so you can dial in only what you need.
Logging spans three layers:
- HTTP transport (
[mcp] http …) — the incoming client connection, tagged with thetransportthat served it (streamable-httporsse): client IP (honouringX-Forwarded-For/X-Real-IP), method, path, status, size, latency. - MCP JSON-RPC (
[mcp] request/[mcp] response) — the decoded call, with thetoolname andresp_bytesthe model receives. - GitLab API (
[gitlab] …) — each upstream call with status, size, latency.
Credentials (Authorization, Private-Token, Cookie, …) are redacted in
header logs, and GitLab/response sizes are measured as the body is streamed, so
large job traces and artifacts are counted without being buffered in memory.
Every line is tagged with a service field (--log-name, default
gitlab-mcp). When several MCP servers share a pod and their stderr is merged,
give each a distinct --log-name (or GITLAB_MCP_LOG_NAME) and filter by
service=… to isolate one server's logs. The [mcp]/[gitlab] message
prefixes identify the subsystem within a server; service identifies which
server.
Tools
Discovery: list_groups, list_projects, list_group_projects, search.
Users: list_users, get_current_user, get_user, get_user_activity
(contribution events by user id or username).
Code: get_file_contents, list_repository_tree.
Pipelines/Jobs: list_pipelines, get_pipeline, list_pipeline_jobs,
list_pipeline_bridges (trigger jobs → downstream/child pipelines), get_job,
get_job_log, get_job_log_compacted, retry_pipeline, retry_job,
cancel_pipeline.
Artifacts: list_job_artifacts, get_artifact_file.
Merge requests: list_merge_requests, get_merge_request,
get_merge_request_changes, get_merge_request_discussions,
get_merge_request_approvals, create_merge_request_note,
approve_merge_request, unapprove_merge_request, create_merge_request,
update_merge_request.
Settings: get_project_settings, get_repository_settings,
get_merge_request_settings, list_project_runners, get_runner_details.
* write tool — omitted when --read-only is set.
search
Runs a query against the GitLab Search API.
With neither group nor project set, the search runs globally across the
whole instance; with group set it is scoped to that group (and its
subgroups); with project set it is scoped to that project. Not every scope
supports every level: projects supports global/group only, snippet_titles
supports global only, and notes supports project only.
Examples:
search(scope="blobs", query="TODO", group="my-group/my-subgroup")
search(scope="merge_requests", query="fix login", project="my-group/my-subgroup/api")
list_group_projects
Enumerates a group's projects via GET /groups/:id/projects — use this to
list everything in a group rather than filtering by a search term.
get_job_log_compacted
Compacts a job's trace into frequency-ranked log-line templates using
drain3 template mining — a much smaller,
more legible alternative to get_job_log for large logs (thousands of
near-identical dependency/test/progress lines). Each cluster is a template with
<*> wildcards where tokens varied, a count of matching lines, and a concrete
sample line. ANSI colour codes, \r progress redraws, and GitLab
section_start/section_end markers are stripped before mining.
The trace is mined up to --max-log-scan-bytes (tail kept when larger);
total_lines, bytes, and a truncated flag accompany the clusters.
Limitations
File, job-log, and artifact reads are bounded on output by
--max-artifact-file-size, but the underlying GitLab SDK buffers the full
HTTP response in memory before that truncation is applied. Fetching a very
large file, job log, or artifact can therefore spike server memory even
though the returned content is capped — set a sensible limit and avoid
requesting enormous artifacts.