docs: ADR-0001 confirm+extend MCP Control Plane boundaries (#71)

Add docs/architecture/adr-0001-mcp-control-plane-boundaries.md. Confirms the
already-documented Control Plane direction and extends it to Jenkins and
GlitchTip: package/server boundaries (common, gitea-mcp, jenkins-mcp,
glitchtip-mcp/observability-mcp/ops-mcp, release-mcp), recommended GlitchTip
placement with alternatives, reaffirmed rules (one server per boundary,
service-local creds, read-only first, namespaced allowed_operations, audited
mutations, orchestrators are not credential sinks), hard phase-1 non-goals,
consequences, and explicit open owner decisions. Does not reopen the settled
direction.

Documentation only; no code behavior changed. No Jenkins/GlitchTip
implementation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-02 13:43:13 -04:00
parent 093945254d
commit 6b6a8fb17d
@@ -0,0 +1,156 @@
# ADR-0001: Confirm and extend the MCP Control Plane trust-boundary model for Jenkins and GlitchTip
- **Status:** Proposed
- **Date:** 2026-07-02
- **Issue:** #71
- **Supersedes / relates to:** confirms and extends
[`docs/tool-boundaries.md`](../tool-boundaries.md),
[`docs/safety-model.md`](../safety-model.md),
[`docs/credential-isolation.md`](../credential-isolation.md),
[`docs/release-workflows.md`](../release-workflows.md),
[`docs/gitea-execution-profiles.md`](../gitea-execution-profiles.md).
Umbrella tracking: #75. Applied to docs by: #79.
---
## 1. Context
The MCP Control Plane direction is **already decided** in the existing
architecture docs. This ADR does **not** reopen that decision. It exists to:
1. **Confirm** the established model in one authoritative place, and
2. **Extend** it to the two concrete services now under consideration —
**Jenkins** (CI/CD visibility) and **GlitchTip** (error/observability).
What the existing docs already establish (and this ADR reaffirms):
- The project is named **MCP Control Plane** and is intended to group the
packages `common`, `gitea-mcp`, `jenkins-mcp`, `ops-mcp`, and `release-mcp`
(`tool-boundaries.md`).
- **One MCP server per trust boundary**; there is no single "everything" server
(`tool-boundaries.md`).
- **Separate credentials per service**, each server instantiated with its own
dedicated `.env`; strict isolation — e.g. `gitea-mcp` has no Jenkins or Ops
tokens (`credential-isolation.md`).
- **All mutating actions are audited and require confirmation**; production
actions need a hard gate; **secrets are redacted** from logs, tool output, and
storage (`safety-model.md`).
- The execution-profile model — `allowed_operations` / `forbidden_operations`
(forbidden wins), deny-by-default, fail-closed, verified identity via
`gitea_whoami`, self-review/self-merge prevention, token-by-reference only
(`gitea-execution-profiles.md`). That doc **already names GlitchTip** as a
boundary `gitea-mcp` must **not** absorb.
- A future **`release-mcp` orchestrator** may coordinate across packages but
must be **"coordination, not consolidation"** — it must not hold every
service's credentials (`release-workflows.md`).
`gitea-execution-profiles.md` is explicit: `gitea-mcp` "must **not** add or
absorb Jenkins, Ops, GlitchTip, Release, deploy, rollback, migration, restart,
or production behavior." This ADR builds directly on that rule.
## 2. Decision
### 2.1 Repository shape
This repository (`Gitea-Tools`) is, in practice, the **`gitea-mcp` package** of
the Control Plane. Jenkins and GlitchTip work **must not** be added to it.
Recommended direction: extract the Control Plane into a dedicated
**`mcp-control-plane` monorepo** (as the existing docs already name) with one
package per boundary, and treat this repo either as the seed of that monorepo's
`gitea-mcp` package or as a package that is moved into it. The final choice
(monorepo now vs. later; move vs. seed) is an **open owner decision** (§5).
Until that is settled, the operative rule stands: **no Jenkins/GlitchTip code
lands in this repo.**
### 2.2 Package / server boundaries
| Package | Trust boundary | Phase-1 posture |
|---|---|---|
| `common` | Shared, credential-free library (profile schema, audit/redaction helpers, API/pagination/failure client, `allowed_operations` model). **Holds no credentials.** | Extract shared pieces only as approved (§5) |
| `gitea-mcp` | Source-control & work items (issues, PRs, comments). Gitea creds only. | Exists (this repo) |
| `jenkins-mcp` | CI/CD visibility. Jenkins creds only. | **Read-only** build status/visibility |
| GlitchTip boundary — `glitchtip-mcp` (recommended), or `observability-mcp`, or fold into `ops-mcp` | Error/event observability. GlitchTip creds only. | **Read-only** error/event listing |
| `ops-mcp` | Live host/health/status/log checks. Ops creds only. | Read-only (per existing docs) |
| `release-mcp` / orchestrator | Coordinates across boundaries; composes other servers' tools. **Not a credential sink.** | Design-only in phase 1 |
**Recommended GlitchTip placement:** a dedicated **`glitchtip-mcp`** server.
Rationale — GlitchTip has its own API surface and its own credential, and error
tracking is a distinct trust boundary from live host checks; a dedicated server
is the cleanest fit for "one server per trust boundary."
- **Alternative A — `observability-mcp`:** a broader observability boundary that
could later also cover metrics/log/error sources beyond GlitchTip. Choose this
if you expect several observability backends and want one boundary for them.
- **Alternative B — fold into `ops-mcp`:** minimizes package count, but mixes
app-error observability with host/health checks; acceptable only if kept
strictly read-only and the credentials are still GlitchTip-specific. Least
recommended because it blurs two boundaries.
The final name/placement is an **open owner decision** (§5). Downstream issues
(#73 GlitchTip read tools, #79 doc updates, #80 labels) must use whatever name
is chosen here.
### 2.3 Rules (reaffirmed, now spanning all boundaries)
- **One MCP server per trust boundary.** No single "everything" MCP server.
- **Separate credentials per service; tokens remain service-local.** Each server
has its own `.env`; e.g. a GlitchTip server never holds Gitea write tokens.
- **Read-only first.** New service surfaces (Jenkins, GlitchTip) begin read-only.
- **Mutations require explicit `allowed_operations`**, namespaced per service
(e.g. `gitea.issue.create`, `jenkins.build.read`, `glitchtip.event.read`);
`forbidden_operations` always overrides; deny-by-default; fail-closed.
- **Mutating actions require audit logging** with verified identity and outcome,
and secret redaction (per `safety-model.md`).
- **Orchestrators coordinate; they must not become credential sinks.**
Cross-service workflows compose per-service tools; no orchestrator centralizes
all tokens.
## 3. Phase-1 non-goals (hard)
- **No Jenkins build triggers.**
- **No deploy triggers.**
- **No parameterized job launches.**
- **No automatic GlitchTip-to-Gitea issue filing.**
- **No GlitchTip MCP server holding Gitea write credentials.**
- **No Jenkins or GlitchTip code inside `mcp_server.py`** (the `gitea-mcp`
entrypoint).
## 4. Consequences
- Jenkins/GlitchTip work proceeds as **design/read-only** first, in their own
packages/servers, gated behind this ADR's decisions.
- The `gitea-mcp` monolith (`mcp_server.py`) does not grow to host other
services; that pressure is redirected to separate packages and the `common`
extraction (relates to the #65 refactor, which remains independent).
- Cross-service "file a Gitea issue from a GlitchTip error" becomes an
**orchestrator/runbook** concern (composing GlitchTip-read + Gitea-write
tools), never a single dual-credential server (see #74/#78).
- Naming/label decisions (#80) and doc updates (#79) inherit the choices here.
## 5. Open owner decisions
These are intentionally **not** decided by this ADR and must be resolved by the
repo owner before the corresponding implementation issues proceed:
1. **Monorepo vs. this repo.** Does a separate `mcp-control-plane` repo get
created now (with `gitea-mcp` moved into it), or does this repo become the
monorepo, or does it stay Gitea-only for now while others live elsewhere?
2. **GlitchTip placement.** `glitchtip-mcp` (recommended), `observability-mcp`,
or folded into `ops-mcp`?
3. **Cross-service filing mechanism.** Should GlitchTip-to-Gitea filing be an
**LLM runbook only**, or a carefully constrained **orchestrator workflow**
(`release-mcp`) with dry-run + explicit invocation + Gitea profile gating?
4. **`common` package scope.** Which shared pieces are approved for extraction
into `common` now — profile schema, audit/redaction, API client
(pagination/failure handling), `allowed_operations` model — versus deferred?
## 6. References
- [`docs/tool-boundaries.md`](../tool-boundaries.md)
- [`docs/safety-model.md`](../safety-model.md)
- [`docs/credential-isolation.md`](../credential-isolation.md)
- [`docs/release-workflows.md`](../release-workflows.md)
- [`docs/gitea-execution-profiles.md`](../gitea-execution-profiles.md)
- Tracking/umbrella: #75 · Doc application: #79 · Jenkins design: #72/#77 ·
GlitchTip design: #73 · Cross-service filing: #74/#78 · Labels: #80