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
96 changes: 96 additions & 0 deletions pkg/workflow/safe_outputs_config_generation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package workflow

import (
"fmt"
"sort"
)

// 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)

// Build the tool definition
tool := map[string]any{
"name": jobName,
}

// Add description if present
if jobConfig.Description != "" {
tool["description"] = jobConfig.Description
} else {
// Provide a default description if none is specified
tool["description"] = fmt.Sprintf("Execute the %s custom job", jobName)
}

// Build the input schema
inputSchema := map[string]any{
"type": "object",
"properties": make(map[string]any),
}

// Track required fields
var requiredFields []string

// Add each input to the schema
if len(jobConfig.Inputs) > 0 {
properties := inputSchema["properties"].(map[string]any)

for inputName, inputDef := range jobConfig.Inputs {
property := map[string]any{}

// Add description
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"
case "string", "":
// Default to string if type is not specified
property["type"] = "string"
default:
// For any unknown type, default to string
property["type"] = "string"
}

// Add default value if present
if inputDef.Default != nil {
property["default"] = inputDef.Default
}

// Track required fields
if inputDef.Required {
requiredFields = append(requiredFields, inputName)
}

properties[inputName] = property
}
}

// Add required fields array if any inputs are required
if len(requiredFields) > 0 {
sort.Strings(requiredFields)
inputSchema["required"] = requiredFields
}

// Prevent additional properties to maintain schema strictness
inputSchema["additionalProperties"] = false

tool["inputSchema"] = inputSchema

safeOutputsConfigLog.Printf("Generated tool definition for %s with %d inputs, %d required",
jobName, len(jobConfig.Inputs), len(requiredFields))

return tool
}
94 changes: 0 additions & 94 deletions pkg/workflow/safe_outputs_generation.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,100 +14,6 @@ import (
"github.com/github/gh-aw/pkg/stringutil"
)

// ========================================
// Safe Output Configuration Generation
// ========================================

// 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)

// Build the tool definition
tool := map[string]any{
"name": jobName,
}

// Add description if present
if jobConfig.Description != "" {
tool["description"] = jobConfig.Description
} else {
// Provide a default description if none is specified
tool["description"] = fmt.Sprintf("Execute the %s custom job", jobName)
}

// Build the input schema
inputSchema := map[string]any{
"type": "object",
"properties": make(map[string]any),
}

// Track required fields
var requiredFields []string

// Add each input to the schema
if len(jobConfig.Inputs) > 0 {
properties := inputSchema["properties"].(map[string]any)

for inputName, inputDef := range jobConfig.Inputs {
property := map[string]any{}

// Add description
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"
case "string", "":
// Default to string if type is not specified
property["type"] = "string"
default:
// For any unknown type, default to string
property["type"] = "string"
}

// Add default value if present
if inputDef.Default != nil {
property["default"] = inputDef.Default
}

// Track required fields
if inputDef.Required {
requiredFields = append(requiredFields, inputName)
}

properties[inputName] = property
}
}

// Add required fields array if any inputs are required
if len(requiredFields) > 0 {
sort.Strings(requiredFields)
inputSchema["required"] = requiredFields
}

// Prevent additional properties to maintain schema strictness
inputSchema["additionalProperties"] = false

tool["inputSchema"] = inputSchema

safeOutputsConfigLog.Printf("Generated tool definition for %s with %d inputs, %d required",
jobName, len(jobConfig.Inputs), len(requiredFields))

return tool
}

func populateDispatchWorkflowFiles(data *WorkflowData, markdownPath string) {
if data.SafeOutputs == nil || data.SafeOutputs.DispatchWorkflow == nil {
return
Expand Down
Loading