jenkins-mcp
# Jenkins MCP
A single-file [MCP](https://modelcontextprotocol.io) server that exposes a Jenkins
instance (plus a thin GitLab layer) as tools an LLM can call. It lets Claude Code — or any
MCP client — list jobs, trigger and watch deployments, read build logs, and answer
"is this ticket deployed yet?" without anyone touching the Jenkins UI.
Built on `FastMCP` from the `mcp` SDK. Python ≥ 3.10. One module: `server.py`.
---
## What it can do
The tools are layered — higher tiers compose the lower ones.
**Low-level Jenkins**
| Tool | Purpose |
|------|---------|
| `list_jobs` | List jobs (with status) at the root or in a folder |
| `get_job_info` | Job details: parameters (with defaults/types), last build numbers, health |
| `trigger_build` | Start a build with **raw** Jenkins parameter names |
| `get_queue` / `get_queue_item` | Inspect the build queue |
| `wait_for_build` | Block until a queued item gets a build number |
| `get_build_info` | Result, timestamp, duration, parameters, git revision of a build |
| `get_build_log` | Console output (tail) for a build |
| `list_recent_builds` | Recent builds for a job |
| `get_last_successful_build` | Last green build of a job |
| `is_deployed_since` | Did a build run after a given timestamp? |
**High-level deploy** (friendly args → whatever raw params the job actually defines)
| Tool | Purpose |
|------|---------|
| `deploy` | Deploy a job by `branch` / `tickets`, etc. Maps friendly names to real Jenkins params. A bare environment defaults to the configured **app** job (`dev` → `dev_app_deployment`; say `dev backend` for the backend job). Accepts multiple comma-separated ticket URLs. Refuses to deploy a job that mandates a deployment purpose until one is supplied (or `use_dummy_purpose=True`), and warns if a supplied URL is a **merge request** rather than a ticket (override with `allow_mr_purpose=True`). |
| `deploy_tickets` | Deploy by environment + scope from a set of ticket URLs |
| `list_deployments` | The configured `(env, scope) → job` table |
| `resolve_job_name` | Fuzzy-resolve a rough name (`"dev backend"`) to the real job |
**GitLab ticket verification**
| Tool | Purpose |
|------|---------|
| `get_ticket_mrs` | Find MRs linked to a ticket |
| `is_ticket_deployed` | Check whether a ticket's merge commits are in the deployed branch tip |
---
## Prerequisites
- Python ≥ 3.10
- Network access to your Jenkins controller (and to your GitLab instance for the ticket tools)
- A Jenkins **API token** (Jenkins → user → Configure → API Token)
- A GitLab **access token** with `read_api` on the relevant project (only needed for the
ticket-deployment tools)
---
## Configuration
Two pieces: credentials in `.env`, and your site's job mapping in `config.json`.
### Credentials
Copy the template and fill it in. `.env` is git-ignored — never commit it.
```bash
cp .env.example .env
```
| Variable | Required | Notes |
|----------|----------|-------|
| `JENKINS_URL` | yes | e.g. `https://jenkins.example.com` |
| `JENKINS_USER` | yes | Your Jenkins username |
| `JENKINS_TOKEN` | yes | Jenkins API token (not your password) |
| `GITLAB_URL` | for GitLab tools | e.g. `https://gitlab.example.com` |
| `GITLAB_PROJECT` | for GitLab tools | e.g. `your-group/your-project` |
| `GITLAB_TOKEN` | for GitLab tools | Falls back to a token found in `~/.claude.json` if unset |
| `JENKINS_MCP_CONFIG` | no | Path to the job mapping. Defaults to `./config.json` |
| `MCP_TRANSPORT` | hosted mode | `stdio` (default) or `streamable-http` |
| `MCP_HOST` / `MCP_PORT` | hosted mode | Defaults `0.0.0.0` / `8765` |
| `MCP_AUTH_TOKEN` | hosted mode | Shared bearer token for the HTTP endpoint. **Set this** when running HTTP — without it the endpoint is unauthenticated and the server logs a warning |
Generate a bearer token with:
```bash
python -c "import secrets; print(secrets.token_urlsafe(32))"
```
### Job mapping
The Jenkins tools are generic. The `deploy` tools need to know your job names and the
parameter names those jobs define:
```bash
cp config.example.json config.json # then edit; config.json is git-ignored
```
`env_jobs` maps `"<environment>/<scope>"` to a Jenkins job name; `param_map` maps friendly
argument names to the raw Jenkins parameters to set. Without a config file the low-level
Jenkins tools still work and `deploy` reports that no mapping is configured.
---
## Setup A — local (stdio)
Run the server as a subprocess your MCP client spawns over stdio. Simplest for single-user use.
```bash
python3 -m venv .venv
./.venv/bin/pip install -e .
cp .env.example .env # then edit
cp config.example.json config.json # then edit
./.venv/bin/python server.py # smoke test — Ctrl-C to stop
```
Register it with your MCP client. For Claude Code, add to `.mcp.json` or `~/.claude.json`:
```json
{
"mcpServers": {
"jenkins-mcp": {
"command": "/absolute/path/to/jenkins-mcp/.venv/bin/python",
"args": ["/absolute/path/to/jenkins-mcp/server.py"]
}
}
}
```
Leave `MCP_TRANSPORT` unset (defaults to `stdio`). The `.env` beside `server.py` supplies
the credentials.
---
## Setup B — hosted (HTTP), shared by a team
Run it as a `systemd` service so everyone's client talks to one shared instance instead of
each person running their own.
```bash
git clone <your-fork-url> jenkins-mcp
cd jenkins-mcp
cp .env.example .env # set JENKINS_*, GITLAB_*, MCP_TRANSPORT, MCP_AUTH_TOKEN
cp config.example.json config.json # set your job mapping
bash deploy/install.sh # venv, systemd unit, start
```
`deploy/install.sh` is idempotent: it builds `.venv`, installs the package, copies
`deploy/jenkins-mcp.service` to `/etc/systemd/system/`, enables it and starts it. Edit the
unit's `User` and paths to suit your host — it ships pointing at `/opt/jenkins-mcp`.
For HTTP mode your `.env` needs:
```ini
MCP_TRANSPORT=streamable-http
MCP_HOST=0.0.0.0
MCP_PORT=8765
MCP_AUTH_TOKEN=<token from secrets.token_urlsafe(32)>
```
Bind to `127.0.0.1` and front it with a reverse proxy if the host is reachable beyond your
trusted network.
### Client config for the hosted server
```json
{
"mcpServers": {
"jenkins-mcp": {
"type": "http",
"url": "http://your-server:8765/mcp",
"headers": { "Authorization": "Bearer <MCP_AUTH_TOKEN>" }
}
}
}
```
---
## Updating a hosted instance
The host runs from a git clone, so updates are pull-and-restart:
```bash
cd ~/jenkins-mcp
git pull
./.venv/bin/pip install -e . # only if dependencies changed
sudo systemctl restart jenkins-mcp.service
journalctl -u jenkins-mcp -f # watch it come back up
```
---
## Tests
```bash
./.venv/bin/pip install -e ".[dev]"
./.venv/bin/python -m pytest -q
```
The suite covers CSRF crumb caching and 403 retry, deploy-target resolution, the
deployment-purpose gate, and the pure helpers. It runs without a live Jenkins.
---
## Notes
- Jenkins ties each CSRF crumb to a session, so all traffic goes through one
`requests.Session`. A 403 on POST is retried exactly once with a fresh crumb.
- Job names are resolved with an exact → normalized → substring → close-match cascade
against a 60-second-cached job list.
- `deploy` refuses to run a job whose `DEPLOYMENT_PURPOSE` parameter is a validating
string until a purpose is supplied, and warns when the supplied URL looks like a merge
request rather than a ticket.
## License
MIT — see [LICENSE](LICENSE).
TDQS
Scored across 17 tools
Most tools target distinct resources, but there is real overlap among queue-related tools (get_queue_item vs wait_for_build vs get_queue) and deploy helpers (deploy vs deploy_tickets vs trigger_build). The detailed descriptions help disambiguate, but some tools could still be confusing to an agent.
Tool names use consistent snake_case and mostly follow a verb_noun pattern: list_* and get_* for reads, trigger_build, deploy, resolve_job_name for actions. The mix of list/get and imperative verbs is minor and still predictable.
17 tools is slightly above the ideal 3-15 range, but the count is justified by combining core Jenkins introspection with deployment-specific and ticket-verification workflows. Each tool has a reasonably distinct niche.
The toolset covers job discovery, build triggering, queue monitoring, build results, logs, deployment configuration, and ticket-based deployment verification. Minor gaps such as cancel/abort build or job CRUD exist, but the core CI/CD and deployment workflows are well covered.