Skip to content

Conversation

@tgrunnagle
Copy link
Contributor

@tgrunnagle tgrunnagle commented Dec 1, 2025

Issue: #2771
Branch: issue_2771_2025-12-01

Overview

Added the missing Output field to the VirtualMCPServer and VirtualMCPCompositeToolDefinition CRDs to match the existing implementation in pkg/vmcp/config/config.go. This enables users to define structured output schemas for composite tools via Kubernetes CRDs.

Changes Made

1. CRD Type Definitions

2. Converter Implementation

  • Added convertOutputSpec() (converter.go:346-363)

    • Converts CRD OutputSpec to vmcp config OutputConfig
  • Added convertOutputProperty() (converter.go:365-397)

    • Recursively converts output properties
    • Handles runtime.RawExtension to any conversion for defaults
    • Properly unmarshals JSON for scalars, objects, and arrays

3. CRD Manifests

  • Generated updated CRDs
    • virtualmcpservers.yaml - includes Output field
    • virtualmcpcompositetooldefinitions.yaml - includes Output field

4. Comprehensive Testing

  • Converter Tests (converter_test.go:460-705)

    • Nil output spec handling
    • Simple string properties
    • Multiple properties with scalar defaults (string, integer, boolean)
    • Object and array default values
    • Nested object properties
  • Integration Tests (virtualmcpserver_compositetool_watch_test.go:116-131)

    • Updated to include Output spec in test definitions
    • Validates CRD parsing and reconciliation

Testing Summary

All tests pass:

  • Unit tests: Converter logic with 5 comprehensive test cases
  • Integration tests: Operator reconciliation with Output field
  • Linting: No issues (task lint-fix)
  • Existing functionality: All existing tests pass

Operator Chart Contribution Guidelines Verification

Verified all contribution practices per deploy/charts/operator/CONTRIBUTING.md:

Commit Signature: GPG signed (RSA key A8177AE...19AD2ADD)
Helm Template: Renders successfully without errors
Chart Linting: All tests passed (6 different value configurations)
Documentation: Pre-commit helm-docs hook passed
Chart Version: Operator CRD version bumped

Impact

What Users Can Now Do

Users can define structured output schemas for composite tools that:

  • Specify output property types and descriptions
  • Use template expansion to construct output from step results
  • Define nested object structures
  • Provide default fallback values
  • Mark fields as required

Backward Compatibility

✅ Fully backward compatible - the Output field is optional and defaults to the last step's output when not specified.

@tgrunnagle tgrunnagle linked an issue Dec 1, 2025 that may be closed by this pull request
@tgrunnagle
Copy link
Contributor Author

@claude Review this PR

@claude
Copy link
Contributor

claude bot commented Dec 1, 2025

Claude finished @tgrunnagle's task —— View job


Code Review Complete ✅

I've completed a thorough review of this PR adding Output Schema Support to the CompositeToolSpec CRD. Overall, this is excellent work with comprehensive implementation, testing, and attention to detail. The code is production-ready.


Summary

This PR successfully adds the missing Output field to VirtualMCPServer and VirtualMCPCompositeToolDefinition CRDs to enable structured output schemas for composite tools. The implementation properly bridges the CRD types to the existing vmcp config model.

Key Strengths:

  • ✅ Comprehensive type definitions with proper kubebuilder markers
  • ✅ Robust converter implementation with recursive property handling
  • ✅ Excellent test coverage (5 test cases covering edge cases)
  • ✅ Proper handling of JSON Schema types and nested objects
  • ✅ Correct deepcopy generation for Kubernetes resources
  • ✅ Full backward compatibility (Output field is optional)

Detailed Review

1. Type Definitions (virtualmcpserver_types.go, virtualmcpcompositetooldefinition_types.go)

Strengths:

  • OutputSpec and OutputPropertySpec are well-designed with clear field documentation
  • Proper use of runtime.RawExtension for flexible default values
  • Kubebuilder validation markers are appropriate (+kubebuilder:validation:Enum, +optional, etc.)
  • Support for all JSON Schema types: string, integer, number, boolean, object, array
  • Recursive structure for nested object properties works correctly

Observations:

  • Line comments properly explain template expansion syntax (e.g., {{.steps.step_id.output.field}})
  • The +kubebuilder:pruning:PreserveUnknownFields marker on Default field is correct for RawExtension

File references:

  • cmd/thv-operator/api/v1alpha1/virtualmcpserver_types.go:217-255
  • cmd/thv-operator/api/v1alpha1/virtualmcpcompositetooldefinition_types.go:61-65

2. Converter Implementation (converter.go)

Strengths:

  • convertOutputSpec() correctly handles nil input (lines 346-363)
  • convertOutputProperty() properly handles recursive nested properties (lines 365-403)
  • Correct use of json.Unmarshal() to convert runtime.RawExtension to any type
  • Proper error handling with context logging for unmarshal failures
  • Integration into convertCompositeTools() is clean (line 327-329)

Observations:

  • The JSON unmarshal correctly handles all scalar types:
    • JSON strings → Go strings
    • JSON numbers → Go float64 (standard JSON behavior)
    • JSON booleans → Go bool
    • JSON objects → Go map[string]any
    • JSON arrays → Go []any
  • Error logging provides helpful context (property name, raw JSON) for debugging

Minor suggestion (non-blocking):
Consider if there are any validation checks that should happen at conversion time vs admission time. The current approach of logging errors and continuing is reasonable since validation webhooks should catch issues earlier.

