From 4b1cec560b5c8b73596ef328e0ca47f79ef22224 Mon Sep 17 00:00:00 2001 From: Ondrej Metelka Date: Thu, 2 Jul 2026 21:20:58 +0200 Subject: [PATCH] OLS-3185 spec: operator MCP auto-injection and disableDefaultMCP Add disableDefaultMCP field to ToolsSpec CRD spec. Add rules for auto-injecting the default OpenShift MCP server into sandbox pods when OLSConfig.introspectionEnabled is true, and cross-operator RBAC to read OLSConfig. Co-authored-by: Cursor --- .ai/spec/what/crd-api.md | 5 +++-- .ai/spec/what/sandbox-execution.md | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.ai/spec/what/crd-api.md b/.ai/spec/what/crd-api.md index 4a27d58c..fe025e8f 100644 --- a/.ai/spec/what/crd-api.md +++ b/.ai/spec/what/crd-api.md @@ -39,7 +39,8 @@ Kubernetes API surface for the agentic operator. **Lifecycle and gates** are in 33. **EscalationResult**: `status.summary`, `status.content`, optional `failureReason`, `sandbox`; no `retryIndex`. 34. **RemediationOption**: Cohesion rules require `diagnosis` and `proposal` to be paired when present; `components` holds schemaless JSON for adapter data shaped by `spec.analysisOutput.schema`. 35. **RBACResult / RBACRule**: Analysis MAY request namespace-scoped and cluster-scoped rules with verb/apigroup/resource metadata and mandatory `justification`; `namespace` on rules MUST align with proposal targeting rules (validated at runtime by policy engine per field comments). -36. **ToolsSpec**: MAY include `skills` (unique images), `mcpServers` (unique names), `requiredSecrets` (unique names). `SkillsSource.image` MUST be a valid pullspec; optional `paths` restrict mounted subtrees. +36. **ToolsSpec**: MAY include `skills` (unique images), `mcpServers` (unique names), `requiredSecrets` (unique names), and `disableDefaultMCP` (bool). `SkillsSource.image` MUST be a valid pullspec; optional `paths` restrict mounted subtrees. +36a. **ToolsSpec — `disableDefaultMCP`**: Optional bool, default `false`. When `true`, the operator MUST NOT auto-inject the default OpenShift MCP server into `LIGHTSPEED_MCP_SERVERS` for sandbox pods using this `ToolsSpec`, even when introspection is enabled on the classic `OLSConfig`. User-defined `mcpServers` entries are unaffected. When `false` or absent and introspection is enabled, the operator prepends the default OpenShift MCP server entry to the MCP server list (see `sandbox-execution.md`). 37. **SecretRequirement**: Names a namespace-local `Secret`; `mountAs` discriminates `EnvVar` vs `FilePath` with required nested config per type. 38. **MCPHeaderValueSource**: Discriminated by `type`; `Secret` requires nested `secret` name reference. 39. **Result CR ownership**: Result CRs MUST declare controller `ownerReferences` to their `Proposal` for GC; naming follows operator conventions (see `sandbox-execution.md` for when they are created). @@ -79,7 +80,7 @@ Kubernetes API surface for the agentic operator. **Lifecycle and gates** are in - `metadata.name`, `metadata.namespace`, `spec.*`, `status.*` ### Shared / embedded types -- `ToolsSpec`: `skills[]`, `mcpServers[]`, `requiredSecrets[]` +- `ToolsSpec`: `skills[]`, `mcpServers[]`, `requiredSecrets[]`, `disableDefaultMCP` - `SkillsSource`: `image`, `paths[]` - `SecretRequirement`: `name`, `description`, `mountAs.*` - `StepResultRef`: `name`, `outcome` diff --git a/.ai/spec/what/sandbox-execution.md b/.ai/spec/what/sandbox-execution.md index d3a9dcb8..6eb2cfb1 100644 --- a/.ai/spec/what/sandbox-execution.md +++ b/.ai/spec/what/sandbox-execution.md @@ -44,6 +44,8 @@ Behavioral specification for how workflow steps run inside ephemeral **sandboxes 17. **Secrets — MCP headers**: When an MCP header sources a Secret, the template MUST mount that secret on a dedicated read-only path suitable for header injection configuration. 18. **Skills volumes**: Skills MUST be conveyed as OCI image volume(s) on the sandbox pod template; when `SkillsSource.paths` is set, the controller MUST mount each path as a `subPath` under the configured skills mount root using stable mount naming derived from the path’s final segment. When multiple `skills` entries exist in `ToolsSpec`, template derivation MUST apply image/path patching based on the **first** non-empty skills source (current behavior). 19. **MCP servers**: MCP configuration MUST be serialized to an environment variable payload listing servers, URLs, timeouts, and header sources so the agent runtime can open MCP connections without CR-specific code in the agent. +19a. **Default MCP server auto-injection**: When the classic `OLSConfig` CR has `spec.olsConfig.introspectionEnabled` set to `true` (or absent, since it defaults to `true`) AND the effective `ToolsSpec` for the step does NOT have `disableDefaultMCP: true`, the operator MUST prepend the default OpenShift MCP server entry to the MCP server list before serializing to `LIGHTSPEED_MCP_SERVERS`. The default entry MUST use: name `openshift`, URL pointing to the `Service` created by the classic `lightspeed-operator` for the ocp-mcp sidecar (e.g., `http://openshift-mcp-server..svc:8080/mcp`), and an `Authorization` header with source `ServiceAccountToken`. When `introspectionEnabled` is `false` or `disableDefaultMCP` is `true`, the default entry MUST NOT be injected. User-defined `mcpServers` from the `ToolsSpec` are always included regardless of this flag. +19b. **OLSConfig cross-operator read**: The agentic operator MUST have RBAC to `get` and `watch` `OLSConfig` resources (API group `ols.openshift.io`, version `v1alpha1`) to read `introspectionEnabled`. When no `OLSConfig` CR exists, the operator MUST behave as if `introspectionEnabled` is `true` (matching the classic operator's default). 20. **Sandbox observability patch**: Immediately after creating a claim (or Pod in `bare-pod` mode), the controller MUST patch `Proposal.status.steps..sandbox` with the resource name and operator namespace so consoles/CLIs can tail logs before the sandbox is ready. In `bare-pod` mode, `status.steps..sandbox.claimName` MUST be set to the Pod name (same field used for SandboxClaim names in `sandbox-claim` mode). 21. **Execution RBAC materialization**: When the approved remediation option includes RBAC requests, before execution the controller MUST create a **per-proposal ServiceAccount** named `ls-exec-{proposal-namespace}-{proposal-name}` in the operator namespace (truncated to 63 chars), then create namespace-scoped `Role`+`RoleBinding` pairs in each target namespace and `ClusterRole`+`ClusterRoleBinding` for cluster-scoped rules, binding subjects to **this per-proposal SA** (not the shared `lightspeed-agent` SA). The per-proposal SA MUST NOT carry an owner reference — cross-namespace owner refs are unsupported by Kubernetes GC (the SA lives in the operator namespace while the Proposal may be in a different namespace). Cleanup is handled explicitly by `cleanupExecutionRBAC` via the Proposal's finalizer. Idempotent create MUST tolerate existing objects (`AlreadyExists` is a no-op). Only execution sandbox pods use this SA — analysis and verification pods continue using `lightspeed-agent`. The operator's `cluster-admin` privilege (external prerequisite) ensures no RBAC escalation issues when creating arbitrary Roles/SAs. See `agentic-security.md` rules 7-15 for full specification. 22. **RBAC subjects namespace**: RoleBindings MUST reference the **per-proposal service account** in the **operator namespace** (where sandbox pods run), even when roles live in target namespaces.