Skip to content

session: scoped customization enablement - #385

Draft
Connor Peet (connor4312) wants to merge 1 commit into
mainfrom
customization/scoped-enablement
Draft

session: scoped customization enablement#385
Connor Peet (connor4312) wants to merge 1 commit into
mainfrom
customization/scoped-enablement

Conversation

@connor4312

Copy link
Copy Markdown
Member

Protocol side of the scoped customization enablement work in microsoft/vscode#329047. Draft until that PR settles, since the two need to agree.

What changed

Customizations gain an optional enablement array of explicit decisions, one entry per scope that has one:

CustomizationEnablement =
  | { kind: 'global'; enabled: boolean }
  | { kind: 'workspace'; uri: URI; enabled: boolean }
  | { kind: 'session'; enabled: boolean }

The array is a wire contract, not just a bag of data. Producers MUST publish entries sorted by descending specificity — session, workspace, then global — and the agent host emits at most one workspace entry, for the session's primary working directory. Consumers MAY therefore treat enablement[0] as decisive, with enablement?.[0]?.enabled ?? true as the effective value, and never have to implement precedence themselves. An absent or empty array means no explicit decision, so the customization is enabled by default.

Only the host publishes this; clients treat it as read-only provenance.

The field lives on the customization base rather than on McpServerCustomization alone, so it applies to every customization type. The VS Code side already uses it for both MCP servers and plugins.

session/customizationToggled now carries enablement in place of enabled, and replaces the complete decision set. A caller changing one scope must include every decision it intends to preserve; an empty array clears all decisions and restores the default. Wholesale replacement avoids needing a separate "clear this scope" action and keeps the reducer total.

Why not merge semantics

Merging would need a way to express "remove the workspace decision" distinctly from "no opinion about the workspace decision", which either means a nullable field or a second action. Replacement makes the client state the full intent it wants, which is also what the UI naturally has on hand.

Container cascade

Note for reviewers, since the guide currently says a child's enabled is independent of its container: on the VS Code side a disabled container short-circuits its children, but that is applied at consumption, not by rewriting the child's published enabled. A child keeps its own decisions and its own resolved value, so re-enabling a container restores each child's prior state. This is what keeps the enablement[0] invariant true for the customization it appears on. I have deliberately not changed the guide wording here — if you would rather the spec state the cascade explicitly, say so and I will add it.

Validation

  • npm run generate is reproducible: re-running it produces zero diff, so the generated Go/Kotlin/Rust/Swift sources and JSON schemas are genuinely generated rather than hand-edited.
  • npm test — 389 passing, 0 failing, 100% reducer coverage.
  • go test ./... and cargo test --workspace pass. Both consume the shared types/test-cases/reducers/ fixtures, so the new fixture exercises the TypeScript, Go, and Rust reducers alike.
  • New fixture 263-session-customizationtoggled-clears-enablement pins the subtle case: an empty array drops the field and restores the default.

Not verified: Kotlin Gradle tests (no Java runtime available) and Swift tests (missing CoreSimulator). Swift sources compile. Those two clients' reducer ports would benefit from a reviewer running them.

Customizations gain an optional `enablement` array of explicit decisions, one
per scope that has one:

    CustomizationEnablement =
      | { kind: 'global'; enabled: boolean }
      | { kind: 'workspace'; uri: URI; enabled: boolean }
      | { kind: 'session'; enabled: boolean }

The array is a wire contract. Producers MUST publish entries sorted by
descending specificity (session, workspace, then global), and the agent host
emits at most one workspace entry, for the session's primary working directory.
Consumers MAY therefore treat `enablement[0]` as decisive, with
`enablement?.[0]?.enabled ?? true` as the effective value. An absent or empty
array means no explicit decision, so the customization is enabled by default.
Only the host publishes this; clients treat it as read-only provenance.

The field lives on the customization base rather than on MCP servers alone, so
it applies to every customization type.

`session/customizationToggled` carries `enablement` in place of `enabled` and
replaces the complete decision set, so a caller changing one scope must include
every decision it intends to preserve. An empty array clears all decisions and
restores the default.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.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.

1 participant