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
Refactor cmd/vmcp/app/commands.go to become a thin wrapper over the new pkg/vmcp/cli/ package created in #4879. All serve and validate business logic is removed from cmd/vmcp/app/ and replaced with direct calls to pkg/vmcp/cli/Serve() and pkg/vmcp/cli/Validate(). The standalone vmcp binary's external behavior — flags, defaults, output, error messages — must remain identical; this is a pure internal restructuring with no user-visible changes.
Context
After #4879 creates pkg/vmcp/cli/ with the extracted business logic, cmd/vmcp/app/commands.go still contains the original copies of runServe, loadAndValidateConfig, discoverBackends, createSessionFactory, loadAuthServerConfig, and getStatusReportingInterval — approximately 350 lines of duplicated logic. This item removes that duplication by replacing each function body with a call to the corresponding exported function in pkg/vmcp/cli/. The result matches the thin wrapper principle enforced for all cmd/thv/app/ files and ensures that future callers (thv vmcp serve, thv vmcp validate) share a single implementation path.
This is a Phase 1 item in RFC THV-0059. It is safe to merge independently once #4879 is merged, and it must land before #4883 introduces thv vmcp serve/validate so those commands delegate to a single canonical implementation.
Dependencies: #4879 Blocks: (none — can merge independently after #4879)
Acceptance Criteria
cmd/vmcp/app/commands.go no longer contains the bodies of runServe, loadAndValidateConfig, discoverBackends, createSessionFactory, loadAuthServerConfig, or getStatusReportingInterval; each is replaced by a delegation to pkg/vmcp/cli/
newServeCmd().RunE delegates to pkg/vmcp/cli.Serve(ctx, cfg) — no business logic remains in the Cobra callback
newValidateCmd().RunE delegates to pkg/vmcp/cli.Validate(ctx, cfg) — no business logic remains in the Cobra callback
cmd/vmcp/app/commands.go only parses flags, builds a cli.ServeConfig or cli.ValidateConfig, and calls into pkg/vmcp/cli/
The standalone vmcp serve binary behaves identically before and after the change: same flags, same defaults (--host 127.0.0.1, --port 4483), same log output, same error messages
The standalone vmcp validate binary behaves identically before and after the change
go build ./cmd/vmcp/... succeeds
go vet ./cmd/vmcp/... passes with no issues
The //nolint:gocyclo directive on runServe is removed (complexity now lives in pkg/vmcp/cli/serve.go which is the appropriate place to manage it)
No new external Go module dependencies are introduced
All existing tests pass (no regressions)
Code reviewed and approved
Technical Approach
Recommended Implementation
cmd/vmcp/app/commands.go retains its Cobra command scaffolding (rootCmd, NewRootCmd, newServeCmd, newValidateCmd, newVersionCmd) and flag definitions unchanged. The implementation of runServe becomes a three-step function: extract flag values → build cli.ServeConfig → call vmcpcli.Serve(ctx, cfg). The implementation of newValidateCmd().RunE becomes: read --config → build cli.ValidateConfig → call vmcpcli.Validate(ctx, cfg).
The five helper functions (loadAndValidateConfig, discoverBackends, createSessionFactory, loadAuthServerConfig, getStatusReportingInterval) are deleted from cmd/vmcp/app/commands.go entirely. Their logic already lives in pkg/vmcp/cli/ after #4879 merges.
Pay attention to the enableAudit flag handling inside runServe (lines 393–399 of the current commands.go): this is flag-parsing logic that remains in the CLI layer, passed into ServeConfig.EnableAudit so pkg/vmcp/cli/Serve can act on it.
The getVersion() helper in cmd/vmcp/app/commands.go stays — it is injected by ldflags at build time and must be passed into ServeConfig as the version string.
Patterns & Frameworks
Thin wrapper principle (hard rule from .claude/rules/cli-commands.md): cmd/ files must only parse flags, call pkg/ functions, and format output — zero business logic
SPDX headers: The file already has the required SPDX header; preserve it unchanged
Error wrapping: Errors from pkg/vmcp/cli/ bubble up directly; no re-wrapping in the CLI layer unless adding CLI-specific context
Cobra flag extraction pattern: Use cmd.Flags().GetString("host"), cmd.Flags().GetInt("port"), cmd.Flags().GetBool("enable-audit") to build the config struct — same pattern as the current runServe
Immutable variable assignment: Prefer cfg := cli.ServeConfig{...} over var cfg cli.ServeConfig followed by field mutations
Code Pointers
cmd/vmcp/app/commands.go — The file being refactored; runServe (lines 378–665) and newValidateCmd().RunE (lines 144–190) are the primary replacement targets; the five helper functions (lines 199–373) are deleted
cmd/thv/app/mcp.go — Reference pattern for how a thin-wrapper CLI command calls pkg/ functions; observe flag extraction → struct population → single pkg/ call structure
Component Interfaces
After this item, the entirety of runServe should collapse to approximately the following shape. The exact field names depend on the ServeConfig struct defined in #4879.
test/integration/vmcp/vmcp_integration_test.go must continue to pass without modification — it exercises the full serve path end-to-end via the compiled binary, verifying that the delegation chain produces identical behavior
Edge Cases
vmcp serve with no --config flag still returns "no configuration file specified, use --config flag" (validation in the CLI layer, not delegated)
vmcp validate with no --config flag still returns the same error
vmcp serve with a valid config starts and accepts connections identically to the pre-refactor binary (verified by existing integration tests)
Description
Refactor
cmd/vmcp/app/commands.goto become a thin wrapper over the newpkg/vmcp/cli/package created in #4879. All serve and validate business logic is removed fromcmd/vmcp/app/and replaced with direct calls topkg/vmcp/cli/Serve()andpkg/vmcp/cli/Validate(). The standalonevmcpbinary's external behavior — flags, defaults, output, error messages — must remain identical; this is a pure internal restructuring with no user-visible changes.Context
After #4879 creates
pkg/vmcp/cli/with the extracted business logic,cmd/vmcp/app/commands.gostill contains the original copies ofrunServe,loadAndValidateConfig,discoverBackends,createSessionFactory,loadAuthServerConfig, andgetStatusReportingInterval— approximately 350 lines of duplicated logic. This item removes that duplication by replacing each function body with a call to the corresponding exported function inpkg/vmcp/cli/. The result matches the thin wrapper principle enforced for allcmd/thv/app/files and ensures that future callers (thv vmcp serve,thv vmcp validate) share a single implementation path.This is a Phase 1 item in RFC THV-0059. It is safe to merge independently once #4879 is merged, and it must land before #4883 introduces
thv vmcp serve/validateso those commands delegate to a single canonical implementation.Dependencies: #4879
Blocks: (none — can merge independently after #4879)
Acceptance Criteria
cmd/vmcp/app/commands.gono longer contains the bodies ofrunServe,loadAndValidateConfig,discoverBackends,createSessionFactory,loadAuthServerConfig, orgetStatusReportingInterval; each is replaced by a delegation topkg/vmcp/cli/newServeCmd().RunEdelegates topkg/vmcp/cli.Serve(ctx, cfg)— no business logic remains in the Cobra callbacknewValidateCmd().RunEdelegates topkg/vmcp/cli.Validate(ctx, cfg)— no business logic remains in the Cobra callbackcmd/vmcp/app/commands.goonly parses flags, builds acli.ServeConfigorcli.ValidateConfig, and calls intopkg/vmcp/cli/vmcp servebinary behaves identically before and after the change: same flags, same defaults (--host 127.0.0.1,--port 4483), same log output, same error messagesvmcp validatebinary behaves identically before and after the changego build ./cmd/vmcp/...succeedsgo vet ./cmd/vmcp/...passes with no issues//nolint:gocyclodirective onrunServeis removed (complexity now lives inpkg/vmcp/cli/serve.gowhich is the appropriate place to manage it)Technical Approach
Recommended Implementation
cmd/vmcp/app/commands.goretains its Cobra command scaffolding (rootCmd,NewRootCmd,newServeCmd,newValidateCmd,newVersionCmd) and flag definitions unchanged. The implementation ofrunServebecomes a three-step function: extract flag values → buildcli.ServeConfig→ callvmcpcli.Serve(ctx, cfg). The implementation ofnewValidateCmd().RunEbecomes: read--config→ buildcli.ValidateConfig→ callvmcpcli.Validate(ctx, cfg).The five helper functions (
loadAndValidateConfig,discoverBackends,createSessionFactory,loadAuthServerConfig,getStatusReportingInterval) are deleted fromcmd/vmcp/app/commands.goentirely. Their logic already lives inpkg/vmcp/cli/after #4879 merges.Pay attention to the
enableAuditflag handling insiderunServe(lines 393–399 of the currentcommands.go): this is flag-parsing logic that remains in the CLI layer, passed intoServeConfig.EnableAuditsopkg/vmcp/cli/Servecan act on it.The
getVersion()helper incmd/vmcp/app/commands.gostays — it is injected by ldflags at build time and must be passed intoServeConfigas the version string.Patterns & Frameworks
.claude/rules/cli-commands.md):cmd/files must only parse flags, callpkg/functions, and format output — zero business logicpkg/vmcp/cli/bubble up directly; no re-wrapping in the CLI layer unless adding CLI-specific contextcmd.Flags().GetString("host"),cmd.Flags().GetInt("port"),cmd.Flags().GetBool("enable-audit")to build the config struct — same pattern as the currentrunServecfg := cli.ServeConfig{...}overvar cfg cli.ServeConfigfollowed by field mutationsCode Pointers
cmd/vmcp/app/commands.go— The file being refactored;runServe(lines 378–665) andnewValidateCmd().RunE(lines 144–190) are the primary replacement targets; the five helper functions (lines 199–373) are deletedpkg/vmcp/cli/serve.go— Created by Extract shared vMCP logic intopkg/vmcp/cli/(serve + validate) #4879; providesServe(ctx context.Context, cfg ServeConfig) errorand theServeConfigstruct — this is whatrunServemust delegate topkg/vmcp/cli/validate.go— Created by Extract shared vMCP logic intopkg/vmcp/cli/(serve + validate) #4879; providesValidate(ctx context.Context, cfg ValidateConfig) error— this is whatnewValidateCmd().RunEmust delegate tocmd/thv/app/mcp.go— Reference pattern for how a thin-wrapper CLI command callspkg/functions; observe flag extraction → struct population → singlepkg/call structureComponent Interfaces
After this item, the entirety of
runServeshould collapse to approximately the following shape. The exact field names depend on theServeConfigstruct defined in #4879.And
newValidateCmd().RunEcollapses to:Testing Strategy
Unit Tests
pkg/vmcp/cli/(Unit tests forpkg/vmcp/cli/serve and validate logic #4881). The CLI layer is thin enough to be covered by E2E tests.Integration Tests
test/integration/vmcp/vmcp_integration_test.gomust continue to pass without modification — it exercises the full serve path end-to-end via the compiled binary, verifying that the delegation chain produces identical behaviorEdge Cases
vmcp servewith no--configflag still returns"no configuration file specified, use --config flag"(validation in the CLI layer, not delegated)vmcp validatewith no--configflag still returns the same errorvmcp servewith a valid config starts and accepts connections identically to the pre-refactor binary (verified by existing integration tests)Out of Scope
pkg/vmcp/cli/or any new files in that package (that is Extract shared vMCP logic intopkg/vmcp/cli/(serve + validate) #4879)pkg/vmcp/cli/(that is Unit tests forpkg/vmcp/cli/serve and validate logic #4881)cmd/thv/app/vmcp.goor addingthv vmcpsubcommands (that is Addthv vmcp serveandthv vmcp validatesubcommands #4883)vmcpbinaryvmcpbinary (it is preserved for K8s deployments)pkg/vmcp/packages other than importing the newpkg/vmcp/cli/References
pkg/vmcp/cli/(serve + validate) #4879 issue — Creates thepkg/vmcp/cli/package that this item delegates to.claude/rules/cli-commands.md— Thin wrapper principle; adding new commands checklist.claude/rules/go-style.md— SPDX headers, error handling conventionscmd/thv/app/mcp.go— Canonical thin-wrapper pattern to follow