From 2771c6ea1f80c8912506544e23c7b5ea71759411 Mon Sep 17 00:00:00 2001 From: blublinsky Date: Wed, 27 May 2026 13:17:05 +0100 Subject: [PATCH] Enable MCP write tools by setting read_only=false in openshift-mcp-server config --- .ai/spec/what/security.md | 2 +- internal/controller/utils/mcp_server_config.go | 3 +++ internal/controller/utils/mcp_server_config_test.go | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.ai/spec/what/security.md b/.ai/spec/what/security.md index e27cd385e..daa6090ea 100644 --- a/.ai/spec/what/security.md +++ b/.ai/spec/what/security.md @@ -32,7 +32,7 @@ The operator enforces security boundaries through RBAC, network policies, pod se 15. MCP server header secrets must contain a specific key `header` (constant `MCPSECRETDATAPATH`) and are mounted read-only at `/etc/mcp/headers//`. ### OpenShift MCP Server Security -16. The shipped OpenShift MCP server runs with the `--read-only` flag and is configured via a TOML config file that blocks access to Secret and RBAC resources, preventing secret data from reaching the LLM. +16. The shipped OpenShift MCP server is configured via a TOML config file (`read_only = false`, denied Secret/RBAC resources) so the LLM can use core write tools (e.g. `resources_create_or_update`) while secret data stays blocked at the server level. The sidecar does not pass `--read-only` on the command line; `read_only = false` in TOML overrides the RHEL image build default of `ReadOnly: true`. 17. The denied resources are configured in the `openshift-mcp-server-config` ConfigMap as a TOML config with entries blocking `core/v1/secrets`, `rbac.authorization.k8s.io/v1/roles`, `rbac.authorization.k8s.io/v1/rolebindings`, `rbac.authorization.k8s.io/v1/clusterroles`, and `rbac.authorization.k8s.io/v1/clusterrolebindings`. 18. User-defined MCP servers (via `spec.mcpServers`) are the user's responsibility to secure. diff --git a/internal/controller/utils/mcp_server_config.go b/internal/controller/utils/mcp_server_config.go index 4522f1150..d0bf55097 100644 --- a/internal/controller/utils/mcp_server_config.go +++ b/internal/controller/utils/mcp_server_config.go @@ -20,11 +20,14 @@ import ( // Other sensitive resource types (RBAC) are also denied as defense in depth. // Toolsets are listed explicitly so upstream default changes do not affect OLS; the metrics toolset // uses in-cluster Thanos Querier and Alertmanager endpoints. +// read_only = false is required: openshift-mcp-server-rhel9 sets ReadOnly=true in build-time defaults; +// omitting this leaves only readOnlyHint tools (no resources_create_or_update, etc.). const OpenShiftMCPServerConfigTOML = `# Denied resources prevent the MCP server from accessing these Kubernetes resource types. # This ensures secret data never reaches the LLM through the shipped MCP server. # User-brought MCP servers (spec.mcpServers) are the user's responsibility to secure. # Toolsets are pinned explicitly so upstream default changes do not affect OLS. +read_only = false toolsets = ["core", "config", "helm", "metrics"] [[denied_resources]] diff --git a/internal/controller/utils/mcp_server_config_test.go b/internal/controller/utils/mcp_server_config_test.go index 90d114a81..7af84b76f 100644 --- a/internal/controller/utils/mcp_server_config_test.go +++ b/internal/controller/utils/mcp_server_config_test.go @@ -24,6 +24,9 @@ func TestOpenShiftMCPServerConfigTOML(t *testing.T) { t.Error("TOML config should use denied_resources table array syntax") } + if !strings.Contains(config, "read_only = false") { + t.Error("TOML config must set read_only = false to override openshift-mcp-server-rhel9 build defaults") + } if !strings.Contains(config, `toolsets = ["core", "config", "helm", "metrics"]`) { t.Error("TOML config should pin default toolsets explicitly") }