Skip to content

Commit dc5c0d2

Browse files
lpcoxCopilotgithub-actions[bot]Copilotgh-aw-bot
authored
fix(compiler): normalize gateway policy server IDs (#58023)
* fix(setup): validate plural gateway agent IDs Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * fix(compiler): normalize gateway policy server IDs Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1ad74329-b8a6-4ad4-b378-805e3fbfc0ce * fix(compiler): defer shared enclave backends Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1ad74329-b8a6-4ad4-b378-805e3fbfc0ce * fix(compiler): hand gateway key to enclave host Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1ad74329-b8a6-4ad4-b378-805e3fbfc0ce * fix(compiler): preserve deferred enclave readiness contract Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> * docs: clarify gateway API key alias Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> * fix(cli): sync embedded workflow fallback manifest Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> Copilot-Session: 1ad74329-b8a6-4ad4-b378-805e3fbfc0ce
1 parent 86c0e5a commit dc5c0d2

9 files changed

Lines changed: 68 additions & 3 deletions

actions/setup/js/start_mcp_gateway.cjs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1356,7 +1356,9 @@ async function main() {
13561356
// Write GitHub Actions step outputs
13571357
// -----------------------------------------------------------------------
13581358
if (githubOutput) {
1359-
const outputs = [`gateway-pid=${gatewayPid}`, `gateway-port=${gatewayPort}`, `gateway-agent-id=${agentId}`, `gateway-domain=${gatewayDomain}`].join("\n");
1359+
// The gateway authenticates with its agent ID; expose the same value under the
1360+
// API-key output name expected by the AWF host handoff.
1361+
const outputs = [`gateway-pid=${gatewayPid}`, `gateway-port=${gatewayPort}`, `gateway-agent-id=${agentId}`, `gateway-api-key=${agentId}`, `gateway-domain=${gatewayDomain}`].join("\n");
13601362
try {
13611363
fs.appendFileSync(githubOutput, outputs + "\n");
13621364
} catch {

actions/setup/js/start_mcp_gateway.test.cjs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ describe("start_mcp_gateway logging", () => {
4646
expect(source).toContain(`stdio: ["pipe", outputFd, stderrFd]`);
4747
expect(source).toContain(`path.join(os.tmpdir(), "gh-aw-mcp-gateway-")`);
4848
});
49+
50+
it("exports the gateway agent ID as the AWF API key handoff", () => {
51+
const source = fs.readFileSync(new URL("./start_mcp_gateway.cjs", import.meta.url), "utf8");
52+
expect(source).toContain("`gateway-api-key=${agentId}`");
53+
});
4954
});
5055

5156
describe("start_mcp_gateway health timeout", () => {

actions/setup/sh/start_mcp_gateway.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,5 +524,6 @@ echo ""
524524
echo "gateway-pid=$GATEWAY_PID"
525525
echo "gateway-port=${MCP_GATEWAY_PORT}"
526526
echo "gateway-agent-id=${MCP_GATEWAY_AGENT_ID}"
527+
echo "gateway-api-key=${MCP_GATEWAY_AGENT_ID}"
527528
echo "gateway-domain=${MCP_GATEWAY_DOMAIN}"
528529
} >> "$GITHUB_OUTPUT"

actions/setup/sh/start_mcp_gateway_test.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,12 @@ test_validation_functions_exist() {
248248
else
249249
print_result "Configuration input handling missing" "FAIL"
250250
fi
251+
252+
if grep -q 'gateway-api-key=${MCP_GATEWAY_AGENT_ID}' "$SCRIPT_PATH"; then
253+
print_result "Gateway API key output handoff exists" "PASS"
254+
else
255+
print_result "Gateway API key output handoff missing" "FAIL"
256+
fi
251257

252258
# Check for JSON validation
253259
if grep -q "not valid JSON" "$SCRIPT_PATH"; then

pkg/workflow/awf_env.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ func ComputeAWFExcludeEnvVarNames(workflowData *WorkflowData, coreSecretVarNames
103103
if HasMCPServers(workflowData) {
104104
addUnique("MCP_GATEWAY_AGENT_ID")
105105
}
106+
if enclavesEnabled(workflowData) {
107+
addUnique("MCP_GATEWAY_API_KEY")
108+
}
106109

107110
// GitHub MCP server token is always a secret when the GitHub tool is present.
108111
if hasGitHubTool(workflowData.ParsedTools) {

pkg/workflow/copilot_engine_execution.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,9 @@ func (e *CopilotEngine) buildCopilotExecutionStep(workflowData *WorkflowData, co
730730
// This is a security measure to prevent exposing unnecessary secrets to the AWF container
731731
allowedSecrets := e.GetRequiredSecretNames(workflowData)
732732
filteredEnv := FilterEnvForSecrets(env, allowedSecrets)
733+
if enclavesEnabled(workflowData) {
734+
filteredEnv["MCP_GATEWAY_API_KEY"] = "${{ steps.start-mcp-gateway.outputs.gateway-api-key }}"
735+
}
733736
// Inject GH_TOKEN for CLI proxy (added after filtering since it uses a special
734737
// fallback expression that is always allowed when cli-proxy is enabled)
735738
addCliProxyGHTokenToEnv(filteredEnv, workflowData)

pkg/workflow/enclave_github_proxy_test.go

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,36 @@ func TestEnclaveGitHubMCPAgentPolicy(t *testing.T) {
2929

3030
func TestEnclaveGitHubMCPGatewayConfiguration(t *testing.T) {
3131
data := enclaveGitHubIssuesWorkflowData()
32+
data.Tools["github"] = map[string]any{}
33+
data.SafeOutputs = &SafeOutputsConfig{AddComments: &AddCommentsConfig{}}
3234
config := buildMCPGatewayConfig(data)
3335

3436
assert.Empty(t, config.AgentID)
3537
assert.Equal(t, []string{"${MCP_GATEWAY_AGENT_ID}", "${AWF_ENCLAVE_GITHUB_MCP_AGENT_ID}"}, config.AgentIDs)
36-
assert.Equal(t, []string{enclaveMCPServerName}, config.AgentPolicies["${MCP_GATEWAY_AGENT_ID}"].Servers)
38+
assert.Equal(t, []string{enclaveMCPServerName, "github", constants.SafeOutputsMCPServerID.String()}, config.AgentPolicies["${MCP_GATEWAY_AGENT_ID}"].Servers)
3739
assert.Equal(t, []string{"github"}, config.AgentPolicies["${AWF_ENCLAVE_GITHUB_MCP_AGENT_ID}"].Servers)
40+
41+
generatedServers := make(map[string]struct{})
42+
for _, server := range collectMCPServersForManifest(data) {
43+
generatedServers[server.Name] = struct{}{}
44+
}
45+
for agentID, policy := range config.AgentPolicies {
46+
for _, server := range policy.Servers {
47+
assert.Contains(t, generatedServers, server, "policy for %s references an unknown MCP server", agentID)
48+
}
49+
}
50+
}
51+
52+
func TestToolsWithEnclaveGitHubIssuesUnionsTypedToolsets(t *testing.T) {
53+
data := enclaveGitHubIssuesWorkflowData()
54+
tools := map[string]any{
55+
"github": map[string]any{"toolsets": []string{"context"}},
56+
}
57+
58+
updated := toolsWithEnclaveGitHubIssues(tools, data)
59+
60+
assert.Equal(t, []string{"context", "issues"}, updated["github"].(map[string]any)["toolsets"])
61+
assert.Equal(t, []string{"context"}, tools["github"].(map[string]any)["toolsets"], "original tools must remain unchanged")
3862
}
3963

4064
func TestCompileEnclaveGitHubSharedGateway(t *testing.T) {
@@ -45,6 +69,11 @@ on: workflow_dispatch
4569
strict: false
4670
network: defaults
4771
engine: copilot
72+
tools:
73+
github:
74+
toolsets: [context]
75+
safe-outputs:
76+
add-comment:
4877
sandbox:
4978
agent:
5079
id: awf
@@ -72,9 +101,17 @@ Read the assigned repository's issues through the enclave.
72101

73102
assert.Equal(t, 1, strings.Count(lock, "--name awmg-mcpg"))
74103
assert.Contains(t, lock, `"agentIds": ["${MCP_GATEWAY_AGENT_ID}","${AWF_ENCLAVE_GITHUB_MCP_AGENT_ID}"]`)
104+
assert.Contains(t, lock, `"safeoutputs": {`)
105+
assert.Contains(t, lock, `"awf-enclave": {`)
106+
assert.NotContains(t, lock, `"required": false`)
107+
assert.Contains(t, lock, `"GITHUB_TOOLSETS": "context,issues"`)
108+
assert.Contains(t, lock, `"${MCP_GATEWAY_AGENT_ID}":{"servers":["awf-enclave","github","safeoutputs"],"tools":{"github":["get_me"]}}`)
109+
assert.NotContains(t, lock, `"servers":["awf-enclave","github","safe-outputs"]`)
75110
assert.Contains(t, lock, `"agentPolicies": {"${AWF_ENCLAVE_GITHUB_MCP_AGENT_ID}":{"servers":["github"],"tools":{"github":["list_issues","issue_read"]},"allow-only":{"min-integrity":"approved","repos":["octo-org/private-service"]}}`)
76111
assert.Contains(t, lock, `AWF_ENCLAVE_GITHUB_MCP_AGENT_ID=$(openssl rand -base64 45 | tr -d '/+=')`)
77112
assert.Contains(t, lock, `printf '%s=%s\n' AWF_ENCLAVE_GITHUB_MCP_AGENT_ID "$AWF_ENCLAVE_GITHUB_MCP_AGENT_ID"`)
113+
assert.Contains(t, lock, `MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}`)
114+
assert.Contains(t, lock, `--exclude-env MCP_GATEWAY_API_KEY`)
78115
assert.Contains(t, lock, "--exclude-env AWF_ENCLAVE_GITHUB_MCP_AGENT_ID")
79116
assert.NotContains(t, lock, "Enclave GitHub Proxy")
80117
assert.NotContains(t, lock, "start_enclave_github_proxy")

pkg/workflow/mcp_gateway_config.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,11 @@ func buildMCPGatewayConfig(workflowData *WorkflowData) *MCPGatewayRuntimeConfig
208208
OTLPHeaders: workflowData.OTLPHeaders,
209209
}
210210
if enclaveGitHubIssuesEnabled(workflowData) {
211-
primaryServers := collectMCPTools(workflowData)
211+
manifestServers := collectMCPServersForManifest(workflowData)
212+
primaryServers := make([]string, 0, len(manifestServers))
213+
for _, server := range manifestServers {
214+
primaryServers = append(primaryServers, server.Name)
215+
}
212216
primaryGitHubEnabled := false
213217
if githubTool, hasGitHub := workflowData.Tools["github"]; hasGitHub && githubTool != false {
214218
primaryGitHubEnabled = !isGitHubCLIModeEnabled(workflowData)

pkg/workflow/mcp_setup_generator.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,10 @@ func toolsWithEnclaveGitHubIssues(tools map[string]any, workflowData *WorkflowDa
164164
if !slices.Contains(toolsets, "issues") {
165165
githubConfig["toolsets"] = append(toolsets, "issues")
166166
}
167+
} else if toolsets, ok := githubConfig["toolsets"].([]string); ok {
168+
if !slices.Contains(toolsets, "issues") {
169+
githubConfig["toolsets"] = append(toolsets, "issues")
170+
}
167171
} else if toolsets, ok := githubConfig["toolsets"].(string); ok {
168172
if !slices.Contains(strings.Split(toolsets, ","), "issues") {
169173
githubConfig["toolsets"] = toolsets + ",issues"

0 commit comments

Comments
 (0)