Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
33cc47b
docs(vscode): mockups and task plan for editable mcp.json config source
s2005 Jun 21, 2026
28a5bd8
feat(vscode): config source model, mcp.json detection and reverse parser
s2005 Jun 21, 2026
b813f1d
feat(vscode): editable mcp.json source — source switcher, auto-detect…
s2005 Jun 21, 2026
312a5de
test(vscode): integration coverage for shared mcp.json merge + docs
s2005 Jun 21, 2026
4d33153
docs(vscode): remove mcp.json config source mockups
s2005 Jun 21, 2026
c5f90c4
fix(vscode): address review feedback for PR #89
s2005 Jun 21, 2026
ca2b0f4
fix(vscode): address review feedback for PR #89 (round 2)
s2005 Jun 21, 2026
5813de1
fix(vscode): address review feedback for PR #89 (round 3)
s2005 Jun 21, 2026
baf060b
fix(vscode): address review feedback for PR #89 (round 4)
s2005 Jun 22, 2026
facea19
docs(vscode): backfill commit hash into PR #89 round 4 analysis
s2005 Jun 22, 2026
14ebba4
fix(vscode): address review feedback for PR #89 (round 5)
s2005 Jun 22, 2026
5fc162d
fix(vscode): guard export actions after file-source reset
claude Jun 22, 2026
36db372
fix(vscode): address round-6 codex review feedback for PR #89
claude Jun 22, 2026
508adf8
fix(vscode): refuse masked shell/profile edits and fix variable-cwd c…
claude Jun 22, 2026
f671c11
fix(vscode): lock non-transport tabs for http/sse file sources
claude Jun 22, 2026
e41189c
fix(vscode): round-6 codex review follow-ups for PR #89 (parser + sav…
s2005 Jun 22, 2026
a25717e
fix(vscode): round-7 codex review follow-ups for PR #89 (parser + sav…
s2005 Jun 23, 2026
378cffb
fix(vscode): round-8 codex review follow-ups for PR #89 (file-source …
s2005 Jun 23, 2026
461e83d
docs(tasks): record round-8 commit hash in PR #89 analysis files
s2005 Jun 23, 2026
3727aec
fix(vscode): round-9 codex review follow-ups for PR #89 (P55/P56)
s2005 Jun 23, 2026
ce3f1a7
docs(tasks): record round-9 commit hash in PR #89 analysis files
s2005 Jun 23, 2026
30c8b75
docs(tasks): record round-10 re-audit findings P57-P60 for PR #89
s2005 Jun 23, 2026
a49ce18
fix(vscode): round-10 codex review follow-ups for PR #89 (P57-P60)
s2005 Jun 23, 2026
9a55636
fix(vscode): round-11 codex review follow-ups for PR #89 (P61-P62)
s2005 Jun 23, 2026
7c18d9f
fix(vscode): round-12 codex review follow-ups for PR #89 (P63-P66)
s2005 Jun 23, 2026
ee70af8
fix(vscode): round-13 codex review follow-ups for PR #89 (P67-P70)
s2005 Jun 23, 2026
487f6cc
fix(vscode): round-14 codex review follow-ups for PR #89 (P71-P73)
s2005 Jun 24, 2026
bb6fe6c
fix(vscode): round-15 codex review follow-ups for PR #89 (P74-P78)
s2005 Jun 24, 2026
3b4a3e9
docs(vscode): backfill commit hash in round-15 analysis files
s2005 Jun 24, 2026
de724b2
fix(vscode): round-16 codex review follow-ups for PR #89 (P79-P82)
s2005 Aug 23, 2026
88e40b3
fix(vscode): round-17 codex review follow-ups for PR #89 (P83-P86)
s2005 Aug 23, 2026
845f9d4
fix(vscode): round-18 codex review follow-ups for PR #89 (P87-P88)
s2005 Aug 23, 2026
fa257cf
fix(vscode): round-19 codex review follow-ups for PR #89 (P89-P92)
s2005 Aug 23, 2026
a511197
fix(vscode): round-20 codex review follow-ups for PR #89 (P93-P97)
s2005 Aug 23, 2026
16ad205
fix(vscode): round-21 codex review follow-up for PR #89 (P98)
s2005 Aug 23, 2026
4a494d6
fix(vscode): round-22 codex review follow-up for PR #89 (P99)
s2005 Aug 23, 2026
b1676fb
fix(vscode): round-23 codex review follow-ups for PR #89 (P100-P102)
s2005 Aug 23, 2026
d5eb761
fix(vscode): round-24 codex review follow-up for PR #89 (P103)
s2005 Aug 23, 2026
32a16f7
test(vscode): pin the empty allowed-dir round-trip; reject P104 for P…
s2005 Aug 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions docs/tasks/autodetect_mcp_source/PRD.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# PRD: Auto-detect and load .vscode/mcp.json as an editable configuration source

## Objective

Let the wcli0 configuration panel detect an existing workspace `.vscode/mcp.json` that already defines a
`servers.wcli0` entry, surface a one-click banner to load it, and let the user edit it and save the
changes back to that file. This adds a `load -> edit -> save` round trip alongside today's write-only
`new -> export` path, anchored by a new "configuration source" switcher that always makes clear what the
form is editing and where Save will write. Scope is the mockup 02 (auto-detect on open) and the minimal
mockup 03 (edit/save the detected file) needed to make detection useful.

## Background

The extension is settings-driven today. The webview form (`vscode-extension/src/webview.ts`) only ever
edits VS Code settings (`wcli0.*`) at the User or Workspace scope, and files are produced one-way by the
Export tab: `wcli0: Write .vscode/mcp.json` (`writeWorkspaceMcpJson` in
`vscode-extension/src/commands.ts`) and `wcli0: Generate config.json` (`generateConfigFile`). There is
no way to open an existing `.vscode/mcp.json`, see it in the form, edit it, and save it back. A team that
already committed an `mcp.json` cannot round-trip it through the panel.

The mockups in `vscode-extension/docs/mockups/` propose a "configuration source bar": a header that
names the active source (VS Code Settings, the workspace `.vscode/mcp.json`, or a file opened
explicitly) and routes Save to that source. Mockup 02 (`02-autodetect-mcp-json.html`) covers the
detection banner and the source switcher menu; mockup 03 (`03-editing-mcp-json.html`) covers editing the
file and saving back.

The existing writer already contains the hard parts of safe `mcp.json` editing: JSONC parse
(`parseJsonc`), preserve other `servers.*` entries, refuse a non-object root or non-object `servers`,
and warn before dropping comments. What is missing is the reverse direction (parse an `mcp.json`
`servers.wcli0` entry back into the form's settings model) and the UI to drive load/save against a file
rather than a settings scope.

Source: feature request to support `load -> edit -> export` for `.vscode/mcp.json`; mockup set in
`vscode-extension/docs/mockups/` (the `index.html` source table). Side-by-side simultaneous
settings + file editing is explicitly deferred to a future task.

## Requirements

### REQ-1: Configuration source model and switcher

Introduce a "configuration source" concept the form edits one at a time. The source bar shows the active
source (kind + path) and a switcher listing: VS Code Settings, the detected workspace `.vscode/mcp.json`
(when present), and explicit actions. The current behavior (editing settings, with the
User/Workspace scope radio) becomes the `settings` source and remains the default when nothing is
detected.

### REQ-2: Auto-detect .vscode/mcp.json with a wcli0 entry

On panel open, the host reads `${workspaceFolder}/.vscode/mcp.json` (tolerating JSONC via `parseJsonc`)
and reports whether `servers.wcli0` exists. When it does, the form shows a detection banner offering a
one-click "Load & edit .vscode/mcp.json". Detection never mutates the file and never blocks the panel
when the file is absent, unreadable, or malformed.

### REQ-3: Load a mcp.json wcli0 entry into the form

Loading the detected source parses the `servers.wcli0` entry back into the settings model that drives the
form: `type` -> transport mode; `command`/`args` -> launch method, package spec / node script / custom
command, and the recognized server flags (`--shell`, repeated `--allowedDir`, `--commandTimeout`,
`--maxCommandLength`, `--safetyMode` equivalents, transport flags, `--config`, etc.); `cwd` and `env` ->
the launch cwd / env. Flags the form does not model are preserved verbatim in `extraArgs` so a save does
not silently drop them. After load, every tab reflects the file's values and the source bar shows the
file path and the `servers.wcli0` pointer.

### REQ-4: Save edits back to the same file

When the source is the `.vscode/mcp.json` file, Save writes the edited `servers.wcli0` entry back into
that file using the existing safe-merge rules (preserve other servers, refuse a non-object root /
`servers`, warn before discarding comments) rather than writing VS Code settings. Saving the file source
does not modify any `wcli0.*` setting. A dirty indicator reflects unsaved edits, and Revert reloads from
disk.

### REQ-5: The existing export path is preserved

"New config from current settings" stays available as an explicit switcher action and behaves exactly
like today's Export tab (`writeWorkspaceMcpJson` / `generateConfigFile`). Switching the source back to
VS Code Settings restores the scope radio and the settings save behavior unchanged.

### REQ-6: Global home config is read-only, never a silent save target

The server's implicit `~/.win-cli-mcp/config.json` is listed in the switcher as a read-only preview only.
It can never be selected as an editable/save source, so a save can never silently overwrite the user's
global config.

### REQ-7: Unsaved-changes guard on source switch

Switching the source (or loading a file) while the form has unsaved edits prompts for confirmation
before discarding them, mirroring the existing scope-switch guard (`scopeChangeRequest` in `webview.ts`).

### REQ-8: Documentation

`README.md` and any relevant `markdownDescription` text explain the source switcher, auto-detection, and
the `load -> edit -> save` round trip for `.vscode/mcp.json`.

## Non-Requirements

- Side-by-side / simultaneous settings + file editing (a future task; explicitly out of scope now).
- Editing arbitrary user-picked files via a Browse dialog (mockup 04) beyond the detected workspace
`.vscode/mcp.json`; the "Open another file..." action may appear but its full browse behavior is
deferred.
- Editing the richer wcli0 `--config` `config.json` format in place (mockup 05), including per-shell and
profiles round-trip into a file source. When a detected entry references a `--config` file carrying
per-shell/profiles, the form surfaces a note that those are not editable here rather than editing them.
- Any change to the MCP server definition provider's launch behavior or to the server's schema.
- Multi-root: only the primary workspace folder's `.vscode/mcp.json` is detected (matching
`primaryWorkspaceFolder()` usage elsewhere).

