About gitlab-mcp
gitlab-mcp is a stdio server for people working in gitlab.com projects, covering issues, merge requests, code review, repositories and CI. Its focused tool set includes safeguards such as untrusted-content boundaries, protected write operations and OAuth sign-in through an OS keyring.
Key features
- 01Tools for issues and merge requests
- 02Code review and discussion management
- 03Repository, branch and commit operations
- 04CI pipeline and job inspection
- 05OAuth login with OS keyring storage
- 06Untrusted content boundaries and gated tools
Use cases
- 01Search and update project issues
- 02Review merge request diffs and submit comments
- 03Inspect pipeline jobs, logs and test reports
- 04Manage branches and commits in a project
MCP support
The server runs over stdio and exposes GitLab project operations as MCP tools, plus resources for issues, merge requests and job logs. MCP clients can connect using a server configuration or the Claude Desktop bundle.
Configuration
Local server config is the same for every client. Copy it into your client’s MCP config file.
We haven’t collected a package or remote endpoint for this server. Follow the instructions in the project README.
Tools
Local servers expose tools only once running on your machine, and the maintainer has not provided a tool list.
Details
From the project READMEgitlab-mcp
An MCP server for gitlab.com. One binary over stdio, signed in as you. It works inside projects: issues, merge requests, reviews, the repository and CI. Instance and group administration, runners, CI variables and tokens are out of scope.
Unofficial, and not affiliated with GitLab Inc. See NOTICE.
What makes this one different
- A small surface, gated by registration. About sixty tools rather than an API mirror, and a tool that is switched off is not registered, so it cannot be called at all.
- Nothing it writes runs a quick action.
/mergein a comment is refused or escaped, never executed. - Content is marked as untrusted. Issues, reviews, files and job logs come back inside boundaries the content cannot close.
- Signs in like a desktop app. Your own OAuth application, a browser tab, the token in the OS keyring.
- Verifiable releases. Signed checksums, build provenance and SBOMs.
Status
Stable: the release badge above names the newest tag, and
CHANGELOG.md says what each one holds. The tool surface is a
contract: tools keep their names and output fields.
docs/architecture.md §16 is the plan.
Tools
| Tool | What it does |
|---|---|
get_me | Who is signed in, with which scopes, and what GitLab reports about itself |
resolve_url | Turn a GitLab web URL into the arguments another tool takes, and name that tool |
search_projects | Find projects by name, or within a group |
get_project | One project: default branch, visibility, what it has turned on |
list_members | Who has access to a project, and with which role |
find_users | Accounts by exact username or by name |
search_issues | Find issues across gitlab.com, a group or a project |
get_issue | One issue, its description marked as untrusted content, and its linked merge requests |
create_issue | Create an issue; labels, assignees and milestone checked first |
update_issue | Change an issue's fields, refused if it changed since you read it |
list_discussions | The comment threads on an issue or a merge request |
list_item_events | An issue's or a merge request's label, state, milestone and weight changes, newest first |
add_comment | Comment on an issue or merge request, reply in a thread, or start one, on a diff line or not |
update_comment | Edit one of your own comments in place, in its thread and on its diff line |
resolve_discussion | Resolve or reopen a thread on a merge request or an issue |
link_issues | Link two issues, in one project or two |
unlink_issues | Remove the link between two issues |
link_merge_requests | Make one merge request wait for another to merge, in one project or two |
unlink_merge_requests | Remove a dependency between two merge requests |
search_merge_requests | Find merge requests across gitlab.com or a project |
get_merge_request | One merge request with its approvals, reviewer states, linked issues and dependencies |
list_mr_files | The files a merge request changes, with line counts and GitLab's markers |
get_mr_diff | A merge request's diffs, file by file, bounded |
list_mr_commits | A merge request's commits |
list_mr_versions | A merge request's diff versions, one per push |
compare_mr_versions | What changed between two versions of a merge request, or since one |
create_merge_request | Open a merge request from a branch |
update_merge_request | Change a merge request's fields, refused if it changed since you read it |
track_time | Set or reset an issue's or a merge request's time estimate, and add or reset its time spent |
add_review_comment | A draft review comment, on the merge request or a diff line |
list_review_comments | Your unpublished review comments on a merge request |
delete_review_comment | Delete one of your drafts |
update_review_comment | Change the text of one of your drafts, keeping its place |
publish_review_comment | Publish one of your drafts on its own |
submit_review | Publish all your drafts at once, with a summary and reviewer state |
rerequest_review | Ask a reviewer for a new review once their requested changes are addressed |
get_file | A file at a ref, bounded |
list_tree | A directory listing at a ref |
list_branches | A project's branches |
list_commits | Commits on a ref or a path |
get_commit | One commit and its diff, bounded, and the merge requests that contain it |
compare_refs | The commits and diffs between two refs, bounded |
list_tags | A project's tags |
create_branch | Create a branch from a ref |
create_commit | Commit file changes to a branch; never the default or a protected one |
get_blame | Who last changed each line of a file, bounded |
cherry_pick_commit | Apply a commit to a branch; never the default or a protected one |
revert_commit | Undo a commit on a branch with a new one; never the default or a protected one |
list_pipelines | A project's CI pipelines |
get_pipeline | One pipeline with the jobs that failed, trigger jobs included |
list_jobs | A pipeline's jobs |
get_job_log | A window of a job's log, secrets masked; the failing section on request |
get_test_report | A pipeline's test counts and failed cases, secrets masked, bounded |
lint_ci | Check a project's CI configuration at a ref, or configuration you pass |
list_job_artifacts | The files a job kept as artifacts |
get_job_artifact | One text file of a job's artifacts, secrets masked, bounded |
list_labels | The labels a project's issues and merge requests can carry |
list_milestones | A project's or a group's milestones |
list_boards | A project's issue boards and their lists, each with the search_issues arguments that read it |
search | Code, commits, comments and more, in a project, a group or everywhere |
list_todos | Your to-do items |
mark_todos_done | Mark your to-do items done |
add_todo | Add a to-do for yourself on an issue or a merge request |
subscribe | Subscribe to an issue's or a merge request's notifications, or unsubscribe |
react | Add or remove your emoji reaction on an issue, a merge request or a comment |
upload_file | Upload an image from a directory you allowed and get the Markdown that shows it in a comment or description |
merge_merge_request | Merge at the head you reviewed, or when the pipeline succeeds (Ship) |
cancel_auto_merge | Stop a merge request merging when its pipeline succeeds (Ship) |
approve_merge_request | Approve at the head you reviewed (Ship) |
unapprove_merge_request | Withdraw your approval (Ship) |
apply_suggestions | Commit suggestions from a merge request's diff comments to its source branch; never a protected one (Ship) |
rebase_merge_request | Rebase a merge request's source branch, from the head you reviewed (Ship) |
move_issue | Move an issue to another project, never to one more people can see (Ship) |
run_pipeline | Run a pipeline for a ref, with variables whose values are never shown (Ship) |
run_merge_request_pipeline | Run a merge request's pipeline, merged results or detached as GitLab picks; not for a fork's merge request (Ship) |
retry_pipeline | Retry a pipeline's failed and canceled jobs (Ship) |
retry_job | Run a finished job again, with inputs (Ship) |
play_job | Start a manual job, with variables and inputs (Ship) |
cancel_pipeline | Cancel a running pipeline (Ship) |
delete_branch | Delete a branch; never the default, a protected or an unmerged one unless asked (Destructive) |
delete_comment | Delete one of your own comments (Destructive) |
make_issue_public | Make a confidential issue public (Destructive) |
list_wiki_pages | A project wiki's pages (wiki toolset) |
get_wiki_page | One wiki page, bounded (wiki toolset) |
save_wiki_page | Create a wiki page, or change one unchanged since you read it (wiki toolset) |
delete_wiki_page | Delete a wiki page (wiki toolset, Destructive) |
list_snippets | A project's snippets, or your own (snippets toolset) |
get_snippet | One snippet and a file of it, bounded (snippets toolset) |
create_snippet | Create a snippet, always private (snippets toolset) |
update_snippet | Change one of your own private snippets: title, description, files; refused if it changed since you read it (snippets toolset) |
delete_snippet | Delete one of your own snippets (snippets toolset, Destructive) |
list_releases | A project's releases (releases toolset) |
get_release | One release and its notes (releases toolset) |
create_release | Create a release, and its tag at a ref, with asset links to the project's own pages (releases toolset, Ship) |
create_tag | Create a tag at a ref; never a protected one (releases toolset) |
delete_tag | Delete a tag; never a protected one (releases toolset, Destructive) |
create_label | Create a project label (planning toolset) |
update_label | Change a project label unchanged since you read it (planning toolset) |
delete_label | Delete a project label (planning toolset, Destructive) |
create_milestone | Create a project milestone (planning toolset) |
update_milestone | Change, close or reopen a milestone unchanged since you read it (planning toolset) |
delete_milestone | Delete a project milestone (planning toolset, Destructive) |
list_environments | A project's environments and their last deployment (deployments toolset) |
list_deployments | What was deployed where, and by which job (deployments toolset) |
list_events | Recent activity, yours or a project's (activity toolset) |
Ship and Destructive tools are registered only with the settings below,
and the six toolsets only when GITLAB_MCP_TOOLSETS names them.
Three resources carry the same text for clients that attach rather than call: an issue, a merge request and a job log.
Install
Download an archive for your platform from the releases page, or:
go install github.com/mmedum/gitlab-mcp/v4/cmd/gitlab-mcp@latest
Claude Desktop users can open the .mcpb bundle from the same release.
It does not log you in; do the steps below first.
Sign in
You bring your own OAuth application. Nothing is shipped in the binary, and there are no personal access tokens.
-
Register an application, once. On gitlab.com: your avatar → Edit profile → Applications (or a group's). Redirect URI
http://127.0.0.1/callback, Confidential unchecked, scopeapi(read_apifor read-only).docs/setup.mdhas the exact values. -
Log in from a terminal:
gitlab-mcp login --client-id <application id>It prints the scopes it will ask for, opens your browser, and stores the token in the OS keyring. On a machine without a browser,
--no-browserprints the URL and thessh -Lline to forward the callback. -
Check it:
gitlab-mcp doctordoctorwalks the connection, TLS, the sign-in, the application, the granted scopes and one call as you, and names what is missing.statusandlogoutdo what they say;--profilekeeps two gitlab.com logins side by side.
Connect a client
Claude Code:
claude mcp add gitlab -- gitlab-mcp
Any client that takes a JSON server list:
{
"mcpServers": {
"gitlab": { "command": "gitlab-mcp" }
}
}
Claude Desktop can instead open the .mcpb bundle from a release. It
asks for the same settings: merging, approving, applying suggestions,
moving issues, CI and releases (enable_ship) and deleting
(enable_destructive) stay off unless you turn them on, and
write_namespaces confines writes to the groups and projects you name.
This server asks nobody before a call. Claude Desktop approves each
tool, and a tool you always allow then runs unasked, so
leave a switch off unless you need its tools.
Configuration
Every setting is an environment variable with the GITLAB_MCP_ prefix
and a matching flag. docs/configuration.md lists them all.
Safety
GitLab content was written by someone other than you, and some of it is written to steer an agent. The server marks it as untrusted data, never fetches what it references, and nothing it adds tells the model to act on it.
What can be registered depends on the settings, because GitLab's api
scope cannot separate any of it:
| Setting | Registers |
|---|---|
GITLAB_MCP_READ_ONLY=true | Read tools only, with a read_api token |
| default | Read and Write: issues, comments, reviews, branches, merge requests |
GITLAB_MCP_ENABLE_SHIP=true | adds merging, approving, running CI and releases |
GITLAB_MCP_ENABLE_DESTRUCTIVE=true | adds deletion and making an issue public, and each call must pass confirm: true |
Your MCP client approves each call; the server does not ask you
itself. claude.ai and Codex prompt for the Ship and Destructive tools,
which are marked destructive, and VS Code prompts for every tool that
writes. In Claude Code, delete_label, delete_milestone and
make_issue_public ask on every call whatever your rules say, since
none can be undone and each reaches past the one item: a label or
milestone comes off every issue and merge request, and a public issue
is read by everyone. For the rest, list the
calls you want to approve one by one under permissions.ask in your
settings, with the name you gave the server in place of gitlab. For
example, the writes that ship, publish, upload or delete:
{
"permissions": {
"ask": [
"mcp__gitlab__merge_merge_request", "mcp__gitlab__approve_merge_request",
"mcp__gitlab__apply_suggestions", "mcp__gitlab__run_pipeline",
"mcp__gitlab__run_merge_request_pipeline", "mcp__gitlab__play_job",
"mcp__gitlab__create_release", "mcp__gitlab__create_tag", "mcp__gitlab__move_issue",
"mcp__gitlab__upload_file",
"mcp__gitlab__delete_branch", "mcp__gitlab__delete_comment",
"mcp__gitlab__delete_wiki_page", "mcp__gitlab__delete_label",
"mcp__gitlab__delete_milestone", "mcp__gitlab__delete_tag",
"mcp__gitlab__delete_snippet"
]
}
}
- No quick actions. GitLab runs
/merge,/closeand the rest from a description or comment. Every body this server sends is checked, and a quick-action line is refused, or escaped when you ask. - Protected branches. Code reaches the default branch or a protected branch only through a merge request; a direct commit there is refused.
- No blind retries. A create that may or may not have happened is settled by reading, never by creating again.
- Local files only from where you allow.
upload_filereads an image only from the directoriesGITLAB_MCP_UPLOAD_DIRSnames, and only a PNG, JPEG, GIF or WebP judged from its bytes, so a comment asking for a key file to be "attached" gets nothing.
The default token can still merge and approve; leaving Ship off stops
this server doing so, not anything else holding the token.
docs/security.md says more.
Verify a release
Each release signs checksums.txt with a keyless Sigstore certificate
and attests every archive and the bundle:
sha256sum -c checksums.txt --ignore-missing
cosign verify-blob checksums.txt --bundle checksums.txt.bundle \
--certificate-identity "https://github.com/mmedum/gitlab-mcp/.github/workflows/release.yml@refs/tags/<tag>" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
gh attestation verify <archive> --repo mmedum/gitlab-mcp
Contributing
CONTRIBUTING.md. Security reports go through SECURITY.md, not an
issue.
License
Apache-2.0. See LICENSE and NOTICE.
Comments
New to MCP?
From what the protocol is to installing your first server, these answers cover most first questions.
An open protocol that defines how AI applications connect to external tools and data in a uniform way. Clients talk to the model; MCP servers provide tools, resources and prompt templates. They exchange standard messages, so one server can be reused by many clients.