
Gitlab
Let a coding agent inspect and drive GitLab merge requests, CI pipelines, and jobs from the terminal using your PAT and project context.
Overview
GitLab is an agent skill most often used in Ship—Launch (also Ship—Review, Build—Integrations) that calls GitLab REST API v4 for merge requests, pipelines, and jobs.
Install
npx skills add https://github.com/microsoft/hve-core --skill gitlabWhat is this skill?
- GitLab REST API v4 client focused on merge requests, pipelines, and CI jobs
- Requires GITLAB_URL and GITLAB_TOKEN; optional GITLAB_PROJECT with auto-detect from git remote
- Python 3.11+ implementation with pytest, ruff, and optional atheris fuzz harness in dev tooling
- Field-selectable output shaping for agent-friendly JSON responses
- Fits agent workflows that poll pipeline status or summarize MR state without opening the GitLab UI
- Targets GitLab REST API v4
- Requires Python >=3.11
- Dev tooling includes pytest, ruff, and optional atheris fuzz group
Adoption & trust: 25 installs on skills.sh; 1.1k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You need pipeline and merge-request status from GitLab inside an agent session without manually clicking through the GitLab UI or writing ad-hoc curl each time.
Who is it for?
Solo builders on GitLab (SaaS or self-hosted) who want agents to query CI and MR state during daily ship-and-operate loops.
Skip if: Teams on GitHub-only workflows, builders with no GitLab token or project access, or anyone who only needs local git commits without remote CI APIs.
When should I use this skill?
You need authenticated GitLab API access for merge requests, pipelines, or jobs and have GITLAB_URL plus GITLAB_TOKEN configured.
What do I get? / Deliverables
Your agent gets authenticated, structured GitLab API results for MRs, pipelines, and jobs so you can decide whether to merge, retry, or fix before shipping.
- Structured JSON API responses for merge requests, pipelines, and jobs
- Field-filtered output when the client supports selected field lists
Recommended Skills
Journey fit
Spans multiple journey phases - primary shelf plus alternate fits below.
Canonical shelf is Ship because the skill’s core value is moving code through review and CI—the steps right before and during release—not authoring product features. Launch fits pipeline and job operations that gate or execute releases; MR and pipeline checks are launch-prep and ship automation, which this client is built to call.
Where it fits
Pull open merge request metadata and pipeline status before you ask the agent to summarize whether a branch is safe to merge.
List recent pipelines and job outcomes right before a release tag to confirm green CI on main.
Wire the GitLab client into a custom agent workflow that posts structured CI snapshots into your implementation notes.
Inspect failed job details after a production deploy regression to feed a focused fix plan back into the agent.
How it compares
Use this as a lightweight agent-side GitLab API script, not as a full GitLab MCP server or as a substitute for GitLab’s own CI YAML authoring docs.
Common Questions / FAQ
Who is gitlab for?
Indie and solo developers who deploy through GitLab and want their coding agent to read merge requests, pipelines, and jobs via the official REST API.
When should I use gitlab?
Use it in Ship when reviewing MRs or checking launch pipelines, in Build when integrating agent tooling with your GitLab project, and in Operate when verifying job failures or pipeline health after deploy.
Is gitlab safe to install?
It requires a personal access token in the environment, so treat scopes and rotation seriously; review the Security Audits panel on this Prism page and limit token permissions to what MR and CI read/run needs.
SKILL.md
READMESKILL.md - Gitlab
[project] name = "gitlab-skill" version = "0.0.0" requires-python = ">=3.11" dependencies = [] [dependency-groups] dev = [ "pytest>=9.0", "pytest-cov>=7.0", "ruff>=0.15", "pytest-mock>=3.12", ] # Atheris ships manylinux-only wheels; keep separate from dev so uv sync works on macOS. fuzz = [ "atheris>=3.0", ] [tool.pytest.ini_options] testpaths = ["tests"] pythonpath = ["scripts"] python_files = ["test_*.py", "fuzz_harness.py"] [tool.ruff] line-length = 88 target-version = "py311" [tool.ruff.lint] select = ["E", "F", "I", "W"] [tool.pyright] include = ["tests", "scripts"] extraPaths = ["scripts"] pythonVersion = "3.11" venvPath = "." venv = ".venv" #!/usr/bin/env python3 # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: MIT # /// script # requires-python = ">=3.11" # /// """GitLab REST API v4 client for merge requests, pipelines, and jobs. Environment variables: GITLAB_URL: Required GitLab base URL. GITLAB_TOKEN: Required personal access token. GITLAB_PROJECT: Optional project id or path. Auto-detected from git remote. """ from __future__ import annotations import json import os import re import subprocess import sys import urllib.error import urllib.parse import urllib.request from typing import Any, Callable, NoReturn, cast EXIT_SUCCESS = 0 EXIT_FAILURE = 1 EXIT_USAGE = 2 selected_fields: list[str] | None = None gitlab_url = "" gitlab_token = "" api_url = "" sys.dont_write_bytecode = True def die(message: str, exit_code: int = EXIT_FAILURE) -> NoReturn: """Print an error and raise SystemExit. Args: message: Error text to print. exit_code: Process exit code. Returns: Never returns. The annotation is kept simple for CLI usage. """ print(f"error: {message}", file=sys.stderr) raise SystemExit(exit_code) def require_environment() -> None: """Load and validate required environment variables.""" global api_url global gitlab_token global gitlab_url gitlab_url = os.environ.get("GITLAB_URL", "") gitlab_token = os.environ.get("GITLAB_TOKEN", "") if not gitlab_url: die("GITLAB_URL is not set", EXIT_USAGE) if not re.match(r"^https?://", gitlab_url): die( "GITLAB_URL must start with https:// (or http:// for local dev)", EXIT_USAGE, ) if not gitlab_token: die("GITLAB_TOKEN is not set", EXIT_USAGE) api_url = gitlab_url.rstrip("/") + "/api/v4" def strip_git_suffix(path: str) -> str: """Remove a trailing .git suffix when present.""" if path.endswith(".git"): return path[:-4] return path def project() -> str: """Resolve the target GitLab project from environment or git remote.""" configured_project = os.environ.get("GITLAB_PROJECT", "") if configured_project: return urllib.parse.quote(configured_project, safe="") try: remote_url = subprocess.check_output( ["git", "remote", "get-url", "origin"], stderr=subprocess.DEVNULL, text=True, ).strip() except (subprocess.CalledProcessError, FileNotFoundError): die("GITLAB_PROJECT not set and no git remote found", EXIT_USAGE) if remote_url.startswith("git@"): path = remote_url.split(":", 1)[1] elif re.match(r"^https?://", remote_url): path = re.sub(r"^https?://[^/]*/", "", remote_url) else: die(f"cannot parse git remote URL: {remote_url}", EXIT_USAGE) path = strip_git_suffix(path) if not path: die(f"cannot extract project path from remote: {remote_url}", EXIT_USAGE) return urllib.parse.quote(path, safe="") def validate_numeric_id(value: str) -> None: """Validate that a CLI argument is a numeric identifier.""" if not re.match(r"^\d+$", value): die(f"expected numeric ID, got: {value}", EXIT_USAGE) def validate_positive_int(value: str, label: str = "value") -> None: """Validate that a CLI argument is a posi