Skip to content

fix(mcp-market): isolate registry header variables from URLs - #706

Merged
vastsa merged 6 commits into
vastsa:mainfrom
ct-jaryn:fix/registry-url-header-isolation
Sep 21, 2026
Merged

vastsa merged 6 commits into
vastsa:mainfrom
ct-jaryn:fix/registry-url-header-isolation

Conversation

@ct-jaryn

@ct-jaryn ct-jaryn commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

Registry headers using {name} now resolve through header-local bindings while URL templates retain their existing ${UPPERCASE} behavior. Inputs, defaults, optional flags and fixed literals stay scoped to their declaring header. The host protocol and persisted config format are unchanged.

When headerBindings is present, even as an empty or partial map, unbound tokens in every header remain literal. For example, with only Authorization: Bearer {token} bound, X-Unbound: {token} stays literal instead of receiving the Authorization credential. Catalogs with no binding metadata retain their legacy behavior. Placeholder discovery and resolution use the same rule.

This completes the scope fixes requested in #646 and the blocking review on this PR. The branch incorporates current main ec944a7150c4498a1b51d174daec3d9180e55b31 without rewriting the published history; the ADR-index conflict keeps both upstream and proposed entries.

Validation on 30559d28cda1e364e3e89c656c2cad3683c483f5 (the final base refresh leaves the separately checked shared, Rust host and desktop market sources identical; all five protocol E2E cases were rerun on this head):

  • Seven new native cases: six fail on the prior resolver and all pass after the fix. Covers partial/empty maps, brace/dollar tokens, defaults, undeclared and inherited inputs, and legacy compatibility.
  • Complete shared source suite: 461 passed; shared typecheck and build passed. Additional independent synthetic scope matrix: 12/12 passed.
  • Desktop market/registry tests: 9 passed.
  • Current-base Rust host rebuilt with cargo build -p host-core --locked --offline; protocol E2E 5/5 passed, including partial bindings through resolution, mcp.upsert, mcp.list and disk persistence.
  • Documentation checks: 78 locale pairs / 496 pages. Architecture, agent-policy sync, configured repository Biome lint, base ancestry and whitespace checks passed. The new scenario and proposed ADR are documented in both indexes/specification surfaces.

The remote HTTP fixtures use synthetic values, disabled servers and temporary storage. No live MCP service, model, production credential or Electron UI was exercised. Local checks do not replace current-head upstream CI or maintainer approval. Implementation and tests used AI assistance and a separate automated code review; no human-review claim is made.

Registry header declarations must not authorize substitution in URL paths
or queries. Select placeholder rules per field while preserving legacy
URL and stdio tokens, header-local defaults and fixed values.

Cover the same-name boundary through registry mapping, resolution and
real host persistence, following the review on vastsa#646.
Copilot AI lite review requested due to automatic review settings September 20, 2026 10:58

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

The new ADR is missing from the Chinese ADR index, leaving the documented decision map out of sync.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 Low severity

Open (1)
What changed in this PR

This PR fixes MCP Registry header-variable mapping so {name} credentials resolve only in headers while URL templates retain ${UPPERCASE} behavior.

Changes:

  • Adds header-scoped editable/fixed bindings and collision-safe inputs.
  • Preserves literals and URL scope during resolution and persistence.
  • Adds regression, E2E, ADR, and specification coverage.
File Description
packages/​shared/​src/​mcp-registry.ts Maps registry header variables into scoped bindings.
packages/​shared/​src/​mcp-catalog.ts Resolves header and URL templates with separate matchers.
packages/​shared/​src/​mcp-registry.test.ts Covers registry mapping and isolation cases.
packages/​shared/​src/​mcp-catalog.test.ts Covers catalog binding validation and resolution.
scripts/​e2e-mcp-market.mjs Adds host persistence scope validation.
docs/​spec/​06-delivery/​04-e2e-test-plan.md Documents new E2E scenarios.
docs/​zh-CN/​spec/​06-delivery/​04-e2e-test-plan.md Mirrors E2E documentation.
docs/​adr/​registry-header-variable-spelling.md Records the design decision.
docs/​adr/​README.md Adds the ADR to the English index.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread docs/adr/README.md
| 0298 | [The app ships no fonts](0298-remove-bundled-fonts.md) | Accepted (D598; amends ADR 0083 / D232) |
| turn-process-and-thinking-display | [Turn process and thinking presentation](turn-process-and-thinking-display.md) | Accepted |
| provider-display-order | [Provider display order](provider-display-order.md) | Accepted |
| registry-header-variable-spelling | [Remote header variables accept the registry's `{name}` spelling](registry-header-variable-spelling.md) | Proposed |

@vastsa vastsa left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks for the focused registry-boundary fix. I found one blocking scope issue in packages/shared/src/mcp-catalog.ts (the collect/fill paths around the header-binding handling): when headerBindings contains bindings for one header but another header has {token} text without its own binding map, the latter falls back to the global requiredEnv set and can substitute a credential declared for the first header. That can leak a header credential into another header.

Please make unbound header tokens literal whenever header bindings are in use (or reject partial binding maps), and add a regression covering two headers with the same token plus a partial headerBindings map. The current DIRTY merge state and missing current code CI are additional blockers. Thanks.

Keep the proposed registry header ADR alongside the upstream certificate\ndecision while incorporating the current base without rewriting the\npublished request branch.
Treat the presence of header binding metadata as an explicit scope for
all headers, so a missing per-header map cannot consume another header
input or default through the legacy global fallback.

Cover partial and empty maps in discovery and resolution, preserve legacy
catalogs, and verify isolated values through real host persistence.
Include the latest upstream base so the validated request meets the
repository ancestry gate without rewriting the published branch.
@ct-jaryn

Copy link
Copy Markdown
Contributor Author

Addressed the blocking partial-map scope issue in 30559d28cda1e364e3e89c656c2cad3683c483f5 (includes latest main ec944a7150c4498a1b51d174daec3d9180e55b31). When headerBindings exists, missing per-header maps now mean no bindings, so both brace and dollar tokens remain literal instead of falling back to global inputs/defaults. Catalogs without this metadata retain the legacy path.

Added seven native cases: six reproduce failures on the previous resolver and all pass with the fix, including the two-header/same-token partial-map case, empty maps and an undeclared token that must not be collected. Current validation: 461 shared source tests, shared typecheck/build, 9 desktop market tests, a separate 12-case scope matrix and 5/5 real-host protocol E2E. The new E2E checks resolution → upsert → list → persisted config with disabled remote entries and synthetic values.

The ADR-index conflict is resolved without dropping upstream content or rewriting branch history. GitHub now reports MERGEABLE; its actual PR merge ref 97dcce996684ebd74c51c12ff13c11dde8eb610e has the same tree as the validated head. The PR description records exact validation scope.

The current-head CI, Docs check and PR base runs were triggered but are action_required; they need repository approval before code CI can execute. Vercel likewise reports deployment authorization required. Local checks are not being represented as completed upstream CI. Please authorize the pending runs and re-review when available.

@vastsa
vastsa merged commit c5b3f6a into vastsa:main Sep 21, 2026
7 of 8 checks passed

This branch had an error being deployed

1 failed deployment
Preview — 92d7b091 Deployed Sep 21, 2026 by vercel[bot]
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.

3 participants