Merge pull request 'docs: separate Jenkins trigger onto jenkins-write-mcp boundary (#152)' (#264) from codex/issue-152-jenkins-trigger-boundary into master

This commit was merged in pull request #264.
This commit is contained in:
2026-07-06 13:26:16 -05:00
4 changed files with 51 additions and 10 deletions
@@ -5,7 +5,7 @@
- **Related:** #77 (repo/branch/PR → job mapping, designed separately) - **Related:** #77 (repo/branch/PR → job mapping, designed separately)
- **Date:** 2026-07-02 - **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 server boundary now contains gated trigger (see #56), but read tools remain as designed. 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 Client registration and reload instructions live in
[`../mcp-client-registration.md`](../mcp-client-registration.md). [`../mcp-client-registration.md`](../mcp-client-registration.md).
@@ -18,7 +18,9 @@ detail (build URL, number, timing, result) to report or investigate.
Phase 1 is **primarily read-only**, per ADR-0001 Phase 1 is **primarily read-only**, per ADR-0001
([`adr-0001-mcp-control-plane-boundaries.md`](adr-0001-mcp-control-plane-boundaries.md)): ([`adr-0001-mcp-control-plane-boundaries.md`](adr-0001-mcp-control-plane-boundaries.md)):
- Build triggers are gated behind dedicated profile + exact confirmation (landed in #4, boundary correction in #56). - 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: deploy triggers.**
- **Excluded: parameterized job launches.** - **Excluded: parameterized job launches.**
- Excluded: job creation/deletion/config changes, queue manipulation, node - Excluded: job creation/deletion/config changes, queue manipulation, node
@@ -109,7 +111,7 @@ by #76):
`forbidden_operations: ["jenkins.build.trigger", "jenkins.deploy", "jenkins.job.configure"]` `forbidden_operations: ["jenkins.build.trigger", "jenkins.deploy", "jenkins.job.configure"]`
as belt-and-braces even though no mutating tool exists. as belt-and-braces even though no mutating tool exists.
- Missing URL/user/token/profile ⇒ **fail closed** with a clear message. - Missing URL/user/token/profile ⇒ **fail closed** with a clear message.
- Since every tool is read-only, no confirmation gates are needed — but - 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 identity (`jenkins_whoami`) must still work so workflows can prove which
Jenkins account they act as. Jenkins account they act as.
+18 -4
View File
@@ -9,9 +9,14 @@ Use these exact MCP server names in clients:
| Server name | Boundary | Default capability | | Server name | Boundary | Default capability |
|---|---|---| |---|---|---|
| `jenkins-mcp` | Jenkins CI inspection | Read-only build/job inspection | | `jenkins-mcp` | Jenkins CI inspection (read) | Read-only build/job inspection |
| `jenkins-write-mcp` | Jenkins build trigger (write) | Gated `jenkins_trigger_build` only |
| `glitchtip-mcp` | GlitchTip observability inspection | Read-only issue/event inspection | | `glitchtip-mcp` | GlitchTip observability inspection | Read-only issue/event inspection |
The write boundary (`jenkins-write-mcp`) is **not** registered by default (#152).
It exposes a single mutating tool and requires operator approval of a dedicated
trigger profile before any client config references it.
Historical names such as `jenkins-readonly` and `glitchtip-readonly` are Historical names such as `jenkins-readonly` and `glitchtip-readonly` are
descriptive profile labels only. They are not the canonical MCP server names descriptive profile labels only. They are not the canonical MCP server names
unless an operator intentionally creates aliases and documents them. unless an operator intentionally creates aliases and documents them.
@@ -54,7 +59,7 @@ entry, reconnect or reload the MCP client before claiming the tools are usable.
Before using either server in a task, prove the expected tools are visible in Before using either server in a task, prove the expected tools are visible in
the client. It is not enough for the config entry to exist. the client. It is not enough for the config entry to exist.
Expected Jenkins tools: Expected Jenkins read tools (`jenkins-mcp` only):
- `jenkins_whoami` - `jenkins_whoami`
- `jenkins_list_jobs` - `jenkins_list_jobs`
@@ -62,6 +67,10 @@ Expected Jenkins tools:
- `jenkins_build_status` - `jenkins_build_status`
- `jenkins_get_build` - `jenkins_get_build`
`jenkins_trigger_build` must **not** appear on `jenkins-mcp`. When an operator
explicitly enables the write boundary, the only expected tool on
`jenkins-write-mcp` is `jenkins_trigger_build`.
Expected GlitchTip tools: Expected GlitchTip tools:
- `glitchtip_whoami` - `glitchtip_whoami`
@@ -78,8 +87,13 @@ back to shell commands, raw service APIs, or unrelated MCP servers.
## Boundary Rules ## Boundary Rules
- `jenkins-mcp` read profiles must not expose build trigger tools. - `jenkins-mcp` read profiles must not expose build trigger tools.
- Jenkins build triggers require a separately named write profile, exact - Build triggers live on the separate `jenkins-write-mcp` server
confirmation, and fail-closed mutation audit. (`jenkins_mcp.write_server`), not on `jenkins-mcp`.
- Jenkins build triggers require a dedicated trigger profile with
`jenkins.build.trigger` allowed, exact confirmation
(`TRIGGER BUILD <job-path>`), and fail-closed mutation audit.
- Do not register `jenkins-write-mcp` until an operator approves a trigger
profile; no shipped profile carries trigger capability by default.
- `glitchtip-mcp` remains read-only. It must not file or mutate Gitea issues. - `glitchtip-mcp` remains read-only. It must not file or mutate Gitea issues.
- GlitchTip-to-Gitea filing is a separate orchestrator that composes GlitchTip - GlitchTip-to-Gitea filing is a separate orchestrator that composes GlitchTip
read tools with Gitea issue-write tools. read tools with Gitea issue-write tools.
+7 -2
View File
@@ -21,5 +21,10 @@ Note on naming: Historical design docs used `jenkins-readonly` / `glitchtip-read
## 5. Mutation Gating ## 5. Mutation Gating
Any mutating action (e.g., Gitea issue creation from GlitchTip, or Jenkins builds) must be explicitly allowed by the execution profile. Any mutating action (e.g., Gitea issue creation from GlitchTip, or Jenkins builds) must be explicitly allowed by the execution profile.
- **Jenkins build triggers** exist in jenkins-mcp (landed #4) but require dedicated profile/identity and exact confirmation; not on standard reader profiles. See #56 for boundary correction. - **Jenkins build triggers** are gated on a separate write boundary
- **GlitchTip to Gitea issue filing** is documented as a gated, orchestrated workflow (not in glitchtip-mcp), currently partial (mocked, dedup not wired, audit missing). See #57. (`jenkins-write-mcp` / `jenkins_mcp.write_server`), not on the read-only
`jenkins-mcp` surface. Triggers require a dedicated profile with
`jenkins.build.trigger`, exact confirmation, and fail-closed mutation audit.
No default profile carries trigger capability (#152 / mcp-control-plane #56).
- **GlitchTip to Gitea issue filing** is a library-only orchestrator in
mcp-control-plane (not on `glitchtip-mcp`). See #153 / mcp-control-plane #57.
+21 -1
View File
@@ -1,4 +1,4 @@
"""Documentation checks for external Jenkins/GlitchTip MCP registration (#151).""" """Documentation checks for external Jenkins/GlitchTip MCP registration (#151/#152)."""
from pathlib import Path from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent REPO_ROOT = Path(__file__).resolve().parent.parent
@@ -52,11 +52,20 @@ def test_registration_doc_preserves_boundaries():
"Do not add Jenkins or GlitchTip credentials to the Gitea MCP server", "Do not add Jenkins or GlitchTip credentials to the Gitea MCP server",
"do not add Gitea write credentials to the GlitchTip server", "do not add Gitea write credentials to the GlitchTip server",
"must not expose build trigger tools", "must not expose build trigger tools",
"jenkins-write-mcp",
"jenkins_mcp.write_server",
"remains read-only", "remains read-only",
): ):
assert phrase in text assert phrase in text
def test_registration_doc_separates_jenkins_trigger_from_read_surface():
text = _doc_text()
assert "jenkins_trigger_build" in text
assert "must **not** appear on `jenkins-mcp`" in text
assert "not" in text.lower() and "registered by default" in text.lower()
def test_registration_doc_has_no_secret_material_or_live_urls(): def test_registration_doc_has_no_secret_material_or_live_urls():
text = _doc_text() text = _doc_text()
for marker in ( for marker in (
@@ -80,3 +89,14 @@ def test_related_docs_link_registration_doc():
text = (REPO_ROOT / name).read_text(encoding="utf-8") text = (REPO_ROOT / name).read_text(encoding="utf-8")
assert "mcp-client-registration.md" in text, ( assert "mcp-client-registration.md" in text, (
f"{name} does not link docs/mcp-client-registration.md") f"{name} does not link docs/mcp-client-registration.md")
def test_related_docs_keep_jenkins_trigger_off_read_surface():
for name in (
"docs/safety-model.md",
"docs/architecture/jenkins-readonly-build-status-design.md",
):
text = (REPO_ROOT / name).read_text(encoding="utf-8")
assert "jenkins-write-mcp" in text
assert "jenkins_mcp.write_server" in text
assert "jenkins-mcp" in text