Skip to content

Mode-aware ConfigMap and RBAC generation in operator #3003

@jhrozek

Description

@jhrozek

Summary

Update the VirtualMCPServer operator to generate different ConfigMap content and RBAC resources based on the outgoingAuth.source field (dynamic vs static mode).

Background

Part of the vMCP K8s-aware refactor (THV-2884). The operator behavior changes based on discovery mode:

Mode ConfigMap RBAC vMCP K8s Access
discovered (dynamic) Minimal Full Namespace-scoped read + status write
inline (static) Full with backends None Zero

Proposed Design

Dynamic Mode ConfigMap

name: my-vmcp-server
group_ref: my-group
namespace: default
incoming_auth: {...}
outgoing_auth:
  source: discovered  # vMCP discovers at runtime
aggregation: {...}
composite_tools: [...]

Static Mode ConfigMap

name: my-vmcp-server
group_ref: my-group
namespace: default
incoming_auth: {...}
outgoing_auth:
  source: inline
backends:
  - name: github-mcp
    url: http://github-mcp.default.svc:8080
    auth:
      type: token_exchange
      # ... full auth config
aggregation: {...}
composite_tools: [...]

RBAC Rules (Dynamic Mode Only)

vmcpRBACRules = []rbacv1.PolicyRule{
    {
        APIGroups: []string{""},
        Resources: []string{"configmaps", "secrets"},
        Verbs:     []string{"get", "list", "watch"},
    },
    {
        APIGroups: []string{"toolhive.stacklok.dev"},
        Resources: []string{"mcpgroups", "mcpservers", "mcpexternalauthconfigs", "mcptoolconfigs"},
        Verbs:     []string{"get", "list", "watch"},
    },
    {
        APIGroups: []string{"toolhive.stacklok.dev"},
        Resources: []string{"virtualmcpservers/status"},
        Verbs:     []string{"update", "patch"},
    },
}

Files to Modify

  • cmd/thv-operator/controllers/virtualmcpserver_vmcpconfig.go - Mode-aware ConfigMap generation
  • cmd/thv-operator/controllers/virtualmcpserver_controller.go - Conditional RBAC creation
  • cmd/thv-operator/pkg/vmcpconfig/converter.go - Mode-aware config conversion

Dependencies

Acceptance Criteria

  • Dynamic mode: minimal ConfigMap (groupRef, incomingAuth, aggregation)
  • Static mode: full ConfigMap with backends and inline auth
  • Dynamic mode: create full RBAC including status update permission
  • Static mode: no RBAC resources (vMCP has no K8s API access)
  • Clean up RBAC if mode switches from dynamic to static
  • Clear branching logic based on source field
  • Unit tests for both modes

Notes

  • Mode switching triggers pod restart via ConfigMap checksum change
  • Static mode secrets mounted as env vars (existing pattern)
  • Dynamic mode secrets fetched via K8s API at runtime

Metadata

Metadata

Assignees

Labels

enhancementNew feature or requestgoPull requests that update go codekubernetesItems related to KubernetesoperatorvmcpVirtual MCP Server related issues

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions