Skip to content

Commit 66fff4a

Browse files
yroblataskbot
andauthored
Implement discovered mode for vMCP authentication (#2747)
Add runtime authentication discovery for VirtualMCPServer, enabling vMCP to automatically discover and apply authentication configurations from backend MCPServer ExternalAuthConfigRefs. Changes: - Add DiscoverAndResolveAuth() function in converters package to fetch and convert MCPExternalAuthConfig to auth strategy metadata with dynamic secret resolution from Kubernetes - Implement discoverAuthConfig() in k8s workload discoverer to populate backend AuthStrategy and AuthMetadata fields from ExternalAuthConfigRef - Add applyAuthConfigToBackend() method in aggregator discoverer to decide which auth to use based on source mode (discovered/mixed/inline) - Add comprehensive unit tests for auth discovery scenarios (token exchange, header injection, no auth, error cases) - Add e2e test validating discovered auth with real MCP requests, tool aggregation, and tool calls through VirtualMCPServer The discovered mode allows backends with heterogeneous authentication (OAuth 2.0 token exchange, API key header injection, or no auth) to coexist in the same group, with vMCP automatically discovering and applying the appropriate auth to each backend request. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: taskbot <[email protected]>
1 parent f657a07 commit 66fff4a

File tree

6 files changed

+1748
-16
lines changed

6 files changed

+1748
-16
lines changed

pkg/vmcp/aggregator/discoverer.go

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -133,27 +133,15 @@ func (d *backendDiscoverer) Discover(ctx context.Context, groupRef string) ([]vm
133133
continue
134134
}
135135

136-
// Apply authentication configuration if provided
137-
var authStrategy string
138-
var authMetadata map[string]any
139-
if d.authConfig != nil {
140-
authStrategy, authMetadata = d.authConfig.ResolveForBackend(name)
141-
backend.AuthStrategy = authStrategy
142-
backend.AuthMetadata = authMetadata
143-
if authStrategy != "" {
144-
logger.Debugf("Backend %s configured with auth strategy: %s", name, authStrategy)
145-
}
146-
}
136+
// Apply authentication configuration to backend
137+
d.applyAuthConfigToBackend(backend, name)
147138

148139
// Set group metadata (override user labels to prevent conflicts)
149140
if backend.Metadata == nil {
150141
backend.Metadata = make(map[string]string)
151142
}
152143
backend.Metadata["group"] = groupRef
153144

154-
logger.Debugf("Discovered backend %s: %s (%s) with health status %s",
155-
backend.ID, backend.BaseURL, backend.TransportType, backend.HealthStatus)
156-
157145
backends = append(backends, *backend)
158146
}
159147

@@ -165,3 +153,58 @@ func (d *backendDiscoverer) Discover(ctx context.Context, groupRef string) ([]vm
165153
logger.Infof("Discovered %d backends in group %s", len(backends), groupRef)
166154
return backends, nil
167155
}
156+
157+
// applyAuthConfigToBackend applies authentication configuration to a backend based on the source mode.
158+
// It determines whether to use discovered auth from the MCPServer or auth from the vMCP config.
159+
//
160+
// Auth resolution logic:
161+
// - "discovered" mode: Use discovered auth if available, otherwise fall back to Default or backend-specific config
162+
// - "mixed" mode: Use discovered auth unless there's an explicit backend override in config
163+
// - "inline" mode (or ""): Always use config-based auth, ignore discovered auth
164+
// - unknown mode: Default to config-based auth for safety
165+
//
166+
// When useDiscoveredAuth is false, ResolveForBackend is called which handles:
167+
// 1. Backend-specific config (d.authConfig.Backends[backendName])
168+
// 2. Default config fallback (d.authConfig.Default)
169+
// 3. No auth if neither is configured
170+
func (d *backendDiscoverer) applyAuthConfigToBackend(backend *vmcp.Backend, backendName string) {
171+
if d.authConfig == nil {
172+
return
173+
}
174+
175+
// Determine if we should use discovered auth or config-based auth
176+
var useDiscoveredAuth bool
177+
switch d.authConfig.Source {
178+
case "discovered":
179+
// In discovered mode, use auth discovered from MCPServer (if any exists)
180+
// If no auth is discovered, fall back to config-based auth via ResolveForBackend
181+
// which will use backend-specific config, then Default, then no auth
182+
useDiscoveredAuth = backend.AuthStrategy != ""
183+
case "mixed":
184+
// In mixed mode, use discovered auth as default, but allow config overrides
185+
// If there's no explicit config for this backend, use discovered auth
186+
_, hasExplicitConfig := d.authConfig.Backends[backendName]
187+
useDiscoveredAuth = !hasExplicitConfig && backend.AuthStrategy != ""
188+
case "inline", "":
189+
// For inline mode or empty source, always use config-based auth
190+
// Ignore any discovered auth from backends
191+
useDiscoveredAuth = false
192+
default:
193+
// Unknown source mode - default to config-based auth for safety
194+
logger.Warnf("Unknown auth source mode: %s, defaulting to config-based auth", d.authConfig.Source)
195+
useDiscoveredAuth = false
196+
}
197+
198+
if useDiscoveredAuth {
199+
// Keep the auth discovered from MCPServer (already populated in backend)
200+
logger.Debugf("Backend %s using discovered auth strategy: %s", backendName, backend.AuthStrategy)
201+
} else {
202+
// Use auth from config (inline mode or explicit override in mixed mode)
203+
authStrategy, authMetadata := d.authConfig.ResolveForBackend(backendName)
204+
if authStrategy != "" {
205+
backend.AuthStrategy = authStrategy
206+
backend.AuthMetadata = authMetadata
207+
logger.Debugf("Backend %s configured with auth strategy from config: %s", backendName, authStrategy)
208+
}
209+
}
210+
}

0 commit comments

Comments
 (0)