diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index 006df083a7..abd8a466d7 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -571,7 +571,8 @@ jobs: 3. **Bash Tool Testing**: Execute bash commands to verify file creation was successful (use `cat` to read the file back) 4. **GitHub MCP Default Toolset Testing**: Verify that the `get_me` tool is NOT available with default toolsets. Try to use it and confirm it fails with a tool not found error. 5. **Cache Memory Testing**: Write a test file to `/tmp/gh-aw/cache-memory/smoke-test-__GH_AW_GITHUB_RUN_ID__.txt` with content "Cache memory test for run __GH_AW_GITHUB_RUN_ID__" and verify it was created successfully - 6. **Available Tools Display**: List all available tools that you have access to in this workflow execution. + 6. **Web Fetch Testing**: Use the web_fetch tool to fetch content from https://api.github.com/repos/githubnext/gh-aw (verify the tool is available and returns valid JSON) + 7. **Available Tools Display**: List all available tools that you have access to in this workflow execution. ## Output diff --git a/.github/workflows/smoke-copilot.md b/.github/workflows/smoke-copilot.md index ae695bf8dc..7943ad7b7c 100644 --- a/.github/workflows/smoke-copilot.md +++ b/.github/workflows/smoke-copilot.md @@ -26,6 +26,7 @@ tools: bash: - "*" github: + web-fetch: safe-outputs: add-comment: hide-older-comments: true @@ -53,7 +54,8 @@ strict: true 3. **Bash Tool Testing**: Execute bash commands to verify file creation was successful (use `cat` to read the file back) 4. **GitHub MCP Default Toolset Testing**: Verify that the `get_me` tool is NOT available with default toolsets. Try to use it and confirm it fails with a tool not found error. 5. **Cache Memory Testing**: Write a test file to `/tmp/gh-aw/cache-memory/smoke-test-${{ github.run_id }}.txt` with content "Cache memory test for run ${{ github.run_id }}" and verify it was created successfully -6. **Available Tools Display**: List all available tools that you have access to in this workflow execution. +6. **Web Fetch Testing**: Use the web_fetch tool to fetch content from https://api.github.com/repos/githubnext/gh-aw (verify the tool is available and returns valid JSON) +7. **Available Tools Display**: List all available tools that you have access to in this workflow execution. ## Output diff --git a/pkg/workflow/copilot_engine.go b/pkg/workflow/copilot_engine.go index 4a0ed9f903..e0403f7fe4 100644 --- a/pkg/workflow/copilot_engine.go +++ b/pkg/workflow/copilot_engine.go @@ -37,7 +37,7 @@ func NewCopilotEngine() *CopilotEngine { supportsToolsAllowlist: true, supportsHTTPTransport: true, // Copilot CLI supports HTTP transport via MCP supportsMaxTurns: false, // Copilot CLI does not support max-turns feature yet - supportsWebFetch: false, // Copilot CLI does not have built-in web-fetch support + supportsWebFetch: true, // Copilot CLI has built-in web-fetch support supportsWebSearch: false, // Copilot CLI does not have built-in web-search support supportsFirewall: true, // Copilot supports network firewalling via AWF }, diff --git a/pkg/workflow/copilot_engine_tools.go b/pkg/workflow/copilot_engine_tools.go index 3300075dc8..30f2401188 100644 --- a/pkg/workflow/copilot_engine_tools.go +++ b/pkg/workflow/copilot_engine_tools.go @@ -85,9 +85,15 @@ func (e *CopilotEngine) computeCopilotToolArguments(tools map[string]any, safeOu args = append(args, "--allow-tool", constants.SafeInputsMCPServerID) } + // Handle web-fetch builtin tool (Copilot CLI uses web_fetch with underscore) + if _, hasWebFetch := tools["web-fetch"]; hasWebFetch { + // web-fetch -> web_fetch + args = append(args, "--allow-tool", "web_fetch") + } + // Built-in tool names that should be skipped when processing MCP servers // Note: GitHub is NOT included here because it needs MCP configuration in CLI mode - // Note: web-fetch is NOT included here because it may be an MCP server for engines without native support + // Note: web-fetch is NOT included here because it needs explicit --allow-tool argument builtInTools := map[string]bool{ "bash": true, "edit": true, diff --git a/pkg/workflow/fetch_integration_test.go b/pkg/workflow/fetch_integration_test.go index 702a22d282..366ef32513 100644 --- a/pkg/workflow/fetch_integration_test.go +++ b/pkg/workflow/fetch_integration_test.go @@ -137,6 +137,68 @@ Fetch content from the web. } } +// TestWebFetchNotAddedForCopilotEngine tests that when a Copilot workflow uses web-fetch, +// the web-fetch MCP server is NOT added (because Copilot has native support) +func TestWebFetchNotAddedForCopilotEngine(t *testing.T) { + // Create a temporary directory for the test + tmpDir := testutil.TempDir(t, "test-*") + + // Create a test workflow that uses web-fetch with Copilot engine (which supports web-fetch natively) + workflowContent := `--- +on: workflow_dispatch +permissions: + contents: read + issues: read + pull-requests: read +engine: copilot +tools: + web-fetch: +--- + +# Test Workflow + +Fetch content from the web. +` + + workflowPath := filepath.Join(tmpDir, "test-workflow.md") + if err := os.WriteFile(workflowPath, []byte(workflowContent), 0644); err != nil { + t.Fatalf("Failed to write test workflow: %v", err) + } + + // Create a compiler + compiler := NewCompiler(false, "", "test") + + // Compile the workflow + err := compiler.CompileWorkflow(workflowPath) + if err != nil { + t.Fatalf("Failed to compile workflow: %v", err) + } + + // Read the generated lock file + lockPath := strings.TrimSuffix(workflowPath, ".md") + ".lock.yml" + lockData, err := os.ReadFile(lockPath) + if err != nil { + t.Fatalf("Failed to read lock file: %v", err) + } + + // Verify that the compiled workflow does NOT contain the web-fetch MCP server configuration + lockContent := string(lockData) + + // Check that web-fetch is NOT configured as an MCP server (no mcp_servers configuration) + if strings.Contains(lockContent, `[mcp_servers."web-fetch"]`) { + t.Errorf("Expected Copilot workflow NOT to contain web-fetch MCP server (since Copilot has native web-fetch support), but it did") + } + + // Also check for JSON format MCP server config (though Copilot doesn't use JSON for MCP config) + if strings.Contains(lockContent, `"web-fetch": {`) && strings.Contains(lockContent, `"command": "docker"`) { + dockerIdx := strings.Index(lockContent, `"command": "docker"`) + webFetchIdx := strings.Index(lockContent, `"web-fetch": {`) + if dockerIdx > 0 && webFetchIdx > 0 && dockerIdx-webFetchIdx < 200 { + t.Errorf("Expected Copilot workflow NOT to contain web-fetch MCP server, but it did") + } + } +} + // TestNoWebFetchNoMCPFetchServer tests that when a workflow doesn't use web-fetch, // the web-fetch MCP server is not added func TestNoWebFetchNoMCPFetchServer(t *testing.T) { diff --git a/pkg/workflow/fetch_test.go b/pkg/workflow/fetch_test.go index 606a3ed5b6..549c203b32 100644 --- a/pkg/workflow/fetch_test.go +++ b/pkg/workflow/fetch_test.go @@ -214,7 +214,7 @@ func TestEngineSupportsWebFetch(t *testing.T) { }{ {"claude", true}, {"codex", false}, - {"copilot", false}, + {"copilot", true}, // Copilot now supports web-fetch {"custom", false}, }