1 Commits

Author SHA1 Message Date
sysadmin e67932fcc3 docs: propose labels for Jenkins/GlitchTip workflow (#80) 2026-07-02 15:07:13 -04:00
7 changed files with 2 additions and 459 deletions
-41
View File
@@ -1,41 +0,0 @@
# Changelog
All notable changes to this project will be documented in this file.
## [v1.1.0] - 2026-07-02
### Added
- Read-only identity and eligibility tooling: `gitea_whoami` authenticated-user lookup (#11), `gitea_get_profile` runtime-profile discovery (#13), and `gitea_check_pr_eligibility` fail-closed PR eligibility checks (#14).
- Identity lookup aliases (`gitea_get_authenticated_user` and `gitea_get_current_user`) for common MCP/LLM tool discovery (#9).
- Gated PR review actions (`gitea_submit_pr_review`) reusing the eligibility gates (#15).
- Gated PR merge workflow (`gitea_merge_pr`) with explicit `MERGE PR <n>` confirmation, head-SHA and changed-file pinning, and self-merge blocking as the only merge path (#16).
- Task-scoped Gitea MCP execution profiles: documented profile model (#12) and runtime profiles via environment config with `allowed_operations` (#19).
- Audit logging for all mutating MCP actions with execution-profile metadata and secret redaction (#18).
- Shared API pagination (`api_get_all`) and hardened failure handling in `gitea_auth.api_request`: request timeouts, clear network/DNS errors, explicit 502/503/504 upstream errors, malformed-JSON handling, and redacted error text (#67).
- `scripts/release-tag` SemVer-gated annotated-tag helper (safe-by-default, master-only, tests required) (#50).
- Automatic `status:in-progress` release on issue close and PR close/merge (#56, #58).
- `LLM-Agent-SHA` opaque agent attribution convention (Phase 0): documentation, handoff/review templates, and negative tests proving the SHA can never bypass self-review/self-merge gates (#86).
- macOS `com.apple.provenance` cleanup helper tool and documentation (#3).
- `manage_labels.py` refactored into reusable modes (`--create-labels`, `--apply-mapping`, `--add-label`) (#6).
### Changed
- HTTP 429 responses now honor `Retry-After` with jittered exponential backoff (#27).
- Read-only list tools (`gitea_list_issues`, `gitea_list_prs`, `gitea_list_labels`) now paginate across pages with bounded page caps (#67).
- Automatic `status:in-progress` cleanup on issue/PR close and merge.
- Label cleanup now utilizes safe targeted label deletion behavior rather than replacing the entire label set.
### Documentation
- MCP security model and trust-boundary documentation (#8).
- Developer testing guidelines (#70).
- Jenkins read-only build-status tools design (#72).
- Jenkins repo/branch/PR → job mapping design (#77).
- Safety and boundary docs updated for Jenkins/GlitchTip: `glitchtip-mcp` boundary, read-only-first policy, mutation gating (#79).
- Proposed label taxonomy for Jenkins/GlitchTip workflows (#80).
- GlitchTip read-only error/event tools design (#73).
- Multi-service MCP profile model extension (#76).
## [v1.0.1]
- Fix Recent Timesheets Remove button text clipping and copy theme/whats_new in build.
## [v1.0.0]
- Initial versioned release.
@@ -1,174 +0,0 @@
# GlitchTip Read-Only Error/Event Tools — Design Notes
- **Status:** Design (implementation-ready notes; **no implementation in this repo**)
- **Issue:** #73 (umbrella: #75; boundary decision: ADR-0001, #71)
- **Related:** #74 (GlitchTip→Gitea filing workflow — composes these read tools),
#78 (dedup/linking, child of #74), #76 (per-service profile schema)
- **Date:** 2026-07-02
## 1. Purpose and scope
Define the minimum **read-only** GlitchTip MCP tool set that lets an LLM answer:
*"What unresolved errors does project X have (by environment/release), and what
is this specific error?"* — with privacy-safe output suitable for LLM context,
issue bodies, and audit logs.
Strictly read-only, per ADR-0001:
- **No mutation tools** — no resolving/ignoring/assigning issues, no comment
posting, no project/team/key administration, no deletes.
- **No automatic GlitchTip→Gitea filing** (that is #74's *orchestrated,
explicitly-invoked* workflow; it composes these read tools and Gitea write
tools — never one dual-credential server).
- **This server never holds Gitea write credentials.**
## 2. Boundary placement (namespace pending)
These tools belong to the GlitchTip observability boundary of the MCP Control
Plane — `glitchtip-mcp` (ADR-0001's recommendation), `observability-mcp`, or
folded into `ops-mcp`. **ADR-0001 open owner decision #2 picks the name; this
design does not assume it.** Tool names below use the `glitchtip_` prefix for
readability and rename mechanically with the decision.
Fixed regardless of the name (per `tool-boundaries.md`,
`credential-isolation.md`):
- Own server process, own `.env`, GlitchTip credentials only.
- No Gitea, Jenkins, or Ops tokens in this runtime; no GlitchTip token
anywhere else.
## 3. API surface note (Sentry compatibility)
GlitchTip implements a Sentry-compatible REST API (`/api/0/...` — organizations,
projects, issues, events). The design targets **GlitchTip's documented subset**
only; Sentry-only endpoints must not be assumed. The implementation should pin
against a tested GlitchTip version and treat missing endpoints/fields as
degraded-but-safe (omit field, never crash).
## 4. Minimum read-only tool set
| Tool | Purpose |
|---|---|
| `glitchtip_whoami` | Verify authenticated identity + active profile (mirror of `gitea_whoami`; fail-closed identity proof) |
| `glitchtip_list_projects` | Projects visible to the token (org-scoped), with pagination bounds |
| `glitchtip_list_unresolved` | Unresolved issues for a project, filterable (§6), sorted by last-seen |
| `glitchtip_get_issue` | Safe detail of one issue (fields §5) |
| `glitchtip_recent_events` | Recent events for an issue (summaries only, §5) |
| `glitchtip_search` | Issue search within a project (query + filters §6) |
All tools are `GET`-only. No tool issues PUT/POST/DELETE.
## 5. Privacy: field-level allowlist (the core rule)
Error events routinely contain PII and secrets (request bodies, cookies,
headers, tokens, user emails/IPs, local variables). Therefore: **allowlist
projection only — raw event/issue payloads are never passed through.**
### Issue-level safe fields (`glitchtip_list_unresolved`, `glitchtip_get_issue`, `glitchtip_search`)
| Field | Notes |
|---|---|
| `issue_id` | GlitchTip issue ID (dedup key for #78) |
| `fingerprint` | When available (dedup key for #78) |
| `title` / `culprit` | Error type + short message/transaction — redactor-passed |
| `project` | Slug |
| `level` | error/warning/… |
| `status` | unresolved/… |
| `environment` | When filtered/available |
| `release` | Version string |
| `first_seen` / `last_seen` | ISO-8601 UTC |
| `event_count` / `user_count` | Numbers only — never user identities |
| `permalink` | GlitchTip web URL (the "link, not dump" principle) |
### Event-level safe fields (`glitchtip_recent_events`)
`event_id`, `timestamp`, `level`, `environment`, `release`, redactor-passed
`message`, and a **stack summary** only: top N (default 5) frames as
`module/filename:function:line` — in-app frames preferred.
### Redact / omit — never returned
Request headers; cookies; auth/session fields; user emails, usernames, IPs;
request/form bodies; query strings; local variables; full raw stack frames
(source context lines); SDK/device metadata beyond platform name; breadcrumbs;
any `extra`/`context` blobs.
Full raw frames or request context require a **separate, explicitly approved**
operation (`glitchtip.event.read_raw`) that is absent from default profiles —
same pattern as `jenkins.console.read` in the #72 design. Even then, output
passes the shared secret redactor; redaction failure ⇒ error, never raw text.
**Default output = fingerprint / release / summary + permalink.** The
permalink carries the human to the full data in GlitchTip's own UI, where its
access control applies — the MCP layer does not re-serve raw payloads.
## 6. Filtering and pagination
Filters (all optional, combinable): `project` (required for issue/event
queries), `environment`, `release`, `fingerprint`, free-text `query`
(GlitchTip search syntax, e.g. `is:unresolved`).
Pagination: cursor-based per the API. Bounds: per-page cap 50; default overall
cap 100 items; hard cap `max_pages` (default 10) against runaway loops —
mirroring `gitea_auth.api_get_all`. Truncation is **explicit** in the return
(`"truncated": true`) — never silent.
## 7. Credentials and profile requirements
Per-service profile model (`gitea-execution-profiles.md`, extended by #76):
- Env/config: `GLITCHTIP_URL`, `GLITCHTIP_ORG`, `GLITCHTIP_TOKEN_SOURCE_NAME`
(secret **name** only; value resolved at runtime, never logged/committed).
- Profile: e.g. `glitchtip-readonly` with namespaced
`allowed_operations: ["glitchtip.read", "glitchtip.event.read"]`
(+ `glitchtip.event.read_raw` only with explicit approval);
`forbidden_operations: ["glitchtip.issue.mutate", "glitchtip.admin"]`
belt-and-braces though no mutating tool exists.
- Missing URL/org/token/profile ⇒ **fail closed** before any network call.
- Read-only ⇒ no confirmation gates; identity (`glitchtip_whoami`) must work so
workflows can prove which account they read as.
## 8. Failure behavior (fail closed, clear, safe)
| Condition | Behavior |
|---|---|
| Unknown project/issue | Explicit `{"found": false, ...}` — no fuzzy matching |
| GlitchTip unreachable (DNS/timeout) | `"network error contacting GlitchTip: <redacted reason>"` — mirror `gitea_auth.api_request` conversion |
| 502/503/504 | "GlitchTip upstream unavailable" |
| 401/403 | "GlitchTip auth failed / insufficient permissions" — no credential echo |
| 429 | Honor Retry-After with capped jittered backoff (as `gitea_auth`) |
| Malformed JSON | "malformed JSON response from GlitchTip" — no raw-body dump |
| Missing profile/creds | Fail closed before any network call (§7) |
All error text passes the shared secret redactor.
## 9. Testing strategy (mocked; for the implementing package)
Mocked-GlitchTip unit tests only, per `docs/developer-testing-guidelines.md`:
- Assert method is always `GET`; URL/filter/cursor shape correct.
- **Projection tests:** response fixtures containing emails, IPs, cookies,
headers, request bodies, locals, full frames ⇒ none appear in output
(explicit negative assertions per §5's redact list).
- Stack summary: top-N frame cap enforced; source-context lines absent.
- Pagination: per-page/overall/max-pages caps; explicit `truncated` flag.
- Filters: environment/release/fingerprint/query passed through correctly.
- Failure matrix of §8 incl. no-token-in-error assertions.
- Profile gate: missing/insufficient profile ⇒ no network call
(`mock_api.assert_not_called()` pattern).
- `read_raw` op absent ⇒ raw-frame request refused without an API call.
## 10. Implementation-readiness checklist
Ready to implement once:
1. ADR-0001 owner decision #2 (namespace/placement) is made — mechanical
rename of the `glitchtip_` prefix if needed.
2. ADR-0001 owner decision #1 (repo home) is made.
3. #76 profile schema exists (or a minimal `glitchtip-readonly` profile is
hand-rolled to the same rules).
4. A pinned GlitchTip version is chosen for API-subset testing (§3).
Explicitly **not** unlocked by this document: any GlitchTip mutation, any
automatic Gitea filing (#74 designs that as a gated, explicitly-invoked
orchestrated workflow), any Gitea credentials in this boundary.
@@ -1,165 +0,0 @@
# Jenkins Repo/Branch/PR → Job Mapping — Design Notes
- **Status:** Design (implementation-ready notes; **no implementation in this repo**)
- **Issue:** #77 (parent: #72 read-only tools design; umbrella: #75; boundary: ADR-0001, #71)
- **Related docs:** [`jenkins-readonly-build-status-design.md`](jenkins-readonly-build-status-design.md)
- **Date:** 2026-07-02
## 1. Purpose
The #72 tool set addresses Jenkins jobs by **explicit fully-qualified job
path**. This document designs the layer above it: how a *(repository, branch,
PR)* tuple — the vocabulary of Gitea workflows — resolves deterministically to
a Jenkins job path, so an LLM can ask "did the build for `Gitea-Tools`
`master` pass?" without knowing Jenkins internals.
Hard constraints inherited from #72 / ADR-0001:
- **No silent guessing of job names.** Unmapped input returns an explicit
"no mapping" result — never a fuzzy match, never a constructed-and-probed
name.
- **Read-only.** Mapping introduces no Jenkins write actions.
- Lives in the **`jenkins-mcp`** boundary; no Gitea credentials involved.
## 2. Mapping format
Declarative, versioned config (TOML or JSON — match whatever config format
`jenkins-mcp` adopts; illustrated here as TOML):
```toml
version = 1
[[mapping]]
# Source side (what the caller supplies)
repo = "Scaled-Tech-Consulting/Gitea-Tools" # org/repo, exact
# Target side (where it lives in Jenkins)
job = "scaled-tech/gitea-tools" # foldered job path
type = "multibranch" # "multibranch" | "single" | "parameterized-view"
[[mapping]]
repo = "Scaled-Tech-Consulting/Timesheet"
branch = "master" # optional: branch-specific override
job = "scaled-tech/timesheet-master"
type = "single"
```
Field semantics:
| Field | Required | Meaning |
|---|---|---|
| `repo` | yes | Exact `org/repo` (case-insensitive compare, stored canonical) |
| `branch` | no | Exact branch name this entry pins; absent = all branches |
| `job` | yes | Fully-qualified Jenkins job path, folders `/`-joined |
| `type` | yes | How branch/PR resolves under the job (§3) |
Rules:
- **Exact matching only** on `repo` and `branch`. No globs in v1 (globs invite
accidental over-matching; add later behind an explicit `pattern = true` flag
if ever needed).
- Unknown `type` or malformed entry ⇒ config load fails closed with a clear
error naming the entry — a broken mapping file must not half-load.
- Duplicate `(repo, branch)` keys ⇒ load error (ambiguity is refused, not
resolved).
## 3. Resolution semantics by job type
Given caller input `(repo, branch?, pr?)`:
- **`multibranch`** — branch job addressed as `<job>/<url-encoded-branch>`
(e.g. `feature/x``feature%2Fx`). PRs addressed as `<job>/PR-<number>`
(Jenkins multibranch PR-discovery naming). Both per #72 §8.
- **`single`** — the job path is used as-is; `branch`/`pr` input beyond the
entry's pinned branch is a **no-mapping** result (a single job cannot answer
for arbitrary branches).
- **`parameterized-view`** — read-only variant for jobs that encode branch as
a build parameter: resolution returns the base job path plus a
`branch_param` filter hint the status tools may apply client-side when
scanning recent builds. It never triggers anything (read-only rule).
## 4. Precedence
Most-specific entry wins, evaluated in this order:
1. `(repo, branch)` exact entry — branch-pinned override.
2. `(repo)` entry — repo-wide (multibranch typical).
3. Nothing → **no mapping** (§5).
PR input resolves through the same chain: a PR belongs to its **base repo**'s
mapping; forks never introduce their own mapping (a fork's head repo is not
consulted — CI runs live under the base repo's job). If the base repo is
unmapped, the PR is unmapped.
Ties are impossible by construction (duplicate keys refused at load).
## 5. No-match behavior
```json
{
"mapped": false,
"repo": "org/unknown-repo",
"branch": "master",
"error": "no Jenkins job mapping for this repo/branch",
"hint": "add an entry to the jenkins-mcp mapping config"
}
```
- Deterministic, explicit, machine-checkable (`mapped: false`).
- **Never** falls back to name construction ("repo name probably equals job
name"), never probes Jenkins for candidates, never string-similarity ranks.
- The hint names the config, not a guessed job.
## 6. Where the mapping config lives
- **In the `jenkins-mcp` package/deployment** (e.g. `jenkins-mcp/mapping.toml`),
version-controlled next to the server that consumes it — *not* in Gitea-Tools
and *not* in per-user env vars (mappings are shared team facts, not
credentials).
- Path overridable via env (`JENKINS_MCP_MAPPING_FILE`) for tests/containers.
- Contains **no secrets** — job paths and repo names only — so it is safe to
commit and review like any other config.
- Reloaded at server start; a hot-reload tool is out of scope (restart is the
documented path).
## 7. Exposed tool surface (read-only)
One addition to the #72 tool set:
| Tool | Purpose |
|---|---|
| `jenkins_resolve_job` | `(repo, branch?, pr?)``{mapped, job, addressed_path, type}` or the §5 no-match result. Pure config lookup — **no Jenkins API call at all.** |
Status tools (`jenkins_latest_build` etc.) accept either an explicit job path
(as designed in #72) **or** `(repo, branch)` which they resolve via the same
mapping layer first. Resolution failure surfaces the §5 payload rather than
querying Jenkins.
## 8. Testing strategy (mocked; for the implementing package)
Config-layer tests (no network at all):
- Exact-match hit: repo-wide and branch-pinned entries.
- Precedence: branch-pinned beats repo-wide.
- Multibranch encoding: `feature/x``<job>/feature%2Fx`; PR → `<job>/PR-7`.
- `single` type with non-pinned branch ⇒ no-mapping.
- Fork PR resolves through base repo; unmapped base ⇒ no-mapping.
- Unknown repo/branch ⇒ §5 payload, and **no Jenkins client call**
(`mock_api.assert_not_called()`).
- Malformed config / duplicate keys / unknown type ⇒ load fails closed with
entry-naming error.
- No-secret check: mapping load/error paths never touch or print credentials.
Integration with mocked Jenkins API (per #72 §9): resolved path is used
verbatim in the GET URL; no write verbs anywhere.
## 9. Standalone-worthiness and readiness
#77 was split from #72 on the condition it stays "standalone only if mapping
is nontrivial." The precedence rules, fork/PR semantics, three job types, and
fail-closed config loading above are the nontrivial part; this document is the
justification.
Ready to implement in `jenkins-mcp` when #72's readiness checklist clears
(ADR-0001 owner decision #1; profile schema per #76 or hand-rolled
`jenkins-readonly`). Nothing here unlocks build triggers, deploys, or
parameterized launches.
@@ -1,68 +0,0 @@
# Multi-Service MCP Profile and Configuration Model
- **Status:** Design (no implementation in this repo yet)
- **Issue:** #76 (parent umbrella: #75; boundary decision: ADR-0001, #71)
- **Date:** 2026-07-02
## 1. Purpose and Scope
Extend the existing Gitea execution-profile model (`docs/gitea-execution-profiles.md`) into a generic **per-service** MCP profile/config model. This supports integrating Jenkins and GlitchTip into the MCP Control Plane while strictly preserving isolation and fail-closed safety.
**Crucial Constraints:**
* The shared profile/config model is a **schema / library**, **not a shared credential pool**.
* Tokens remain **service-local**; profiles are **per service**.
* Orchestrators **must not** directly hold every service credential.
## 2. Profile Schema (Per Service)
The schema reuses the proven Gitea field model, adapted per service.
```json
{
"profile_name": "readonly-metrics",
"service": "glitchtip",
"token_source_name": "GLITCHTIP_API_TOKEN_READONLY",
"allowed_operations": [
"glitchtip.event.read",
"glitchtip.issue.read"
],
"forbidden_operations": [
"glitchtip.issue.resolve",
"glitchtip.issue.delete"
]
}
```
### Schema Rules
* `allowed_operations` are **namespaced** (e.g., `gitea.issue.create`, `jenkins.build.read`, `glitchtip.event.read`).
* `forbidden_operations`, if present, **always override** `allowed_operations`.
* `token_source_name` records the source **name only, never the value**. Tokens must never be printed, logged, or included in telemetry.
## 3. Fail-Closed Behavior
The model enforces strict fail-closed constraints before any network call occurs:
* **Missing Profile:** If a requested profile is undefined for the target service, the operation fails immediately.
* **Missing Credentials:** If the `token_source_name` cannot be resolved to a valid token at runtime, the operation fails immediately without retrying or prompting.
## 4. Environment Overrides
Profiles can be dynamically overridden or injected via environment variables, following the established hierarchy:
1. **Explicit Environment Variable:** (Highest precedence) e.g., `MCP_GLITCHTIP_TOKEN` overrides any JSON profile.
2. **Profile Mapping in JSON:** Resolved via `token_source_name` (e.g., `GLITCHTIP_API_TOKEN_READONLY`) mapping to an environment variable or secret store.
3. **No Auth:** Fails closed.
## 5. Audit Logging
To maintain accountability across multi-service workflows, all mutating actions must include the audit identity and source:
* The audit log must record the `profile_name`, the orchestrator source (e.g., `sysadmin`, `jenkins-mcp`), and the action taken.
* The audit system must sanitize all output to ensure tokens are stripped (see `safety-model.md`).
## 6. Backward Compatibility
The existing Gitea profile behavior (`gitea_whoami`, etc.) remains strictly backward compatible. The generic profile library will parse existing Gitea profile objects without requiring them to migrate their schemas, defaulting the `service` attribute to `gitea`.
## 7. Implementation Boundary
Per the namespace decisions in #71 and #75, this generic model belongs in the `common` package or library. It will be imported by `gitea-mcp` (this repo), `jenkins-mcp`, and `glitchtip-mcp` without forcing a monolithic architecture.
+1 -1
View File
@@ -5,5 +5,5 @@ This document describes how credentials and sensitive environment variables are
## Separate Credentials ## Separate Credentials
Even though multiple MCP servers share the same monorepo, they **must** have separate credentials and runtimes. Even though multiple MCP servers share the same monorepo, they **must** have separate credentials and runtimes.
- **No Shared Environments**: Each MCP server (`gitea-mcp`, `jenkins-mcp`, `glitchtip-mcp`, `ops-mcp`, etc.) must be instantiated as an independent service with its own dedicated `.env` configuration file. - **No Shared Environments**: Each MCP server (`gitea-mcp`, `jenkins-mcp`, `ops-mcp`, etc.) must be instantiated as an independent service with its own dedicated `.env` configuration file.
- **Strict Isolation**: A server will only have access to the credentials required for its specific trust boundary. For instance, `gitea-mcp` has no access to Jenkins or Ops authentication tokens. - **Strict Isolation**: A server will only have access to the credentials required for its specific trust boundary. For instance, `gitea-mcp` has no access to Jenkins or Ops authentication tokens.
-8
View File
@@ -13,11 +13,3 @@ To maintain a secure environment, all secrets, tokens, passwords, and sensitive
- System and application logs - System and application logs
- Tool return values/outputs - Tool return values/outputs
- Any form of persistent storage or console output - Any form of persistent storage or console output
## 4. Read-Only First Policy
By default, MCP servers (such as `jenkins-mcp` and `ops-mcp`) operate in a **read-only** mode. Mutation capabilities are deny-by-default and fail-closed.
## 5. Mutation Gating
Any mutating action (e.g., Gitea issue creation from GlitchTip, or Jenkins builds) must be explicitly allowed by the execution profile.
- **Jenkins build triggers** are explicitly deferred for phase 1.
- **GlitchTip to Gitea issue filing** is documented as a gated, orchestrated workflow, not a direct unprompted automatic action.
+1 -2
View File
@@ -2,7 +2,7 @@
This document defines the strict boundaries between the different MCP server packages within the monorepo. This document defines the strict boundaries between the different MCP server packages within the monorepo.
The project is named **MCP Control Plane** and lives in the `mcp-control-plane` repository. It groups the following packages: `common`, `gitea-mcp`, `jenkins-mcp`, `glitchtip-mcp`, `ops-mcp`, and `release-mcp`. The project is named **MCP Control Plane** and lives in the `mcp-control-plane` repository. It groups the following packages: `common`, `gitea-mcp`, `jenkins-mcp`, `ops-mcp`, and `release-mcp`.
## 1. Architectural Philosophy ## 1. Architectural Philosophy
- **One MCP Server per Trust Boundary**: While the packages share a monorepo, their runtime services must remain entirely separate. There is no single "everything" server. - **One MCP Server per Trust Boundary**: While the packages share a monorepo, their runtime services must remain entirely separate. There is no single "everything" server.
@@ -10,5 +10,4 @@ The project is named **MCP Control Plane** and lives in the `mcp-control-plane`
## 2. Package-Specific Boundaries ## 2. Package-Specific Boundaries
- **gitea-mcp**: Restricted to source-control and work-item capabilities (issues, PRs, comments). This package **must not** have Jenkins or Ops credentials, nor can it execute deploy operations. - **gitea-mcp**: Restricted to source-control and work-item capabilities (issues, PRs, comments). This package **must not** have Jenkins or Ops credentials, nor can it execute deploy operations.
- **jenkins-mcp**: Focused on CI/CD capabilities. This package **must not** have Ops credentials unless explicitly configured for a specific, isolated pipeline later. - **jenkins-mcp**: Focused on CI/CD capabilities. This package **must not** have Ops credentials unless explicitly configured for a specific, isolated pipeline later.
- **glitchtip-mcp**: Dedicated to observability and error reporting. This package **must not** have Gitea write credentials or Jenkins deploy capabilities.
- **ops-mcp**: Dedicated to live environment and host checks. In its initial state, this package starts as strictly read-only (e.g., health checks, status checks, log reading). - **ops-mcp**: Dedicated to live environment and host checks. In its initial state, this package starts as strictly read-only (e.g., health checks, status checks, log reading).