Double auth transformation when MCPServer is behind VirtualMCPServer
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 2.2k
- Forks
- 300
- Avg merge
- 1d 15h
- Merged PRs (30d)
- 184
Description
Problem
When a VirtualMCPServer fronts an MCPServer that has externalAuthConfigRef (or authServerRef), auth transformations are applied twice:
- The vMCP performs outgoing auth (token exchange, upstream inject, etc.) and sends an already-transformed token
- The MCPServer's proxy applies the same transformation again via its own middleware
This affects tokenExchange and embeddedAuthServer type configs. Other types (headerInjection, upstreamInject, unauthenticated) are not affected because the MCPServer proxy either no-ops or defers to vMCP for those.
Root cause
externalAuthConfigRef on MCPServer serves two purposes:
- Proxy middleware config — tells the MCPServer's proxy runner how to transform tokens (for standalone access)
- Discovery metadata — tells the vMCP what auth strategy the backend needs (
discoveredmode)
These are conflated into one field. Both the vMCP and the MCPServer independently act on the same config.
Current workaround
Don't set externalAuthConfigRef on the MCPServer. Specify outgoing auth inline on the vMCP's spec.outgoingAuth.backends. The MCPServer's proxy passes tokens through without transformation. Downside: the MCPServer can't work standalone.
Proposed solution: authMode field
Add an authMode field to MCPServerSpec and MCPRemoteProxySpec:
// AuthMode controls whether this server applies its own auth transformation.
// - "self" (default): Server applies auth middleware from externalAuthConfigRef/authServerRef.
// Use when the server is accessed directly by clients.
// - "external": Auth is handled by an external proxy (e.g., VirtualMCPServer).
// externalAuthConfigRef is retained as discovery metadata only; no auth
// middleware is added to the proxy. authServerRef is not started.
// +kubebuilder:validation:Enum=self;external
// +kubebuilder:default=self
// +optional
AuthMode string `json:"authMode,omitempty"`
Behavior
authMode |
externalAuthConfigRef effect |
authServerRef effect |
|---|---|---|
self (default) |
Configures proxy middleware (token exchange, bearer token, etc.) | Starts embedded auth server + upstream swap middleware |
external |
Retained for vMCP discovery only, no middleware added | No auth server started, no upstream swap middleware |
Files to change
CRD types:
cmd/thv-operator/api/v1alpha1/mcpserver_types.go— addAuthModefield toMCPServerSpeccmd/thv-operator/api/v1alpha1/mcpremoteproxy_types.go— addAuthModefield toMCPRemoteProxySpec
RunConfig generation (the core fix):
cmd/thv-operator/controllers/mcpserver_runconfig.go(~line 242) — gateAddExternalAuthConfigOptionsandValidateAndAddAuthServerRefOptionsonauthMode != "external"cmd/thv-operator/controllers/mcpremoteproxy_runconfig.go(~line 135) — same gate
Deployment generation (env vars, volumes):
cmd/thv-operator/controllers/mcpserver_controller.go(~lines 1098, 1210) — gateGenerateTokenExchangeEnvVars,GenerateBearerTokenEnvVar,GenerateAuthServerConfigonauthMode != "external"cmd/thv-operator/controllers/mcpremoteproxy_controller.go— equivalent gates
CRD generation:
- Run
task operator-generate,task operator-manifests,task crdref-gen
Tests:
- Unit tests for RunConfig generation with
authMode=external - Integration tests verifying no middleware is added when
authMode=external - Existing tests continue passing (default
authMode=selfpreserves current behavior)
Documentation:
docs/operator/crd-api.md— document the new fielddocs/operator/virtualmcpserver-kubernetes-guide.md— guidance on when to useexternal
Backward compatibility
Default is self — existing resources behave identically. No CRD migration needed.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the AuthMode fields in cmd/thv-operator/api/v1alpha1/mcpserver_types.go and mcpremoteproxy_types.go, then trace RunConfig and deployment generation in the listed controllers. Run the specified operator-generation tasks and add RunConfig and integration coverage for authMode=external. Done means external mode keeps discovery metadata but adds no auth middleware, auth server, environment variables, or volumes, while the default preserves existing behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, kubernetes
- Domain
- backend, devops
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 52/100