# GitLab MCP Server

[![npm version](https://img.shields.io/npm/v/gitlab-mcp-cli)](https://www.npmjs.com/package/gitlab-mcp-cli)
[![npm downloads](https://img.shields.io/npm/dm/gitlab-mcp-cli)](https://www.npmjs.com/package/gitlab-mcp-cli)
[![CI](https://github.com/DevquasarX9/mcp-gitlab/actions/workflows/ci.yml/badge.svg)](https://github.com/DevquasarX9/mcp-gitlab/actions/workflows/ci.yml)

`gitlab-mcp-server` is a [Model Context Protocol](https://modelcontextprotocol.io/) server for GitLab.com and self-managed GitLab. It runs over stdio by default and also supports opt-in Streamable HTTP for local clients that need an HTTP endpoint.

It gives AI agents and developer tools structured access to GitLab projects, repositories, issues, merge requests, pipelines, releases, governance data, and higher-level delivery summaries. The server is read-only by default and uses explicit gates for write and destructive actions.

- npm package: [gitlab-mcp-cli](https://www.npmjs.com/package/gitlab-mcp-cli)
- Repository: [DevquasarX9/mcp-gitlab](https://github.com/DevquasarX9/mcp-gitlab)
- Works with: Claude Code, Claude Desktop, Codex, Cursor, and other MCP clients

## Why This Server

- Safe defaults: read-only mode is the default, with separate write and destructive-action gates.
- GitLab coverage: projects, groups, repositories, issues, merge requests, pipelines, releases, packages, approvals, and protected branches.
- AI-friendly tools: higher-level tools summarize project health, review risk, release notes, delivery status, and pipeline failures.
- Self-managed support: works with `https://gitlab.com` and private GitLab instances.
- Operational controls: allowlists, denylist, payload caps, timeout control, optional audit logging, and secret redaction.

## When To Use This

Use this server when you want a local or self-managed GitLab MCP server with conservative defaults, explicit write gates, project/group allowlists, and workflow-oriented summaries. It is especially useful for agents that need to inspect repositories, review merge requests, triage CI, assess release readiness, or produce delivery digests without cloning repositories first.

The official GitLab MCP server is a good default when you want GitLab's beta hosted tool surface and Duo-backed features. `glab mcp serve` can be useful for CLI-centered experimentation. This package focuses on read-only-by-default local operation, guarded writes, broader GitLab API coverage, and higher-level DevOps workflow intelligence. See [docs/parity.md](https://github.com/DevquasarX9/mcp-gitlab/blob/main/docs/parity.md) for the current mapping.

## Install

Requirements:

- Node.js `>=20.11.0`
- A GitLab token with the scopes needed for the resources you want to access

Install globally:

```bash
npm install -g gitlab-mcp-cli
```

Run without a global install:

```bash
npx -y gitlab-mcp-cli
```

The published package name is `gitlab-mcp-cli`. The installed executable is `gitlab-mcp-server`.

## Quick Start

Run the server directly after setting the required environment variables:

```bash
GITLAB_BASE_URL=https://gitlab.com \
GITLAB_TOKEN=glpat-xxxxxxxxxxxxxxxxxxxx \
gitlab-mcp-server
```

From source:

```bash
npm ci
npm run build
GITLAB_BASE_URL=https://gitlab.com \
GITLAB_TOKEN=glpat-xxxxxxxxxxxxxxxxxxxx \
node dist/cli.js
```

For local development, copy `.env.example` to `.env` and keep credentials out of git.

Run a setup diagnostics pass before wiring the server into a client:

```bash
gitlab-mcp-server doctor
```

From source:

```bash
npm run build
node dist/cli.js doctor
```

The doctor report validates GitLab connectivity and summarizes:

- authenticated user and GitLab version
- read-only, write-enabled, or destructive-enabled posture
- token scope visibility when PAT introspection is available
- allowlists, denylist, and alias counts
- tool profile, HTTP bind posture, and payload limits
- likely blocked capabilities and recommended next checks

## MCP Client Setup

Example client configs live in [`examples/clients/`](https://github.com/DevquasarX9/mcp-gitlab/tree/main/examples/clients):

- [Claude Code guide](https://github.com/DevquasarX9/mcp-gitlab/blob/main/examples/clients/claude_code.md)
- [Shared client setup guide](https://github.com/DevquasarX9/mcp-gitlab/blob/main/examples/clients/README.md)
- [HTTP transport guide](https://github.com/DevquasarX9/mcp-gitlab/blob/main/examples/clients/http.md)
- Raw config examples: [Claude Desktop JSON](https://github.com/DevquasarX9/mcp-gitlab/blob/main/examples/clients/claude_desktop_config.json), [Codex TOML](https://github.com/DevquasarX9/mcp-gitlab/blob/main/examples/clients/codex-config.toml), [Cursor JSON](https://github.com/DevquasarX9/mcp-gitlab/blob/main/examples/clients/cursor.mcp.json)

### Generic stdio config

```json
{
  "mcpServers": {
    "gitlab": {
      "command": "gitlab-mcp-server",
      "env": {
        "GITLAB_BASE_URL": "https://gitlab.com",
        "GITLAB_TOKEN": "your-token-here",
        "ENABLE_WRITE_TOOLS": "false",
        "ENABLE_DESTRUCTIVE_TOOLS": "false"
      }
    }
  }
}
```

### `npx` config

```json
{
  "mcpServers": {
    "gitlab": {
      "command": "npx",
      "args": ["-y", "gitlab-mcp-cli"],
      "env": {
        "GITLAB_BASE_URL": "https://gitlab.com",
        "GITLAB_TOKEN": "your-token-here",
        "ENABLE_WRITE_TOOLS": "false",
        "ENABLE_DESTRUCTIVE_TOOLS": "false"
      }
    }
  }
}
```

### Codex TOML config

```toml
[mcp_servers.gitlab]
command = "gitlab-mcp-server"

[mcp_servers.gitlab.env]
GITLAB_BASE_URL = "https://gitlab.com"
GITLAB_TOKEN = "your-token-here"
ENABLE_WRITE_TOOLS = "false"
ENABLE_DESTRUCTIVE_TOOLS = "false"
```

### HTTP transport

Stdio remains the default transport. To run the local Streamable HTTP server, use the CLI subcommand:

```bash
GITLAB_TOKEN=glpat-xxxxxxxxxxxxxxxxxxxx \
gitlab-mcp-server serve-http
```

You can also enable HTTP with environment configuration:

```bash
MCP_TRANSPORT=http \
GITLAB_TOKEN=glpat-xxxxxxxxxxxxxxxxxxxx \
gitlab-mcp-server
```

CLI mode takes precedence over `MCP_TRANSPORT`, so `gitlab-mcp-server doctor` still runs diagnostics even when `MCP_TRANSPORT=http` is present.

Default HTTP endpoint:

```text
http://127.0.0.1:3333/mcp
```

HTTP safety defaults:

- Binds to `127.0.0.1`.
- Allows `localhost`, `127.0.0.1`, and `[::1]` host headers.
- Allows missing origins and localhost browser origins.
- Requires explicit `MCP_HTTP_ALLOWED_ORIGINS` entries for remote browser origins.
- Refuses non-local binds unless both `MCP_HTTP_ALLOW_NON_LOCALHOST=true` and `MCP_HTTP_AUTH_TOKEN` are configured.

## Configuration

The server normalizes `GITLAB_BASE_URL` to `/api/v4` automatically. If you already pass an `/api/v4` URL, it is preserved.

### Core settings

| Variable | Required | Default | Notes |
|---|---|---:|---|
| `GITLAB_BASE_URL` | No | `https://gitlab.com` | GitLab instance base URL or `/api/v4` URL |
| `GITLAB_TOKEN` | Yes |  | GitLab PAT, project access token, group access token, or OAuth bearer token |
| `GITLAB_TOKEN_HEADER_MODE` | No | `bearer` | Use `private-token` when required by some self-managed setups |
| `GITLAB_MCP_TOOL_PROFILE` | No | `readonly` | Limits MCP tool discovery to a workflow profile |
| `GITLAB_MCP_ENABLED_TOOLS` | No | empty | Comma-separated explicit tool allowlist applied after the profile |
| `GITLAB_MCP_DISABLED_TOOLS` | No | empty | Comma-separated explicit tool denylist applied after the profile |
| `GITLAB_MCP_EXPOSE_DISABLED_WRITES` | No | `false` | Compatibility override to advertise write/destructive tools even when server-side gates are disabled |
| `MCP_TRANSPORT` | No | `stdio` | Use `http` to run Streamable HTTP when no CLI mode is provided |
| `MCP_HTTP_HOST` | No | `127.0.0.1` | HTTP bind host |
| `MCP_HTTP_PORT` | No | `3333` | HTTP bind port |
| `MCP_HTTP_PATH` | No | `/mcp` | Streamable HTTP MCP path |
| `MCP_HTTP_ALLOWED_ORIGINS` | No | empty | Comma-separated browser origins allowed in HTTP mode |
| `MCP_HTTP_ALLOWED_HOSTS` | No | `localhost,127.0.0.1,[::1]` | Comma-separated allowed hostnames for DNS rebinding protection |
| `MCP_HTTP_AUTH_TOKEN` | No | unset | Optional bearer token required by HTTP mode; required for non-local binds |
| `MCP_HTTP_ALLOW_NON_LOCALHOST` | No | `false` | Allows non-local HTTP binds only when a bearer token is also configured |
| `ENABLE_WRITE_TOOLS` | No | `false` | Enables write-capable tools |
| `ENABLE_DESTRUCTIVE_TOOLS` | No | `false` | Enables destructive tools that also require per-call confirmation |
| `ENABLE_DRY_RUN` | No | `false` | Returns intended write requests without mutating GitLab |
| `PROJECT_ALIASES` | No | empty | Comma-separated `alias=group/project` mappings for `project_id` inputs |
| `GROUP_ALIASES` | No | empty | Comma-separated `alias=my-group` mappings for `group_id` inputs |

### Access controls and limits

| Variable | Default | Purpose |
|---|---:|---|
| `PROJECT_ALLOWLIST` | empty | Comma-separated project IDs or paths that are allowed |
| `GROUP_ALLOWLIST` | empty | Comma-separated group IDs or paths that are allowed |
| `PROJECT_DENYLIST` | empty | Comma-separated project IDs or paths that are always denied |
| `MAX_FILE_SIZE_BYTES` | `1048576` | Maximum repository file payload |
| `MAX_DIFF_SIZE_BYTES` | `2097152` | Maximum diff payload |
| `MAX_API_RESPONSE_BYTES` | `4194304` | Maximum total API response payload |
| `GITLAB_HTTP_TIMEOUT_MS` | `30000` | Request timeout |

### Operational settings

| Variable | Default | Purpose |
|---|---:|---|
| `GITLAB_USER_AGENT` | `gitlab-mcp-server` | Custom outbound user agent |
| `LOG_LEVEL` | `info` | `debug`, `info`, `warn`, or `error` |
| `AUDIT_LOG_PATH` | unset | Optional JSON-line audit log path |
| `EXPOSE_SECRET_VARIABLE_VALUES` | `false` | Keeps CI/CD secret values redacted unless explicitly enabled |

See [`.env.example`](https://github.com/DevquasarX9/mcp-gitlab/blob/main/.env.example) for a complete local template.

### Aliases

If you repeatedly work with the same projects or groups, you can define explicit aliases:

```bash
PROJECT_ALIASES=platform-api=platform/backend-api,storefront=commerce/storefront
GROUP_ALIASES=platform=platform,commerce=commerce
```

After that, any tool expecting `project_id` or `group_id` can use the alias instead of the full path. Alias resolution is explicit and local to this server configuration.

### Tool profiles

The default profile is `readonly`, so MCP clients discover read-only tools first and disabled write/destructive tools stay hidden. Use a narrower profile when you want agents to see only the tools for a workflow:

```bash
GITLAB_MCP_TOOL_PROFILE=mr-review
```

Available profiles:

- `readonly`
- `core`
- `mr-review`
- `ci-triage`
- `delivery`
- `release`
- `governance`
- `maintainer-write`
- `full`

Explicit allow/deny lists are applied after the selected profile:

```bash
GITLAB_MCP_ENABLED_TOOLS=gitlab_get_merge_request,gitlab_get_merge_request_diff
GITLAB_MCP_DISABLED_TOOLS=gitlab_merge_merge_request
```

For compatibility with older broad tool discovery, set `GITLAB_MCP_TOOL_PROFILE=full` and `GITLAB_MCP_EXPOSE_DISABLED_WRITES=true`. Execution is still protected by `ENABLE_WRITE_TOOLS`, `ENABLE_DESTRUCTIVE_TOOLS`, and per-call destructive confirmation.

## Guided Prompts

The server now exposes reusable MCP prompts so users do not need to memorize the full tool catalog first.

Core workflow prompts:

- `gitlab_review_merge_request_workflow`
- `gitlab_explain_failed_pipeline_workflow`
- `gitlab_summarize_project_status_workflow`
- `gitlab_generate_weekly_delivery_summary_workflow`
- `gitlab_assess_project_write_safety_workflow`

Hero workflow prompts:

- `gitlab_stale_merge_request_cleanup_workflow`
- `gitlab_flaky_ci_triage_workflow`
- `gitlab_release_readiness_check_workflow`
- `gitlab_team_delivery_digest_workflow`
- `gitlab_portfolio_delivery_overview_workflow`
- `gitlab_summarize_commit_range_workflow`
- `gitlab_summarize_directory_workflow`

Example prompt requests inside an MCP client:

```text
Use gitlab_review_merge_request_workflow for project_id="platform-api" and merge_request_iid="42".
```

```text
Use gitlab_flaky_ci_triage_workflow for project_id="platform-api" and ref="main".
```

These prompts point the model at the relevant `gitlab_*` tools for each workflow while keeping the actual data access explicit and structured.

## Recommended Starting Points

If you are trying the MCP for the first time, start with the orchestration tools rather than the lower-level primitives.

Recommended first tools:

- `gitlab_release_readiness_check`: one-call release go/caution/hold assessment for a project
- `gitlab_flaky_ci_triage`: separates likely flaky CI from deterministic failures
- `gitlab_stale_merge_request_cleanup`: identifies stale merge requests and recommends the next action for each sampled item
- `gitlab_team_delivery_digest`: produces a concise project or group delivery summary plus a chat-ready status line

Example calls:

```json
{
  "project_id": "platform-api",
  "output_format": "markdown"
}
```

Use that with `gitlab_release_readiness_check`.

```json
{
  "project_id": "platform-api",
  "ref": "main",
  "output_format": "markdown"
}
```

Use that with `gitlab_flaky_ci_triage`.

```json
{
  "project_id": "platform-api",
  "stale_after_days": 14,
  "output_format": "markdown"
}
```

Use that with `gitlab_stale_merge_request_cleanup`.

```json
{
  "scope_type": "group",
  "scope_id": "platform",
  "days": 7,
  "output_format": "markdown"
}
```

Use that with `gitlab_team_delivery_digest`.

## Shareable Output Formats

Selected higher-level tools support `output_format="markdown"` in addition to the default structured JSON response envelope.

Current markdown-capable tools:

- `gitlab_summarize_project_status`
- `gitlab_explain_failed_pipeline`
- `gitlab_stale_merge_request_cleanup`
- `gitlab_flaky_ci_triage`
- `gitlab_compare_pipeline_runs`
- `gitlab_trace_job_to_commit_and_merge_request`
- `gitlab_release_readiness_check`
- `gitlab_team_delivery_digest`
- `gitlab_portfolio_delivery_overview`
- `gitlab_get_group_delivery_overview`
- `gitlab_summarize_commit_range`
- `gitlab_summarize_directory`
- `gitlab_review_merge_request_risks`
- `gitlab_get_merge_request_review_state`
- `gitlab_generate_release_notes`
- `gitlab_get_project_dashboard`

Example calls:

```json
{
  "project_id": "platform-api",
  "output_format": "markdown"
}
```

```json
{
  "project_id": "platform-api",
  "pipeline_id": 12345,
  "output_format": "markdown"
}
```

```json
{
  "scope_type": "project",
  "scope_id": "platform-api",
  "days": 7,
  "output_format": "markdown"
}
```

This is useful when the result is intended for chat, a GitLab comment, or a status update, while `structured` remains the best default for agents that want to post-process the result.

## Token Setup

Recommended scopes:

- Read-only mode: `read_api`
- Write mode: `api`

Notes:

- `ENABLE_WRITE_TOOLS=true` only enables the MCP server's write-capable tools. It does not add GitLab permissions to the configured token.
- If write tools are enabled but GitLab returns `insufficient_scope`, the MCP write-mode guard has passed and the token is still missing the required GitLab scope, usually `api`.
- Project and group access tokens work when their scopes match the requested resources.
- Some self-managed GitLab instances work better with `GITLAB_TOKEN_HEADER_MODE=private-token`.
- Keep write and destructive modes off unless you explicitly need them.

## Safety Model

- Read-only is the default and recommended starting point.
- The default tool profile is `readonly`; disabled write/destructive tools are hidden from MCP discovery.
- Write-capable tools require `ENABLE_WRITE_TOOLS=true`.
- Destructive tools require `ENABLE_DESTRUCTIVE_TOOLS=true` and `confirm_destructive=true` in the tool call.
- `ENABLE_DRY_RUN=true` lets agents inspect a write request before changing GitLab.
- Allowlists and the denylist are enforced before risky operations.
- HTTP mode is localhost-only by default and requires a bearer token before non-local binds are allowed.
- Secret CI/CD variable values remain redacted unless `EXPOSE_SECRET_VARIABLE_VALUES=true`.
- The server does not execute shell commands. It talks directly to the GitLab REST and GraphQL APIs.

Security details: [SECURITY.md](https://github.com/DevquasarX9/mcp-gitlab/blob/main/SECURITY.md) and [docs/security-model.md](https://github.com/DevquasarX9/mcp-gitlab/blob/main/docs/security-model.md)

## Available Tool Areas

The server exposes concrete `gitlab_*` MCP tools. Representative examples:

- Instance and access: `gitlab_validate_token`, `gitlab_get_current_user`, `gitlab_list_accessible_projects`
- Projects and groups: `gitlab_search`, `gitlab_search_projects`, `gitlab_search_labels`, `gitlab_get_project_dashboard`, `gitlab_get_group_delivery_overview`
- Repository: `gitlab_get_file`, `gitlab_search_code`, `gitlab_compare_refs`, `gitlab_get_commit_diff`
- Issues: `gitlab_list_issues`, `gitlab_create_issue`, `gitlab_add_issue_comment`
- Merge requests: `gitlab_get_merge_request`, `gitlab_get_merge_request_commits`, `gitlab_get_merge_request_pipelines`, `gitlab_list_draft_notes`, `gitlab_create_draft_note`, `gitlab_bulk_publish_draft_notes`, `gitlab_get_merge_request_review_state`, `gitlab_merge_merge_request`
- Pipelines: `gitlab_list_pipelines`, `gitlab_explain_failed_pipeline`, `gitlab_find_flaky_jobs`
- Releases and packages: `gitlab_list_releases`, `gitlab_create_release`, `gitlab_get_package`
- Governance: `gitlab_get_project_approval_rules`, `gitlab_check_project_write_risk`
- Intelligence: `gitlab_summarize_project_status`, `gitlab_stale_merge_request_cleanup`, `gitlab_flaky_ci_triage`, `gitlab_release_readiness_check`, `gitlab_team_delivery_digest`, `gitlab_portfolio_delivery_overview`, `gitlab_summarize_commit_range`, `gitlab_summarize_directory`, `gitlab_review_merge_request_risks`, `gitlab_generate_release_notes`

Write-capable tools stay unavailable until you explicitly enable them.

For design notes and implementation details, see:

- [docs/tool-design.md](https://github.com/DevquasarX9/mcp-gitlab/blob/main/docs/tool-design.md)
- [docs/architecture.md](https://github.com/DevquasarX9/mcp-gitlab/blob/main/docs/architecture.md)
- [docs/parity.md](https://github.com/DevquasarX9/mcp-gitlab/blob/main/docs/parity.md)

## Common AI Workflows

This server is useful when you want an agent to:

- inspect a GitLab repository without cloning it first
- summarize a commit range and identify risky files or directories with `gitlab_summarize_commit_range`
- understand an unfamiliar repository area with `gitlab_summarize_directory`
- review merge request diffs, discussions, approvals, and pipeline state together
- summarize recent team activity across issues, merge requests, and pipelines with `gitlab_team_delivery_digest`
- assess cross-project portfolio health with `gitlab_portfolio_delivery_overview`
- assess release readiness with `gitlab_release_readiness_check`
- triage flaky CI with `gitlab_flaky_ci_triage`
- clean up stale merge requests with `gitlab_stale_merge_request_cleanup`
- trace a failed job back to its pipeline, commit, and merge request context
- draft release notes from tags, compares, and recent delivery activity
- assess whether a project is safe for AI-assisted writes before enabling write mode
- produce a chat-ready delivery digest with markdown output
- use guided prompts instead of manually selecting low-level tools

If you want agents and other developers to discover the right tools quickly, refer to the actual MCP tool names in prompts, examples, and client instructions.

## Troubleshooting

- Run `gitlab-mcp-server doctor` first when setup behavior is unclear.
- `401 Unauthorized`: the token is invalid, expired, or using the wrong header mode.
- `403 Forbidden`: the token lacks access or the resource is outside the configured allowlists.
- `403 insufficient_scope` on write tools after `ENABLE_WRITE_TOOLS=true`: write mode is enabled, but the GitLab credential is still not authorized for writes. Use `gitlab_validate_token` or `doctor`; personal access tokens should show `api` in their scopes. For project, group, or OAuth tokens, verify the token scope and project membership directly in GitLab because PAT scope introspection may be unavailable.
- `404 Not Found`: the resource is missing or hidden by GitLab permissions.
- `429 Too Many Requests`: the GitLab rate limit was hit.
- HTTP server refuses to start on `0.0.0.0` or another non-local host: set both `MCP_HTTP_ALLOW_NON_LOCALHOST=true` and `MCP_HTTP_AUTH_TOKEN`, then restrict `MCP_HTTP_ALLOWED_HOSTS` and `MCP_HTTP_ALLOWED_ORIGINS` to trusted values.
- PAT about to expire: the `doctor` report and `gitlab_validate_token` advisory will flag short remaining lifetime when PAT introspection is available.
- Large file or diff errors: raise payload limits only when you trust the workload.
- CLI not found from source: run `npm run build` and invoke `node dist/cli.js`.

## Development

```bash
npm ci
npm run typecheck
npm test
npm run build
npm run pack:dry-run
```

Supporting docs:

- [CONTRIBUTING.md](https://github.com/DevquasarX9/mcp-gitlab/blob/main/CONTRIBUTING.md)
- [CHANGELOG.md](https://github.com/DevquasarX9/mcp-gitlab/blob/main/CHANGELOG.md)
- [docs/release-checklist.md](https://github.com/DevquasarX9/mcp-gitlab/blob/main/docs/release-checklist.md)
- [docs/](https://github.com/DevquasarX9/mcp-gitlab/tree/main/docs)

## Publishing

This repository uses npm trusted publishing from GitHub Actions through [`publish.yml`](https://github.com/DevquasarX9/mcp-gitlab/blob/main/.github/workflows/publish.yml).

Release flow:

1. Update `package.json` version.
2. Commit and push.
3. Create and push a matching tag such as `v<package-version>`.
4. Publish a GitHub Release for that tag.
5. GitHub Actions publishes the package to npm through OIDC.

Manual fallback:

```bash
npm login
npm whoami
npm run clean
npm run build
npm test
npm run pack:dry-run
npm publish --access public
```

No `NPM_TOKEN` secret is required for the default GitHub Actions release path.

## Published Package Contents

The npm tarball intentionally stays small and only publishes:

- `dist/`
- `README.md`
- `LICENSE`
- `package.json`