File reference:

  • cmd/thv-operator/pkg/vmcpconfig/converter.go:346-403

3. Test Coverage (converter_test.go)

Strengths:

  • Excellent test coverage with 5 comprehensive test cases:
    1. Nil output spec handling
    2. Simple string properties
    3. Multiple properties with scalar defaults (string, integer, boolean)
    4. Object and array default values
    5. Nested object properties (including deeply nested 4-level test)
  • Recursive validation helper validateOutputProperties() is well-designed (lines 460-484)
  • Tests verify both structure and values, including type coercion (float64 for JSON numbers)
  • Clear test descriptions and good use of table-driven tests

Observations:

  • The deeply nested test case (lines 662-766) is particularly valuable for catching recursion bugs
  • Default value handling is thoroughly tested across all JSON types
  • Tests correctly expect float64 for numeric defaults (standard JSON unmarshal behavior)

File reference:

  • cmd/thv-operator/pkg/vmcpconfig/converter_test.go:460-818

4. Generated Code

Strengths:

  • DeepCopy methods are correctly generated for OutputSpec and OutputPropertySpec
  • Proper handling of maps and slices in deepcopy
  • Recursive deepcopy for nested properties works correctly

File reference:

  • cmd/thv-operator/api/v1alpha1/zz_generated.deepcopy.go

5. CRD Manifests

Strengths:

  • Generated CRD YAML properly includes the Output field
  • Schema validation is correctly defined in the manifest
  • x-kubernetes-preserve-unknown-fields: true is correctly set on Default field

File references:

  • deploy/charts/operator-crds/crds/toolhive.stacklok.dev_virtualmcpservers.yaml
  • deploy/charts/operator-crds/crds/toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml

6. Integration Tests

Strengths:

  • Integration test updated to include Output spec (lines 117-132)
  • Test validates end-to-end flow: CRD → Reconciliation → Status
  • Uses realistic test data with multiple properties and defaults

File reference:

  • cmd/thv-operator/test-integration/virtualmcp/virtualmcpserver_compositetool_watch_test.go:116-131

Issues Found

None. No bugs, security issues, or significant concerns identified.


Recommendations

Optional Improvements (Future Work)

  1. Documentation: Consider adding examples to the CRD comments showing complete YAML snippets for common output patterns. The existing comments are good but examples would help users.

  2. Validation: Consider adding a validating webhook to check:

    • Type field matches the structure (e.g., if Type="object", Properties or Value should be set)
    • Template syntax is valid in Value fields
    • Required properties exist in Properties map

    Note: This could be a follow-up enhancement, not blocking for this PR.

  3. Edge case handling: The converter logs errors but continues on invalid JSON in Default. Consider if any invalid defaults should fail loudly vs silently:

    // Current: logs error, continues with nil default
    // Alternative: could return error to fail reconciliation

    Current behavior is reasonable since admission validation should catch this.


Testing Verification

I verified the following:

  • ✅ All unit tests pass (converter_test.go)
  • ✅ Test coverage includes all critical paths
  • ✅ DeepCopy generation is correct
  • ✅ Integration test includes Output spec

Conclusion

This PR is ready to merge. The implementation is solid, well-tested, and follows Kubernetes operator best practices. The Output field successfully extends the CRD to match the existing vmcp config model while maintaining backward compatibility.

Approval: ✅ LGTM (Looks Good To Me)

Great work on the comprehensive testing and attention to detail! 🎉


Related:


@tgrunnagle tgrunnagle force-pushed the issue_2771_2025-12-01 branch from 0a96ff0 to 8d3e115 Compare December 1, 2025 16:39
@github-actions github-actions bot added the size/L Large PR: 600-999 lines changed label Dec 1, 2025
@codecov
Copy link

codecov bot commented Dec 1, 2025

Codecov Report

❌ Patch coverage is 75.86207% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 56.61%. Comparing base (6a9a2c1) to head (bdc9752).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
cmd/thv-operator/pkg/vmcpconfig/converter.go 75.86% 4 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2824      +/-   ##
==========================================
+ Coverage   56.59%   56.61%   +0.01%     
==========================================
  Files         320      320              
  Lines       30899    30928      +29     
==========================================
+ Hits        17487    17509      +22     
- Misses      11910    11914       +4     
- Partials     1502     1505       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions github-actions bot added size/L Large PR: 600-999 lines changed and removed size/L Large PR: 600-999 lines changed labels Dec 1, 2025
@github-actions github-actions bot added size/L Large PR: 600-999 lines changed and removed size/L Large PR: 600-999 lines changed labels Dec 1, 2025
@github-actions github-actions bot added size/L Large PR: 600-999 lines changed and removed size/L Large PR: 600-999 lines changed labels Dec 1, 2025
@github-actions github-actions bot added size/L Large PR: 600-999 lines changed and removed size/L Large PR: 600-999 lines changed labels Dec 1, 2025
@github-actions github-actions bot added size/L Large PR: 600-999 lines changed and removed size/L Large PR: 600-999 lines changed labels Dec 1, 2025
@tgrunnagle tgrunnagle merged commit 34ea873 into main Dec 1, 2025
38 of 39 checks passed
@tgrunnagle tgrunnagle deleted the issue_2771_2025-12-01 branch December 1, 2025 19:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/L Large PR: 600-999 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Output schema support to CompositeToolSpec CRD

3 participants