gitlab-ci-mcp
# gitlab-ci-mcp
<!-- mcp-name: io.github.mshegolev/gitlab-ci-mcp -->
[](https://pypi.org/project/gitlab-ci-mcp/)
[](https://pypi.org/project/gitlab-ci-mcp/)
[](LICENSE)
[](https://pepy.tech/project/gitlab-ci-mcp)
MCP server for GitLab CI/CD. Lets an LLM agent (Claude Code, Cursor, OpenCode, DevX Agent, etc.) work with pipelines, jobs, schedules, branches, tags, merge requests and repository files.
Python, [FastMCP](https://github.com/modelcontextprotocol/python-sdk), stdio transport.
Works with any GitLab — SaaS `gitlab.com` or self-hosted / on-prem. Designed with corporate networks in mind: configurable `NO_PROXY` handling, optional SSL-verify toggle, per-project scoping via env vars.
## Design highlights
- **Tool annotations** — every tool carries `readOnlyHint` / `destructiveHint` / `idempotentHint` / `openWorldHint` so MCP clients can classify operations (e.g. ask for confirmation only on destructive ones like `gitlab_merge_mr`, `gitlab_delete_schedule`).
- **Structured output on every tool** — each tool declares a TypedDict return type, so FastMCP auto-generates an `outputSchema` and every result carries `structuredContent` alongside a pre-rendered markdown text block. Clients that can render structured data use it; agents that prefer compact text get the markdown. No `response_format` parameter needed.
- **Structured errors** — authentication, 404, 403, 429 (rate-limit), 5xx, missing-env errors are converted to actionable `ToolError` messages (e.g. _"GitLab authentication failed… verify GITLAB_TOKEN has `api` scope"_) and surfaced as `isError=True` results.
- **Pydantic input validation** — every argument has typed constraints (ranges, lengths, literals) auto-exposed as JSON Schema.
- **Project scoping per call** — every tool accepts an optional `project_path` that overrides `GITLAB_PROJECT_PATH` for cross-project queries.
- **Pagination** — list tools return a `pagination` block with `page`, `total`, `has_more`, `next_page` and a next-page hint in the markdown footer.
- **MCP Context integration** — `gitlab_pipeline_health` and `gitlab_get_job_log` are `async` and emit `info` logs / `report_progress` events through the MCP Context so clients can show progress bars.
- **MCP Resources** — `gitlab://project/info` and `gitlab://project/ci-config` mirror common lookups for clients that prefer the Resource model over tools.
- **Lifespan management** — `python-gitlab` HTTP sessions are closed cleanly on server shutdown via an `asynccontextmanager` lifespan hook.
- **Log grep** — `gitlab_get_job_log` accepts `grep_pattern` + `grep_context` (surrounding lines) for regex-filtering megabyte-scale CI logs without pulling the whole trace into agent context.
### Threading model
FastMCP automatically runs synchronous tools in a worker thread
(`anyio.to_thread.run_sync`), so they do not block the asyncio event loop —
`python-gitlab` is a synchronous library and wrapping every call in
`asyncio.to_thread` ourselves would be ceremony. Tools that benefit from the
MCP Context (progress, info logs) are written as `async def` and explicitly
wrap `python-gitlab` calls with `asyncio.to_thread`.
## Features
23 tools covering the everyday CI/CD surface:
**Pipelines**
`gitlab_list_pipelines` · `gitlab_get_pipeline` · `gitlab_get_pipeline_jobs` · `gitlab_get_job_log` · `gitlab_trigger_pipeline` · `gitlab_retry_pipeline` · `gitlab_cancel_pipeline` · `gitlab_pipeline_health`
**Schedules**
`gitlab_list_schedules` · `gitlab_create_schedule` · `gitlab_update_schedule` · `gitlab_delete_schedule`
**Branches & tags**
`gitlab_list_branches` · `gitlab_list_tags` · `gitlab_compare_branches`
**Merge requests**
`gitlab_list_merge_requests` · `gitlab_get_merge_request` · `gitlab_get_merge_request_changes` · `gitlab_create_merge_request` · `gitlab_merge_mr`
**Repository & project**
`gitlab_get_file` · `gitlab_list_repository_tree` · `gitlab_project_info`
### Pipeline health report
`gitlab_pipeline_health` returns a ready-to-read summary over 7/30 days:
```
Last 7d: 96.4% up | 27/28 success
Last 30d: 92.1% | 105/114 success
Last 10: success success success failed success ...
```
Handy for on-call / triage: `покажи health master за последние 7 дней`.
## Installation
Requires Python 3.10+.
```bash
# via uvx (recommended)
uvx --from gitlab-ci-mcp gitlab-ci-mcp
# or via pip/pipx
pipx install gitlab-ci-mcp
```
## Configuration
All config is via environment variables:
| Variable | Required | Description |
| --- | --- | --- |
| `GITLAB_URL` | **yes** | Base URL, e.g. `https://gitlab.example.com` |
| `GITLAB_TOKEN` | **yes** | Personal Access Token with `api` scope |
| `GITLAB_PROJECT_PATH` | **yes** | Default project, e.g. `my-org/my-repo` |
| `GITLAB_SSL_VERIFY` | no | `true` (default) / `false` |
| `GITLAB_NO_PROXY_DOMAINS` | no | Comma-separated domains to add to `NO_PROXY` (useful in corp networks behind a local HTTP proxy — e.g. `.corp.example.com,gitlab.internal`) |
Every tool accepts an optional `project_path` arg that overrides `GITLAB_PROJECT_PATH` per call — useful for cross-project queries.
## Claude Code
**Full walkthrough:** [docs/claude-code.md](docs/claude-code.md) — prerequisites, two install paths, multi-project setup, self-hosted GitLab behind a corp proxy, troubleshooting, uninstall.
Short version:
```bash
claude mcp add gitlab uvx --from gitlab-ci-mcp gitlab-ci-mcp \
--env GITLAB_URL=https://gitlab.example.com \
--env GITLAB_TOKEN=glpat-xxxxxx \
--env GITLAB_PROJECT_PATH=my-org/my-repo
```
Or in `~/.claude.json` / project `.mcp.json`:
```json
{
"mcpServers": {
"gitlab": {
"type": "stdio",
"command": "uvx",
"args": ["--from", "gitlab-ci-mcp", "gitlab-ci-mcp"],
"env": {
"GITLAB_URL": "https://gitlab.example.com",
"GITLAB_TOKEN": "${GITLAB_TOKEN}",
"GITLAB_PROJECT_PATH": "my-org/my-repo",
"GITLAB_SSL_VERIFY": "true"
}
}
}
}
```
Check:
```bash
claude mcp list
# gitlab: uvx --from gitlab-ci-mcp gitlab-ci-mcp - ✓ Connected
```
## Cursor / OpenCode / DevX Agent
Same idea — point the MCP config to `uvx --from gitlab-ci-mcp gitlab-ci-mcp` with the env vars above. See each tool's own MCP config syntax.
## Example prompts
```
что сломалось в последнем pipeline master
```
```
покажи health master за 7 дней для проекта my-org/other-repo
```
```
создай MR из feature/foo в master с title "feat: foo"
```
```
покажи содержимое .gitlab-ci.yml из master
```
## Rate limits & connection reuse
GitLab enforces a per-user rate limit (typically 2000 req/h for the REST API,
configurable by the admin — see your instance's `/admin/application_settings/network`).
- The server caches one `python-gitlab` HTTP session **per `project_path`**, so
repeated tool calls against the same project reuse the connection and do not
re-authenticate each time.
- List tools default to `per_page=20` to keep a single call within a small
number of API requests.
- If you hit a `429 Too Many Requests`, the error handler returns an actionable
message — wait and try again with larger `per_page` or fewer calls.
## Self-hosted GitLab behind a corporate proxy
When your laptop has a local HTTP proxy (e.g. `http://127.0.0.1:3128` for corp web access) but GitLab is on the intranet, the proxy intercepts and kills internal requests. Two options:
1. Set `GITLAB_NO_PROXY_DOMAINS` — the server will add them to `NO_PROXY` at startup **and clear `HTTP_PROXY`/`HTTPS_PROXY` from its own process** so they don't affect GitLab traffic.
2. Pass explicitly empty `HTTP_PROXY=""` etc. in the MCP `env` section.
## Development
```bash
git clone https://github.com/mshegolev/gitlab-ci-mcp
cd gitlab-ci-mcp
python -m venv .venv && . .venv/bin/activate
pip install -e '.[dev]'
pytest
```
Run the server directly (stdio transport, waits on stdin for MCP messages):
```bash
GITLAB_URL=... GITLAB_TOKEN=... GITLAB_PROJECT_PATH=... gitlab-ci-mcp
```
## License
MIT — see [LICENSE](LICENSE).
## Acknowledgements
Built on [python-gitlab](https://github.com/python-gitlab/python-gitlab) and the [MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk).
TDQS
Scored across 23 tools
Each tool targets a distinct resource and action, with clear descriptions preventing confusion even for similar operations like `gitlab_get_merge_request` vs `gitlab_get_merge_request_changes` or `gitlab_trigger_pipeline` vs `gitlab_retry_pipeline`.
All tool names follow a consistent `gitlab_verb_noun` pattern in snake_case, making them predictable and easy to understand across the entire set.
With 23 tools, the count is higher than typical but still justified by the breadth of GitLab CI/CD features covered (pipelines, MRs, schedules, repository operations). No tools feel redundant.
The tool set covers core CI/CD workflows well, including pipeline lifecycle, merge request management, schedules, and file operations. Minor gaps exist, such as lacking a dedicated branch creation tool or merge request update tool, but these are not critical.