Skip to content

Custom MCP container env passthrough never reaches the server (step env mapping missing) #945

Closed

Description

@kotlarmilos

Description

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:

  1. 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.
  2. 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.

Activity

  1. jamesadevine commented on Jun 10, 2026

    @jamesadevine
    Collaborator

    @kotlarmilos thank you! Will investigate.

    In future, you can ask ado-aw agent to debug (ado-aw init) from your cli.

  2. jamesadevine commented on Jun 10, 2026

    @jamesadevine
    Collaborator

    /plan

  3. github-actions commented on Jun 10, 2026

    @github-actions
    Contributor

    ✅ Issue Plan Maker completed successfully!

  4. jamesadevine commented on Jun 10, 2026

    @jamesadevine
    Collaborator

    /plan

  5. github-actions commented on Jun 10, 2026

    @github-actions
    Contributor

    ✅ Issue Plan Maker completed successfully!

  6. jamesadevine commented on Jun 10, 2026

    @jamesadevine
    Collaborator

    @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

  7. jamesadevine commented on Jun 10, 2026

    @jamesadevine
    Collaborator

    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

  8. github-actions commented on Jun 10, 2026

    @github-actions
    Contributor

    Implementation Plan — Custom MCP container env passthrough (step env mapping missing)

    Objective

    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
      • Update the doc comment to mention the two sources
    • 2. 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 ~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

  9. added
    bugSomething isn't working
    rustPull requests that update rust code
    on Jul 1, 2026
  10. jamesadevine commented on Aug 6, 2026

    @jamesadevine
    Collaborator

    /scout determine if this issue is still relevant in main

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingrustPull requests that update rust code

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions