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
MCP server for private gitlab
ดาว
1
เครื่องมือ
—
อัปไทม์
—
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):
-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:
Transport
Endpoint(s)
Notes
Streamable HTTP
/mcp
Current MCP spec; use this unless your client is older.
HTTP + SSE
/sse (event stream), /message (client → server)
Legacy 2024-11-05 transport for clients that only speak SSE.
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
Flag
Env
Default
--gitlab-url
GITLAB_URL
(required)
--gitlab-token
GITLAB_TOKEN
(required)
--listen
GITLAB_MCP_LISTEN
127.0.0.1:3030
--request-timeout
GITLAB_MCP_REQUEST_TIMEOUT
30s
--max-artifact-file-size
GITLAB_MCP_MAX_ARTIFACT_FILE_SIZE
5242880
--max-log-scan-bytes
GITLAB_MCP_MAX_LOG_SCAN_BYTES
10485760
--read-only
GITLAB_MCP_READ_ONLY
false
--verbose / -v
GITLAB_MCP_VERBOSE
3
--log-name
GITLAB_MCP_LOG_NAME
gitlab-mcp
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 the transport that served it (streamable-http or sse): client IP
(honouring X-Forwarded-For/X-Real-IP), method, path, status, size, latency.
MCP JSON-RPC ([mcp] request/[mcp] response) — the decoded call, with
the tool name and resp_bytes the model receives.
GitLab API ([gitlab] …) — each upstream call with status, size, latency.
Level
-v
What you get
info
3
One line per response with elapsed time and size: [mcp] http response (transport, client, path, status, resp_bytes), [mcp] response (tool + resp_bytes), [gitlab] response (status, resp_bytes). Handy for spotting a tool returning a large payload to the model.
debug
4
Adds a line per request: [mcp] http request (transport, client IP, method, path, user agent), [mcp] request, and [gitlab] request (method + URL).
trace
5
Adds full HTTP request headers and body, full MCP request params and response content, and GitLab request headers.
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.
one of projects, blobs, issues, merge_requests, commits, milestones, wiki_blobs, users, snippet_titles, notes
query
yes
search term
group
no
scope to a group (id or full path); ignored if project is set
project
no
scope to a project (id or group/repo path); takes precedence over group
ref
no
branch/tag/commit, used by blobs and commits
page
no
1-based page number
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.
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.
Param
Required
Notes
group
yes
group id or full path, e.g. my-group/my-subgroup
include_subgroups
no
also include projects from subgroups (default true)
search
no
filter by project name/path substring
page
no
1-based page number
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.
Param
Required
Notes
project
yes
git remote URL or group/repo path
job_id
yes
job ID
max_clusters
no
max templates to return, ranked by count (default 100)
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.