# Jenkins Read-Only Build Status Tools — Design Notes

- **Status:** Design (implementation-ready notes; **no implementation in this repo**)
- **Issue:** #72 (parent umbrella: #75; boundary decision: ADR-0001, #71)
- **Related:** #77 (repo/branch/PR → job mapping, designed separately)
- **Date:** 2026-07-02

Note on naming: This design used historical `jenkins-readonly` skill name in Gitea-Tools. Actual package/server is `jenkins-mcp` (see mcp-control-plane registration in #55). The read server boundary remains read-only; gated triggers live on the separate `jenkins-write-mcp` / `jenkins_mcp.write_server` boundary (see #56 / #152).
Client registration and reload instructions live in
[`../mcp-client-registration.md`](../mcp-client-registration.md).

## 1. Purpose and scope

Define the minimum **read-only** Jenkins MCP tool set that lets an LLM answer:
*"Did the latest build for this project/branch succeed or fail?"* — plus enough
detail (build URL, number, timing, result) to report or investigate.

Phase 1 is **primarily read-only**, per ADR-0001
([`adr-0001-mcp-control-plane-boundaries.md`](adr-0001-mcp-control-plane-boundaries.md)):

- Build triggers are outside this read-only surface and require the separate
  `jenkins-write-mcp` boundary, a dedicated profile, exact confirmation, and
  fail-closed mutation audit (landed in #4, boundary correction in #56 / #152).
- **Excluded: deploy triggers.**
- **Excluded: parameterized job launches.**
- Excluded: job creation/deletion/config changes, queue manipulation, node
  management — any Jenkins mutation whatsoever unless explicitly configured.

## 2. Boundary placement

These tools belong to the **`jenkins-mcp`** package/server of the MCP Control
Plane — **never** inside `gitea-mcp` (`mcp_server.py` in this repo).
Consequences (from `tool-boundaries.md`, `credential-isolation.md`, ADR-0001):

- `jenkins-mcp` runs as its own server process with its own `.env`.
- **Jenkins credentials never enter the Gitea MCP runtime**, and Gitea
  credentials never enter `jenkins-mcp`.
- This document lands in this repo only because the repo currently hosts the
  Control Plane's architecture docs; the code ships elsewhere (owner decision
  #1 of ADR-0001).

## 3. Minimum read-only tool set

| Tool | Purpose |
|---|---|
| `jenkins_whoami` | Verify authenticated Jenkins identity + active profile (mirror of `gitea_whoami`; fail-closed identity proof before anything else) |
| `jenkins_list_jobs` | List visible jobs (supports folder paths), with pagination bounds |
| `jenkins_latest_build` | The primary question: latest build of a job (or job+branch for multibranch) → status summary |
| `jenkins_build_status` | Status of a specific build number (job, number) |
| `jenkins_get_build` | Full safe detail of a build (fields in §4) |
| `jenkins_console_tail` | Bounded, redacted tail of a build's console log (§6) — optional, approval-gated addition |

All tools are `GET`-only against the Jenkins JSON API (`/api/json`,
`.../lastBuild/api/json`, `.../consoleText`). No tool issues POST/PUT/DELETE.

## 4. Return payloads (safe fields)

`jenkins_latest_build` / `jenkins_build_status` / `jenkins_get_build` return:

| Field | Source | Notes |
|---|---|---|
| `job` | request | Fully-qualified job path (folders joined with `/`) |
| `build_number` | `number` | int |
| `result` | `result` | `SUCCESS` / `FAILURE` / `UNSTABLE` / `ABORTED` / `NOT_BUILT`; `null` → `IN_PROGRESS` when `building=true` |
| `building` | `building` | bool |
| `url` | `url` | Build URL |
| `branch` | multibranch job name / SCM action | Best-effort; omitted when unknown |
| `timestamp` | `timestamp` | ISO-8601 UTC (converted from epoch ms) |
| `duration_seconds` | `duration` | 0/omitted while building |
| `commit_sha` | SCM build action | Best-effort; omitted when unknown |

Rules: no raw Jenkins payload passthrough (allowlist projection only); no
`Authorization` header, token, or crumb material in any output or error
(reuse the shared redaction approach of `safety-model.md` §3 / `gitea_audit`).

## 5. Failure behavior (fail closed, clear, safe)

| Condition | Behavior |
|---|---|
| Unknown job | Explicit `{"found": false, "job": "<path>", "error": "job not found"}` — never guess or fuzzy-match a job name (hard rule; see also #77) |
| Jenkins unreachable (DNS/timeout/conn refused) | Clear `"network error contacting Jenkins: <redacted reason>"`; no retry storm — mirror `gitea_auth.api_request` timeout + failure conversion |
| 502/503/504 | Explicit "Jenkins upstream unavailable" |
| 401/403 | "Jenkins auth failed / insufficient permissions" — **without** echoing credentials or the request's auth material |
| Malformed JSON | "malformed JSON response from Jenkins" (no raw-body dump) |
| Missing profile/creds | Fail closed before any network call (§7) |

## 6. Console tail safety (`jenkins_console_tail`)

Console logs are the highest-risk surface (secrets, tokens, internal hosts
routinely leak into build logs). If included at all (owner may defer it):

- **Bounded:** hard server-side cap (default: last 200 lines AND ≤ 64 KiB,
  whichever is smaller; caller may request less, never more).
- **Redacted:** pass through the shared secret redactor (token/`Basic`/`Bearer`/
  password/key-value patterns) before returning; redaction failure ⇒ return an
  error, never the raw text.
- **Default off:** summary fields (`result`, failing stage if cheaply available)
  are preferred; the tail requires an explicit `allowed_operations` entry
  (`jenkins.console.read`) distinct from plain `jenkins.build.read`.

## 7. Credentials and profile requirements

Follows the per-service profile model (`gitea-execution-profiles.md`, extended
by #76):

- Env/config: `JENKINS_URL`, `JENKINS_USER`, `JENKINS_TOKEN_SOURCE_NAME`
  (name-of-secret only — value resolved at runtime, never logged/committed).
- Profile: e.g. `jenkins-readonly` with namespaced
  `allowed_operations: ["jenkins.read", "jenkins.build.read"]`
  (+ `jenkins.console.read` only if the tail tool is approved);
  `forbidden_operations: ["jenkins.build.trigger", "jenkins.deploy", "jenkins.job.configure"]`
  as belt-and-braces even though no mutating tool exists.
- Missing URL/user/token/profile ⇒ **fail closed** with a clear message.
- Since every tool on `jenkins-mcp` is read-only, no confirmation gates are needed — but
  identity (`jenkins_whoami`) must still work so workflows can prove which
  Jenkins account they act as.

## 8. Job addressing and mapping

Tools accept an explicit fully-qualified job path (folder-aware:
`folder/subfolder/job`). How a *repo/branch/PR* resolves to that job path is
**out of scope here** and designed in **#77**, with these fixed constraints:

- No silent guessing of job names — unmapped input returns an explicit
  "no mapping" result.
- Multibranch pipelines address a branch job as `<job>/<branch>` with proper
  URL-encoding of branch names (e.g. `feature%2Fx`).

## 9. Testing strategy (for the implementing package)

Mocked-Jenkins unit tests only (no live Jenkins in unit CI), mirroring this
repo's conventions (`docs/developer-testing-guidelines.md`):

- Patch the HTTP client; assert method is always `GET` and URL shape is correct
  (folders, multibranch encoding).
- Success projections: field allowlist exactly as §4; unknown fields dropped.
- `result=null + building=true` ⇒ `IN_PROGRESS`.
- Unknown job ⇒ found:false, no fuzzy match, no API retry.
- Timeout/DNS/5xx/malformed-JSON ⇒ safe errors, no secret/credential leakage
  (explicit no-token-in-error assertions).
- Console tail: cap enforcement (lines and bytes), redaction applied, redaction
  failure ⇒ error not raw text, gated behind `jenkins.console.read`.
- Profile gate: missing/insufficient profile ⇒ no network call
  (`mock_api.assert_not_called()` pattern).

## 10. Implementation-readiness checklist

Ready to operate through `jenkins-mcp` once:

1. The MCP client registers the external server under the exact `jenkins-mcp`
   name and is reconnected/reloaded.
2. Tool discoverability proves the expected `jenkins_*` read tools are visible;
   if the server is enabled but exposes no usable tools, report `SKIPPED` and
   stop.
3. #76 profile schema exists (or a minimal `jenkins-readonly` profile is
   hand-rolled to the same rules).
4. #77 mapping design is accepted (or tools ship path-addressed only, mapping
   deferred).

Explicitly **not** unlocked by this document: build triggers, deploys,
parameterized launches, any Jenkins code in `mcp_server.py`.
