Skip to content

fix(mcp): refuse the environment writes that resolve to a wrong or hidden entry - #660

Merged
using-system merged 2 commits into
mainfrom
fix/config-environment-validation
Sep 26, 2026
Merged

using-system merged 2 commits into
mainfrom
fix/config-environment-validation

Conversation

@using-system

Copy link
Copy Markdown
Owner

Summary

The MCP server's configuration validation no longer lets three environment states through:

  • a custom stack whose name a built-in ends in (monitor next to azure-monitor) is refused, and the effective entry selects <environment>-<stack> only when that key parses back to the configured pair, so a pre-existing monitor declaration can no longer read the built-in's entry;
  • an environment write while the resulting stack is local is refused ("the local stack takes no environment - switch the stack first, or in the same call"), and a switch to local clears the stored environment, so nothing re-arms on the next remote switch;
  • local is refused as an environment value and as a key prefix, like unknown.

The implementation choices that go beyond or narrow the proposal are recorded on the issue: #656 (comment)

Tests

  • Unit: new and adapted tests in tests/mcp-server/test_config.py / test_server.py (268 passed).
  • Integration: integration-tests/mcp-server/test-config-surface.sh covers the refused writes, the remote-switch-with-environment acceptance and the clear-on-local-switch.
  • ruff 0.16.4 check / format, apm install --target claude + apm audit (apm-cli 0.31.0) green.

Review

A separate reviewer sub-agent checked the whole branch against the issue:

  • Round 1 found one blocking defect: the built-in-suffix check also fired when an already-stored custom (monitor) split the built-in, so every new custom declaration was refused with a message blaming the wrong name. Fixed in 2403386 (only the name being declared is blamed) with a regression test. It also asked for the decision record on the issue before the PR — posted.
  • Round 2: no finding, ready to merge.

Harness measurement

Coarse non-regression check only (test-plugin-harnessing, copilot / openai/gpt-5.6-luna / medium, /odd-observe drive mission on the local llms-benchmark stack, ABBA). The runtime change lives in the MCP server, which the lab runs from PyPI 1.13.1 on both sides; the only measured difference is six lines of backend-configuration/SKILL.md.

Side (mean of 2) Turns Commands Tokens in Tokens out Wall Preflight Observation
main 43 39.5 3.07 M 14.3 k 415 s 71 s 216 s
branch 40 45.5 3.01 M 14.4 k 366 s 53 s 186 s

Main's spread today over six samples: turns 35–45, commands 30–51, tokens in 2.66–3.41 M, wall 310–431 s. Both branch samples sit inside it (1 premium request each, 0 authored scripts, 0 resets, 0 --help). No measurable change expected and none measured.

Closes #656

🤖 Generated with Claude Code

using-system and others added 2 commits September 26, 2026 20:04
…dden entry

A custom name a built-in stack ends in (monitor, for azure-monitor) is
refused, and the effective entry only selects a prefixed key that parses
back to the configured pair, so a hand-edited declaration never reads a
built-in's entry. local is refused as an environment value and prefix,
like unknown. An environment write while the stack is local is refused -
switch the stack first, or in the same call - and a switch to local
clears the stored environment, so nothing re-arms on the next remote
switch.

Closes #656

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A custom stack stored before #656 that a built-in ends in no longer makes
every later declaration fail: the built-in check raises only when the
suffix splitting the built-in is the name being declared.

Refs #656

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.

fix(mcp): environment validation lets a custom stack read a built-in entry, stores an inert environment on local and accepts local on a remote stack

1 participant