Metadata-Version: 2.4
Name: gitlab-ci-mcp
Version: 0.5.1
Summary: MCP server for self-hosted GitLab — pipelines, schedules, branches, merge requests and repository files
Project-URL: Homepage, https://github.com/mshegolev/gitlab-ci-mcp
Project-URL: Issues, https://github.com/mshegolev/gitlab-ci-mcp/issues
Author: Mikhail Shchegolev
License: MIT
License-File: LICENSE
Keywords: anthropic,ci-cd,claude,devops,gitlab,mcp
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Version Control
Requires-Python: >=3.10
Requires-Dist: mcp>=1.2
Requires-Dist: python-gitlab>=4.0
Requires-Dist: urllib3>=2.0
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: responses>=0.25; extra == 'dev'
Requires-Dist: ruff>=0.5; extra == 'dev'
Description-Content-Type: text/markdown

# gitlab-ci-mcp

<!-- mcp-name: io.github.mshegolev/gitlab-ci-mcp -->

[![PyPI](https://img.shields.io/pypi/v/gitlab-ci-mcp.svg?logo=pypi&logoColor=white)](https://pypi.org/project/gitlab-ci-mcp/)
[![Python](https://img.shields.io/pypi/pyversions/gitlab-ci-mcp.svg?logo=python&logoColor=white)](https://pypi.org/project/gitlab-ci-mcp/)
[![License: MIT](https://img.shields.io/pypi/l/gitlab-ci-mcp.svg)](LICENSE)
[![Downloads](https://static.pepy.tech/badge/gitlab-ci-mcp/month)](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).
