Repository navigation
Releases
v0.5.5 — per-user PATs and server hardening
Added
- Opt-in Streamable HTTP transport for shared deployments. Every request uses
the caller's GitLab PAT fromX-GitLab-Token; stdio remains the default and
continues to useGITLAB_TOKEN. - Per-user manager/session isolation keyed by GitLab URL, project, and an
HMAC-only PAT fingerprint, with bounded cache eviction and session cleanup. - Release workflow runs pytest and Ruff against the exact tag commit before
publishing through PyPI Trusted Publisher/OIDC. - Explicit update check: disabled by default and enabled only with
MCP_GITLAB_UPDATE_CHECK_ENABLED=true.
Fixed
- Pipeline health now distinguishes API failures, incomplete pagination,
malformed dates, and insufficient comparison data. - Schedule variable failures and partial writes are reported instead of
appearing successful. - Job log regex and output sizes are bounded; MR diff truncation is explicit.
- GitLab tools require explicit project scope unless the legacy fallback is
explicitly enabled.
v0.5.4 — documentation cleanup
Docs-only release: no change under src/ behaviour.
Changed
docs/claude-code.md: troubleshooting heading uses a generic*.example.comhost instead of an internal domain.
v0.5.3 — CI and entrypoint coverage
Housekeeping release: no change under src/; adds CI and tests only.
Added
- CI actually runs the tests. New
.github/workflows/test.yml(pytest +
ruff checkon Python 3.10/3.11/3.12, on every push and PR tomain). Until
now the only workflow waspublish.yml, which builds and uploads on av*
tag without ever invoking pytest or ruff — which is how 0.5.1 went out green
while onemcprelease away from never starting.ruff format --checkis
intentionally not in the workflow: it flags 5 files of pre-existing drift
from a newer ruff, and a gate that is red on day one gets disabled on day two. - Entry-point smoke tests (
tests/test_entrypoint.py, 8 tests). Covers the
path a real MCP client walks and no other test touched:console_scripts
metadata →gitlab_ci_mcp.server:main→mcp.run()→ stdio → clean exit on
EOF, plus protocol-levellist_tools()/list_resources()and a check that
the server builds with no credentials in the environment. Offline; no GitLab,
no token. Verified mutationally: makingmain()fail at runtime leaves the
previous 21 tests andruff checkgreen and turns only these red.
Changed
[tool.pytest.ini_options]inpyproject.tomlanchors pytest's rootdir at
the repo, so a straypytest.iniin a parent directory can no longer inject
itsaddoptsand break collection before a test runs.
v0.5.2 — MCP SDK compatibility fix
Fixed
- Pin
mcp>=1.2,<2.mcp 2.0removedmcp.server.fastmcp, which this
server imports in six modules. Without an upper bound a clean
pip install gitlab-ci-mcpresolvedmcp 2.0and thegitlab-ci-mcp
console script died on import with
ModuleNotFoundError: No module named 'mcp.server.fastmcp'— from an MCP
client this looked like an opaque transport failure, easy to misread as a
credentials or URL problem. Test collection failed for the same reason. __version__ingitlab_ci_mcp/__init__.pywas stuck at0.5.0and is now
back in sync withpyproject.tomlandserver.json.
v0.5.1 — MCP Registry metadata
Added
mcp-name: io.github.mshegolev/gitlab-ci-mcpmarker in README —
required for ownership verification when publishing to the official
MCP Registry.
v0.5.0 — structured outputs and modular server
Big one. Every tool is now a fully typed MCP citizen: outputSchema,
structuredContent, and the markdown text block you liked are now all
present on every response.
Added
- TypedDict output schemas for all 23 tools (new
models.py) —
FastMCP auto-generates anoutputSchemafrom the return annotation
and exposes the typed payload asstructuredContenton every tool
result. - Dual-channel responses via
CallToolResult— thecontent
block still carries the compact markdown rendering (context-efficient
for agents), whilestructuredContentcarries the full validated
payload (for clients that can render / process structured data). - New
output.pywithok()(builds theCallToolResult) and
fail()(raisesToolErrorwrapping the actionable error
message). All 23 tools share this pattern.
Changed
- Server split into domain modules: the 1500-line
server.pyis
now a thin composition point; tools live undertools/
(pipelines.py,schedules.py,branches_tags.py,mrs.py,
repo.py), MCP resources inresources.py, and shared FastMCP
instance + lifespan + helpers in_mcp.py. - Shared helpers
_get_ci/_ts/_is_secret_key/
_mask_variablesrenamed without the leading underscore and moved
to_mcp(they are no longer single-module private). - Error path is now uniform: tools raise
ToolError(errors.handle(…))
and FastMCP reports the result asisError=True— no more
string-based error returns.
Removed
response_formatparameter (breaking): no longer needed because
every tool now returns both markdown and structured data. Existing
callers that passedresponse_format='markdown'or'json'must
drop the argument — markdown remains the text content by default,
structured data is onresult.structuredContent.- Dead code from
ci_manager.py:wait_for_pipeline,
ScheduleCompareResult(unused from the MCP path).
Migration notes (v0.4.x → v0.5.0)
- Tools no longer return plain strings. If you call them programmatically,
inspectresult.structuredContentfor the typed payload and
result.content[0].textfor the markdown summary. - Module imports moved:
from gitlab_ci_mcp.server import mcpstill
works, but tool functions now live undergitlab_ci_mcp.tools.*.
v0.4.0 — lifespan, MCP Context, Resources
Closes the last four optional items from the /mcp-builder best-practices checklist.
- Lifespan management —
asynccontextmanagerhook cleans uppython-gitlabHTTP sessions and the manager cache on shutdown. - MCP Context integration —
gitlab_pipeline_healthandgitlab_get_job_logare nowasync defand emitctx.info/ctx.report_progressevents. Clients with progress UIs (Claude Code, Cursor) will show a progress bar while the 30-day history is being fetched. - MCP Resources — two URI templates for clients that prefer Resources over tools:
gitlab://project/info— markdown snapshot of the default projectgitlab://project/ci-config— raw.gitlab-ci.yml
- Threading model documented — FastMCP auto-runs sync tools in a worker thread (
anyio.to_thread.run_sync), so not converting every tool toasync defis an intentional choice, not an oversight. README has a new section spelling this out.
Package health: 16 tests (9 smoke + 7 mock), ruff clean, all 23 tools + 2 resources registered.
v0.3.1 — tool examples and usage guidance
Added
Examplessection on every remaining tool docstring — all 23 tools now
include "Use when / Don't use when" guidance for LLM tool-picking.CHANGELOG.md.- README section on rate limits and connection reuse.
v0.3.0 — pagination, mock tests, evaluations
Closes four remaining gaps against the MCP server best-practices checklist.
Pagination
- New
paginationmodule with a uniformpaginationblock (page, per_page, total, total_pages, next_page, has_more) extracted frompython-gitlablist results. gitlab_list_pipelines,gitlab_list_branches,gitlab_list_tags,gitlab_list_merge_requests,gitlab_list_repository_treenow takepageand return the pagination block.- Markdown formatters include a footer line with a next-page hint.
Docstring Examples
- Added
Examples:section to 14 tool docstrings — 'Use when … → args' / 'Don't use when … use <other_tool> instead'. Improves LLM tool-picking accuracy.
Mock tests
- New
tests/test_tools_mocked.pywith 7 tests using theresponseslibrary — happy paths (JSON + markdown), pagination, 401, 404, missing env, project info. Guards against shape regressions inpython-gitlabupgrades.
Evaluations
evaluations/questions.xml— 22 tool-picking QA pairs (one per tool) for agent evaluation.evaluations/README.md— runner snippet + fixture-based evaluation notes.
Total: 13 tests (6 smoke + 7 mock). Ruff clean. 23 tools, 1 pagination helper, 2 test files.
v0.2.0 — best-practice refactor
Refactor pass aligning the server with the MCP server best practices checklist.
Highlights
- Tool annotations on every tool — `readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`. MCP clients can classify operations and ask for confirmation only where it matters (merge, delete, cancel, update-with-variables).
- Dual response format — every read tool now accepts `response_format='markdown'` (default, compact) or `'json'` (full raw structure).
- Actionable error messages — `errors.handle()` converts `GitlabAuthenticationError`, 404/403, rate-limits, config errors into messages that tell the agent what to do next (check `GITLAB_TOKEN` scope, verify project path, etc.) instead of raw tracebacks.
- Pydantic input validation — all args use `typing.Annotated` + `pydantic.Field` with `description`, `min_length`/`max_length`, `ge`/`le`, `Literal[...]` for enums. JSON Schema auto-generated for MCP clients.
- Markdown formatters for pipelines, jobs, schedules, branches, tags, MRs, MR changes, repo tree, file content, diff, project info.
- Server name bumped from `'gitlab'` to `'gitlab_ci_mcp'` to match `{service}_mcp` convention.
Compatibility
- Python `>=3.10` (unchanged)
- Config env vars unchanged: `GITLAB_URL`, `GITLAB_TOKEN`, `GITLAB_PROJECT_PATH`, `GITLAB_SSL_VERIFY`, `GITLAB_NO_PROXY_DOMAINS`
- All tool names unchanged, only new optional `response_format` parameter on read tools
Install
```bash
uvx --from gitlab-ci-mcp gitlab-ci-mcp
or
pipx install gitlab-ci-mcp
```