Jenkins MCP server
Read-first Jenkins MCP server in Go for agent-driven build debugging. 20 tools including compare_builds, flaky-test detection, JUnit/Ginkgo failure parsing, and disk-cached console logs with on-disk path handoff. Write tools (trigger/stop/cancel) gated by JENKINS_MCP_READONLY env var.
Documentation
jenkins-mcp-go
A focused, fast Model Context Protocol (MCP) server for Jenkins, written in Go.
Connect Jenkins to Claude Desktop, Claude Code, Cursor, or any MCP-compatible AI agent: fetch console logs, inspect pipeline stages, parse JUnit and Ginkgo test reports, diff two builds, rank flaky tests, trigger and abort builds, and manage the build queue — all over a single MCP stdio transport, from one static Go binary.
Why jenkins-mcp-go?
Most Jenkins integrations expect a human at a keyboard. LLM agents need something different: small, structured responses; a clear path from "build failed" to "here is the failing line"; and the ability to grep a multi-gigabyte console log without re-downloading it on every question.
jenkins-mcp-go is built for that workflow:
- Read-first, opt-out writes. Read tools are always on. Write tools
(
trigger_build,stop_build,cancel_queue_item) are gated byJENKINS_MCP_READONLY: set the env var and the server registers only the read surface. - Single-host, single-credential. It talks to one Jenkins URL with one API token, configured by environment variables. No multi-tenant surface, no credential vault to misuse.
- Triage-shaped, not API-shaped. Tools answer the questions agents
actually ask — "what changed between build A and B?" (
compare_builds), "which tests in this job are flaky?" (get_flaky_candidates), "what commits and files touched this build?" (get_scm_context) — instead of mirroring Jenkins endpoints one-for-one. - Built for context windows. Every listing tool takes an RE2 filter and
a cap.
get_console_log_pathreturns the on-disk path of a finished build's log so the agent canRead/Grep/Bashit natively instead of streaming gigabytes through MCP. - Disk-cached console logs. Finished builds are saved once and reused. The cache is keyed by job path + build number, capped by total size, and evicted by LRU mtime.
- Pipeline- and Ginkgo-aware. Beyond the raw console, dedicated tools
parse
/wfapi/describe,/testReport/api/json, and Ginkgo'sSummarizing N Failureblock so the agent gets pre-digested failure information. - One static binary. Pure Go. No Python runtime, no Docker required.
Tools
| Tool | Purpose |
|---|---|
health_check | Validate the server's setup: Jenkins reachability and version, authenticated user, CSRF crumb issuer, Pipeline/JUnit plugin presence, online/offline node counts, clock skew, and the effective config the process is running with. |
get_plugin_versions | List installed Jenkins plugins with versions, pinned flag, and pending-update flag. Optional name_filter (RE2) and include_inactive. 403 degrades to a clear hint. |
whoami_can | Probe a job for the configured token's effective Read / Build / Cancel / Configure permissions via read-only GET requests. Useful up-front to avoid a 403 on a trigger or cancel. Stays read-only even when JENKINS_MCP_READONLY=false. |
list_jobs | Enumerate jobs and folders under a path (or root). Optional recursion and case-insensitive RE2 name filter; capped at 500 entries. |
list_branches | Enumerate the branches of a WorkflowMultiBranchProject with per-branch last-build number, result, duration, and timestamp. Optional name_filter (RE2) and healthy_only. |
get_console_log | Tail the build's /consoleText. Defaults to last 500 lines; pass tail_lines: -1 for the full log. |
get_console_log_path | Force-cache the full log for a completed build and return its on-disk path so the agent can Read/Grep/Bash it natively. |
search_console_log | RE2 regex search over the console log with line-number-aware context windows. |
tail_running_build | Capped, offset-tracked tail of an in-flight build's console via Jenkins' progressiveText. Echo Next since_byte back to paginate. Never writes to the on-disk cache. |
get_build_info | Pretty-printed build summary: result, duration, parameters, change set. |
get_build_environment | Three sections for a build: Cause (trigger reason verbatim), Parameters (masked secrets surfaced as (masked)), and Injected Env Vars via EnvInject. Optional RE2 name_filter narrows the env-var section. Degrades on EnvInject 404. |
get_scm_context | Per-commit history for one build: commit id, author, timestamp, message subject, and each commit's touched paths with A/M/D edit codes. Pipeline change sets are flattened with per-set headers. Optional RE2 path_filter. |
last_green_build | Report the most recent successful build of a job. Returns number, finish time (UTC), and URL — the start point for bisect-from-green. |
changes_since_last_green | Union the commits across every completed build since the job's last green. Walks previousCompletedBuild, dedupes by commitId, supports path_filter and max_commits. Surfaces 'all green' and wide-window footers. |
compare_builds | Diff two builds of the same job across result, duration, parameters, SCM commits, pipeline stages, and JUnit tests. The agent answers "what changed between A and B?" in one call. |
get_pipeline_stages | List Declarative/Scripted Pipeline stages via /wfapi/describe with status and duration. |
get_stage_log | Fetch a single pipeline stage's log via /execution/node/<id>/wfapi/log. |
get_pipeline_script | Return the Jenkinsfile a specific build actually ran. Tries the Replay plugin first (build-pinned), falls back to config.xml (job-level — provenance surfaced). Returns SCM coords as a hint for Pipeline-from-SCM jobs. |
get_test_report | Structured JUnit results from /testReport/api/json, with failed cases and head+tail of stack traces. |
get_flaky_candidates | Rank flaky tests across the latest N completed builds of one job by counting pass↔fail flips. Returns a sorted table of test name, flip count, pass/fail tallies, and last-seen build. |
get_test_history | Per-build trend of a single test across the last N completed builds — the follow-up to get_flaky_candidates once a suspect is known. Timeline (build #, result, status, duration, error head) plus a summary of counts and flips. |
find_test_by_name | Locate which job runs a test whose full name contains a substring. Walks list_jobs(recursive) under folder_path, fans out per-job probes against lastCompletedBuild/testReport with a 5s per-job timeout, renders a sorted table of hits. |
find_recent_failures | Survey failed builds across the jobs under folder_path within a lookback window. Per-job probe of the last 5 builds; filters by since (24h default; supports Nd) and result_filter (FAILURE/UNSTABLE/ABORTED/ANY_NON_SUCCESS). |
list_pr_builds | List all builds for a PR on a WorkflowMultiBranchProject. Probes the common PR branch naming conventions in parallel (PR-N, pull/N/head, change-N, pr/N) and renders the build history for the first match. |
get_ginkgo_failure_summary | Parse Ginkgo's Summarizing N Failure block and surface the first [ERROR] tagged with each spec name. |
list_nodes | List Jenkins agents/nodes with status, executor counts, labels, and monitor summaries. |
get_node | Per-node detail: status, per-executor idle state, labels, full monitor data. |
list_queue | List pending Jenkins queue items with the block reason for each. |
cancel_queue_item | Drop a pending queue item by id. Mutating; suppressed when JENKINS_MCP_READONLY is set. |
trigger_build | Queue a build, optionally with parameters; can block until the build is assigned a number. Mutating. |
stop_build | Abort a running build. Mutating. |
Build-targeted tools take a job_path (slash-separated, e.g.
Builds/team/job-name) and an optional build_number (0 or omitted =
lastBuild). A URL like
https://jenkins.example.com/job/Builds/job/team/job/job-name/86/ becomes
job_path="Builds/team/job-name", build_number=86. list_jobs takes a
folder_path in the same slash-separated form (empty = root).
See docs/TOOLS.md for the full parameter reference.
Install
Pre-built binaries
Grab the archive for your OS and architecture from the
Releases page and put
the jenkins-mcp binary on your PATH.
Via go install
go install github.com/2001adarsh/jenkins-mcp-go@latest
The binary lands in $(go env GOBIN) (or $(go env GOPATH)/bin).
Via Docker
Pre-built multi-arch images are published to GitHub Container Registry:
docker pull ghcr.io/2001adarsh/jenkins-mcp-go:latest
Tags:
:latest— most recent release:vX.Y.Z— pinned to a specific release:vX.Y.Z-amd64/:vX.Y.Z-arm64— per-arch (the unsuffixed tags above are multi-arch manifests;docker pullresolves the right one automatically)
The image is built on gcr.io/distroless/static:nonroot — runs as a non-root
user, ships with CA roots so HTTPS to Jenkins works out of the box, and is
under 20 MB compressed. See the Docker MCP client setup
section for a docker run-based Claude Desktop config.
From source
git clone https://github.com/2001adarsh/jenkins-mcp-go.git
cd jenkins-mcp-go
make build
./bin/jenkins-mcp -h 2>/dev/null || true # the server speaks MCP over stdio; -h prints nothing
Configuration
Configuration is read from the environment at startup. There is no config file and no command-line flags — keep credentials out of process arguments.
| Variable | Required | Default | Description |
|---|---|---|---|
JENKINS_URL | yes | — | Base URL of the Jenkins instance, e.g. https://jenkins.example.com. |
JENKINS_USER | yes | — | Username for HTTP Basic auth. |
JENKINS_API_TOKEN | yes | — | API token (not the password). Generate one at /me/configure in your Jenkins UI. |
JENKINS_MCP_CACHE_DIR | no | $XDG_CACHE_HOME/jenkins-mcp (or ~/.cache/jenkins-mcp) | Where finished build logs are cached on disk. |
JENKINS_MCP_CACHE_MAX | no | 1073741824 (1 GiB) | Soft cap on cache size in bytes. Evicts oldest-mtime files first. |
JENKINS_MCP_TIMEOUT | no | 90s | HTTP timeout (Go duration: 30s, 2m, etc.). |
JENKINS_MCP_DEBUG | no | unset | When set to any non-empty value, emits one stderr line per outbound Jenkins request and cache event. See docs/DEBUGGING.md. |
JENKINS_MCP_READONLY | no | unset | When truthy (1/true/yes, case-insensitive), suppresses registration of any tool that mutates Jenkins state. Active mode is logged at startup. |
Note —
JENKINS_API_TOKENshould be a Jenkins API token, not your account password. In Jenkins, navigate to your user menu → Configure → API Token → Add new Token.
MCP client setup
The server speaks MCP over stdio. Hook it up by adding an entry to your client's MCP server configuration.
Claude Desktop — ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows)
{
"mcpServers": {
"jenkins": {
"command": "/usr/local/bin/jenkins-mcp",
"env": {
"JENKINS_URL": "https://jenkins.example.com",
"JENKINS_USER": "your-username",
"JENKINS_API_TOKEN": "your-api-token"
}
}
}
}
Claude Desktop via Docker — same config file, no binary on PATH required
{
"mcpServers": {
"jenkins": {
"command": "docker",
"args": [
"run", "--rm", "-i",
"-e", "JENKINS_URL",
"-e", "JENKINS_USER",
"-e", "JENKINS_API_TOKEN",
"ghcr.io/2001adarsh/jenkins-mcp-go:latest"
],
"env": {
"JENKINS_URL": "https://jenkins.example.com",
"JENKINS_USER": "your-username",
"JENKINS_API_TOKEN": "your-api-token"
}
}
}
}
-i keeps stdin open (MCP speaks stdio); --rm cleans up the container
after Claude Desktop disconnects. The console-log cache lives inside the
container by default, so it's lost on restart — add
-v "$HOME/.cache/jenkins-mcp:/home/nonroot/.cache/jenkins-mcp" and
-e XDG_CACHE_HOME=/home/nonroot/.cache to the args array if you want
the cache to survive across sessions.
Claude Code (CLI)
claude mcp add jenkins /usr/local/bin/jenkins-mcp \
--env JENKINS_URL=https://jenkins.example.com \
--env JENKINS_USER=your-username \
--env JENKINS_API_TOKEN=your-api-token
Cursor / Continue / any other MCP client
Any client that supports stdio MCP servers will accept a configuration of the form:
{
"command": "jenkins-mcp",
"args": [],
"env": {
"JENKINS_URL": "https://jenkins.example.com",
"JENKINS_USER": "your-username",
"JENKINS_API_TOKEN": "your-api-token"
}
}
Example session
Once the server is registered, ask your agent things like:
- "What integration-test jobs do we have under
Builds/team?" → callslist_jobswithfolder_path: "Builds/team",recursive: true,name_filter: "integration". - "What was the result of build 86 of
Builds/team/integration-tests?" → callsget_build_info. - "Show me the last 200 lines of the most recent run of
nightly." → callsget_console_logwithtail_lines: 200. - "Find every line matching
panic|fatalin build 4521 with five lines of context." → callssearch_console_logwithpattern: "panic|fatal",context_lines: 5. - "Build 91 passed but 92 failed — what changed?"
→ calls
compare_buildswithbuild_a: 91,build_b: 92. - "Which tests in
Builds/team/integration-testshave been flipping between pass and fail recently?" → callsget_flaky_candidates. - "Which commits in build 86 touched anything under
internal/auth/?" → callsget_scm_contextwithpath_filter: "^internal/auth/". - "Which Ginkgo specs failed in build 92 and what was the first error each emitted?"
→ calls
get_ginkgo_failure_summary. - "Cache the full log for build 4521 so I can grep it locally."
→ calls
get_console_log_path; the agent then uses its ownRead/Grep/Bashtools on the returned path.
Caching
Only finished builds are cached (the writer requires Jenkins' Finished:
marker), and the cache is evicted by LRU mtime once it exceeds
JENKINS_MCP_CACHE_MAX. Files live in JENKINS_MCP_CACHE_DIR.
Security notes
- No credential echo. The server never includes credentials in tool output, error messages, or cached files.
- Filesystem boundary. Cache filenames are sanitized; the cache directory is the only path the server writes to.
If you find a security issue, please follow SECURITY.md rather than opening a public issue.
Development
make build / test / lint / fmt. See CONTRIBUTING.md for the
full contributor guide and docs/DEBUGGING.md for how to
exercise the server locally with MCP Inspector.
Compatibility
- Go: 1.23+
- Jenkins: any version that exposes the standard
/api/json,/consoleText,/wfapi/describe,/testReport/api/jsonendpoints. Pipeline-specific tools require the Pipeline plugin. - MCP: uses
github.com/modelcontextprotocol/go-sdkv1.6+.
Alternatives
How jenkins-mcp-go compares to other ways of exposing Jenkins to an LLM:
| Option | Runtime | Read-only mode | Console-log cache | Pipeline / JUnit / Ginkgo tools |
|---|---|---|---|---|
jenkins-mcp-go (this repo) | Single static Go binary | Yes, gated by JENKINS_MCP_READONLY | LRU disk cache for finished builds | First-class, pre-digested responses |
| Python-based Jenkins MCP servers | Python interpreter + deps | Varies by project | Typically none — re-fetches each call | Usually raw /api/json pass-through |
| Generic HTTP-to-MCP gateways | Node / Python runtime | Whatever the gateway enforces | None | None — agent has to parse raw JSON |
Direct curl / shell tools from the agent | Shell | Manual | None | None — agent reasons over raw text |
If you want a small, predictable surface tailored to "the agent is debugging a Jenkins build", this project is for you. If you need a generic JSON proxy or multi-tenant credential routing, a generic HTTP-MCP gateway is a better fit.
FAQ
How do I connect Jenkins to Claude?
Install the jenkins-mcp binary, then add it to your Claude Desktop or
Claude Code MCP configuration with your JENKINS_URL, JENKINS_USER, and
JENKINS_API_TOKEN. See MCP client setup above for the
exact JSON / CLI snippets.
Does this work with Cursor, Continue, or Windsurf?
Yes. Any MCP client that supports stdio servers will accept the same
command + env configuration shown in the MCP client setup
section.
Is the server read-only?
Reads are always on. Write tools (trigger_build, stop_build,
cancel_queue_item) are registered by default but can be suppressed entirely
by setting JENKINS_MCP_READONLY=1 — the server then never registers a
mutating tool, so an agent literally cannot call one.
Do I need a Jenkins plugin to use this?
No extra plugin is required for the core read tools — they hit standard
Jenkins endpoints. Pipeline-specific tools (get_pipeline_stages,
get_stage_log) require the Pipeline plugin, which most Jenkins installations
already have.
Can the LLM agent grep through a multi-gigabyte console log?
Yes. Use get_console_log_path to force-cache the full log for a finished
build to disk; the tool returns a local path the agent can then Read,
Grep, or Bash natively. The cache is LRU-evicted and capped by
JENKINS_MCP_CACHE_MAX.
Does it handle Ginkgo test failures specifically?
Yes. get_ginkgo_failure_summary parses Ginkgo's Summarizing N Failure block and
surfaces the first [ERROR] line tagged with each spec name plus surrounding
context — much faster than asking the agent to scan the whole log.
Is it safe to give an LLM API token access to Jenkins?
The server uses a single Jenkins user's API token (not a password), confined
to one JENKINS_URL. Combine with JENKINS_MCP_READONLY=1 and a least-privilege
Jenkins user for the safest default. See SECURITY.md for the
full threat model.
License
MIT © Adarsh Singh