⚠️ Upgrade notes
This release fixes seven security advisories (see Security below). Four of the fixes change behavior for some setups:
1. Callback proxy mode: client callback allowlist and consent page (GHSA-5w3p-qj7g-79v5)
With GITLAB_MCP_OAUTH=true and GITLAB_OAUTH_CALLBACK_PROXY=true:
/authorizenow shows a consent page with the client name and the callback host. Users approve once per client and callback URL (remembered for 30 days; withoutOAUTH_STATELESS_MODE=truethe signing key is per process, so users are asked again after a restart).- Client callbacks must be listed in
GITLAB_OAUTH_ALLOWED_REDIRECT_URIS. The default covers Claude, ChatGPT, Cursor and loopback callbacks. Other clients fail registration withinvalid_redirect_uri(the server log names the refused URL). Add them withGITLAB_OAUTH_ALLOWED_REDIRECT_URIS=defaults,https://mcp.example.com/oauth/callback.*turns the allowlist off (not recommended).
2. download_attachment saves under GITLAB_DOWNLOAD_DIR (GHSA-vhp9-2xfv-rrfv)
In stdio mode:
- Files are saved under
GITLAB_DOWNLOAD_DIR(default.gitlab-mcp-downloadsin the working directory, created with a.gitignore).local_pathis now a subdirectory of that directory, not of the working directory. - Existing files are never replaced, and
local_pathsegments or filenames starting with a dot (.git,.vscode,.envrc) are rejected. - In readonly mode (
GITLAB_PERMISSION_MODE=readonlyorGITLAB_READ_ONLY_MODE=true) no files are written:download_attachmentonly returns images inline, anddownload_job_artifactsis rejected.
3. Local OAuth: shared callback server needs a key and a loopback host (GHSA-64g9-hrwx-9g6m)
With GITLAB_USE_OAUTH=true, stdio sessions can share one OAuth callback port only when they run as the same OS user with the same home directory (the key is in ~/.gitlab-mcp-oauth-<port>.key) and all run this release or later. Restart all sessions after upgrading. The callback server only answers requests addressed to 127.0.0.1, localhost, [::1], or the host in GITLAB_OAUTH_REDIRECT_URI.
4. GITLAB_OAUTH_ALLOWED_GROUPS is enforced on every request (GHSA-2jv7-p2g4-9f2h)
The group check used to run only at OAuth code exchange. It now also runs on every /mcp request and on token refresh:
JOB-TOKENheader auth gets403while allowed groups are set (a CI job token cannot list the user's groups). Use OAuth or aPrivate-Tokenheader from CI.Private-Tokenheaders, OAuth Bearer tokens (including tokens from other GitLab OAuth apps) and stateless session ids of users outside the allowed groups get403.- Token refresh is re-checked and fails with
invalid_grantfor non-members. - Results are cached per token for 5 minutes per process, so removing a user takes effect within 5 minutes.
Also: tools listed in GITLAB_TOOL_POLICY_HIDDEN are now callable only when their toolset (or GITLAB_TOOLS) enables them (GHSA-3gfg-wh5g-cf74). If you relied on hiding a tool to make it callable, enable it explicitly.
Changes in v2.3.0
🔐 Security
- fix(oauth): check client callbacks and ask for consent in callback proxy mode (GHSA-5w3p-qj7g-79v5, high). With
GITLAB_OAUTH_CALLBACK_PROXY=true, a client registered with anyredirect_uricould receive another user's authorization and redeem it for their GitLab tokens. - fix(download): save attachments as new files under a download directory (GHSA-vhp9-2xfv-rrfv, high). In stdio mode
download_attachmentcould create or overwrite any file under the working directory, including.gitand editor config, also in readonly mode. - fix(scope): check target projects and work item notes against the allowlist (GHSA-ffr3-5f57-gf93, medium). With
GITLAB_ALLOWED_PROJECT_IDSset,create_issue_linkandcreate_merge_requestdid not checktarget_project_id, and the work item note emoji tools acted on any note. - fix(oauth): require a key and a loopback host on the shared callback server (GHSA-64g9-hrwx-9g6m, medium). With
GITLAB_USE_OAUTH=true, the shared local callback server returned new tokens to any caller of/auth-request. - fix(tools): keep hidden-policy tools inside the enabled toolsets (GHSA-3gfg-wh5g-cf74, medium).
GITLAB_TOOL_POLICY_HIDDENmade tools callable even when their toolset was off, includinggitlab_api_requestandexecute_graphql. - fix(oauth): enforce allowed groups on every request and refresh (GHSA-2jv7-p2g4-9f2h, medium).
GITLAB_OAUTH_ALLOWED_GROUPSwas only checked when an OAuth code was exchanged. - fix(http): redact session ids in logs and bind OAuth sessions to their user (GHSA-g4cw-m236-q72h, low). Full session ids were logged, and another user's token could use an MCP OAuth session.
All users should upgrade.
✨ Features
- feat: add rebase_merge_request tool #844
📝 Documentation
- docs(docker): make Docker quick starts start on first run #849
🔬 CI/Test
- ci: make lint and format checks pass so they can be enforced #850
- test: run the excluded mock suites in test:mock #851
🔨 Chore
- chore(release): regenerate server-card and auto-add v3 deprecation notice #846
- chore(tools): shorten generic sentences in tool descriptions #852
Contributors
⚠️ Deprecation notice
Deprecated configuration options (e.g. GITLAB_READ_ONLY_MODE, USE_GITLAB_WIKI, and other legacy flags) still work during the grace period, but they will be removed in v3.0.0. Please migrate to the replacements described in the pinned issue #815.