From d43cc831e70dca65de2f619652caf419c703a2b0 Mon Sep 17 00:00:00 2001 From: "anthropic-code-agent[bot]" <242468646+Claude@users.noreply.github.com> Date: Fri, 6 Feb 2026 01:27:09 +0000 Subject: [PATCH 1/2] Initial plan From a3c1e0ebbf69468f13b8519052eb434c9d07015b Mon Sep 17 00:00:00 2001 From: "anthropic-code-agent[bot]" <242468646+Claude@users.noreply.github.com> Date: Fri, 6 Feb 2026 01:41:11 +0000 Subject: [PATCH 2/2] feat: add payloadDir configuration for MCP gateway Configure payloadDir for sharing large MCP response payloads between agent and gateway containers. Adds default directory, environment variable setup, volume mounting, and JSON config rendering. Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> --- pkg/constants/constants.go | 4 +++ pkg/workflow/codex_engine_test.go | 3 +- pkg/workflow/mcp_gateway_config.go | 13 +++++-- pkg/workflow/mcp_gateway_config_test.go | 47 +++++++++++++++++++++---- pkg/workflow/mcp_renderer.go | 8 ++++- pkg/workflow/mcp_setup_generator.go | 18 +++++++++- pkg/workflow/tools_types.go | 1 + 7 files changed, 82 insertions(+), 12 deletions(-) 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