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
71 changes: 71 additions & 0 deletions pkg/workflow/safe_outputs_config_generation.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package workflow

import (
"encoding/json"
"fmt"
"sort"
"strconv"
"strings"
)
Expand Down Expand Up @@ -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,
}
}
71 changes: 0 additions & 71 deletions pkg/workflow/safe_outputs_config_helpers.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package workflow

import (
"fmt"
"maps"
"sort"
"strings"

"github.com/github/gh-aw/pkg/logger"
Expand Down Expand Up @@ -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,
}
}
Loading