74a7e8f792
Add docs/architecture/jenkins-readonly-build-status-design.md: implementation-ready design notes for the jenkins-mcp read-only tool set — minimum tools (whoami, list_jobs, latest_build, build_status, get_build, gated console_tail), safe return-field allowlist (url, number, timestamp, duration, branch, result, commit), fail-closed failure behavior (unknown job, unreachable, 5xx, auth, malformed JSON), bounded+redacted console tail behind a distinct jenkins.console.read operation, per-service credential/ profile requirements (token by reference, fail closed), explicit exclusions (build/deploy triggers, parameterized launches), job addressing with mapping deferred to #77, and a mocked-Jenkins testing strategy. Design only; no implementation, no code behavior changed, no Jenkins code in mcp_server.py. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
152 lines
7.5 KiB
Markdown
152 lines
7.5 KiB
Markdown
# 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
|
|
|
|
## 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 **strictly read-only**, per ADR-0001
|
|
([`adr-0001-mcp-control-plane-boundaries.md`](adr-0001-mcp-control-plane-boundaries.md)):
|
|
|
|
- **Excluded: build triggers.**
|
|
- **Excluded: deploy triggers.**
|
|
- **Excluded: parameterized job launches.**
|
|
- Excluded: job creation/deletion/config changes, queue manipulation, node
|
|
management — any Jenkins mutation whatsoever.
|
|
|
|
## 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 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 implement in `jenkins-mcp` once:
|
|
|
|
1. ADR-0001 owner decision #1 (where `jenkins-mcp` lives) is made.
|
|
2. #76 profile schema exists (or a minimal `jenkins-readonly` profile is
|
|
hand-rolled to the same rules).
|
|
3. #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`.
|