diff --git a/.github/workflows/ai-moderator.lock.yml b/.github/workflows/ai-moderator.lock.yml index 436c036f98..d67ae137d4 100644 --- a/.github/workflows/ai-moderator.lock.yml +++ b/.github/workflows/ai-moderator.lock.yml @@ -739,6 +739,19 @@ jobs: } } GH_AW_MCP_CONFIG_e663c9f713ba8ef0_EOF + + # Sync converter output to writable CODEX_HOME for Codex + mkdir -p /tmp/gh-aw/mcp-config + cat > "/tmp/gh-aw/mcp-config/config.toml" << GH_AW_CODEX_SHELL_POLICY_41a4d5c2d909bed6_EOF + [shell_environment_policy] + inherit = "core" + include_only = ["CODEX_API_KEY", "GH_AW_ASSETS_ALLOWED_EXTS", "GH_AW_ASSETS_BRANCH", "GH_AW_ASSETS_MAX_SIZE_KB", "GH_AW_SAFE_OUTPUTS", "GITHUB_PERSONAL_ACCESS_TOKEN", "GITHUB_REPOSITORY", "GITHUB_SERVER_URL", "HOME", "OPENAI_API_KEY", "PATH"] + GH_AW_CODEX_SHELL_POLICY_41a4d5c2d909bed6_EOF + cat "${RUNNER_TEMP}/gh-aw/mcp-config/config.toml" >> "/tmp/gh-aw/mcp-config/config.toml" + chmod 600 "/tmp/gh-aw/mcp-config/config.toml" + mkdir -p "${CODEX_HOME}" + cp "/tmp/gh-aw/mcp-config/config.toml" "${CODEX_HOME}/config.toml" + chmod 600 "${CODEX_HOME}/config.toml" - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true diff --git a/.github/workflows/changeset.lock.yml b/.github/workflows/changeset.lock.yml index 5c8d5bcc41..ac1aa3a9c9 100644 --- a/.github/workflows/changeset.lock.yml +++ b/.github/workflows/changeset.lock.yml @@ -758,6 +758,19 @@ jobs: } } GH_AW_MCP_CONFIG_6c95ba396f64fff8_EOF + + # Sync converter output to writable CODEX_HOME for Codex + mkdir -p /tmp/gh-aw/mcp-config + cat > "/tmp/gh-aw/mcp-config/config.toml" << GH_AW_CODEX_SHELL_POLICY_20aa83733790ce7c_EOF + [shell_environment_policy] + inherit = "core" + include_only = ["CODEX_API_KEY", "GH_AW_ASSETS_ALLOWED_EXTS", "GH_AW_ASSETS_BRANCH", "GH_AW_ASSETS_MAX_SIZE_KB", "GH_AW_SAFE_OUTPUTS", "GITHUB_PERSONAL_ACCESS_TOKEN", "GITHUB_REPOSITORY", "GITHUB_SERVER_URL", "HOME", "OPENAI_API_KEY", "PATH"] + GH_AW_CODEX_SHELL_POLICY_20aa83733790ce7c_EOF + cat "${RUNNER_TEMP}/gh-aw/mcp-config/config.toml" >> "/tmp/gh-aw/mcp-config/config.toml" + chmod 600 "/tmp/gh-aw/mcp-config/config.toml" + mkdir -p "${CODEX_HOME}" + cp "/tmp/gh-aw/mcp-config/config.toml" "${CODEX_HOME}/config.toml" + chmod 600 "${CODEX_HOME}/config.toml" - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true diff --git a/.github/workflows/codex-github-remote-mcp-test.lock.yml b/.github/workflows/codex-github-remote-mcp-test.lock.yml index 4e2f89d8ff..85fc47a1ae 100644 --- a/.github/workflows/codex-github-remote-mcp-test.lock.yml +++ b/.github/workflows/codex-github-remote-mcp-test.lock.yml @@ -435,6 +435,19 @@ jobs: } } GH_AW_MCP_CONFIG_65f825e6fcc54e90_EOF + + # Sync converter output to writable CODEX_HOME for Codex + mkdir -p /tmp/gh-aw/mcp-config + cat > "/tmp/gh-aw/mcp-config/config.toml" << GH_AW_CODEX_SHELL_POLICY_6ef9da006a49787c_EOF + [shell_environment_policy] + inherit = "core" + include_only = ["CODEX_API_KEY", "GITHUB_PERSONAL_ACCESS_TOKEN", "HOME", "OPENAI_API_KEY", "PATH"] + GH_AW_CODEX_SHELL_POLICY_6ef9da006a49787c_EOF + cat "${RUNNER_TEMP}/gh-aw/mcp-config/config.toml" >> "/tmp/gh-aw/mcp-config/config.toml" + chmod 600 "/tmp/gh-aw/mcp-config/config.toml" + mkdir -p "${CODEX_HOME}" + cp "/tmp/gh-aw/mcp-config/config.toml" "${CODEX_HOME}/config.toml" + chmod 600 "${CODEX_HOME}/config.toml" - name: Download activation artifact uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: diff --git a/.github/workflows/daily-fact.lock.yml b/.github/workflows/daily-fact.lock.yml index eb534d4b7b..1ce4cd0a6e 100644 --- a/.github/workflows/daily-fact.lock.yml +++ b/.github/workflows/daily-fact.lock.yml @@ -863,6 +863,19 @@ jobs: } } GH_AW_MCP_CONFIG_1b4b127ec8d7bd00_EOF + + # Sync converter output to writable CODEX_HOME for Codex + mkdir -p /tmp/gh-aw/mcp-config + cat > "/tmp/gh-aw/mcp-config/config.toml" << GH_AW_CODEX_SHELL_POLICY_252c56bd99e6f878_EOF + [shell_environment_policy] + inherit = "core" + include_only = ["CODEX_API_KEY", "GH_AW_ASSETS_ALLOWED_EXTS", "GH_AW_ASSETS_BRANCH", "GH_AW_ASSETS_MAX_SIZE_KB", "GH_AW_SAFE_OUTPUTS", "GITHUB_PERSONAL_ACCESS_TOKEN", "GITHUB_REPOSITORY", "GITHUB_SERVER_URL", "HOME", "OPENAI_API_KEY", "PATH"] + GH_AW_CODEX_SHELL_POLICY_252c56bd99e6f878_EOF + cat "${RUNNER_TEMP}/gh-aw/mcp-config/config.toml" >> "/tmp/gh-aw/mcp-config/config.toml" + chmod 600 "/tmp/gh-aw/mcp-config/config.toml" + mkdir -p "${CODEX_HOME}" + cp "/tmp/gh-aw/mcp-config/config.toml" "${CODEX_HOME}/config.toml" + chmod 600 "${CODEX_HOME}/config.toml" - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true diff --git a/.github/workflows/daily-observability-report.lock.yml b/.github/workflows/daily-observability-report.lock.yml index 3b3ea69de4..124f133d4a 100644 --- a/.github/workflows/daily-observability-report.lock.yml +++ b/.github/workflows/daily-observability-report.lock.yml @@ -791,6 +791,19 @@ jobs: } } GH_AW_MCP_CONFIG_929e8f2937cc782f_EOF + + # Sync converter output to writable CODEX_HOME for Codex + mkdir -p /tmp/gh-aw/mcp-config + cat > "/tmp/gh-aw/mcp-config/config.toml" << GH_AW_CODEX_SHELL_POLICY_08284ac4f16ebe02_EOF + [shell_environment_policy] + inherit = "core" + include_only = ["CODEX_API_KEY", "GH_AW_ASSETS_ALLOWED_EXTS", "GH_AW_ASSETS_BRANCH", "GH_AW_ASSETS_MAX_SIZE_KB", "GH_AW_SAFE_OUTPUTS", "GITHUB_PERSONAL_ACCESS_TOKEN", "GITHUB_REPOSITORY", "GITHUB_SERVER_URL", "GITHUB_TOKEN", "HOME", "OPENAI_API_KEY", "PATH"] + GH_AW_CODEX_SHELL_POLICY_08284ac4f16ebe02_EOF + cat "${RUNNER_TEMP}/gh-aw/mcp-config/config.toml" >> "/tmp/gh-aw/mcp-config/config.toml" + chmod 600 "/tmp/gh-aw/mcp-config/config.toml" + mkdir -p "${CODEX_HOME}" + cp "/tmp/gh-aw/mcp-config/config.toml" "${CODEX_HOME}/config.toml" + chmod 600 "${CODEX_HOME}/config.toml" - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml index b1128c971a..133f592ba0 100644 --- a/.github/workflows/duplicate-code-detector.lock.yml +++ b/.github/workflows/duplicate-code-detector.lock.yml @@ -765,6 +765,19 @@ jobs: } } GH_AW_MCP_CONFIG_64aeaad4be91ee91_EOF + + # Sync converter output to writable CODEX_HOME for Codex + mkdir -p /tmp/gh-aw/mcp-config + cat > "/tmp/gh-aw/mcp-config/config.toml" << GH_AW_CODEX_SHELL_POLICY_5f2b419d8d1a0762_EOF + [shell_environment_policy] + inherit = "core" + include_only = ["CODEX_API_KEY", "GH_AW_ASSETS_ALLOWED_EXTS", "GH_AW_ASSETS_BRANCH", "GH_AW_ASSETS_MAX_SIZE_KB", "GH_AW_SAFE_OUTPUTS", "GITHUB_PERSONAL_ACCESS_TOKEN", "GITHUB_REPOSITORY", "GITHUB_SERVER_URL", "HOME", "OPENAI_API_KEY", "PATH"] + GH_AW_CODEX_SHELL_POLICY_5f2b419d8d1a0762_EOF + cat "${RUNNER_TEMP}/gh-aw/mcp-config/config.toml" >> "/tmp/gh-aw/mcp-config/config.toml" + chmod 600 "/tmp/gh-aw/mcp-config/config.toml" + mkdir -p "${CODEX_HOME}" + cp "/tmp/gh-aw/mcp-config/config.toml" "${CODEX_HOME}/config.toml" + chmod 600 "${CODEX_HOME}/config.toml" - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true diff --git a/.github/workflows/grumpy-reviewer.lock.yml b/.github/workflows/grumpy-reviewer.lock.yml index 3eb2f6275c..824626f9f3 100644 --- a/.github/workflows/grumpy-reviewer.lock.yml +++ b/.github/workflows/grumpy-reviewer.lock.yml @@ -786,6 +786,19 @@ jobs: } } GH_AW_MCP_CONFIG_8427183129186c74_EOF + + # Sync converter output to writable CODEX_HOME for Codex + mkdir -p /tmp/gh-aw/mcp-config + cat > "/tmp/gh-aw/mcp-config/config.toml" << GH_AW_CODEX_SHELL_POLICY_fd0494d129bb4dc0_EOF + [shell_environment_policy] + inherit = "core" + include_only = ["CODEX_API_KEY", "GH_AW_ASSETS_ALLOWED_EXTS", "GH_AW_ASSETS_BRANCH", "GH_AW_ASSETS_MAX_SIZE_KB", "GH_AW_SAFE_OUTPUTS", "GITHUB_PERSONAL_ACCESS_TOKEN", "GITHUB_REPOSITORY", "GITHUB_SERVER_URL", "HOME", "OPENAI_API_KEY", "PATH"] + GH_AW_CODEX_SHELL_POLICY_fd0494d129bb4dc0_EOF + cat "${RUNNER_TEMP}/gh-aw/mcp-config/config.toml" >> "/tmp/gh-aw/mcp-config/config.toml" + chmod 600 "/tmp/gh-aw/mcp-config/config.toml" + mkdir -p "${CODEX_HOME}" + cp "/tmp/gh-aw/mcp-config/config.toml" "${CODEX_HOME}/config.toml" + chmod 600 "${CODEX_HOME}/config.toml" - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true diff --git a/.github/workflows/issue-arborist.lock.yml b/.github/workflows/issue-arborist.lock.yml index 785c858b74..5b592ced4d 100644 --- a/.github/workflows/issue-arborist.lock.yml +++ b/.github/workflows/issue-arborist.lock.yml @@ -798,6 +798,19 @@ jobs: } } GH_AW_MCP_CONFIG_ae10dba5409c9fdc_EOF + + # Sync converter output to writable CODEX_HOME for Codex + mkdir -p /tmp/gh-aw/mcp-config + cat > "/tmp/gh-aw/mcp-config/config.toml" << GH_AW_CODEX_SHELL_POLICY_8df1e17e12a5553f_EOF + [shell_environment_policy] + inherit = "core" + include_only = ["CODEX_API_KEY", "GH_AW_ASSETS_ALLOWED_EXTS", "GH_AW_ASSETS_BRANCH", "GH_AW_ASSETS_MAX_SIZE_KB", "GH_AW_SAFE_OUTPUTS", "GITHUB_PERSONAL_ACCESS_TOKEN", "GITHUB_REPOSITORY", "GITHUB_SERVER_URL", "HOME", "OPENAI_API_KEY", "PATH"] + GH_AW_CODEX_SHELL_POLICY_8df1e17e12a5553f_EOF + cat "${RUNNER_TEMP}/gh-aw/mcp-config/config.toml" >> "/tmp/gh-aw/mcp-config/config.toml" + chmod 600 "/tmp/gh-aw/mcp-config/config.toml" + mkdir -p "${CODEX_HOME}" + cp "/tmp/gh-aw/mcp-config/config.toml" "${CODEX_HOME}/config.toml" + chmod 600 "${CODEX_HOME}/config.toml" - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true diff --git a/.github/workflows/schema-feature-coverage.lock.yml b/.github/workflows/schema-feature-coverage.lock.yml index 141f884257..c98f6457a6 100644 --- a/.github/workflows/schema-feature-coverage.lock.yml +++ b/.github/workflows/schema-feature-coverage.lock.yml @@ -690,6 +690,19 @@ jobs: } } GH_AW_MCP_CONFIG_f7f791fc4a40ada4_EOF + + # Sync converter output to writable CODEX_HOME for Codex + mkdir -p /tmp/gh-aw/mcp-config + cat > "/tmp/gh-aw/mcp-config/config.toml" << GH_AW_CODEX_SHELL_POLICY_12a1c9e5a4cc208c_EOF + [shell_environment_policy] + inherit = "core" + include_only = ["CODEX_API_KEY", "GH_AW_ASSETS_ALLOWED_EXTS", "GH_AW_ASSETS_BRANCH", "GH_AW_ASSETS_MAX_SIZE_KB", "GH_AW_SAFE_OUTPUTS", "GITHUB_PERSONAL_ACCESS_TOKEN", "GITHUB_REPOSITORY", "GITHUB_SERVER_URL", "HOME", "OPENAI_API_KEY", "PATH"] + GH_AW_CODEX_SHELL_POLICY_12a1c9e5a4cc208c_EOF + cat "${RUNNER_TEMP}/gh-aw/mcp-config/config.toml" >> "/tmp/gh-aw/mcp-config/config.toml" + chmod 600 "/tmp/gh-aw/mcp-config/config.toml" + mkdir -p "${CODEX_HOME}" + cp "/tmp/gh-aw/mcp-config/config.toml" "${CODEX_HOME}/config.toml" + chmod 600 "${CODEX_HOME}/config.toml" - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true diff --git a/.github/workflows/smoke-call-workflow.lock.yml b/.github/workflows/smoke-call-workflow.lock.yml index 8ed1393832..ff1f644ecd 100644 --- a/.github/workflows/smoke-call-workflow.lock.yml +++ b/.github/workflows/smoke-call-workflow.lock.yml @@ -699,6 +699,19 @@ jobs: } } GH_AW_MCP_CONFIG_c186530a09a3396a_EOF + + # Sync converter output to writable CODEX_HOME for Codex + mkdir -p /tmp/gh-aw/mcp-config + cat > "/tmp/gh-aw/mcp-config/config.toml" << GH_AW_CODEX_SHELL_POLICY_2d745eed178272e7_EOF + [shell_environment_policy] + inherit = "core" + include_only = ["CODEX_API_KEY", "GH_AW_ASSETS_ALLOWED_EXTS", "GH_AW_ASSETS_BRANCH", "GH_AW_ASSETS_MAX_SIZE_KB", "GH_AW_SAFE_OUTPUTS", "GITHUB_PERSONAL_ACCESS_TOKEN", "GITHUB_REPOSITORY", "GITHUB_SERVER_URL", "HOME", "OPENAI_API_KEY", "PATH"] + GH_AW_CODEX_SHELL_POLICY_2d745eed178272e7_EOF + cat "${RUNNER_TEMP}/gh-aw/mcp-config/config.toml" >> "/tmp/gh-aw/mcp-config/config.toml" + chmod 600 "/tmp/gh-aw/mcp-config/config.toml" + mkdir -p "${CODEX_HOME}" + cp "/tmp/gh-aw/mcp-config/config.toml" "${CODEX_HOME}/config.toml" + chmod 600 "${CODEX_HOME}/config.toml" - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index 260b5113bc..289dc2dff5 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -1179,6 +1179,19 @@ jobs: } } GH_AW_MCP_CONFIG_78a63514935420e3_EOF + + # Sync converter output to writable CODEX_HOME for Codex + mkdir -p /tmp/gh-aw/mcp-config + cat > "/tmp/gh-aw/mcp-config/config.toml" << GH_AW_CODEX_SHELL_POLICY_6ca2c660677622ac_EOF + [shell_environment_policy] + inherit = "core" + include_only = ["CODEX_API_KEY", "GH_AW_ASSETS_ALLOWED_EXTS", "GH_AW_ASSETS_BRANCH", "GH_AW_ASSETS_MAX_SIZE_KB", "GH_AW_SAFE_OUTPUTS", "GITHUB_PERSONAL_ACCESS_TOKEN", "GITHUB_REPOSITORY", "GITHUB_SERVER_URL", "HOME", "OPENAI_API_KEY", "PATH"] + GH_AW_CODEX_SHELL_POLICY_6ca2c660677622ac_EOF + cat "${RUNNER_TEMP}/gh-aw/mcp-config/config.toml" >> "/tmp/gh-aw/mcp-config/config.toml" + chmod 600 "/tmp/gh-aw/mcp-config/config.toml" + mkdir -p "${CODEX_HOME}" + cp "/tmp/gh-aw/mcp-config/config.toml" "${CODEX_HOME}/config.toml" + chmod 600 "${CODEX_HOME}/config.toml" - name: Download activation artifact uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: diff --git a/pkg/workflow/codex_engine.go b/pkg/workflow/codex_engine.go index 9b09d45a09..049de52680 100644 --- a/pkg/workflow/codex_engine.go +++ b/pkg/workflow/codex_engine.go @@ -463,9 +463,7 @@ func (e *CodexEngine) expandNeutralToolsToCodexToolsFromMap(tools map[string]any return result.ToMap() } -// renderShellEnvironmentPolicy generates the [shell_environment_policy] section for config.toml -// This controls which environment variables are passed through to MCP servers for security -func (e *CodexEngine) renderShellEnvironmentPolicy(yaml *strings.Builder, tools map[string]any, mcpTools []string, workflowData *WorkflowData) { +func (e *CodexEngine) getShellEnvironmentPolicyVars(tools map[string]any, mcpTools []string) []string { // Collect all environment variables needed by MCP servers envVars := make(map[string]bool) @@ -509,13 +507,20 @@ func (e *CodexEngine) renderShellEnvironmentPolicy(yaml *strings.Builder, tools } } - // Sort environment variable names for consistent output var sortedEnvVars []string for envVar := range envVars { sortedEnvVars = append(sortedEnvVars, envVar) } sort.Strings(sortedEnvVars) + return sortedEnvVars +} + +// renderShellEnvironmentPolicy generates the [shell_environment_policy] section for config.toml +// This controls which environment variables are passed through to MCP servers for security +func (e *CodexEngine) renderShellEnvironmentPolicy(yaml *strings.Builder, tools map[string]any, mcpTools []string) { + sortedEnvVars := e.getShellEnvironmentPolicyVars(tools, mcpTools) + // Render [shell_environment_policy] section yaml.WriteString(" \n") yaml.WriteString(" [shell_environment_policy]\n") @@ -530,6 +535,21 @@ func (e *CodexEngine) renderShellEnvironmentPolicy(yaml *strings.Builder, tools yaml.WriteString("]\n") } +func (e *CodexEngine) renderShellEnvironmentPolicyToml(yaml *strings.Builder, tools map[string]any, mcpTools []string, indent string) { + sortedEnvVars := e.getShellEnvironmentPolicyVars(tools, mcpTools) + + yaml.WriteString(indent + "[shell_environment_policy]\n") + yaml.WriteString(indent + "inherit = \"core\"\n") + yaml.WriteString(indent + "include_only = [") + for i, envVar := range sortedEnvVars { + if i > 0 { + yaml.WriteString(", ") + } + yaml.WriteString("\"" + envVar + "\"") + } + yaml.WriteString("]\n") +} + // RenderMCPConfig is implemented in codex_mcp.go // renderCodexMCPConfig is implemented in codex_mcp.go diff --git a/pkg/workflow/codex_engine_test.go b/pkg/workflow/codex_engine_test.go index 027ee79f05..d4680f3388 100644 --- a/pkg/workflow/codex_engine_test.go +++ b/pkg/workflow/codex_engine_test.go @@ -250,6 +250,19 @@ func TestCodexEngineRenderMCPConfig(t *testing.T) { "}", "}", "GH_AW_MCP_CONFIG_NORM_EOF", + "", + "# Sync converter output to writable CODEX_HOME for Codex", + "mkdir -p /tmp/gh-aw/mcp-config", + "cat > \"/tmp/gh-aw/mcp-config/config.toml\" << GH_AW_CODEX_SHELL_POLICY_NORM_EOF", + "[shell_environment_policy]", + "inherit = \"core\"", + "include_only = [\"CODEX_API_KEY\", \"GITHUB_PERSONAL_ACCESS_TOKEN\", \"HOME\", \"OPENAI_API_KEY\", \"PATH\"]", + "GH_AW_CODEX_SHELL_POLICY_NORM_EOF", + "cat \"${RUNNER_TEMP}/gh-aw/mcp-config/config.toml\" >> \"/tmp/gh-aw/mcp-config/config.toml\"", + "chmod 600 \"/tmp/gh-aw/mcp-config/config.toml\"", + "mkdir -p \"${CODEX_HOME}\"", + "cp \"/tmp/gh-aw/mcp-config/config.toml\" \"${CODEX_HOME}/config.toml\"", + "chmod 600 \"${CODEX_HOME}/config.toml\"", }, }, } diff --git a/pkg/workflow/codex_mcp.go b/pkg/workflow/codex_mcp.go index 19e259b887..7c1c669c28 100644 --- a/pkg/workflow/codex_mcp.go +++ b/pkg/workflow/codex_mcp.go @@ -37,7 +37,7 @@ func (e *CodexEngine) RenderMCPConfig(yaml *strings.Builder, tools map[string]an // Add shell environment policy to control which environment variables are passed through // This is a security feature to prevent accidental exposure of secrets - e.renderShellEnvironmentPolicy(yaml, tools, mcpTools, workflowData) + e.renderShellEnvironmentPolicy(yaml, tools, mcpTools) // Expand neutral tools (like playwright: null) to include the copilot agent tools expandedTools := e.expandNeutralToolsToCodexToolsFromMap(tools) @@ -99,11 +99,44 @@ func (e *CodexEngine) RenderMCPConfig(yaml *strings.Builder, tools map[string]an yaml.WriteString(" # Generate JSON config for MCP gateway\n") // Gateway uses JSON format without Copilot-specific fields and multi-line args - return renderStandardJSONMCPConfig(yaml, tools, mcpTools, workflowData, + if err := renderStandardJSONMCPConfig(yaml, tools, mcpTools, workflowData, "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json", false, false, func(yaml *strings.Builder, toolName string, toolConfig map[string]any, isLast bool) error { return e.renderCodexJSONMCPConfigWithContext(yaml, toolName, toolConfig, isLast, workflowData) - }, nil) + }, nil); err != nil { + return err + } + + // start_mcp_gateway.cjs converts the gateway output and writes Codex config to + // ${RUNNER_TEMP}/gh-aw/mcp-config/config.toml. Codex reads config from + // $CODEX_HOME/config.toml, so copy the converted config into writable CODEX_HOME + // and prepend shell policy (converter output does not include this section). + yaml.WriteString(" \n") + yaml.WriteString(" # Sync converter output to writable CODEX_HOME for Codex\n") + yaml.WriteString(" mkdir -p /tmp/gh-aw/mcp-config\n") + + shellPolicyDelimiter := GenerateHeredocDelimiterFromSeed("CODEX_SHELL_POLICY", workflowData.FrontmatterHash) + yaml.WriteString(" cat > \"/tmp/gh-aw/mcp-config/config.toml\" << " + shellPolicyDelimiter + "\n") + e.renderShellEnvironmentPolicyToml(yaml, tools, mcpTools, " ") + yaml.WriteString(" " + shellPolicyDelimiter + "\n") + yaml.WriteString(" cat \"${RUNNER_TEMP}/gh-aw/mcp-config/config.toml\" >> \"/tmp/gh-aw/mcp-config/config.toml\"\n") + if workflowData.EngineConfig != nil && strings.TrimSpace(workflowData.EngineConfig.Config) != "" { + customConfigDelimiter := GenerateHeredocDelimiterFromSeed("CODEX_CUSTOM_CONFIG", workflowData.FrontmatterHash) + yaml.WriteString(" \n") + yaml.WriteString(" # Append engine-level custom Codex config\n") + yaml.WriteString(" cat >> \"/tmp/gh-aw/mcp-config/config.toml\" << " + customConfigDelimiter + "\n") + yaml.WriteString(workflowData.EngineConfig.Config) + if !strings.HasSuffix(workflowData.EngineConfig.Config, "\n") { + yaml.WriteString("\n") + } + yaml.WriteString(" " + customConfigDelimiter + "\n") + } + yaml.WriteString(" chmod 600 \"/tmp/gh-aw/mcp-config/config.toml\"\n") + yaml.WriteString(" mkdir -p \"${CODEX_HOME}\"\n") + yaml.WriteString(" cp \"/tmp/gh-aw/mcp-config/config.toml\" \"${CODEX_HOME}/config.toml\"\n") + yaml.WriteString(" chmod 600 \"${CODEX_HOME}/config.toml\"\n") + + return nil } // renderCodexMCPConfigWithContext generates custom MCP server configuration for a single tool in codex workflow config.toml