From 515b96cdba71f764d69d2475f05c8738b170c2e3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 16 Nov 2025 01:03:50 +0000 Subject: [PATCH 1/5] Initial plan From faffd5fc408e2bf66b22b55cb5460bf0b86dbec1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 16 Nov 2025 01:08:48 +0000 Subject: [PATCH 2/5] Initial exploration and planning for update tool in MCP server Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/docs-noob-tester.lock.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs-noob-tester.lock.yml b/.github/workflows/docs-noob-tester.lock.yml index 4ab6185946..9516f410d2 100644 --- a/.github/workflows/docs-noob-tester.lock.yml +++ b/.github/workflows/docs-noob-tester.lock.yml @@ -1073,7 +1073,7 @@ jobs: "playwright": { "type": "local", "command": "npx", - "args": ["@playwright/mcp@latest", "--output-dir", "/tmp/gh-aw/mcp-logs/playwright", "--allowed-origins", "localhost:*;127.0.0.1:*;localhost;127.0.0.1"], + "args": ["@playwright/mcp@1.56.1", "--output-dir", "/tmp/gh-aw/mcp-logs/playwright", "--allowed-origins", "localhost:*;127.0.0.1:*;localhost;127.0.0.1"], "tools": ["*"] }, "safeoutputs": { From 17db93a6541b6bda4421d8d50e8aa00555e9282b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 16 Nov 2025 01:14:29 +0000 Subject: [PATCH 3/5] Add update tool to MCP server implementation Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/cli/mcp_server.go | 80 ++++++++++++++++++++++++++++++++++++++ pkg/cli/mcp_server_test.go | 68 +++++++++++++++++++++++++++++++- 2 files changed, 147 insertions(+), 1 deletion(-) diff --git a/pkg/cli/mcp_server.go b/pkg/cli/mcp_server.go index 140001721c..b22f98e8eb 100644 --- a/pkg/cli/mcp_server.go +++ b/pkg/cli/mcp_server.go @@ -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. @@ -529,6 +530,85 @@ 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"` + Engine string `json:"engine,omitempty" jsonschema:"Override AI engine (claude, codex, copilot, custom)"` + PR bool `json:"pr,omitempty" jsonschema:"Create a pull request with the workflow changes"` + Dir string `json:"dir,omitempty" jsonschema:"Relative directory containing workflows (default: .github/workflows)"` + NoStopAfter bool `json:"no_stop_after,omitempty" jsonschema:"Remove any stop-after field from the updated workflow"` + StopAfter string `json:"stop_after,omitempty" jsonschema:"Override stop-after value in the updated workflow (e.g., '+48h', '2025-12-31 23:59:59')"` + } + + 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") + } + if args.Engine != "" { + cmdArgs = append(cmdArgs, "--engine", args.Engine) + } + if args.PR { + cmdArgs = append(cmdArgs, "--pr") + } + if args.Dir != "" { + cmdArgs = append(cmdArgs, "--dir", args.Dir) + } + if args.NoStopAfter { + cmdArgs = append(cmdArgs, "--no-stop-after") + } + if args.StopAfter != "" { + cmdArgs = append(cmdArgs, "--stop-after", args.StopAfter) + } + + // 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 } diff --git a/pkg/cli/mcp_server_test.go b/pkg/cli/mcp_server_test.go index e6b5b645bf..550a901516 100644 --- a/pkg/cli/mcp_server_test.go +++ b/pkg/cli/mcp_server_test.go @@ -7,6 +7,7 @@ import ( "os" "os/exec" "path/filepath" + "strings" "testing" "time" @@ -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 @@ -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) +} From 4a014fd4594eb9fa2e154b063bcd0935f999e2ab Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 16 Nov 2025 01:26:03 +0000 Subject: [PATCH 4/5] Remove engine, pr, dir, no-stop-after, and stop-after flags from update tool Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/cli/mcp_server.go | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/pkg/cli/mcp_server.go b/pkg/cli/mcp_server.go index b22f98e8eb..e2d0d9bf02 100644 --- a/pkg/cli/mcp_server.go +++ b/pkg/cli/mcp_server.go @@ -532,14 +532,9 @@ Returns formatted text output showing: // 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"` - Engine string `json:"engine,omitempty" jsonschema:"Override AI engine (claude, codex, copilot, custom)"` - PR bool `json:"pr,omitempty" jsonschema:"Create a pull request with the workflow changes"` - Dir string `json:"dir,omitempty" jsonschema:"Relative directory containing workflows (default: .github/workflows)"` - NoStopAfter bool `json:"no_stop_after,omitempty" jsonschema:"Remove any stop-after field from the updated workflow"` - StopAfter string `json:"stop_after,omitempty" jsonschema:"Override stop-after value in the updated workflow (e.g., '+48h', '2025-12-31 23:59:59')"` + 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{ @@ -574,21 +569,6 @@ Returns formatted text output showing: if args.Force { cmdArgs = append(cmdArgs, "--force") } - if args.Engine != "" { - cmdArgs = append(cmdArgs, "--engine", args.Engine) - } - if args.PR { - cmdArgs = append(cmdArgs, "--pr") - } - if args.Dir != "" { - cmdArgs = append(cmdArgs, "--dir", args.Dir) - } - if args.NoStopAfter { - cmdArgs = append(cmdArgs, "--no-stop-after") - } - if args.StopAfter != "" { - cmdArgs = append(cmdArgs, "--stop-after", args.StopAfter) - } // Execute the CLI command cmd := execCmd(ctx, cmdArgs...) From 63bdcb30dbea0eadaac52942d09dc17dd7e445ba Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 16 Nov 2025 01:32:28 +0000 Subject: [PATCH 5/5] Add changeset [skip-ci] --- .changeset/patch-add-update-tool-mcp.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/patch-add-update-tool-mcp.md diff --git a/.changeset/patch-add-update-tool-mcp.md b/.changeset/patch-add-update-tool-mcp.md new file mode 100644 index 0000000000..9be429de80 --- /dev/null +++ b/.changeset/patch-add-update-tool-mcp.md @@ -0,0 +1,7 @@ +--- +"gh-aw": patch +--- + +Add update tool to MCP server + +Exposes the `gh aw update` command through the MCP protocol with essential update flags (workflows, major, force).