github zereight/gitlab-mcp v2.3.0

2 hours ago

⚠️ 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:

  • /authorize now shows a consent page with the client name and the callback host. Users approve once per client and callback URL (remembered for 30 days; without OAUTH_STATELESS_MODE=true the 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 with invalid_redirect_uri (the server log names the refused URL). Add them with GITLAB_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-downloads in the working directory, created with a .gitignore). local_path is now a subdirectory of that directory, not of the working directory.
  • Existing files are never replaced, and local_path segments or filenames starting with a dot (.git, .vscode, .envrc) are rejected.
  • In readonly mode (GITLAB_PERMISSION_MODE=readonly or GITLAB_READ_ONLY_MODE=true) no files are written: download_attachment only returns images inline, and download_job_artifacts is 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-TOKEN header auth gets 403 while allowed groups are set (a CI job token cannot list the user's groups). Use OAuth or a Private-Token header from CI.
  • Private-Token headers, OAuth Bearer tokens (including tokens from other GitLab OAuth apps) and stateless session ids of users outside the allowed groups get 403.
  • Token refresh is re-checked and fails with invalid_grant for 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 any redirect_uri could 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_attachment could create or overwrite any file under the working directory, including .git and 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_IDS set, create_issue_link and create_merge_request did not check target_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_HIDDEN made tools callable even when their toolset was off, including gitlab_api_request and execute_graphql.
  • fix(oauth): enforce allowed groups on every request and refresh (GHSA-2jv7-p2g4-9f2h, medium). GITLAB_OAUTH_ALLOWED_GROUPS was 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.

Don't miss a new gitlab-mcp release

NewReleases is sending notifications on new releases.