You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Custom MCP container env passthrough never reaches the server (step env mapping missing) #945
A custom mcp-servers container that declares an empty-valued env var to forward a same-named ADO pipeline variable never actually receives the value. The variable arrives empty inside the container, which shows up later as an opaque MCP startup/auth failure rather than a compile-time error.
Repro
mcp-servers:
azure-devops:
container: "node:20-slim"
entrypoint: "npx"
entrypoint-args: ["-y", "@azure-devops/mcp", "myorg", "-a", "envvar"]
env:
ADO_MCP_AUTH_TOKEN: "" # intended: pass through the pipeline var of the same name
Set a pipeline variable ADO_MCP_AUTH_TOKEN, compile, and run. The MCP container starts with an empty ADO_MCP_AUTH_TOKEN.
Root cause
The passthrough requires two outputs, both in src/compile/common.rs:
generate_mcpg_docker_env emits the docker run -e ADO_MCP_AUTH_TOKEN flag (host process env → container). This already handled user mcp-servers env vars.
generate_mcpg_step_env emits the step-level env: { ADO_MCP_AUTH_TOKEN: $(ADO_MCP_AUTH_TOKEN) } mapping (pipeline variable → host process env). This only mapped extension pipeline vars and skipped user mcp-servers env entirely.
With (2) missing, the host process running MCPG never has the variable, so the -e VAR flag forwards an empty value. The two halves of the passthrough are out of sync.
Fix generate_mcpg_step_env in src/compile/common.rs to include user-configured MCP server empty-value env vars in the ADO step-level env: block, completing the passthrough chain: ADO pipeline variable → host process env → container env.
Scope
In scope:
Fix generate_mcpg_step_env to consume &FrontMatter alongside extensions and emit VAR: $(VAR) entries for empty-valued user MCP env entries
Update all 3 call sites to pass front_matter
Add/update unit tests
Out of scope:
Non-empty user MCP env values (static overrides — these are intentionally passed directly in the MCPG config, not via step env)
Remote/HTTP MCP servers (no container = no -e flags needed)
Changes to generate_mcpg_docker_env (already correct)
Work Breakdown
1. Update generate_mcpg_step_env signature and body — src/compile/common.rs ~L2805
Change signature from (extensions: &[Extension]) -> String to (front_matter: &FrontMatter, extensions: &[Extension]) -> String
After the existing extensions loop, add a second loop over front_matter.mcp_servers:
Skip if not McpConfig::WithOptions or not enabled
Skip if no container (passthrough only needed for containerized MCPs)
For each (var_name, var_value) in opts.env:
Guard with validate::is_valid_env_var_name(var_name) (log warn + skip invalid)
Skip if var_value is non-empty (static values go directly to MCPG config, not step env)
Skip if already in seen set (dedup against extension vars)
Emit VAR_NAME: $(VAR_NAME) and insert into seen
Retain the existing env:\n block header logic unchanged
3. Update existing tests for generate_mcpg_step_env — src/compile/common.rs ~L7602
test_generate_mcpg_step_env_with_ado_extension: pass &fm as first arg
test_generate_mcpg_step_env_no_extensions: pass &fm (minimal front matter) as first arg
4. Add new unit tests in the generate_mcpg_step_env test section
test_generate_mcpg_step_env_user_mcp_passthrough — empty-valued user MCP env var produces VAR: $(VAR) in step env block; non-empty value does NOT appear
test_generate_mcpg_step_env_dedup_user_and_extension — when user MCP passthrough var name matches an extension pipeline var, it appears exactly once
test_generate_mcpg_step_env_skips_non_container_mcp — user MCP without container: field does not produce step env entries
test_generate_mcpg_step_env_rejects_invalid_var_names — invalid var name (injection attempt) is silently skipped
Validation Strategy
cargo build — no compiler errors
cargo test — all existing tests pass; new tests pass
cargo clippy — no new warnings
Manual compile smoke test: agent with mcp-servers: { my-mcp: { container: node:20-slim, env: { MY_TOKEN: "" } } } produces env:\n MY_TOKEN: $(MY_TOKEN) in the generated YAML MCPG start step
Risks and Mitigations
Risk
Mitigation
Emitting step env entries for remote (HTTP) MCPs that don't need them
Guard with opts.container.is_none() → continue (same guard used in generate_mcpg_docker_env)
Name injection via user-supplied var names
Apply validate::is_valid_env_var_name (already used in generate_mcpg_docker_env)
Duplicate entries when extension var name coincidentally matches user passthrough
Shared seen: HashSet already handles this
Disabled MCP entries emitting step env
Guard with opts.enabled.unwrap_or(true) check (mirrors generate_mcpg_docker_env)
Dependencies and Assumptions
validate::is_valid_env_var_name is already exported and covers the [A-Za-z_][A-Za-z0-9_]* constraint needed here
The bug affects all four compile targets (standalone, 1es, job, stage) through the three call sites above — all are fixed by the single signature change
No template YAML changes needed ({{ mcpg_step_env }} marker already exists in all four base templates)
Generated by Issue Plan Maker for issue #945 · sonnet46 1.2M · ◷ Comment /plan to run again
Activity
jamesadevine commentedon Jun 10, 2026
@kotlarmilos thank you! Will investigate.
In future, you can ask ado-aw agent to debug (
ado-aw init) from your cli.jamesadevine commentedon Jun 10, 2026
/plan
github-actions commentedon Jun 10, 2026
✅ Issue Plan Maker completed successfully!
fix(workflows): lower min-integrity for issue-plan-maker /plan command (
jamesadevine commentedon Jun 10, 2026
/plan
github-actions commentedon Jun 10, 2026
✅ Issue Plan Maker completed successfully!
jamesadevine commentedon Jun 10, 2026
@kotlarmilos just to say, azure devops mcp is a built in - you can just configure the frontmatter https://githubnext.github.io/ado-aw/reference/tools/#azure-devops-mcp-azure-devops
jamesadevine commentedon Jun 10, 2026
Prerequisite is to set up a read only service connection for safe use. https://githubnext.github.io/ado-aw/reference/network/#permissions-ado-access-tokens
github-actions commentedon Jun 10, 2026
Implementation Plan — Custom MCP container env passthrough (step env mapping missing)
Objective
Fix
generate_mcpg_step_envinsrc/compile/common.rsto include user-configured MCP server empty-value env vars in the ADO step-levelenv:block, completing the passthrough chain: ADO pipeline variable → host process env → container env.Scope
In scope:
generate_mcpg_step_envto consume&FrontMatteralongside extensions and emitVAR: $(VAR)entries for empty-valued user MCP env entriesfront_matterOut of scope:
-eflags needed)generate_mcpg_docker_env(already correct)Work Breakdown
1. Update
generate_mcpg_step_envsignature and body —src/compile/common.rs~L2805(extensions: &[Extension]) -> Stringto(front_matter: &FrontMatter, extensions: &[Extension]) -> Stringfront_matter.mcp_servers:McpConfig::WithOptionsor not enabledcontainer(passthrough only needed for containerized MCPs)(var_name, var_value)inopts.env:validate::is_valid_env_var_name(var_name)(log warn + skip invalid)var_valueis non-empty (static values go directly to MCPG config, not step env)seenset (dedup against extension vars)VAR_NAME: $(VAR_NAME)and insert intoseenenv:\nblock header logic unchanged2. Update callers — 3 sites
src/compile/standalone.rs:64→generate_mcpg_step_env(front_matter, &extensions)src/compile/onees.rs:59→generate_mcpg_step_env(front_matter, &extensions)src/compile/common.rs:3656→generate_mcpg_step_env(front_matter, &extensions)3. Update existing tests for
generate_mcpg_step_env—src/compile/common.rs~L7602test_generate_mcpg_step_env_with_ado_extension: pass&fmas first argtest_generate_mcpg_step_env_no_extensions: pass&fm(minimal front matter) as first arg4. Add new unit tests in the
generate_mcpg_step_envtest sectiontest_generate_mcpg_step_env_user_mcp_passthrough— empty-valued user MCP env var producesVAR: $(VAR)in step env block; non-empty value does NOT appeartest_generate_mcpg_step_env_dedup_user_and_extension— when user MCP passthrough var name matches an extension pipeline var, it appears exactly oncetest_generate_mcpg_step_env_skips_non_container_mcp— user MCP withoutcontainer:field does not produce step env entriestest_generate_mcpg_step_env_rejects_invalid_var_names— invalid var name (injection attempt) is silently skippedValidation Strategy
cargo build— no compiler errorscargo test— all existing tests pass; new tests passcargo clippy— no new warningsmcp-servers: { my-mcp: { container: node:20-slim, env: { MY_TOKEN: "" } } }producesenv:\n MY_TOKEN: $(MY_TOKEN)in the generated YAML MCPG start stepRisks and Mitigations
opts.container.is_none() → continue(same guard used ingenerate_mcpg_docker_env)validate::is_valid_env_var_name(already used ingenerate_mcpg_docker_env)seen: HashSetalready handles thisopts.enabled.unwrap_or(true)check (mirrorsgenerate_mcpg_docker_env)Dependencies and Assumptions
validate::is_valid_env_var_nameis already exported and covers the[A-Za-z_][A-Za-z0-9_]*constraint needed here{{ mcpg_step_env }}marker already exists in all four base templates)jamesadevine commentedon Aug 6, 2026
/scout determine if this issue is still relevant in main