## Acceptance Criteria

1. Opening the panel in a workspace whose `.vscode/mcp.json` has a `servers.wcli0` entry shows the
detection banner; opening it without such a file (or with no workspace) shows no banner and the
settings source is active.
2. Clicking "Load & edit" populates every form tab from the parsed entry, and the source bar shows the
file path and `servers.wcli0` pointer.
3. A round trip `buildLaunchSpec(settings) -> parseMcpEntry -> settings` reproduces the modeled fields
for representative stdio/http/sse settings; unmodeled flags survive in `extraArgs`.
4. With the file source active, Save writes the edited entry back to `.vscode/mcp.json`, preserves other
`servers.*` entries, and leaves `wcli0.*` settings untouched.
5. A malformed or absent `.vscode/mcp.json` does not break detection or the panel; a malformed file is
never overwritten on save (refused, matching today's writer).
6. `~/.win-cli-mcp/config.json` appears only as a read-only preview and cannot be chosen as a save target.
7. Switching source / loading a file with unsaved edits prompts before discarding.
8. The existing settings-scope editing and Export-tab behaviors are unchanged when the settings source is
active.
9. `tsc --noEmit`, the unit suite, the integration suite, and markdownlint all pass.

## Deliverables

| Deliverable | Type |
| ----------- | ---- |
| vscode-extension/src/configSource.ts | Create |
| vscode-extension/src/argsBuilder.ts | Update |
| vscode-extension/src/commands.ts | Update |
| vscode-extension/src/webview.ts | Update |
| vscode-extension/src/extension.ts | Update |
| vscode-extension/test/unit/configSource.test.cjs | Create |
| vscode-extension/test/unit/commands.test.cjs | Update |
| vscode-extension/test/unit/webview.test.cjs | Update |
| vscode-extension/test/integration/mcpJson.test.js | Update |
| vscode-extension/README.md | Update |
125 changes: 125 additions & 0 deletions docs/tasks/autodetect_mcp_source/analysis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# Analysis: Auto-detect and load .vscode/mcp.json as an editable configuration source

## Goal

Add a `load -> edit -> save` round trip for the workspace `.vscode/mcp.json` to the configuration panel,
fronted by a configuration-source switcher and an auto-detection banner, without regressing the current
settings-driven editing and one-way export.

## Current Behavior

- The webview (`vscode-extension/src/webview.ts`) edits one settings scope at a time. `setupWebview`
tracks `currentScope` (`Global`/`Workspace`), posts an `init` message with values from
`readSettingsForScope` plus the `setKeys` / `setSelectKeys` / `setArrayKeys` "explicitly set" hints,
and handles inbound messages: `ready`, `scopeChange`, `scopeChangeRequest` (the dirty-edit guard via
`vscode.window.showWarningMessage({modal:true})`), `save`, and the export triggers `generateConfig` /
`writeMcpJson` / `showCommand`.
- The sticky header renders a `Save to: Workspace / User` radio and an "Overridable" isolation chip
(`webview.ts` lines ~469-497). There is no notion of a non-settings source.
- Settings <-> CLI args is one-directional. `buildServerArgs` / `buildLaunchSpec`
(`vscode-extension/src/argsBuilder.ts`) turn a `Wcli0Settings` into a launcher command + flags. There
is no inverse (args -> settings).
- `writeWorkspaceMcpJson` (`vscode-extension/src/commands.ts`) already does the safe write half of the
round trip: reads the file, parses JSONC via `parseJsonc`, refuses a non-object root or non-object
`servers`, warns before discarding comments, sets `servers.wcli0 = entry`, and writes back. It derives
the entry from settings read for the form scope (`readExportSettings`).
- The provider (`vscode-extension/src/mcpProvider.ts`) registers the server from settings only; it never
reads the committed `mcp.json`. Detection helpers exist for related concerns: `homeConfigExists`,
`cwdConfigExists`, `configFileIsLoadable`.

## Feasibility

Feasible and well-scaffolded. The write half (REQ-4) reuses `writeWorkspaceMcpJson`'s existing logic
nearly verbatim. The detection (REQ-2) is a small read + `parseJsonc` + `servers.wcli0` presence check.
The genuinely new piece is the reverse parser (REQ-3): turning an `mcp.json` entry's
`command`/`args`/`cwd`/`env` back into a `Wcli0Settings`. Because the extension owns the forward mapping
(`buildServerArgs`), a paired inverse is tractable and round-trip testable; unknown flags fall through to
`extraArgs` so nothing is lost. The UI work (REQ-1, REQ-5..REQ-7) extends the existing message protocol
and sticky header rather than rebuilding the form.

## Approach

Add a small source model and a reverse parser, then extend the webview protocol so the host can drive the
form from either a settings scope or a file.

### Approach A (recommended): source model + paired reverse parser

A new `src/configSource.ts` defines the source kinds (`settings` | `mcpJson`), detection
(`detectWorkspaceMcpJson(folder)` returning `{ uri, hasWcli0 }`), and the reverse parser
`parseMcpEntry(entry): Wcli0Settings` built on a `parseServerArgs(args)` inverse of `buildServerArgs`.
The webview gains source state alongside `currentScope`; `init` carries the active source, the detected
sources, and (for a file source) values produced by `parseMcpEntry`. Save routes to a refactored
`writeMcpJsonFromSettings(settings, folder, ...)` extracted from `writeWorkspaceMcpJson`.

| Advantages | Disadvantages |
| ---------- | ------------- |
| Reuses the existing safe-merge writer and dirty-guard patterns | A reverse arg parser is new surface with edge cases (`--opt=value`, repeated flags, negations) |
| Round-trippable and unit-testable against `buildServerArgs` | Hand-written `mcp.json` may use flags the form cannot model (mitigated by `extraArgs` passthrough) |
| Keeps one-source-at-a-time, so "where does Save go" stays unambiguous | Webview message protocol grows (new message types) |

### Approach B: open mcp.json as a text document only

Skip the form entirely for files; just open `.vscode/mcp.json` in a text editor with a banner.

| Advantages | Disadvantages |
| ---------- | ------------- |
| Minimal code; no reverse parser | Does not satisfy "edit it in the form" / the mockups; no validation or guided editing |

### Approach C: full source framework now (files of any kind, config.json too)

Build the whole mockup set (04 browse, 05 config.json) in one task.

| Advantages | Disadvantages |
| ---------- | ------------- |
| One cohesive delivery of the source concept | Much larger; config.json reverse-map (per-shell/profiles) is a separate hard problem; contradicts the requested mockup-02 scope |

Approach A is recommended: it delivers the requested auto-detect + round trip on a model that later tasks
(browse, config.json) can extend.

## Implementation Notes

- Reverse parser must mirror the quirks `buildServerArgs` emits: `--option=value` form for dash-prefixed
values (`pushOption`), repeated `--allowedDir` / `--blockedCommand` / etc., `--no-enableTruncation`
negations, `--yolo` / `--unsafe` -> `safetyMode`, transport flags (`--transport`, `--http-host`,
`--http-port`, `--sse-*`), and the npx prefix `-y <packageSpec>`. Anything unrecognized accumulates in
`extraArgs` (round-trips through the existing forward path).
- Launch method inference: `command === 'npx'` with a leading `-y` -> `npx`; `command === 'node'` ->
`node` (first arg is the script path); otherwise `custom` (command + leading non-flag args ->
`customArgs`, until the first recognized server flag).
- A detected entry that references a `--config` file (or whose form maps to per-shell/profiles) cannot be
fully represented; surface a non-blocking note and keep `--config` in `configFile` / `extraArgs`.
Editing that referenced file is the future mockup-05 task.
- Save path: extract the body of `writeWorkspaceMcpJson` after settings-resolution into
`writeMcpJsonFromSettings(settings, folder, configFileLoadable?)`; the existing command builds settings
from scope and calls it (no behavior change), the file-source save builds settings from the form
payload (like `applySettings` collects) and calls it. The file save must NOT call `config.update`.
- Source state lives next to `currentScope` in `setupWebview`. The `init` payload gains
`source: { kind, uri?, label }` and `detected: ConfigSource[]`. New inbound messages: `selectSource`
/ `loadSource` (with dirty guard, reusing the `scopeChangeRequest` modal pattern) and `saveToFile`.
- `onDidChangeConfiguration` re-post (`post(true)`) must not clobber a file source: when the active
source is a file, an external settings change should not reload the form from settings.
- Keep the home config strictly read-only: include it in `detected` with a `readOnly: true` flag the UI
renders disabled; never accept it as a `loadSource`/`saveToFile` target on the host side.

## Risks

| Risk | Mitigation |
| ---- | ---------- |
| Reverse parser drops or mangles flags a hand-written mcp.json uses | Unmodeled flags pass through to `extraArgs`; round-trip unit tests; a note when `--config`/per-shell is referenced |
| Save overwrites a user's hand-formatted/commented mcp.json | Reuse existing refuse-and-warn logic (`parseJsonc`, comment warning, non-object guards) unchanged |
| Source/scope state divergence causes a stale post to overwrite edits | Mirror existing dirty-guard discipline; gate external re-posts when a file source is active |
| Home config accidentally editable | Host-side guard rejects it as a load/save target regardless of UI |
| Webview protocol growth breaks existing tests | Add new message types additively; keep `ready`/`save`/export messages working |

## Test Strategy

- Unit (`test/unit/configSource.test.cjs`): detection on present/absent/malformed/no-`wcli0` files;
`parseServerArgs` / `parseMcpEntry` for npx/node/custom, stdio/http/sse, repeated and `=`-form flags,
negations, and unknown-flag passthrough to `extraArgs`; round-trip against `buildLaunchSpec`.
- Unit (`test/unit/commands.test.cjs`): `writeMcpJsonFromSettings` preserves other servers, refuses a
non-object root/`servers`, warns on comments; never calls `config.update`.
- Unit (`test/unit/webview.test.cjs`): `init` carries detected sources; banner shown only when a wcli0
entry exists; `selectSource`/`loadSource` populate from a file; `saveToFile` routes to the file writer;
dirty guard fires on source switch; home config not offered as a save target.
- Integration (`test/integration/mcpJson.test.js`): with a fixture `.vscode/mcp.json` containing
`servers.wcli0`, the extension detects it and a save-back round trip preserves a second server entry.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Analysis 100 - Preserve unsupported shell names

## Decision: Valid — fix applied

P96 diverted the exact value `all` because the form's shell control uses it as an omission
sentinel. The review is right that the same reasoning covers every value the control cannot hold:
the shell field is a fixed `<select>` offering Inherit, `all`, and the five real shell names
(`SHELL_NAMES`). Assigning `fish` to it leaves the select on its empty value, the form immediately
counts as dirty, and a save drops `--shell` entirely — so an entry the server matched to NO shell
(nothing is named `fish`) becomes one running every default shell. Exactly the P96 escalation, one
step wider.

`divertShellAll` is now `divertShellValue`: a `--shell` value is modeled only when it is one of the
five names the select offers, and anything else (`all`, `fish`, `zsh`, `ALL`) is preserved verbatim
in `extraArgs` for both the space and attached spellings. The five real names stay fully editable,
and choosing one in the form still strips the preserved copy (P61) so the edit wins.

**Why:** The rule "model only what the control can represent" is what P96 established; keying it to
`SHELL_NAMES` — the list the select is built from — makes it structural instead of a special case,
so a new shell name added to the enum stays modelable automatically and everything else is preserved
without further review rounds. See [[analysis_96_preserve_explicit_shell_all]] and
[[analysis_61_strip_preserved_value_flags]].

**Commit:** f03b33f - fix(vscode): round-23 codex review follow-ups for PR #89 (P100-P102)
Loading