diff --git a/pkg/workflow/safe_outputs_config_generation.go b/pkg/workflow/safe_outputs_config_generation.go index bb98e8cbf03..be0b241fc18 100644 --- a/pkg/workflow/safe_outputs_config_generation.go +++ b/pkg/workflow/safe_outputs_config_generation.go @@ -2,6 +2,8 @@ package workflow import ( "encoding/json" + "fmt" + "sort" "strconv" "strings" ) @@ -501,3 +503,72 @@ func generateSafeOutputsConfig(data *WorkflowData) string { safeOutputsConfigLog.Printf("Safe outputs config generation complete: %d tool types configured", len(safeOutputsConfig)) return string(configJSON) } + +// generateCustomJobToolDefinition creates an MCP tool definition for a custom safe-output job. +// Returns a map representing the tool definition in MCP format with name, description, and inputSchema. +func generateCustomJobToolDefinition(jobName string, jobConfig *SafeJobConfig) map[string]any { + safeOutputsConfigLog.Printf("Generating tool definition for custom job: %s", jobName) + + description := jobConfig.Description + if description == "" { + description = fmt.Sprintf("Execute the %s custom job", jobName) + } + + inputSchema := map[string]any{ + "type": "object", + "properties": make(map[string]any), + "additionalProperties": false, + } + + var requiredFields []string + properties := inputSchema["properties"].(map[string]any) + + for inputName, inputDef := range jobConfig.Inputs { + property := map[string]any{} + + if inputDef.Description != "" { + property["description"] = inputDef.Description + } + + // Convert type to JSON Schema type + switch inputDef.Type { + case "choice": + // Choice inputs are strings with enum constraints + property["type"] = "string" + if len(inputDef.Options) > 0 { + property["enum"] = inputDef.Options + } + case "boolean": + property["type"] = "boolean" + case "number": + property["type"] = "number" + default: + // "string", empty string, or any unknown type defaults to string + property["type"] = "string" + } + + if inputDef.Default != nil { + property["default"] = inputDef.Default + } + + if inputDef.Required { + requiredFields = append(requiredFields, inputName) + } + + properties[inputName] = property + } + + if len(requiredFields) > 0 { + sort.Strings(requiredFields) + inputSchema["required"] = requiredFields + } + + safeOutputsConfigLog.Printf("Generated tool definition for %s with %d inputs, %d required", + jobName, len(jobConfig.Inputs), len(requiredFields)) + + return map[string]any{ + "name": jobName, + "description": description, + "inputSchema": inputSchema, + } +} diff --git a/pkg/workflow/safe_outputs_config_helpers.go b/pkg/workflow/safe_outputs_config_helpers.go index 1c4ff1a6920..3d12845ceca 100644 --- a/pkg/workflow/safe_outputs_config_helpers.go +++ b/pkg/workflow/safe_outputs_config_helpers.go @@ -1,9 +1,7 @@ package workflow import ( - "fmt" "maps" - "sort" "strings" "github.com/github/gh-aw/pkg/logger" @@ -219,72 +217,3 @@ func generateTargetConfigWithRepos(targetConfig SafeOutputTargetConfig, max *str return config } - -// generateCustomJobToolDefinition creates an MCP tool definition for a custom safe-output job. -// Returns a map representing the tool definition in MCP format with name, description, and inputSchema. -func generateCustomJobToolDefinition(jobName string, jobConfig *SafeJobConfig) map[string]any { - safeOutputsConfigLog.Printf("Generating tool definition for custom job: %s", jobName) - - description := jobConfig.Description - if description == "" { - description = fmt.Sprintf("Execute the %s custom job", jobName) - } - - inputSchema := map[string]any{ - "type": "object", - "properties": make(map[string]any), - "additionalProperties": false, - } - - var requiredFields []string - properties := inputSchema["properties"].(map[string]any) - - for inputName, inputDef := range jobConfig.Inputs { - property := map[string]any{} - - if inputDef.Description != "" { - property["description"] = inputDef.Description - } - - // Convert type to JSON Schema type - switch inputDef.Type { - case "choice": - // Choice inputs are strings with enum constraints - property["type"] = "string" - if len(inputDef.Options) > 0 { - property["enum"] = inputDef.Options - } - case "boolean": - property["type"] = "boolean" - case "number": - property["type"] = "number" - default: - // "string", empty string, or any unknown type defaults to string - property["type"] = "string" - } - - if inputDef.Default != nil { - property["default"] = inputDef.Default - } - - if inputDef.Required { - requiredFields = append(requiredFields, inputName) - } - - properties[inputName] = property - } - - if len(requiredFields) > 0 { - sort.Strings(requiredFields) - inputSchema["required"] = requiredFields - } - - safeOutputsConfigLog.Printf("Generated tool definition for %s with %d inputs, %d required", - jobName, len(jobConfig.Inputs), len(requiredFields)) - - return map[string]any{ - "name": jobName, - "description": description, - "inputSchema": inputSchema, - } -}