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
7 changes: 7 additions & 0 deletions .changeset/patch-add-update-tool-mcp.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .github/workflows/docs-noob-tester.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

60 changes: 60 additions & 0 deletions pkg/cli/mcp_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ The server provides the following tools:
- audit - Investigate a workflow run and generate a report
- mcp-inspect - Inspect MCP servers in workflows and list available tools
- add - Add workflows from remote repositories to .github/workflows
- update - Update workflows from their source repositories

By default, the server uses stdio transport. Use the --port flag to run
an HTTP server with SSE (Server-Sent Events) transport instead.
Expand Down Expand Up @@ -529,6 +530,65 @@ Returns formatted text output showing:
}, nil, nil
})

// Add update tool
type updateArgs struct {
Workflows []string `json:"workflows,omitempty" jsonschema:"Workflow IDs to update (empty for all workflows)"`
Major bool `json:"major,omitempty" jsonschema:"Allow major version updates when updating tagged releases"`
Force bool `json:"force,omitempty" jsonschema:"Force update even if no changes detected"`
}

mcp.AddTool(server, &mcp.Tool{
Name: "update",
Description: `Update workflows from their source repositories and check for gh-aw updates.

The command:
1. Checks if a newer version of gh-aw is available
2. Updates workflows using the 'source' field in the workflow frontmatter
3. Compiles each workflow immediately after update

For workflow updates, it fetches the latest version based on the current ref:
- If the ref is a tag, it updates to the latest release (use major flag for major version updates)
- If the ref is a branch, it fetches the latest commit from that branch
- Otherwise, it fetches the latest commit from the default branch

Returns formatted text output showing:
- Extension update status
- Updated workflows with their new versions
- Compilation status for each updated workflow`,
}, func(ctx context.Context, req *mcp.CallToolRequest, args updateArgs) (*mcp.CallToolResult, any, error) {
// Build command arguments
cmdArgs := []string{"update"}

// Add workflow IDs if specified
cmdArgs = append(cmdArgs, args.Workflows...)

// Add optional flags
if args.Major {
cmdArgs = append(cmdArgs, "--major")
}
if args.Force {
cmdArgs = append(cmdArgs, "--force")
}

// Execute the CLI command
cmd := execCmd(ctx, cmdArgs...)
output, err := cmd.CombinedOutput()

if err != nil {
return &mcp.CallToolResult{
Content: []mcp.Content{
&mcp.TextContent{Text: fmt.Sprintf("Error: %v\nOutput: %s", err, string(output))},
},
}, nil, nil
}

return &mcp.CallToolResult{
Content: []mcp.Content{
&mcp.TextContent{Text: string(output)},
},
}, nil, nil
})

return server
}

Expand Down
68 changes: 67 additions & 1 deletion pkg/cli/mcp_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -47,7 +48,7 @@ func TestMCPServer_ListTools(t *testing.T) {
}

// Verify expected tools are present
expectedTools := []string{"status", "compile", "logs", "audit", "mcp-inspect", "add"}
expectedTools := []string{"status", "compile", "logs", "audit", "mcp-inspect", "add", "update"}
toolNames := make(map[string]bool)
for _, tool := range result.Tools {
toolNames[tool.Name] = true
Expand Down Expand Up @@ -607,3 +608,68 @@ This is the second test workflow.
t.Error("Expected text content from compile tool")
}
}

// TestMCPServer_UpdateToolSchema tests that the update tool has the correct schema
func TestMCPServer_UpdateToolSchema(t *testing.T) {
// Skip if the binary doesn't exist
binaryPath := "../../gh-aw"
if _, err := os.Stat(binaryPath); os.IsNotExist(err) {
t.Skip("Skipping test: gh-aw binary not found. Run 'make build' first.")
}

// Create MCP client
client := mcp.NewClient(&mcp.Implementation{
Name: "test-client",
Version: "1.0.0",
}, nil)

// Start the MCP server as a subprocess
serverCmd := exec.Command(binaryPath, "mcp-server")
transport := &mcp.CommandTransport{Command: serverCmd}

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

session, err := client.Connect(ctx, transport, nil)
if err != nil {
t.Fatalf("Failed to connect to MCP server: %v", err)
}
defer session.Close()

// List tools
result, err := session.ListTools(ctx, &mcp.ListToolsParams{})
if err != nil {
t.Fatalf("Failed to list tools: %v", err)
}

// Find the update tool
var updateTool *mcp.Tool
for i := range result.Tools {
if result.Tools[i].Name == "update" {
updateTool = result.Tools[i]
break
}
}

if updateTool == nil {
t.Fatal("Update tool not found in MCP server tools")
}

// Verify the tool has a description
if updateTool.Description == "" {
t.Error("Update tool should have a description")
}

// Verify description mentions key functionality
if !strings.Contains(updateTool.Description, "workflows") {
t.Error("Update tool description should mention workflows")
}

// Verify the tool has input schema
if updateTool.InputSchema == nil {
t.Error("Update tool should have an input schema")
}

t.Logf("Update tool description: %s", updateTool.Description)
t.Logf("Update tool schema: %+v", updateTool.InputSchema)
}
Loading