You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Operator: externalAuthConfigRef (headerInjection) on an MCPServerEntry is put into the vMCP config but its secret is not mounted when outgoingAuth.source: inline → vMCP fails to start #6837
A VirtualMCPServer with spec.outgoingAuth.source: inline and a backend MCPServerEntry that references a headerInjectionMCPExternalAuthConfig via spec.externalAuthConfigRef produces a vMCP pod that crashes on startup. The generated vMCP configuration contains the header-injection outgoing auth for that backend (referencing the env var TOOLHIVE_HEADER_INJECTION_VALUE_<CONFIG_NAME>), but the operator does not add that env var (secretKeyRef) to the vMCP Deployment.
VirtualMCPServer with outgoingAuth.source: inline (other backends configured under outgoingAuth.backends, one of them upstreamInject)
Backends as MCPServerEntry (remote streamable-HTTP servers in other namespaces)
Steps to reproduce
MCPExternalAuthConfigmy-backend-auth of type: headerInjection (headerName: Authorization, valueSecretRef to an existing secret/key).
MCPServerEntrymy-backend in the vMCP's group with spec.externalAuthConfigRef: {name: my-backend-auth}.
VirtualMCPServer with spec.outgoingAuth.source: inline (no entry for my-backend under outgoingAuth.backends).
Observed
MCPServerEntry and MCPExternalAuthConfig are Valid; the operator rolls the vMCP Deployment.
The new vMCP pod exits:
Failed to load configuration: failed to process config: outgoingAuth: backend my-backend: environment variable TOOLHIVE_HEADER_INJECTION_VALUE_MY_BACKEND_AUTH not set or empty
The Deployment has no env var TOOLHIVE_HEADER_INJECTION_VALUE_MY_BACKEND_AUTH (only those for configs referenced under spec.outgoingAuth.backends).
Old pods keep running thanks to the PDB, the rollout hangs.
Workaround (works)
Reference the same config under VirtualMCPServer.spec.outgoingAuth.backends.my-backend (type: externalAuthConfigRef, externalAuthConfigRef: {name: my-backend-auth}) and remove it from the MCPServerEntry. The operator then mounts the secret and the backend works.
Analysis (v0.53.0)
buildOutgoingAuthEnvVars mounts secrets for ExternalAuthConfigs discovered on workloads (MCPServers, MCPRemoteProxies, MCPServerEntries) only when OutgoingAuth.Source == discovered; in inline mode it mounts only spec.outgoingAuth.backends and default. The generated vMCP config, however, does include the entry's header-injection auth in inline mode. Config generation and env-var mounting disagree for this combination.
Expected
Either mount the secret in inline mode as well (when an MCPServerEntry carries externalAuthConfigRef and no inline override exists), or do not put the entry's auth into the config in inline mode and surface a clear condition/warning on the VirtualMCPServer/MCPServerEntry ("externalAuthConfigRef on MCPServerEntry is ignored with outgoingAuth.source=inline"). Today the result is a crash-looping vMCP with a hard-to-trace error.
Looking at main (87ed79d94), I see the same mismatch. The affected secret-handling path also covers other auth types.
Same discovery, different gate. buildOutgoingAuthConfig calls discoverExternalAuthConfigs unconditionally, while buildOutgoingAuthEnvVars calls discoverExternalAuthConfigSecrets only when source == discovered. Both use getExternalAuthConfigNameFromWorkload to resolve workload refs.
Scope beyond headerInjection. getExternalAuthConfigSecretEnvVars emits env vars for tokenExchange, headerInjection, obo and xaa, and the helper above covers MCPServer and MCPRemoteProxy as well as MCPServerEntry. headerInjection, tokenExchange and obo have secret env-var checks in processBackendAuthStrategy at config load, explaining the startup failure for headerInjection reported here. xaa also depends on secret env vars, although its validation path differs from the YAML loader checks above.
On the two expectations you listed. In inline mode, OutgoingAuthConfig.ApplyToBackend uses config-based auth, so the strategy the operator wrote into the ConfigMap is the one used at runtime. Mounting the required secret in inline mode would therefore preserve the existing auth selection behavior. Dropping workload-referenced auth from the config instead would also change behavior for strategies that require no secret env vars (unauthenticated, awsSts, upstreamInject).
One note on test coverage: the case named "inline mode ignores discovered configs" uses the same MCPExternalAuthConfig for both the workload ref and the inline override, so it doesn't distinguish the two paths or cover the no-override combination reported here. "Discovered" also means two different things in this area: the operator reading externalAuthConfigRef off a workload, and the runtime's source: discovered backend watcher.
Open question for the fix: whether the env path should mirror the shouldAssign skip so secrets aren't mounted for backends overridden inline, or whether the env vars should be derived from the effective auth configuration. The latter could help keep the two paths aligned, but would be a broader change.
@nilsberberich, if you're planning to fix this yourself, I'm happy to leave it with you. Otherwise, I'd like to pick it up, with tests for the uncovered combination. Could a maintainer assign it to me?
@hellouz818 thanks for the thorough analysis, and yes, please go ahead — I'm not working on a fix myself. Happy to verify the PR in our test cluster with the reported combination (MCPServerEntry with externalAuthConfigRef (headerInjection) behind a VirtualMCPServer with outgoingAuth.source: inline) once it's up.
Activity
hellouz818 commentedon Oct 16, 2026
Looking at
main(87ed79d94), I see the same mismatch. The affected secret-handling path also covers other auth types.Same discovery, different gate.
buildOutgoingAuthConfigcallsdiscoverExternalAuthConfigsunconditionally, whilebuildOutgoingAuthEnvVarscallsdiscoverExternalAuthConfigSecretsonly whensource == discovered. Both usegetExternalAuthConfigNameFromWorkloadto resolve workload refs.Scope beyond headerInjection.
getExternalAuthConfigSecretEnvVarsemits env vars fortokenExchange,headerInjection,oboandxaa, and the helper above coversMCPServerandMCPRemoteProxyas well asMCPServerEntry.headerInjection,tokenExchangeandobohave secret env-var checks inprocessBackendAuthStrategyat config load, explaining the startup failure forheaderInjectionreported here.xaaalso depends on secret env vars, although its validation path differs from the YAML loader checks above.On the two expectations you listed. In inline mode,
OutgoingAuthConfig.ApplyToBackenduses config-based auth, so the strategy the operator wrote into the ConfigMap is the one used at runtime. Mounting the required secret in inline mode would therefore preserve the existing auth selection behavior. Dropping workload-referenced auth from the config instead would also change behavior for strategies that require no secret env vars (unauthenticated,awsSts,upstreamInject).One note on test coverage: the case named "inline mode ignores discovered configs" uses the same
MCPExternalAuthConfigfor both the workload ref and the inline override, so it doesn't distinguish the two paths or cover the no-override combination reported here. "Discovered" also means two different things in this area: the operator readingexternalAuthConfigRefoff a workload, and the runtime'ssource: discoveredbackend watcher.Open question for the fix: whether the env path should mirror the
shouldAssignskip so secrets aren't mounted for backends overridden inline, or whether the env vars should be derived from the effective auth configuration. The latter could help keep the two paths aligned, but would be a broader change.@nilsberberich, if you're planning to fix this yourself, I'm happy to leave it with you. Otherwise, I'd like to pick it up, with tests for the uncovered combination. Could a maintainer assign it to me?
jhrozek commentedon Oct 16, 2026
@hellouz818 thank you for volunteerig to take this issue! Assigned to you!
nilsberberich commentedon Oct 16, 2026
@hellouz818 thanks for the thorough analysis, and yes, please go ahead — I'm not working on a fix myself. Happy to verify the PR in our test cluster with the reported combination (
MCPServerEntrywithexternalAuthConfigRef(headerInjection) behind aVirtualMCPServerwithoutgoingAuth.source: inline) once it's up.