Skip to content

fix(config): expand ${env.X} in the MCP toolset env preflight - #4712

Open
PerryLink wants to merge 1 commit into
docker:mainfrom
PerryLink:mcp-env-preflight-expand
Open

PerryLink wants to merge 1 commit into
docker:mainfrom
PerryLink:mcp-env-preflight-expand

Conversation

@PerryLink

@PerryLink PerryLink commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

The up-front missing-env preflight scans a toolset's env values with raw os.Expand, while the runtime expands the same values through environment.ExpandAll (which normalizes ${env.X} to X first). For the canonical form — exactly what examples/mcp-definitions.yaml uses — the preflight records the literal name env.GITHUB_PERSONAL_ACCESS_TOKEN:

toolsets:
  - type: mcp
    ref: docker:github-official
    env:
      GITHUB_PERSONAL_ACCESS_TOKEN: "${env.GITHUB_PERSONAL_ACCESS_TOKEN}"

GatherEnvVarsForTools then returns ["env.GITHUB_PERSONAL_ACCESS_TOKEN"], and CheckRequiredEnvVars aborts the run even with GITHUB_PERSONAL_ACCESS_TOKEN set:

The following environment variables must be set:
 - env.GITHUB_PERSONAL_ACCESS_TOKEN
 - Shell environment: export env.GITHUB_PERSONAL_ACCESS_TOKEN=<value>

${env.X} is documented as the canonical syntax for every config field, including toolset env values (docs/configuration/overview/index.md), and environment.Refs is documented as the helper for exactly this kind of up-front check.

Change

Use environment.Refs(value) instead of raw os.Expand, so the preflight discovers the same names the runtime will expand.

Test

pkg/config/gather_mcp_env_test.go serves a static MCP catalog and checks three forms (${env.X}, ${X}, $X) with the variable set. On main the canonical subtest fails with the error above; with the change all three pass and the reported name is GITHUB_PERSONAL_ACCESS_TOKEN.

Validation

go test ./pkg/config/... ./pkg/teamloader -count=1      # ok
go vet ./pkg/config ; gofmt -l pkg/config               # clean

The full go test ./... has unrelated baseline failures in this environment (pkg/hooks hook commands exit 127 and pkg/runtime/TestDoCompactAfterHookFires); they reproduce without this change.

Scope

In scope: the up-front missing-env preflight in pkg/config — GatherEnvVarsForTools reads toolset env values with the same normalization the runtime applies, so ${env.X} discovers X rather than the literal env.X; ${X} and $X keep being discovered; literal values and duplicate dedupe are unchanged.

