GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — fix(claude): keep ${VAR} references in project .mcp.json by Aaryan-Dadu · Pull Request #3377 · microsoft/apm

Conversation

@Aaryan-Dadu

@Aaryan-Dadu Aaryan Dadu (Aaryan-Dadu) commented Oct 24, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

Description

With --target claude, apm install resolved ${VAR} values at install time and wrote the real secrets into the project's .mcp.json, which is usually committed. Claude Code expands ${VAR} and ${VAR:-default} itself when it starts a server, so APM now keeps the references there.

How it works:

  • The project .mcp.json keeps ${VAR} and ${VAR:-default} in command, args, env, url and headers. ${env:VAR} and <VAR> become ${VAR}. There are no prompts, and no values end up in .mcp.json, apm.lock.yaml or the output.
  • Static values like LOG_LEVEL: debug stay as written. Registry servers get ${NAME} for their variables and secret arguments.
  • Missing variables are reported by name, once, for every server in the install. Defaulted ones are not reported.
  • Existing installs: reinstall replaces values an earlier release wrote when APM can confirm it wrote them. That means the recorded write hash matches, or the entry equals the declaration expanded from the current environment. It prints the variable names and a reminder to rotate committed credentials. Anything else is kept, and install names the fields to fix by hand.
  • User scope (~/.claude.json) is unchanged.
  • New docs page, MCP variable compatibility, covers every supported harness: native syntax, fields, defaults, unset behavior, approval, what APM writes, the evidence, and the gaps that remain (recorded, not changed).

I checked Claude Code's behavior with Claude Code 2.1.310: a recording stdio command and a local HTTP endpoint showed it expands references and defaults in all five fields, in both .mcp.json and ~/.claude.json, and leaves an unset variable as literal text with a warning.

Closes #3252

Before

After

Issue and approved scope

Issue: #3252

Human scope-approval comment: #3252 (comment)

Does this PR complete the issue, or what remains? It completes the Claude project repair. Gaps in other harnesses are recorded on the new page for separate decisions.

Type of change

  • Bug fix
  • New feature
  • Documentation
  • Maintenance / refactor

Testing

  • Tested locally
  • All existing tests pass
  • Added tests for new functionality (if applicable)

New installed-CLI tests in tests/integration/test_claude_mcp_env_references_e2e.py cover set, unset and defaulted variables, a registry server, user scope, mixed targets, unrelated entries and Claude's approval file, and reinstall over old values (replaced, or kept when APM can't confirm it wrote them). 6 of the 7 fail on main; the user-scope one pins the unchanged behavior. There are also unit tests in test_claude_mcp.py and test_mcp_ownership.py.

I also installed the same declarations for all 12 harnesses and both scopes on main and on this branch. The only output that changes is Claude's project config.

Spec conformance (OpenAPM v0.1)

Added req-mf-026 (Section 4.5) for the Claude project .mcp.json behaviour, with three tests in tests/spec_conformance/test_claude_mcp_reference_reqs.py.

If this PR changes behaviour that an OpenAPM v0.1 req-XXX covers,
confirm the three-step ritual in the
development guide:

  • Spec edit: docs/src/content/docs/specs/openapm-v0.1.md updated
    (new/changed <a id="req-XXX"></a> anchor + prose + Appendix C
    row).
  • Manifest edit: docs/src/content/docs/specs/manifests/openapm-v0.1.requirements.yml
    updated.
  • Test edit: a @pytest.mark.req("req-XXX") test under
    tests/spec_conformance/ added or extended.
  • CONFORMANCE.{md,json} regenerated via
    uv run --extra dev python -m tests.spec_conformance.gen_statement
    and committed.
  • N/A -- this PR does not change OpenAPM-observable behaviour.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@Aaryan-Dadu
fix(claude): keep ${VAR} references in project .mcp.json
52a6ede
Claude Code expands ${VAR} and ${VAR:-default} in the project .mcp.json,
so apm install keeps the references there instead of prompting for and
writing the real values. Reinstall replaces values an earlier release
wrote when APM can confirm it wrote them; otherwise it keeps the entry
and names the fields to fix. User scope is unchanged.

Adds an MCP variable compatibility page covering every harness.

