RenderMCPConfig generates MCP server configuration for Codex
(yaml *strings.Builder, tools map[string]any, mcpTools []string, workflowData *WorkflowData)
| 19 | |
| 20 | // RenderMCPConfig generates MCP server configuration for Codex |
| 21 | func (e *CodexEngine) RenderMCPConfig(yaml *strings.Builder, tools map[string]any, mcpTools []string, workflowData *WorkflowData) error { |
| 22 | if codexMCPLog.Enabled() { |
| 23 | codexMCPLog.Printf("Rendering MCP config for Codex: mcp_tools=%v, tool_count=%d", mcpTools, len(tools)) |
| 24 | } |
| 25 | |
| 26 | // Create unified renderer with Codex-specific options |
| 27 | // Codex uses TOML format without Copilot-specific fields and multi-line args |
| 28 | createRenderer := func(isLast bool) *MCPConfigRendererUnified { |
| 29 | return NewMCPConfigRenderer(MCPRendererOptions{ |
| 30 | IncludeCopilotFields: false, // Codex doesn't use "type" and "tools" fields |
| 31 | InlineArgs: false, // Codex uses multi-line args format |
| 32 | Format: "toml", |
| 33 | IsLast: isLast, |
| 34 | ActionMode: GetActionModeFromWorkflowData(workflowData), |
| 35 | WriteSinkGuardPolicies: deriveWriteSinkGuardPolicyFromWorkflow(workflowData), |
| 36 | }) |
| 37 | } |
| 38 | |
| 39 | // Build the heredoc content into a temporary buffer so we can derive the |
| 40 | // delimiter from a SHA-256 hash of the content before writing it to the YAML output. |
| 41 | var mcpConfigContent strings.Builder |
| 42 | |
| 43 | // Add history configuration to disable persistence |
| 44 | mcpConfigContent.WriteString(" [history]\n") |
| 45 | mcpConfigContent.WriteString(" persistence = \"none\"\n") |
| 46 | |
| 47 | // Add shell environment policy to control which environment variables are passed through |
| 48 | // This is a security feature to prevent accidental exposure of secrets |
| 49 | e.renderShellEnvironmentPolicy(&mcpConfigContent, tools, mcpTools) |
| 50 | |
| 51 | // Expand neutral tools (like playwright: null) to include the copilot agent tools |
| 52 | expandedTools := e.expandNeutralToolsToCodexToolsFromMap(tools) |
| 53 | |
| 54 | // Generate [mcp_servers] section |
| 55 | for _, toolName := range mcpTools { |
| 56 | renderer := createRenderer(false) // isLast is always false in TOML format |
| 57 | switch toolName { |
| 58 | case "github": |
| 59 | githubTool, _ := expandedTools["github"].(map[string]any) |
| 60 | renderer.RenderGitHubMCP(&mcpConfigContent, githubTool, workflowData) |
| 61 | case "playwright": |
| 62 | playwrightTool := expandedTools["playwright"] |
| 63 | renderer.RenderPlaywrightMCP(&mcpConfigContent, playwrightTool) |
| 64 | case "agentic-workflows": |
| 65 | renderer.RenderAgenticWorkflowsMCP(&mcpConfigContent) |
| 66 | case "safe-outputs": |
| 67 | // Add safe-outputs MCP server if safe-outputs are configured |
| 68 | hasSafeOutputs := workflowData != nil && workflowData.SafeOutputs != nil && HasSafeOutputsEnabled(workflowData.SafeOutputs) |
| 69 | if hasSafeOutputs { |
| 70 | renderer.RenderSafeOutputsMCP(&mcpConfigContent, workflowData) |
| 71 | } |
| 72 | case "mcp-scripts": |
| 73 | // Add mcp-scripts MCP server if mcp-scripts are configured and feature flag is enabled |
| 74 | hasMCPScripts := workflowData != nil && IsMCPScriptsEnabled(workflowData.MCPScripts) |
| 75 | if hasMCPScripts { |
| 76 | renderer.RenderMCPScriptsMCP(&mcpConfigContent, workflowData.MCPScripts, workflowData) |
| 77 | } |
| 78 | default: |