Skip to content

Commit 1a4657a

Browse files
Copilotlpcox
andauthored
Wire dynamic enclave delegation controller env vars and fix primary-agent isolation
Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
1 parent e8135d3 commit 1a4657a

10 files changed

Lines changed: 214 additions & 81 deletions

File tree

docs/src/content/docs/experimental/enclaves.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ sandbox:
7171
id: awf
7272
version: v0.28.14
7373
mcp:
74-
version: v0.4.16
74+
version: v0.4.17
7575
enclaves:
7676
- agent:
7777
model: gpt-5
@@ -102,8 +102,8 @@ enclaves:
102102
- An entry must declare either non-empty static `repos` or `dynamic`, never both.
103103
- `allowed-owners` and `allowed-repositories` use canonical lowercase ASCII selectors. Repository selectors must already match `owner/repo`; the compiler does not trim, case-fold, URL-decode, or normalize them.
104104
- `github-policy` must be `github-repository-read-v1`, which exposes only `list_issues` and `issue_read` through per-invocation delegated identities.
105-
- Dynamic entries require finite resource limits, total quotas, audit labels, an absolute `expires-at` no later than the enclave job lifetime, AWF `v0.28.14` or newer, and mcpg `v0.4.16` or newer.
106-
- The primary and enclave agents do not receive repository credentials or the delegation-control capability. The compiler gives AWF an AWF-only control capability so AWF can request short-lived mcpg identities for admitted repositories.
105+
- Dynamic entries require finite resource limits, total quotas, audit labels, an absolute `expires-at` timestamp, AWF `v0.28.14` or newer, and mcpg `v0.4.17` or newer (the first mcpg release that accepts the delegation controller's atomic bootstrap configuration; see `github/gh-aw-mcpg#12605`). `expires-at` is an upper bound checked into the workflow file; the compiler resolves the effective envelope expiry at workflow setup time as `min(expires-at, job-start + enclave timeout)`, so a fixed, checked-in timestamp never needs to be a short-lived compile-time value.
106+
- The primary and enclave agents do not receive repository credentials or the delegation-control capability. The compiler starts mcpg's `github-repository-delegation-v1` controller with five settings as one atomic configuration (`MCP_GATEWAY_DELEGATION_ENVELOPE`, `MCP_GATEWAY_DELEGATION_CONTROL_KEY`, `MCP_GATEWAY_DELEGATION_STATE_PATH`, `MCP_GATEWAY_DELEGATION_GENERATION`, `MCP_GATEWAY_DELEGATION_CONTROL_LISTEN`) and hands AWF an explicit, host-private control endpoint (`AWF_ENCLAVE_GITHUB_DELEGATION_CONTROL_ENDPOINT`) and capability so AWF can request short-lived mcpg identities for admitted repositories. That endpoint is distinct from the executor-facing `AWF_ENCLAVE_MCP_GATEWAY_ENDPOINT` data plane and is excluded from the primary agent's environment.
107107

108108
## Deprecated `issues-read-v1` profile
109109

pkg/constants/version_constants.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,11 @@ const AWFEnclaveTrustedSensitivityMinVersion Version = "v0.28.14"
169169
// AWFDynamicRepositoryEnclaveMinVersion is the first AWF version that accepts
170170
// dynamic agent enclave repository policy envelopes and performs per-invocation
171171
// repository admission through MCPG's github-repository-delegation-v1 controller.
172+
//
173+
// This value is intentionally kept above DefaultFirewallVersion (provisional,
174+
// fail-closed) until the AWF implementation tracked by gh-aw-firewall#8195
175+
// ships. Once that release is available, raise this constant to the actual
176+
// compatible AWF release and update DefaultFirewallVersion accordingly.
172177
const AWFDynamicRepositoryEnclaveMinVersion Version = "v0.28.14"
173178

174179
// AWFAPIProxyCACertMinVersion is the minimum AWF version that supports
@@ -226,7 +231,17 @@ const MCPGEnclaveAgentToolsMinVersion Version = "v0.4.15"
226231
// MCPGDynamicRepositoryDelegationMinVersion is the first MCPG version that
227232
// advertises the github-repository-delegation-v1 dynamic repository delegation
228233
// controller required by dynamic agent enclave admission.
229-
const MCPGDynamicRepositoryDelegationMinVersion Version = "v0.4.16"
234+
//
235+
// v0.4.16 advertised the controller but rejected the strict-stdin
236+
// "delegationControllers" config field the compiler previously emitted, never
237+
// started the real controller, and never handed AWF a private control
238+
// endpoint (gh-aw-mcpg#12604). That contract is fixed by gh-aw-mcpg#12605; this
239+
// constant is pinned to the first release containing that fix so dynamic
240+
// enclave compilation/runtime setup fails closed on older MCPG builds that
241+
// lack the compatible owner-scoped envelope, bounded dynamic schema
242+
// admission, transactional reconciliation, persisted TTL binding, durability,
243+
// DIFC isolation, and redaction behavior.
244+
const MCPGDynamicRepositoryDelegationMinVersion Version = "v0.4.17"
230245

231246
// DefaultPlaywrightCLIVersion is the default version of the @playwright/cli package.
232247
// Used when tools.playwright is enabled.

pkg/workflow/awf_env.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,9 @@ func ComputeAWFExcludeEnvVarNames(workflowData *WorkflowData, coreSecretVarNames
170170
if enclaveGitHubIssuesEnabled(workflowData) {
171171
addUnique(enclaveGitHubMCPAgentIDEnv)
172172
}
173+
if enclaveDynamicRepositoryPolicyEnabled(workflowData) {
174+
addUnique(enclaveGitHubDelegationControlEndpointEnv)
175+
}
173176

174177
// Explicitly excluded env vars from the frontmatter excluded-env field.
175178
// These are always excluded regardless of their value content.

pkg/workflow/enclave_github_proxy_test.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,20 @@ func TestDynamicEnclaveRegistersGitHubBackend(t *testing.T) {
103103
data := dynamicEnclaveWorkflowData()
104104
config := buildMCPGatewayConfig(data)
105105

106+
// The GitHub backend stays registered so mcpg's delegation controller can
107+
// issue delegated identities for it, but the primary agent identity must
108+
// not gain GitHub MCP access merely because a dynamic enclave is enabled.
106109
assert.Contains(t, collectMCPTools(data), "github")
110+
assert.NotContains(t, config.AgentPolicies["${MCP_GATEWAY_AGENT_ID}"].Servers, "github")
111+
}
112+
113+
func TestDynamicEnclaveWithPrimaryGitHubRetainsPrimaryAccess(t *testing.T) {
114+
data := dynamicEnclaveWorkflowData()
115+
data.Tools["github"] = map[string]any{}
116+
config := buildMCPGatewayConfig(data)
117+
107118
assert.Contains(t, config.AgentPolicies["${MCP_GATEWAY_AGENT_ID}"].Servers, "github")
108-
assert.Equal(t, "github", config.DelegationControllers[enclaveDynamicController].Server)
119+
assert.NotEmpty(t, config.AgentPolicies["${MCP_GATEWAY_AGENT_ID}"].Tools["github"])
109120
}
110121

111122
func TestCompileEnclaveGitHubSharedGateway(t *testing.T) {

pkg/workflow/enclaves.go

Lines changed: 82 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,40 @@ const (
2626
enclaveMCPReadinessTimeoutEnv = "AWF_ENCLAVE_MCP_READINESS_TIMEOUT_MS"
2727
enclaveMCPDeferredServersEnv = "GH_AW_MCP_DEFERRED_SERVERS"
2828
enclaveGitHubDelegationEnv = "AWF_ENCLAVE_GITHUB_DELEGATION_CONTROL_CAPABILITY"
29-
enclaveMCPGatewayRunLabel = "com.github.gh-aw.mcpg.run"
30-
enclaveMCPGatewayContainer = "awmg-mcpg"
31-
enclaveGitHubIssuesProfile = "issues-read-v1"
32-
enclaveDynamicGitHubPolicy = "github-repository-read-v1"
33-
enclaveDynamicController = "github-repository-delegation-v1"
34-
enclaveMCPConnectTimeout = 120
35-
enclaveMCPReadinessTimeoutMS = 120000
36-
maxEnclaveTimingBucketSeconds = 4800
37-
enclaveMCPTransportAllowance = 60
29+
// enclaveGitHubDelegationControlEndpointEnv identifies the AWF-private control
30+
// endpoint for mcpg's github-repository-delegation-v1 controller. This is
31+
// distinct from enclaveMCPGatewayEndpointEnv, which identifies the
32+
// executor-facing /mcp/awf-enclave data plane. Only the AWF host process
33+
// receives this variable; it must never reach the primary agent or the
34+
// enclave executor.
35+
enclaveGitHubDelegationControlEndpointEnv = "AWF_ENCLAVE_GITHUB_DELEGATION_CONTROL_ENDPOINT"
36+
enclaveMCPGatewayRunLabel = "com.github.gh-aw.mcpg.run"
37+
enclaveMCPGatewayContainer = "awmg-mcpg"
38+
enclaveGitHubIssuesProfile = "issues-read-v1"
39+
enclaveDynamicGitHubPolicy = "github-repository-read-v1"
40+
enclaveDynamicController = "github-repository-delegation-v1"
41+
enclaveMCPConnectTimeout = 120
42+
enclaveMCPReadinessTimeoutMS = 120000
43+
maxEnclaveTimingBucketSeconds = 4800
44+
enclaveMCPTransportAllowance = 60
45+
// enclaveDelegationControlPort is the private, host-only listener port for
46+
// mcpg's github-repository-delegation-v1 control plane. It is bound
47+
// separately from MCP_GATEWAY_PORT (the executor-facing data plane) and is
48+
// published only to loopback so neither the primary agent nor the enclave
49+
// executor network can route to it.
50+
enclaveDelegationControlPort = 8090
51+
// enclaveDelegationStateDir is the protected, persistent mount used for mcpg
52+
// delegation controller state so that it survives an in-run restart.
53+
enclaveDelegationStateDir = "${RUNNER_TEMP}/gh-aw/mcpg-delegation"
54+
// enclaveDelegationGeneration is the monotonic policy generation for the
55+
// active envelope. It is fixed for the lifetime of a single workflow run so
56+
// that controller restart/recovery within the run observes a stable value.
57+
enclaveDelegationGeneration = "1"
58+
// enclaveDelegationExpiresAtEnv holds the runtime-resolved RFC3339 envelope
59+
// expiry: min(enclaves[].dynamic.expires-at, job-start + enclave.timeout).
60+
// This keeps checked-in workflows valid without requiring a short-lived
61+
// absolute compile-time timestamp.
62+
enclaveDelegationExpiresAtEnv = "MCP_GATEWAY_DELEGATION_EXPIRES_AT"
3863
)
3964

4065
var enclaveAgentGitHubSupportedTools = map[string]struct{}{
@@ -396,13 +421,14 @@ func validateDynamicEnclavePolicy(index int, enclave *EnclaveConfig) error {
396421
}
397422

398423
func validateDynamicEnclaveBounds(index int, enclave *EnclaveConfig, policy *DynamicEnclavePolicy) error {
399-
expiresAt, err := time.Parse(time.RFC3339, policy.ExpiresAt)
400-
if err != nil {
424+
if _, err := time.Parse(time.RFC3339, policy.ExpiresAt); err != nil {
401425
return fmt.Errorf("enclaves[%d].dynamic.expires-at must be an absolute RFC3339 timestamp: %w", index, err)
402426
}
403-
if expiresAt.After(time.Now().UTC().Add(time.Duration(enclave.Timeout) * time.Second)) {
404-
return fmt.Errorf("enclaves[%d].dynamic.expires-at must not exceed the enclave job lifetime", index)
405-
}
427+
// expires-at is an upper bound only. Checked-in workflows may carry a
428+
// fixed timestamp that grows stale between commits, so the compiler does
429+
// not compare it against compile-time time.Now(); the runtime/job-relative
430+
// expiry contract instead clamps the effective envelope expiry to
431+
// min(expires-at, job-start + enclave.timeout) when the workflow runs.
406432
cpuLimit, err := strconv.ParseFloat(enclave.CPULimit, 64)
407433
if err != nil || cpuLimit <= 0 {
408434
return fmt.Errorf("enclaves[%d].cpu-limit must be a positive finite value", index)
@@ -677,6 +703,48 @@ func buildAWFDynamicEnclavePolicy(enclave *EnclaveConfig) map[string]any {
677703
}
678704
}
679705

706+
// buildMCPGatewayDelegationEnvelope produces the immutable envelope handed to mcpg's
707+
// github-repository-delegation-v1 controller via MCP_GATEWAY_DELEGATION_ENVELOPE.
708+
// The envelope represents owner-scoped runtime discovery, an optional exact repository
709+
// allowlist, the bounded dynamic schema-hash capacity, the exact v1 tool set, the
710+
// maximum identity TTL, and the runtime expiry, without broadening authority beyond
711+
// what enclaves[].dynamic declares at compile time.
712+
func buildMCPGatewayDelegationEnvelope(enclave *EnclaveConfig) map[string]any {
713+
policy := enclave.Dynamic
714+
return map[string]any{
715+
"version": enclaveDynamicGitHubPolicy,
716+
"runId": "${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}",
717+
"backend": "github",
718+
"allowedOwners": stringSliceOrEmpty(policy.AllowedOwners),
719+
"allowedRepositories": stringSliceOrEmpty(policy.AllowedRepositories),
720+
"tools": append([]string(nil), enclaveAgentGitHubDefaultTools...),
721+
"maxSchemaHashes": policy.MaxRepositories,
722+
"maxIdentityTTLSeconds": enclave.Timeout,
723+
"expiresAt": "${" + enclaveDelegationExpiresAtEnv + "}",
724+
"generation": enclaveDelegationGeneration,
725+
"auditLabels": append([]string(nil), policy.AuditLabels...),
726+
}
727+
}
728+
729+
// buildDynamicEnclaveExpiryScript emits the shell lines that resolve the
730+
// runtime/job-relative envelope expiry contract: the effective expiry is the
731+
// earlier of the compiled enclaves[].dynamic.expires-at upper bound and
732+
// job-start + enclave.timeout, so it can never exceed the job or invocation
733+
// lifetime regardless of how stale a checked-in absolute timestamp has grown.
734+
func buildDynamicEnclaveExpiryScript(enclave *EnclaveConfig) string {
735+
var script strings.Builder
736+
fmt.Fprintf(&script, " GH_AW_ENCLAVE_DYNAMIC_JOB_EXPIRES_EPOCH=$(( $(date -u +%%s) + %d ))\n", enclave.Timeout)
737+
fmt.Fprintf(&script, " GH_AW_ENCLAVE_DYNAMIC_CONFIGURED_EXPIRES_EPOCH=$(date -u -d %s +%%s)\n", shellEscapeArg(enclave.Dynamic.ExpiresAt))
738+
script.WriteString(" if [ \"$GH_AW_ENCLAVE_DYNAMIC_CONFIGURED_EXPIRES_EPOCH\" -lt \"$GH_AW_ENCLAVE_DYNAMIC_JOB_EXPIRES_EPOCH\" ]; then\n")
739+
script.WriteString(" GH_AW_ENCLAVE_DYNAMIC_EXPIRES_EPOCH=\"$GH_AW_ENCLAVE_DYNAMIC_CONFIGURED_EXPIRES_EPOCH\"\n")
740+
script.WriteString(" else\n")
741+
script.WriteString(" GH_AW_ENCLAVE_DYNAMIC_EXPIRES_EPOCH=\"$GH_AW_ENCLAVE_DYNAMIC_JOB_EXPIRES_EPOCH\"\n")
742+
script.WriteString(" fi\n")
743+
fmt.Fprintf(&script, " %s=$(date -u -d \"@$GH_AW_ENCLAVE_DYNAMIC_EXPIRES_EPOCH\" +%%Y-%%m-%%dT%%H:%%M:%%SZ)\n", enclaveDelegationExpiresAtEnv)
744+
fmt.Fprintf(&script, " export %s\n", enclaveDelegationExpiresAtEnv)
745+
return script.String()
746+
}
747+
680748
func stringSliceOrEmpty(values []string) []string {
681749
if values == nil {
682750
return []string{}

pkg/workflow/enclaves_test.go

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -360,11 +360,19 @@ func TestBuildAWFConfigJSONDynamicEnclavePolicy(t *testing.T) {
360360
}
361361

362362
func TestValidateDynamicEnclavePolicyBoundsExpiryAndCPU(t *testing.T) {
363+
// expires-at is a checked-in upper bound; it must remain valid even after
364+
// it has grown "stale" relative to compile time, since the runtime/job-
365+
// relative expiry contract (not compile-time comparison) clamps the
366+
// effective envelope expiry at workflow setup time.
363367
data := dynamicEnclaveWorkflowData()
364368
data.Enclaves[0].Dynamic.ExpiresAt = "2999-01-01T00:00:00Z"
369+
require.NoError(t, validateEnclavesConfig(data))
370+
371+
data = dynamicEnclaveWorkflowData()
372+
data.Enclaves[0].Dynamic.ExpiresAt = "not-a-timestamp"
365373
err := validateEnclavesConfig(data)
366374
require.Error(t, err)
367-
assert.Contains(t, err.Error(), "must not exceed the enclave job lifetime")
375+
assert.Contains(t, err.Error(), "must be an absolute RFC3339 timestamp")
368376

369377
data = dynamicEnclaveWorkflowData()
370378
data.Enclaves[0].CPULimit = "0"
@@ -542,9 +550,9 @@ func TestValidateDynamicEnclavePolicy(t *testing.T) {
542550
{
543551
name: "rejects old mcpg version",
544552
mutate: func(data *WorkflowData) {
545-
data.SandboxConfig.MCP.Version = "v0.4.15"
553+
data.SandboxConfig.MCP.Version = "v0.4.16"
546554
},
547-
errContains: "requires MCPG v0.4.16 or newer",
555+
errContains: "requires MCPG v0.4.17 or newer",
548556
},
549557
}
550558

@@ -566,30 +574,36 @@ func TestDynamicEnclaveGatewayContract(t *testing.T) {
566574
require.NotNil(t, gateway)
567575
assert.Equal(t, []string{"${MCP_GATEWAY_AGENT_ID}"}, gateway.AgentIDs)
568576
assert.NotContains(t, gateway.AgentPolicies, "${AWF_ENCLAVE_GITHUB_MCP_AGENT_ID}")
569-
controller := gateway.DelegationControllers[enclaveDynamicController]
570-
assert.Equal(t, "github", controller.Server)
571-
assert.Equal(t, map[string]any{
572-
"version": enclaveDynamicGitHubPolicy,
573-
"tools": []string{"list_issues", "issue_read"},
574-
}, controller.Policy)
575-
assert.Equal(t, "${AWF_ENCLAVE_GITHUB_DELEGATION_CONTROL_CAPABILITY}", controller.ControlCapability)
576577

577578
var output strings.Builder
578579
require.NoError(t, generateMCPGatewaySetup(
579580
&output, data.Tools, []string{enclaveMCPServerName}, NewCopilotEngine(), data, false, nil,
580581
))
581582
generated := output.String()
582-
assert.Contains(t, generated, `"delegationControllers": {"github-repository-delegation-v1"`)
583-
assert.Contains(t, generated, `"version":"github-repository-read-v1"`)
584-
assert.Contains(t, generated, `"tools":["list_issues","issue_read"]`)
583+
// The gateway's strict-stdin config schema does not accept a
584+
// "delegationControllers" field; the compiler must not emit one.
585+
assert.NotContains(t, generated, `"delegationControllers"`)
586+
// The five required settings are bootstrapped as one atomic configuration.
587+
assert.Contains(t, generated, `export MCP_GATEWAY_DELEGATION_CONTROL_KEY="${AWF_ENCLAVE_GITHUB_DELEGATION_CONTROL_CAPABILITY}"`)
588+
assert.Contains(t, generated, `export MCP_GATEWAY_DELEGATION_STATE_PATH="`)
589+
assert.Contains(t, generated, `export MCP_GATEWAY_DELEGATION_GENERATION="1"`)
590+
assert.Contains(t, generated, `export MCP_GATEWAY_DELEGATION_CONTROL_LISTEN="127.0.0.1:8090"`)
591+
assert.Contains(t, generated, `export MCP_GATEWAY_DELEGATION_ENVELOPE=`)
592+
assert.Contains(t, generated, `\"version\":\"github-repository-read-v1\"`)
593+
assert.Contains(t, generated, `\"tools\":[\"list_issues\",\"issue_read\"]`)
594+
assert.Contains(t, generated, `\"expiresAt\":\"${MCP_GATEWAY_DELEGATION_EXPIRES_AT}\"`)
595+
// The private control endpoint is distinct from the executor-facing data plane.
596+
assert.Contains(t, generated, `export AWF_ENCLAVE_GITHUB_DELEGATION_CONTROL_ENDPOINT="http://127.0.0.1:8090/control/github-repository-delegation-v1"`)
585597
assert.Contains(t, generated, `AWF_ENCLAVE_GITHUB_DELEGATION_CONTROL_CAPABILITY=$(openssl rand -hex 32)`)
586598
assert.Contains(t, generated, `::add-mask::${AWF_ENCLAVE_GITHUB_DELEGATION_CONTROL_CAPABILITY}`)
587599
assert.Contains(t, generated, `printf '%s=%s\n' AWF_ENCLAVE_GITHUB_DELEGATION_CONTROL_CAPABILITY "$AWF_ENCLAVE_GITHUB_DELEGATION_CONTROL_CAPABILITY"`)
600+
assert.Contains(t, generated, `printf '%s=%s\n' AWF_ENCLAVE_GITHUB_DELEGATION_CONTROL_ENDPOINT "$AWF_ENCLAVE_GITHUB_DELEGATION_CONTROL_ENDPOINT"`)
588601
assert.NotContains(t, generated, `AWF_ENCLAVE_GITHUB_MCP_AGENT_ID=$(openssl rand`)
589602

590603
excluded := ComputeAWFExcludeEnvVarNames(data, nil)
591604
assert.Contains(t, excluded, enclaveGitHubDelegationEnv)
592605
assert.Contains(t, excluded, enclaveMCPCapabilityEnv)
606+
assert.Contains(t, excluded, enclaveGitHubDelegationControlEndpointEnv)
593607
}
594608

595609
func TestGenerateEnclaveGatewayContract(t *testing.T) {

pkg/workflow/mcp_gateway_config.go

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,11 @@ func buildMCPGatewayConfig(workflowData *WorkflowData) *MCPGatewayRuntimeConfig
217217
if githubTool, hasGitHub := workflowData.Tools["github"]; hasGitHub && githubTool != false {
218218
primaryGitHubEnabled = !isGitHubCLIModeEnabled(workflowData)
219219
}
220-
if !primaryGitHubEnabled && !enclaveDynamicRepositoryPolicyEnabled(workflowData) {
220+
// Dynamic repository delegation keeps the GitHub backend registered so
221+
// mcpg-issued delegated identities can reach it, but the primary agent
222+
// identity must not gain GitHub MCP access merely because a dynamic
223+
// enclave is configured; it requires a separate top-level tools.github.
224+
if !primaryGitHubEnabled {
221225
for i, server := range primaryServers {
222226
if server == "github" {
223227
primaryServers = append(primaryServers[:i], primaryServers[i+1:]...)
@@ -241,18 +245,11 @@ func buildMCPGatewayConfig(workflowData *WorkflowData) *MCPGatewayRuntimeConfig
241245
Tools: map[string][]string{"github": collectGitHubMCPManifestTools(workflowData.Tools["github"])},
242246
}
243247
}
244-
if enclaveDynamicRepositoryPolicyEnabled(workflowData) {
245-
config.DelegationControllers = map[string]MCPGatewayDelegationController{
246-
enclaveDynamicController: {
247-
Server: "github",
248-
Policy: map[string]any{
249-
"version": enclaveDynamicGitHubPolicy,
250-
"tools": enclaveAgentGitHubDefaultTools,
251-
},
252-
ControlCapability: "${" + enclaveGitHubDelegationEnv + "}",
253-
},
254-
}
255-
}
248+
// Dynamic repository delegation is bootstrapped entirely through the five
249+
// MCP_GATEWAY_DELEGATION_* environment variables set up in
250+
// generateMCPGatewaySetup and forwarded via appendMCPGatewayConditionalEnvFlags.
251+
// The gateway's strict-stdin config schema does not accept a
252+
// "delegationControllers" field, so no value is emitted here.
256253
}
257254
return config
258255
}

pkg/workflow/mcp_renderer.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -208,13 +208,6 @@ func RenderJSONMCPConfig( //nolint:largefunc // Existing renderer keeps MCP JSON
208208
} else {
209209
fmt.Fprintf(&configBuilder, " \"agentId\": \"%s\"", options.GatewayConfig.AgentID)
210210
}
211-
if len(options.GatewayConfig.DelegationControllers) > 0 {
212-
delegationControllers, err := json.Marshal(options.GatewayConfig.DelegationControllers)
213-
if err != nil {
214-
return fmt.Errorf("failed to marshal gateway delegation controllers: %w", err)
215-
}
216-
fmt.Fprintf(&configBuilder, ",\n \"delegationControllers\": %s", delegationControllers)
217-
}
218211

219212
// Add optional fields if specified (agentId always precedes them without a trailing comma)
220213
if options.GatewayConfig.PayloadDir != "" {

0 commit comments

Comments
 (0)