Closes microsoft#3252
@Aaryan-Dadu
Aaryan Dadu (Aaryan-Dadu) requested review from Daniel Meppiel (danielmeppiel) and Sergio Sisternes (sergio-sisternes-epam) as code owners October 24, 2026 09:16
Copilot AI balanced review requested due to automatic review settings October 24, 2026 09:16
Copilot AI reviewed Oct 24, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@sergio-sisternes-epam

Sergio Sisternes (sergio-sisternes-epam) commented Nov 3, 2026

Copy link
Copy Markdown
Collaborator

Thank you for contributing this Claude configuration repair. Issue #3252 has an explicit scope-approval record, and this PR appears to address the approved project-scope fix while documenting compatibility findings for other harnesses. The requested reviewers include Daniel Meppiel (@danielmeppiel) and Sergio Sisternes (@sergio-sisternes-epam). This is a recommendation for human review only, not merge approval.


Generated by autopilot-pr-triage-worker. This comment is AI-generated and may contain errors.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@sergio-sisternes-epam Sergio Sisternes (sergio-sisternes-epam) added area/mcp-config MCP server configuration depth, transports, variable resolution. theme/security Secure by default. Content scanning, lockfile integrity, MCP trust boundaries. triage/recommended Automated advice completed; not human scope approval. type/bug Something does not work as documented. status/accepted Human scope approval; verify the issue's approval record and review contact before work. labels Nov 3, 2026
@sergio-sisternes-epam

Sergio Sisternes (sergio-sisternes-epam) commented Nov 4, 2026

Copy link
Copy Markdown
Collaborator

Hi Aaryan Dadu (@Aaryan-Dadu), thanks again for this fix. The scope is accepted, and everything is green except one required check: Spec conformance gate, at the step "Mode B detector (silent-extension gate on critical paths)".

The detector flags 57 new substantive lines under OpenAPM critical paths with no spec citation:

  • src/apm_cli/install/mcp/ownership.py
  • src/apm_cli/integration/mcp_integrator.py
  • src/apm_cli/integration/mcp_integrator_install.py

This PR changes observable MCP install behaviour (env references and ownership on reinstall), so please add a spec citation on this branch:

  1. A new <a id="req-XXX"></a> anchor with short normative prose in docs/src/content/docs/specs/openapm-v0.1.md, plus the matching Appendix C row.
  2. A matching entry in docs/src/content/docs/specs/manifests/openapm-v0.1.requirements.yml.
  3. A @pytest.mark.req("req-XXX") test under tests/spec_conformance/ that pins the new behaviour (it can reuse the setup from your existing tests).

Use the next free req- number. You can check alignment locally with uv run --extra dev python -m tests.spec_conformance.orphan_check and bash tests/spec_conformance/mode_b_detector.sh. Please also tick the matching boxes in the "Spec conformance" section of the PR description. CONTRIBUTING.md, under "Mode B (silent extension)", has the details.

Once that check is green, this can go on to review. Thanks!


Generated by autopilot-pr-ready-worker. This comment is AI-generated and may contain errors.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Aaryan-Dadu
docs(spec): add req-mf-026 for Claude project MCP references
428888a
Adds the spec citation for this PR's install and ownership change:
req-mf-026 in Section 4.5, the Appendix C and manifest rows, three
conformance tests, and the regenerated CONFORMANCE files.
@Aaryan-Dadu

Aaryan Dadu (Aaryan-Dadu) commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

Sergio Sisternes (@sergio-sisternes-epam) I added the spec rule and tests for this. It passes locally, and the checks need approval to run.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Daniel Meppiel (danielmeppiel) and others added 5 commits November 12, 2026 10:44
@danielmeppiel
Merge origin/main into fix/3252-claude-mcp-placeholders
64f9f24
Resolve conflicts with microsoft#3406 (Codex env_vars) and the 0.1.56 spec
amendment. The legacy-entry check now uses the single adapter hook
matches_legacy_managed_server_config(recorded_write=...) that main
introduced; Claude overrides it instead of adding a parallel hook, and
ownership calls it once for self-defined and registry baselines. The
PR's spec changelog row becomes 0.1.57 so it no longer collides with
main's 0.1.56; the working-draft count is 137 (132 MUST, 5 SHOULD).

Co-authored-by: Copilot App <[email protected]>
@danielmeppiel
fix(claude): key MCP upgrade checks by declared name; clearer guidance
c3f29d3
After merging main (microsoft#3493), self-defined servers keep their declared name
(slashes included) as the native key. The security-upgrade snapshot and
the conflict guidance looked entries up by the truncated key, so a
slash-named server lost its upgrade notice and reconciliation guidance.
Both now use the declared key.

Panel follow-ups folded: the conflict guidance names which ${VAR} belongs
in each field (names only, never values or defaults); the --force caveat
sits on its own line; Claude's upgrade notice reads "the project
.mcp.json"; the opaque _begin_env_tracking/_record_env_diagnostics
snapshot contract is documented; a test guards that Claude redeclares
every mutable Copilot aggregation bucket; a symmetric project-scope
assertion; a comment documents the empty-value expansion choice.

Addresses review-panel follow-ups (python-architect, devx-ux-expert,
cli-logging-expert, test-coverage-expert, supply-chain-security-expert).

Co-authored-by: Copilot App <[email protected]>
@danielmeppiel
docs(mcp): record Codex env_vars after microsoft#3406; name exit on c…
5e68347
…onflict

The MCP variable compatibility inventory was written before microsoft#3406 landed
on main and still said Codex project scope writes resolved env values.
Codex project and user scope are now separate rows, and the known-gap
lists name Codex user scope. The producer page no longer uses Codex
project env as the example of a harness needing literal values. The
consumer page says a preserved Claude entry ends the install with an
error naming each field and its reference. The CHANGELOG entry is
trimmed to one line ending in (microsoft#3377).

Addresses review-panel follow-ups (doc-writer, devx-ux-expert).

Co-authored-by: Copilot App <[email protected]>
@danielmeppiel
Merge remote-tracking branch 'origin/main' into w2-3377
114460b
@danielmeppiel
Merge remote-tracking branch 'origin/main' into w2-3377
9cf0adc
@danielmeppiel

Daniel Meppiel (danielmeppiel) commented Nov 12, 2026

Copy link
Copy Markdown
Collaborator

APM Review Panel: ship_now

Closes a Tier-1 secret-leakage vector: Claude project .mcp.json now receives ${VAR} references instead of plaintext secrets, with fail-safe exit-on-unprovable ownership and names-only diagnostics.

panel-mode=full; personas=python-architect,test-coverage-expert,doc-writer,supply-chain-security-expert(adhoc: Tier1 secret exposure),cli-logging-expert(adhoc: diagnostic wording),devx-ux-expert(adhoc: install exit on unprovable entries),apm-ceo

cc Aaryan Dadu (@Aaryan-Dadu) Sergio Sisternes (@sergio-sisternes-epam) -- a fresh advisory pass is ready for your review.

Six panelists converged. Supply-chain security confirms no expanded value reaches .mcp.json, apm.lock.yaml or console output on the project path, and that migration only replaces an entry APM can prove it wrote (recorded write hash, or exact expansion of the declaration from the current environment). Test coverage confirms each done_when axis of the #3252 scope record is asserted, including installed-CLI runs with synthetic values. Every recommended and nit finding was folded into this branch; one architecture follow-up is deferred because it changes Codex wording outside this Claude-only scope.

Shepherd notes: main had landed #3406 (Codex env_vars) and #3493 (declared self-defined keys) after this PR was opened. The merge keeps one legacy-entry hook (matches_legacy_managed_server_config, which #3406 introduced) and has Claude override it, rather than adding a parallel hook. It also fixes a merge regression where Claude's upgrade notice and conflict guidance looked up slash-named self-defined servers by the truncated key. The spec changelog row became 0.1.57 (0.1.56 was taken on main); the working draft has 137 statements (132 MUST, 5 SHOULD).

Aligned with: secure by default (no secrets in shared config, lock or output; fail-safe on unprovable entries); portable by manifest (references survive across machines); pragmatic as npm (the error says which reference goes in which field).

Panel summary

Persona B R N Takeaway
Python Architect 0 1 2 Paired snapshot override needed an explicit contract; ClassVar isolation is correct.
Test Coverage Expert 0 0 2 Installed-CLI and unit tests cover every done_when axis, including secret-free lockfile and both reinstall paths.
Doc Writer 0 2 1 Claude docs accurate; Codex rows in the new inventory predated #3406.
Supply Chain Security Expert 0 0 1 Fix is correct and fail-safe; migration uses a two-gate ownership proof; diagnostics are names-only.
CLI Logging Expert 0 0 2 Diagnostics are target-correct, names-only and emitted once per run.
DevX UX Expert 0 1 2 Exiting non-zero on an unprovable entry is right; the message should say which reference goes where.

B = blocking-severity findings, R = recommended, N = nits.
Counts are signal strength, not gates. The maintainer ships.

Top 1 follow-ups

  1. [Python Architect] Open a follow-up issue to converge the Codex expanded_env conflict wording (fix(codex): forward ${VAR} stdio env values with env_vars instead of writing them #3406) with the adapter native_conflict_guidance hook. -- Two runtime-exclusive guidance paths invite wording drift as more harnesses gain reference support.

Recommendation

All panelists signal ship. Every in-scope finding is folded on this head and CI is green. The one deferred item is a cross-harness refactor outside #3252's Claude-only scope.

Folded in this run

  • (panel) Conflict guidance names which ${VAR} belongs in each field (names only, never values or defaults) -- resolved in c3f29d3.
  • (panel) --force caveat on its own line; Claude upgrade notice says "the project .mcp.json" -- resolved in c3f29d3.
  • (panel) Opaque adapter-owned snapshot contract documented for the _begin_env_tracking / _record_env_diagnostics pair -- resolved in c3f29d3.
  • (panel) Test that Claude redeclares every mutable Copilot diagnostic bucket; symmetric project-scope assertion -- resolved in c3f29d3.
  • (panel) Comment on the deliberate empty-value-as-unset expansion (fail-safe conflict) -- resolved in c3f29d3.
  • (shepherd) Upgrade snapshot and conflict guidance use the declared self-defined key after fix(mcp): preserve declared self-defined server names containing slashes #3493 -- resolved in c3f29d3.
  • (shepherd) Merge with fix(codex): forward ${VAR} stdio env values with env_vars instead of writing them #3406: one legacy-entry hook; spec row 0.1.57; CONFORMANCE regenerated -- resolved in 64f9f24.
  • (panel) MCP variable compatibility page: Codex project and user rows split for fix(codex): forward ${VAR} stdio env values with env_vars instead of writing them #3406 env_vars; known gaps updated -- resolved in 5e68347.
  • (panel) Producer page no longer uses Codex project env as the literal-values example -- resolved in 5e68347.
  • (panel) Consumer page says install ends with an error naming fields and references -- resolved in 5e68347.
  • (panel) CHANGELOG entry trimmed to one line ending (#3377) -- resolved in 5e68347.

Copilot signals reviewed

  • review 5637683746 -- NOT-LEGIT: quota notice, not a review finding; the round-2 re-fetch returned zero inline comments.

Deferred (out-of-scope follow-ups)

  • (panel) Converge Codex expanded_env conflict wording with the adapter native_conflict_guidance hook -- scope boundary: changes Codex conflict wording owned by [BUG] Codex: stdio MCP env ${VAR} written as plaintext into .codex/config.toml instead of using env_vars #3393/fix(codex): forward ${VAR} stdio env values with env_vars instead of writing them #3406; [BUG] --target claude writes resolved secrets as plaintext into project-scoped .mcp.json (VS Code/Copilot keep ${VAR} placeholders) #3252 is the Claude project repair only; suggested follow-up: a refactor issue for unified per-adapter MCP conflict guidance.

Regression-trap evidence (mutation-break gate)

  • tests/unit/test_claude_mcp.py::TestClaudeLegacyEntries::test_conflict_guidance_finds_slash_named_self_defined_entry -- deleted the declared-name lookup in native_conflict_guidance; test FAILED as expected; guard restored.
  • tests/unit/test_claude_mcp.py::TestClaudeLegacyEntries::test_upgrade_snapshot_uses_declared_self_defined_key -- deleted server_info from the snapshot key lookup; test FAILED as expected; guard restored.
  • tests/unit/test_claude_mcp.py::TestClaudeDiagnosticBucketIsolation::test_every_mutable_copilot_classvar_is_redeclared -- deleted Claude's _security_upgraded_keys redeclaration; test FAILED as expected; guard restored.

Lint contract

uv run --extra dev ruff check and ruff format --check (src/, tests/, architecture linter scripts) both silent; pylint R0801 10.00/10; lint-auth-signals.sh and lint-architecture-boundaries.sh clean. CONFORMANCE.{md,json} regenerated after collecting the full spec suite (byte-identical); orphan_check reports 137 aligned requirements.

CI

CI on 9cf0adc: 24 checks pass, 3 skipped, including Lint, Build & Test shards, Lifecycle Smoke, Windows gates, Spec conformance gate and CodeQL; license/cla passes (after 0 CI fix iteration(s)). A local Cursor e2e failure (test_cursor_registry_env_references_keep_optional_semantics[None-None]) also reproduces on main and is unrelated to this PR.

Mergeability status

PR head SHA CEO stance iters folds defers Copilot rounds CI mergeable mergeStateStatus notes
#3377 9cf0adc ship_now 2 13 1 2 green MERGEABLE BLOCKED pending required review

Convergence

2 outer iteration(s); 2 Copilot round(s). Final panel stance: ship_now. Ready for maintainer review.

Full per-persona findings

Python Architect

  • [recommended] _begin_env_tracking / _record_env_diagnostics overrides pass different snapshot types (tuple vs dict or None) at src/apm_cli/adapters/client/claude.py
    A future refactor that lifts shared logic to the parent could break one adapter silently. Folded: documented as an opaque, adapter-owned snapshot that must be overridden as a pair.
  • [nit] No guard that Claude redeclares every mutable Copilot ClassVar bucket. Folded with a test.
  • [nit] _report_native_conflict has two guidance mechanisms (Codex expanded_env and the adapter hook) at src/apm_cli/integration/mcp_integrator_install.py. Deferred (Codex scope).

Test Coverage Expert

  • [nit] No e2e for the no-fingerprint path. Already exercised: the e2e reinstall test's recorded hash is of the reference form, so it goes through exact expansion.
  • [nit] Add a symmetric project-scope assertion in tests/unit/test_copilot_adapter.py. Folded.

Doc Writer

  • [recommended] mcp-variable-compatibility.md Codex rows predate fix(codex): forward ${VAR} stdio env values with env_vars instead of writing them #3406. Folded.
  • [recommended] mcp-as-primitive.md uses Codex project env as the literal-values example. Folded.
  • [nit] Trim the CHANGELOG entry. Folded.

Supply Chain Security Expert

  • [nit] Empty-string env treated as unset in compound legacy expansion (fail-safe conflict); document it. Folded as a comment.

CLI Logging Expert

  • [nit] Claude config label reads as a bare filename. Folded.
  • [nit] --force caveat runs into the rotation sentence. Folded.

DevX UX Expert

  • [recommended] Conflict guidance should say which ${VAR} belongs in each field. Folded (names only).
  • [nit] Line break before the --force caveat. Folded.
  • [nit] Docs should say install exits with an error. Folded.

This panel is advisory. It does not block merge. Re-apply the
panel-review label after addressing feedback to re-run.


Generated by autopilot-pr-merge-worker. This comment is AI-generated and may contain errors.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@danielmeppiel
Daniel Meppiel (danielmeppiel) merged commit 1009d4b into microsoft:main Nov 12, 2026
27 checks passed

Uh oh!

There was an error while loading. Please reload this page.

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

Reviewers

Copilot code review Copilot
Copilot review effort
Applies to this pull request for everyone.Learn more about Copilot code review.
Copilot left review comments
@danielmeppiel Daniel Meppiel (danielmeppiel) Awaiting requested review from danielmeppiel danielmeppiel is a code owner
@sergio-sisternes-epam Sergio Sisternes (sergio-sisternes-epam) Awaiting requested review from sergio-sisternes-epam sergio-sisternes-epam is a code owner

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

area/mcp-config MCP server configuration depth, transports, variable resolution. status/accepted Human scope approval; verify the issue's approval record and review contact before work. theme/security Secure by default. Content scanning, lockfile integrity, MCP trust boundaries. triage/recommended Automated advice completed; not human scope approval. type/bug Something does not work as documented.

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

[BUG] --target claude writes resolved secrets as plaintext into project-scoped .mcp.json (VS Code/Copilot keep ${VAR} placeholders)

Uh oh!

There was an error while loading. Please reload this page.

4 participants

@Aaryan-Dadu @sergio-sisternes-epam @danielmeppiel
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.