Release
FreeNot checkedEnables users to manage multi-repo releases on GitLab through natural language: start, reconcile, approve, bump, and explain releases without blocking pipelines
About
Enables users to manage multi-repo releases on GitLab through natural language: start, reconcile, approve, bump, and explain releases without blocking pipelines.
README
Deterministic, non-blocking release preparation across a fleet of GitLab repositories —
a reconciler engine over a declarative manifest, exposed as a CLI (release) and an
MCP server (release-mcp) so you can drive releases from GitHub Copilot Chat, Claude,
or any other MCP client in plain language.
Built for the reality of multi-repo products: libraries that must publish before services
can pin them, release branches with iterated tags (v1.0.0-1, -2, ...), a QA gate, a
deploy repo that pins the final versions — and CI pipelines that take 40–50 minutes.
The engine never waits: every command returns in seconds, pipelines run on their own time,
and an idempotent reconcile advances whatever is ready whenever you come back.
How it works
- Manifest (
release-manifest.yaml) — declares your repos, their dependency DAG, which files to edit (maven properties, python dependencies, yaml keys), and what variables each pipeline needs. See examples/release-manifest.yaml. The engine hard-codes zero product knowledge — adoption is pure configuration. - Run state — one JSON per release, stored in a small dedicated GitLab project. Shared visibility, full history, optimistic locking; any teammate can resume any release.
- Reconciler — walks the DAG; per node: create branch → commit version pins → cut tag → trigger pipeline (via the API, so per-release variables travel with the build) → poll → record produced versions → unblock dependents. Nothing is ever created twice.
- Gates — a manual checkpoint (e.g.
qa-signoff) the engine will not pass without an explicitrelease approve. - Captures & report — manifest-declared regexes grep job logs for values of interest
(Sonar URLs, image digests);
release reportassembles the whole release — tags, pipelines, versions, captured values — into markdown, optionally published to the state repo (reports/<coordinate>.md). - No LLM in the engine — ever.
release explaindeterministically fetches a failed job's log tail; the model on the client side (Copilot, Claude, ...) interprets it in the same chat.ports.LogExplainerstays as an extension point if you want a hosted model, but nothing requires one.
Install
Requires Python 3.13+ and uv.
git clone <this repo> && cd release_agent
uv sync
uv run release --help
Configure
Everything is environment variables (per-developer PAT model):
| Variable | Required | Meaning |
|---|---|---|
RELEASE_AGENT_GITLAB_URL |
yes | GitLab base URL, e.g. https://gitlab.example.com |
RELEASE_AGENT_GITLAB_TOKEN |
yes | PAT with api scope (falls back to GITLAB_TOKEN) |
RELEASE_AGENT_BOT_TOKEN |
no | Second identity used to approve MRs on repos with merge_request: { bot_approve: true } |
RELEASE_AGENT_STATE_PROJECT |
yes* | Project holding run states + manifest, e.g. group/release-state |
RELEASE_AGENT_STATE_BRANCH |
no | Branch in the state project (default main) |
RELEASE_AGENT_MANIFEST_PATH |
no | Manifest path in the state project (default release-manifest.yaml) |
RELEASE_AGENT_MANIFEST_FILE |
no | Local manifest file (overrides the state project copy) |
RELEASE_AGENT_STATE_DIR |
yes* | Local state directory instead of a state project (single-user/dev) |
* one of STATE_PROJECT / STATE_DIR is required.
Use — CLI
release start 1.0.0 --env sit \
-i jar_bundle_1_version=1.0.1 -i pydantic_version=10.0.0 -i jar_bundle_2_version=2.3.0
release status 1.0.0 # render the DAG, tags, pipeline URLs
release reconcile 1.0.0 # idempotent tick — run it whenever, it never double-fires
release bump 1.0.0 service-a # next vX.Y.Z-(N+1) after a QA fix
release approve 1.0.0 qa-signoff # clear the manual gate
release explain 1.0.0 service-a # advisory: why did the pipeline fail?
Use — MCP (Copilot Chat, Claude, ...)
Register the stdio server with your client, e.g. VS Code .vscode/mcp.json:
{
"servers": {
"release-agent": {
"type": "stdio",
"command": "uv",
"args": ["run", "--directory", "/path/to/release_agent", "release-mcp"],
"env": {
"RELEASE_AGENT_GITLAB_URL": "https://gitlab.example.com",
"RELEASE_AGENT_GITLAB_TOKEN": "${input:gitlab-token}",
"RELEASE_AGENT_STATE_PROJECT": "group/release-state"
}
}
}
}
Then just talk: "start release 1.0.0 on sit with jar-bundle-2 2.3.0, jar-bundle-1 jar 1.0.1,
pydantic 10.0.0" → the model calls release_start; later, "advance the release" →
release_reconcile. Tools exposed: release_start, release_status, release_reconcile,
release_approve, release_bump, release_explain, get_manifest.
Prompt cookbook
| You say | Tool the model calls |
|---|---|
| "Start release 1.2.0 on sit — jar-bundle-1 jar 1.0.2, pydantic 10.1.0, jar-bundle-2 2.4.0" | release_plan (dry-run shown for confirmation) → release_start |
| "What exactly would starting 1.2.0 do?" | release_plan — branches, MRs, predicted tags, edits, variables; nothing touched |
| "Where's release 1.2.0?" / "Did the jar-bundle-2 build finish?" | release_status |
| "Advance the release" / "Pipelines look done, continue" | release_reconcile (idempotent — always safe) |
| "Why is service-a stuck?" | release_status → explains e.g. an AWAITING_MERGE MR with its link |
| "QA signed off, approve the release" | release_approve |
| "Why did service-b fail?" | release_explain → model interprets the failed job's log |
| "Fix is merged on service-b's release branch, rebuild it" | release_bump (warns if the deploy repo pinned the old tag) |
| "Which files get edited during a release? What depends on what?" | get_manifest |
| "What was the last released version of service-a?" | release_tags — reads the repo's tags live, no run state needed |
| "What releases are in flight?" / "What did we ship last?" | list_releases — all coordinates with progress + attention flags |
| "Here's my updated manifest — is it valid?" | validate_manifest — full validation before you commit it |
| "Take 1.2.0 as far as it can go and tell me what's blocking" | chains reconcile → status → summary |
| "Give me the release report" / "…and publish it" | release_report — tags, pipelines, versions, log-captured values (e.g. Sonar URLs) |
Habits that keep it reliable: always name the coordinate ("release 1.2.0") so the model never guesses which release you mean, and ask to "advance" freely — reconcile never double-fires, so an over-eager prompt costs nothing.
Adapt to your product
- Copy examples/release-manifest.yaml and describe your repos, tiers, edits, and pipeline variables.
- Create a
release-stateproject in GitLab, commit the manifest there. - Gate your CI release/publish jobs to run only from API-triggered tag pipelines
(
$CI_COMMIT_TAG && $CI_PIPELINE_SOURCE == "api"— purely additive, design §9). On GitLab 17.7+ also set each project's "Minimum role to use pipeline variables" todeveloper, or API-triggered pipelines with variables are rejected with HTTP 400. - Need a new file mutation? Add one function to
EDIT_KINDSin src/release_agent/core/edits.py. - Have an LLM endpoint? Implement
ports.LogExplainerand wire it in src/release_agent/bootstrap.py.
Development
uv run pytest # unit + engine tests against an in-memory fake GitLab
uv run ruff check .
License
MIT
Install Release in Claude Desktop, Claude Code & Cursor
unyly install release-mcpInstalls into Claude Desktop, Claude Code, Cursor & VS Code — handles npx, uvx and build-from-source repos for you.
First time? Get the CLI: curl -fsSL https://unyly.org/install | sh
Or configure manually
Run in your terminal:
claude mcp add release-mcp -- uvx --from git+https://github.com/stevedevex/orchestrator release-agentFAQ
Is Release MCP free?
Yes, Release MCP is free — one-click install via Unyly at no cost.
Does Release need an API key?
No, Release runs without API keys or environment variables.
Is Release hosted or self-hosted?
A hosted option is available: Unyly runs the server in the cloud, no local setup required.
How do I install Release in Claude Desktop, Claude Code or Cursor?
Open Release on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.
Related MCPs
GitHub
PRs, issues, code search, CI status
by GitHubFilesystem
Secure file operations with configurable access controls.
Memory
Knowledge graph-based persistent memory system.
Template MCP Server
A CLI tool to create a new Model Context Protocol server project with TypeScript support, dual transport options, and an extensible structure
by mcpdotdirectCompare Release with
Not sure what to pick?
Find your stack in 60 seconds
Author?
Embed badge for your README
Browse similar
All development MCPs
