diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 7e79a16699..c8b2356069 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -323,6 +323,10 @@ const DefaultMCPGatewayVersion Version = "v0.0.103" // DefaultMCPGatewayContainer is the default container image for the MCP Gateway const DefaultMCPGatewayContainer = "ghcr.io/github/gh-aw-mcpg" +// DefaultMCPGatewayPayloadDir is the default directory for MCP gateway payload files +// This directory is shared between the agent container and MCP gateway for large payload exchange +const DefaultMCPGatewayPayloadDir = "/tmp/gh-aw/mcp-payloads" + // DefaultFirewallRegistry is the container image registry for AWF (gh-aw-firewall) Docker images const DefaultFirewallRegistry = "ghcr.io/github/gh-aw-firewall" diff --git a/pkg/workflow/codex_engine_test.go b/pkg/workflow/codex_engine_test.go index 7a09d5a2fc..1f937ced60 100644 --- a/pkg/workflow/codex_engine_test.go +++ b/pkg/workflow/codex_engine_test.go @@ -330,7 +330,8 @@ func TestCodexEngineRenderMCPConfig(t *testing.T) { "\"gateway\": {", "\"port\": $MCP_GATEWAY_PORT,", "\"domain\": \"${MCP_GATEWAY_DOMAIN}\",", - "\"apiKey\": \"${MCP_GATEWAY_API_KEY}\"", + "\"apiKey\": \"${MCP_GATEWAY_API_KEY}\",", + "\"payloadDir\": \"${MCP_GATEWAY_PAYLOAD_DIR}\"", "}", "}", "MCPCONFIG_EOF", diff --git a/pkg/workflow/mcp_gateway_config.go b/pkg/workflow/mcp_gateway_config.go index 13ec2eccf9..a0d7933529 100644 --- a/pkg/workflow/mcp_gateway_config.go +++ b/pkg/workflow/mcp_gateway_config.go @@ -95,6 +95,12 @@ func ensureDefaultMCPGatewayConfig(workflowData *WorkflowData) { "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw", } } + + // Ensure default payloadDir is set if not provided + if workflowData.SandboxConfig.MCP.PayloadDir == "" { + mcpGatewayConfigLog.Print("Setting default gateway payloadDir") + workflowData.SandboxConfig.MCP.PayloadDir = constants.DefaultMCPGatewayPayloadDir + } } // buildMCPGatewayConfig builds the gateway configuration for inclusion in MCP config files @@ -117,9 +123,10 @@ func buildMCPGatewayConfig(workflowData *WorkflowData) *MCPGatewayRuntimeConfig // Use ${...} syntax for environment variable references that will be resolved by the gateway at runtime // Per MCP Gateway Specification v1.0.0 section 4.2, variable expressions use "${VARIABLE_NAME}" syntax return &MCPGatewayRuntimeConfig{ - Port: int(DefaultMCPGatewayPort), // Will be formatted as "${MCP_GATEWAY_PORT}" in renderer - Domain: "${MCP_GATEWAY_DOMAIN}", // Gateway variable expression - APIKey: "${MCP_GATEWAY_API_KEY}", // Gateway variable expression + Port: int(DefaultMCPGatewayPort), // Will be formatted as "${MCP_GATEWAY_PORT}" in renderer + Domain: "${MCP_GATEWAY_DOMAIN}", // Gateway variable expression + APIKey: "${MCP_GATEWAY_API_KEY}", // Gateway variable expression + PayloadDir: "${MCP_GATEWAY_PAYLOAD_DIR}", // Gateway variable expression for payload directory } } diff --git a/pkg/workflow/mcp_gateway_config_test.go b/pkg/workflow/mcp_gateway_config_test.go index b8f6bff80e..a8b3b038be 100644 --- a/pkg/workflow/mcp_gateway_config_test.go +++ b/pkg/workflow/mcp_gateway_config_test.go @@ -32,6 +32,7 @@ func TestEnsureDefaultMCPGatewayConfig(t *testing.T) { assert.Equal(t, constants.DefaultMCPGatewayContainer, wd.SandboxConfig.MCP.Container, "Container should be default") assert.Equal(t, string(constants.DefaultMCPGatewayVersion), wd.SandboxConfig.MCP.Version, "Version should be default") assert.Equal(t, int(DefaultMCPGatewayPort), wd.SandboxConfig.MCP.Port, "Port should be default") + assert.Equal(t, constants.DefaultMCPGatewayPayloadDir, wd.SandboxConfig.MCP.PayloadDir, "PayloadDir should be default") assert.Len(t, wd.SandboxConfig.MCP.Mounts, 3, "Should have 3 default mounts") }, }, @@ -133,6 +134,37 @@ func TestEnsureDefaultMCPGatewayConfig(t *testing.T) { assert.Equal(t, "/custom:/mount:ro", wd.SandboxConfig.MCP.Mounts[0], "Custom mount should be preserved") }, }, + { + name: "fills in missing payloadDir field", + workflowData: &WorkflowData{ + SandboxConfig: &SandboxConfig{ + MCP: &MCPGatewayRuntimeConfig{ + Container: "custom-container", + Version: "v1.0.0", + Port: 8080, + }, + }, + }, + validate: func(t *testing.T, wd *WorkflowData) { + assert.Equal(t, constants.DefaultMCPGatewayPayloadDir, wd.SandboxConfig.MCP.PayloadDir, "PayloadDir should be filled with default") + }, + }, + { + name: "preserves custom payloadDir", + workflowData: &WorkflowData{ + SandboxConfig: &SandboxConfig{ + MCP: &MCPGatewayRuntimeConfig{ + Container: "custom-container", + Version: "v1.0.0", + Port: 8080, + PayloadDir: "/custom/payloads", + }, + }, + }, + validate: func(t *testing.T, wd *WorkflowData) { + assert.Equal(t, "/custom/payloads", wd.SandboxConfig.MCP.PayloadDir, "Custom payloadDir should be preserved") + }, + }, } for _, tt := range tests { @@ -169,9 +201,10 @@ func TestBuildMCPGatewayConfig(t *testing.T) { name: "creates default gateway config", workflowData: &WorkflowData{}, expected: &MCPGatewayRuntimeConfig{ - Port: int(DefaultMCPGatewayPort), - Domain: "${MCP_GATEWAY_DOMAIN}", - APIKey: "${MCP_GATEWAY_API_KEY}", + Port: int(DefaultMCPGatewayPort), + Domain: "${MCP_GATEWAY_DOMAIN}", + APIKey: "${MCP_GATEWAY_API_KEY}", + PayloadDir: "${MCP_GATEWAY_PAYLOAD_DIR}", }, }, { @@ -184,9 +217,10 @@ func TestBuildMCPGatewayConfig(t *testing.T) { }, }, expected: &MCPGatewayRuntimeConfig{ - Port: int(DefaultMCPGatewayPort), - Domain: "${MCP_GATEWAY_DOMAIN}", - APIKey: "${MCP_GATEWAY_API_KEY}", + Port: int(DefaultMCPGatewayPort), + Domain: "${MCP_GATEWAY_DOMAIN}", + APIKey: "${MCP_GATEWAY_API_KEY}", + PayloadDir: "${MCP_GATEWAY_PAYLOAD_DIR}", }, }, } @@ -201,6 +235,7 @@ func TestBuildMCPGatewayConfig(t *testing.T) { assert.Equal(t, tt.expected.Port, result.Port, "Port should match") assert.Equal(t, tt.expected.Domain, result.Domain, "Domain should match") assert.Equal(t, tt.expected.APIKey, result.APIKey, "APIKey should match") + assert.Equal(t, tt.expected.PayloadDir, result.PayloadDir, "PayloadDir should match") } }) } diff --git a/pkg/workflow/mcp_renderer.go b/pkg/workflow/mcp_renderer.go index 9467cc855c..90acfed0dd 100644 --- a/pkg/workflow/mcp_renderer.go +++ b/pkg/workflow/mcp_renderer.go @@ -944,7 +944,13 @@ func RenderJSONMCPConfig( // Port as unquoted variable - shell expands to integer (e.g., 8080) for valid JSON fmt.Fprintf(&configBuilder, " \"port\": $MCP_GATEWAY_PORT,\n") fmt.Fprintf(&configBuilder, " \"domain\": \"%s\",\n", options.GatewayConfig.Domain) - fmt.Fprintf(&configBuilder, " \"apiKey\": \"%s\"\n", options.GatewayConfig.APIKey) + fmt.Fprintf(&configBuilder, " \"apiKey\": \"%s\"", options.GatewayConfig.APIKey) + // Add payloadDir if specified + if options.GatewayConfig.PayloadDir != "" { + fmt.Fprintf(&configBuilder, ",\n \"payloadDir\": \"%s\"\n", options.GatewayConfig.PayloadDir) + } else { + configBuilder.WriteString("\n") + } configBuilder.WriteString(" }\n") } else { configBuilder.WriteString(" }\n") diff --git a/pkg/workflow/mcp_setup_generator.go b/pkg/workflow/mcp_setup_generator.go index f82a1bf89d..8a4d9f0a0b 100644 --- a/pkg/workflow/mcp_setup_generator.go +++ b/pkg/workflow/mcp_setup_generator.go @@ -502,6 +502,15 @@ func (c *Compiler) generateMCPSetup(yaml *strings.Builder, tools map[string]any, } else { yaml.WriteString(" export MCP_GATEWAY_API_KEY=\"" + apiKey + "\"\n") } + + // Export payload directory and ensure it exists + payloadDir := gatewayConfig.PayloadDir + if payloadDir == "" { + payloadDir = constants.DefaultMCPGatewayPayloadDir + } + yaml.WriteString(" export MCP_GATEWAY_PAYLOAD_DIR=\"" + payloadDir + "\"\n") + yaml.WriteString(" mkdir -p \"${MCP_GATEWAY_PAYLOAD_DIR}\"\n") + yaml.WriteString(" export DEBUG=\"*\"\n") yaml.WriteString(" \n") yaml.WriteString(" # Register API key as secret to mask it from logs\n") @@ -544,6 +553,7 @@ func (c *Compiler) generateMCPSetup(yaml *strings.Builder, tools map[string]any, containerCmd += " -e MCP_GATEWAY_PORT" containerCmd += " -e MCP_GATEWAY_DOMAIN" containerCmd += " -e MCP_GATEWAY_API_KEY" + containerCmd += " -e MCP_GATEWAY_PAYLOAD_DIR" containerCmd += " -e DEBUG" // Pass environment variables that MCP servers reference in their config // These are needed because awmg v0.0.12+ validates and resolves ${VAR} patterns at config load time @@ -624,7 +634,7 @@ func (c *Compiler) generateMCPSetup(yaml *strings.Builder, tools map[string]any, // Mark standard environment variables as already added standardEnvVars := []string{ - "MCP_GATEWAY_PORT", "MCP_GATEWAY_DOMAIN", "MCP_GATEWAY_API_KEY", "DEBUG", + "MCP_GATEWAY_PORT", "MCP_GATEWAY_DOMAIN", "MCP_GATEWAY_API_KEY", "MCP_GATEWAY_PAYLOAD_DIR", "DEBUG", "MCP_GATEWAY_LOG_DIR", "GH_AW_MCP_LOG_DIR", "GH_AW_SAFE_OUTPUTS", "GH_AW_SAFE_OUTPUTS_CONFIG_PATH", "GH_AW_SAFE_OUTPUTS_TOOLS_PATH", "GH_AW_ASSETS_BRANCH", "GH_AW_ASSETS_MAX_SIZE_KB", "GH_AW_ASSETS_ALLOWED_EXTS", @@ -679,6 +689,12 @@ func (c *Compiler) generateMCPSetup(yaml *strings.Builder, tools map[string]any, } // Add volume mounts + // First, add the payload directory mount (rw for both agent and gateway) + if payloadDir != "" { + containerCmd += " -v " + payloadDir + ":" + payloadDir + ":rw" + } + + // Then add user-configured mounts if len(gatewayConfig.Mounts) > 0 { for _, mount := range gatewayConfig.Mounts { containerCmd += " -v " + mount diff --git a/pkg/workflow/tools_types.go b/pkg/workflow/tools_types.go index 31c098ffe2..d936c8696e 100644 --- a/pkg/workflow/tools_types.go +++ b/pkg/workflow/tools_types.go @@ -374,6 +374,7 @@ type MCPGatewayRuntimeConfig struct { APIKey string `yaml:"api-key,omitempty"` // API key for gateway authentication Domain string `yaml:"domain,omitempty"` // Domain for gateway URL (localhost or host.docker.internal) Mounts []string `yaml:"mounts,omitempty"` // Volume mounts for the gateway container (format: "source:dest:mode") + PayloadDir string `yaml:"payload-dir,omitempty"` // Directory path for storing large payload JSON files (must be absolute path) } // HasTool checks if a tool is present in the configuration