Out of scope: runtime expansion itself, shell-style syntax inside JS-expression fields (#2615 / #2851), path-field expansion (#2944), docs. The preflight path is not one of the code paths #2615's fix ledger lists, which is why no issue is linked.

Re-verified 2026-10-26 against main 9d7700c8: the new test fails on main and passes with the change; go test ./pkg/config/... ./pkg/teamloader -count=1 ok; go vet ./pkg/config clean; gofmt -l pkg/config empty.

@PerryLink
PerryLink requested a review from a team as a code owner October 24, 2026 16:29
@aheritier aheritier added area/config For configuration parsing, YAML, environment variables kind/fix PR fixes a bug (maps to fix:). Use on PRs only. status/needs-signed-commits Some commits in the PR are signed with a valid SSH/GPG key labels Oct 24, 2026
@aheritier

Copy link
Copy Markdown
Collaborator

👋 Some commits in this PR are not signed and verified by GitHub. Please sign your commits with a GPG or SSH key registered in your GitHub account, then force-push.

Commits that are not verified: def156a

See GitHub’s guide on signing commits for setup instructions. I’ve added status/needs-signed-commits; it will be removed automatically once every commit in this PR carries a valid GitHub-verified signature.

Use environment.Refs for the missing-env preflight so toolset env values that use the documented ${env.X} form are discovered with the same normalization the runtime applies, instead of being reported as the literal name env.X.
@PerryLink
PerryLink force-pushed the mcp-env-preflight-expand branch from def156a to e2e29e2 Compare October 25, 2026 02:37
@PerryLink

Copy link
Copy Markdown
Contributor Author

Thanks — the commit is now signed with an SSH signing key registered on this account and force-pushed as e2e29e2 (verified: true). The branch head is signed, no unsigned commits remain.

@aheritier aheritier removed the status/needs-signed-commits Some commits in the PR are signed with a valid SSH/GPG key label Oct 25, 2026

@aheritier aheritier left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code-quality assessment: no demonstrated blocking or nonblocking findings. The change makes MCP secret-reference discovery use the same normalization as runtime expansion. Source review confirms coverage of ${env.X}, ${X}, and $X with a static catalog; literal-value handling and required-variable deduplication remain intact.

Validation: local environment/gateway package tests and existing config preflight tests passed. The new PR-head regression test, full suite, build, and lint were not run; these local results do not establish PR-head validation.

CI / merge readiness: NOT merge-ready. At head e2e29e2, both ci and codeql completed with action_required and zero jobs. No check runs or commit statuses are present, so the required gate is not verified green. The PR is not draft and is conflict-free, but GitHub reports mergeable_state: blocked. CI runs: https://github.com/docker/docker-agent/actions/runs/40155151748 and https://github.com/docker/docker-agent/actions/runs/40155151752. The current commit's signature is verified, resolving the earlier signing comment.

Issue context: needs-decision. No GitHub issue or Jira acceptance reference is linked from the PR description/comments, and closingIssuesReferences is empty. Reviewed the documented syntax contract and background issue #2615, including its closing comment. Confirm that the PR description defines the complete acceptance scope. Approval is withheld pending that confirmation and green CI.

@PerryLink

Copy link
Copy Markdown
Contributor Author

Thanks for the assessment. On the acceptance scope:

Surface: one file, pkg/config/gather.go (+4/−4), inside GatherEnvVarsForTools. It replaces the remaining raw os.Expand in the missing-env preflight with environment.Refs — the helper this file already uses for model / base_url preflight (lines 201, 210, 263), and the one the docs name for exactly this check. Runtime expansion, path fields, JS-expression fields, docs and catalog handling are untouched; ${X}, $X, literal values and duplicate dedupe behave as before.

Why no linked issue: #2615's fix ledger covers runtime expansion per field (#2944, #3257, #3416). The preflight scan is a separate code path that kept os.Expand, which is why ${env.X} is still discovered as env.X after those fixes. I found no existing issue for the preflight path (searched the repo), so the PR stands alone — happy to open one if you would rather have an issue-linked fix.

Acceptance criteria I would check: with GITHUB_PERSONAL_ACCESS_TOKEN set, a toolset env value of ${env.GITHUB_PERSONAL_ACCESS_TOKEN} must not be reported missing; ${GITHUB_PERSONAL_ACCESS_TOKEN} and $GITHUB_PERSONAL_ACCESS_TOKEN must still be discovered; a literal value must not add a required name.

Re-verified today against main 9d7700c8 (22 commits after the branch point; neither touched file changed upstream, mergeable: true):

  • test file only, on today's main → FAIL: the canonical JS form subtest reports env.GITHUB_PERSONAL_ACCESS_TOKEN;
  • with the change → PASS 3/3; go test ./pkg/config/... ./pkg/teamloader -count=1 ok; go vet ./pkg/config clean; gofmt -l pkg/config empty.

On CI: ci and codeql at e2e29e2 are waiting on workflow approval for a fork PR (zero jobs started), so there is no failure log on my side yet; when they run I will fix anything they surface.

This branch has not been deployed

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

Labels

area/config For configuration parsing, YAML, environment variables kind/fix PR fixes a bug (maps to fix:). Use on PRs only.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants