Skip to content

feat(diff): name the env_vars entries that changed in an MCP row (Refs #795) - #1003

Merged
pengfei-threemoonslab merged 1 commit into
mainfrom
claude/795-mcp-env-field
Oct 8, 2026
Merged

pengfei-threemoonslab merged 1 commit into
mainfrom
claude/795-mcp-env-field

Conversation

@pengfei-threemoonslab

Copy link
Copy Markdown
Contributor

Refs #795

What

openai/codex-security#1281 added CODEX_SECURITY_PLUGIN_ROOT to a server's env_vars array in plugins/codex-security/.mcp.json and changed nothing else. The row said the change was "in a detail this output does not show, such as the command's path or another setting". The grant published only the keys of the env map (env_keys), so a change to the env_vars list was visible only through config_sha256.

An mcp_server grant now publishes env_var_names: the plain names its env_vars list declares, in declared order (host-grants 0.9, extended in place; 0.9 is not in v1.2.0, and no schema file present in that tag changes). The row names the ones gained and lost, beside the existing env keys and header keys.

Before:

high  changed  claude-code plugins/codex-security/.mcp.json
      s: no difference in the command name launch_codex_security_mcp, launch arguments, env key names or header key names; the change is in a detail this output does not show, such as the command's path or another setting
      MCP edit; authority direction is unknown

After:

high  changed  claude-code plugins/codex-security/.mcp.json
      codex-security: env_vars names +CODEX_SECURITY_PLUGIN_ROOT
      MCP edit; authority direction is unknown

Other shapes: env keys +API_BASE -DEBUG; env_vars names +X -Y; the same names in another order read env_vars names in a different order (the digest reads the list in order, so it is already a row); an added or removed server's cell lists the names.

Display only

  • Out of grant equality, the inventory digests and saved baselines (DISPLAY_ONLY_GRANT_FIELDS), like package/args_sha256/launch_source. No row, direction, expands, severity, expansion signal or check decision moves; a test compares check's whole result for a named and an unnamed head.
  • The label is the key the list is declared under (env_vars names), so the row claims no meaning for a name beyond that it is listed there.
  • A name is published only if it matches [A-Za-z_][A-Za-z0-9_]{0,79} and neither the digest input's redaction (_redact_secret_values) nor the label redaction rewrites it. A NAME=value entry, an object, a token-shaped string and the entry after a credential word are not published, and a change confined to them still says it is not shown. The generic sentence names env_vars names as compared only when both readings publish them and one lists a name; a saved-baseline side claims nothing.
  • Routes: diff text and --json, verify text/verifier.json/PR comment, check by --base/--head and by --diff, all through the shared review.changes[].change.

What this does not do

  • No env value is named, even as "value changed: KEY". config_sha256's input redacts every env and headers value whole, so a value-only change produces no digest change and no row today (pinned by a test). Naming the key would need a per-key value digest, which is a hashing oracle on secrets and would add a row class (a check decision change). Not done here.
  • cwd, bearer_token_env_var, env_http_headers and header key names beyond the existing header_keys are not published; the issue's evidence names only env_vars.

Tests

  • tests/test_mcp_env_var_names.py (new): the reproduction on every route; added, removed, both, reordered, repeated, credential-word names, more than five names; env + env_vars + headers together; added/removed server cells; Codex config.toml; display-only equivalence (named vs unnamed head); value rotation is no row and prints nothing; env map reorder is no row; unpublished entries (NAME=value with a canary, object, token-shaped, spaced) never reach any output; a credential word before a name hides a change the digest never saw; grant/baseline/digest independence; a baseline side claims nothing.
  • Updated: tests/test_distribution_surface_parity.py (claim comment), docs/distribution-surfaces.md row, tests/shard_seconds.json, STABILITY.md migration note, docs/host-boundary-support.md, CHANGELOG.md, regenerated docs/host-grants-inventory-schema.v0.9.json.

CI mirror (-n auto -m "not perf" --ignore=tests/test_adapter_static_only.py): exit 1 on this loaded machine with only the known local local_settings_enabled_plugins-* failures plus load-sensitive test_instruction_structure_hooks (2) and test_workflow_label_redaction linear-time (3), which pass when rerun alone. tests/test_adapter_static_only.py alone passes; ruff check . passes.

🤖 Generated with Claude Code

…#795)

An MCP server that passes variables through by name in an env_vars list
changed only its list, and the row said the change was in a detail it did not
show, pointing at the command's path. The grant now publishes the plain names
(host-grants 0.9, extended in place, display-only) and the row names the ones
gained and lost. No value is published; rows, direction, severity and check
decisions do not move.
@pengfei-threemoonslab
pengfei-threemoonslab merged commit db776f0 into main Oct 8, 2026
14 checks passed
@pengfei-threemoonslab
pengfei-threemoonslab deleted the claude/795-mcp-env-field branch October 8, 2026 15:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant