Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions internal/modules/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func (m *ModuleController) RegisterModuleTools(mcp *server.MCPServer) {
mcp.AddTool(m.listModulesTool(), m.listModules)
mcp.AddTool(m.createModuleTool(), m.createModule)
mcp.AddTool(m.updateModuleTool(), m.updateModule)
mcp.AddTool(m.createModuleManifestTool(), m.createModuleManifestModule)
}

func (m *ModuleController) validateModuleValues(schema []byte, values map[string]interface{}) (bool, error, error) {
Expand Down
98 changes: 98 additions & 0 deletions internal/modules/manifest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package modules

import (
"context"
"encoding/json"
"fmt"
"gopkg.in/yaml.v2"

"github.com/mark3labs/mcp-go/mcp"

"github.com/cyclops-ui/cyclops/cyclops-ctrl/api/v1alpha1"

"github.com/cyclops-ui/mcp-cyclops/internal/mapper"
)

func (m *ModuleController) createModuleManifestTool() mcp.Tool {
return mcp.NewTool("create_module_manifest",
mcp.WithDescription("Create a Module manifest based on values. Before calling this tool, make sure to call get_template_schema to validate values for the given template"),
mcp.WithString("module_name",
mcp.Required(),
mcp.Description("Name of the Module to update"),
),
mcp.WithString("template_type",
mcp.Required(),
mcp.Description("Type of the Template Stores to create"),
mcp.Enum("git", "helm", "oci"),
),
mcp.WithString("repo",
mcp.Required(),
mcp.Description("Template repo (Helm or Git repo)"),
),
mcp.WithString("path",
mcp.Required(),
mcp.Description("In case of a git repo, folder of the template in the repository. For a helm chart, the name of the chart"),
),
mcp.WithString("version",
mcp.Required(),
mcp.Description("Semantic version of the chart, or in case of a git repo can be a reference as commit hash or branch/tag"),
),
mcp.WithString("values",
mcp.Required(),
mcp.Description("Helm-like values in JSON string format to create the module with"),
),
)
}

func (m *ModuleController) createModuleManifestModule(_ context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
moduleName := request.Params.Arguments["module_name"].(string)
valuesRaw := request.Params.Arguments["values"].(string)
templateType := request.Params.Arguments["template_type"].(string)
repo := request.Params.Arguments["repo"].(string)
path := request.Params.Arguments["path"].(string)
version := request.Params.Arguments["version"].(string)

initialValues, err := m.templateRepo.GetTemplateInitialValues(repo, path, version, v1alpha1.TemplateSourceType(templateType))
if err != nil {
return nil, err
}

var values map[string]interface{}
if len(valuesRaw) > 0 {
if err := json.Unmarshal([]byte(valuesRaw), &values); err != nil {
return nil, fmt.Errorf("failed to parse current values: %w", err)
}
} else {
values = make(map[string]interface{})
}

values = mapper.DeepMerge(initialValues, values)

template, err := m.templateRepo.GetTemplate(repo, path, version, "", v1alpha1.TemplateSourceType(templateType))
if err != nil {
return nil, err
}

valid, validationError, err := m.validateModuleValues(template.RawSchema, values)
if err != nil {
return nil, err
}

if !valid {
return mcp.NewToolResultError(validationError.Error()), nil
}

valuesBytes, err := json.Marshal(values)
if err != nil {
return nil, err
}

module := mapper.CreateModule(moduleName, repo, path, version, templateType, valuesBytes)

moduleData, err := yaml.Marshal(module)
if err != nil {
return nil, err
}

return mcp.NewToolResultText(string(moduleData)), nil
}