Skip to content

Commit 28f8eaf

Browse files
yroblataskbot
andauthored
feat: add validation command for vmcp (#2364)
* feat: add validation command for vmcp Related-to: stacklok/stacklok-epics#153 * fix lint * fix copilot issues --------- Co-authored-by: taskbot <taskbot@users.noreply.github.com>
1 parent a3199a6 commit 28f8eaf

File tree

7 files changed

+2212
-3
lines changed

7 files changed

+2212
-3
lines changed

cmd/vmcp/app/commands.go

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/spf13/viper"
99

1010
"github.com/stacklok/toolhive/pkg/logger"
11+
"github.com/stacklok/toolhive/pkg/vmcp/config"
1112
)
1213

1314
var rootCmd = &cobra.Command{
@@ -120,10 +121,47 @@ This command checks:
120121
}
121122

122123
logger.Infof("Validating configuration: %s", configPath)
123-
// TODO: Validate configuration
124-
// This will be implemented in a future PR when pkg/vmcp is added
125124

126-
return fmt.Errorf("validate command not yet implemented")
125+
// Load configuration from YAML
126+
loader := config.NewYAMLLoader(configPath)
127+
cfg, err := loader.Load()
128+
if err != nil {
129+
logger.Errorf("Failed to load configuration: %v", err)
130+
return fmt.Errorf("configuration loading failed: %w", err)
131+
}
132+
133+
logger.Debugf("Configuration loaded successfully, performing validation...")
134+
135+
// Validate configuration
136+
validator := config.NewValidator()
137+
if err := validator.Validate(cfg); err != nil {
138+
logger.Errorf("Configuration validation failed: %v", err)
139+
return fmt.Errorf("validation failed: %w", err)
140+
}
141+
142+
logger.Infof("✓ Configuration is valid")
143+
logger.Infof(" Name: %s", cfg.Name)
144+
logger.Infof(" Group: %s", cfg.GroupRef)
145+
logger.Infof(" Incoming Auth: %s", cfg.IncomingAuth.Type)
146+
logger.Infof(" Outgoing Auth: %s (source: %s)",
147+
func() string {
148+
if len(cfg.OutgoingAuth.Backends) > 0 {
149+
return fmt.Sprintf("%d backends configured", len(cfg.OutgoingAuth.Backends))
150+
}
151+
return "default only"
152+
}(),
153+
cfg.OutgoingAuth.Source)
154+
logger.Infof(" Conflict Resolution: %s", cfg.Aggregation.ConflictResolution)
155+
156+
if cfg.TokenCache != nil {
157+
logger.Infof(" Token Cache: %s", cfg.TokenCache.Provider)
158+
}
159+
160+
if len(cfg.CompositeTools) > 0 {
161+
logger.Infof(" Composite Tools: %d defined", len(cfg.CompositeTools))
162+
}
163+
164+
return nil
127165
},
128166
}
129167
}

examples/vmcp-config-invalid.yaml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Invalid Virtual MCP Server Configuration Example
2+
# This file has intentional errors to test validation
3+
4+
# Missing required field: name
5+
# name: "test-vmcp"
6+
group: "test-group"
7+
8+
# Invalid auth type
9+
incoming_auth:
10+
type: invalid_type
11+
oidc:
12+
issuer: "https://keycloak.example.com"
13+
client_id: "test-client"
14+
client_secret_env: "TEST_SECRET"
15+
audience: "vmcp"
16+
scopes: ["openid"]
17+
18+
# Invalid source
19+
outgoing_auth:
20+
source: invalid_source
21+
default:
22+
type: pass_through
23+
24+
# Invalid conflict resolution strategy
25+
aggregation:
26+
conflict_resolution: invalid_strategy
27+
conflict_resolution_config:
28+
prefix_format: "{workload}_"

pkg/vmcp/config/config.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@ import (
1111
"github.com/stacklok/toolhive/pkg/vmcp"
1212
)
1313

14+
// Token cache provider types
15+
const (
16+
// CacheProviderMemory represents in-memory token cache provider
17+
CacheProviderMemory = "memory"
18+
// CacheProviderRedis represents Redis token cache provider
19+
CacheProviderRedis = "redis"
20+
)
21+
1422
// Config is the unified configuration model for Virtual MCP Server.
1523
// This is platform-agnostic and used by both CLI and Kubernetes deployments.
1624
//

0 commit comments

Comments
 (0)