Skip to content

Commit 7818144

Browse files
cliffhallclaude
andcommitted
docs(spec): reflect settings persistence + patch-PUT contract in v2_servers_file
The doc was authored when `mcp.json` only stored the server list. Bring it forward to match what shipped in #1352 / PR #1353: - Summary calls out that the file also stores per-server settings and links into the Per-server settings section. - On-disk format note explains that `settings` is an optional Inspector- specific extension that other MCP tools ignore. - New top paragraph on the Per-server settings section explains the basic-config / settings UI split rationale (simpler form for the common case, advanced settings tucked behind a second dialog). - UI bullet is rewritten to match the patch-style PUT semantics that emerged across the review cycle: settings-only and config-only PUTs preserve their counterpart inside the write lock, so neither modal can silently clobber the other half. The earlier "must re-send settings on PUT" hack is gone. - New bullets capture the save cadence (300ms debounce + flush-on-close + failure toast), the patch-PUT contract (omit-preserve / null-clear / rename), and the write-path / read-path validator gates. - Secret storage bullet links to #1356 (OS-keychain follow-up). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent dac8069 commit 7818144

1 file changed

Lines changed: 15 additions & 4 deletions

File tree

specification/v2_servers_file.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
## Summary
99

10-
Replaces the hardcoded `SEED_SERVERS` in `clients/web/src/App.tsx:47` with a file-backed list at `~/.mcp-inspector/mcp.json`, read at startup, mutated via REST endpoints, surfaced through a `useServers` hook.
10+
Replaces the hardcoded `SEED_SERVERS` in `clients/web/src/App.tsx:47` with a file-backed list at `~/.mcp-inspector/mcp.json`, read at startup, mutated via REST endpoints, surfaced through a `useServers` hook. The file also stores the per-server **settings** (custom headers, request metadata, timeouts, pre-configured OAuth credentials) edited by `ServerSettingsForm` — see [Per-server settings](#per-server-settings-1352) below for the on-disk shape, UI rationale, and write/read invariants.
1111

1212
## Goals
1313

@@ -52,7 +52,8 @@ Replaces the hardcoded `SEED_SERVERS` in `clients/web/src/App.tsx:47` with a fil
5252
- Matches `MCPConfig` in `core/mcp/types.ts:68`.
5353
- `type` omitted → normalized to `"stdio"`; `type: "http"``"streamable-http"` (`normalizeServerType` in `core/mcp/node/config.ts:81`).
5454
- The map key is the **server `id`**. `ServerEntry.id` already documents itself this way (`core/mcp/types.ts:89`: "The MCPConfig.mcpServers map key").
55-
- Display name: derived from the map key. The Inspector adds **no extension fields** to keep `mcp.json` clean and tool-interoperable. The edit dialog therefore treats id and display name as the same field; renaming = key-rotate + carry config across.
55+
- Display name: derived from the map key. The edit dialog treats id and display name as the same field; renaming = key-rotate + carry config across.
56+
- Each entry may optionally carry a `settings` node alongside the transport keys (`headers`, `metadata`, timeouts, OAuth credentials — see [Per-server settings](#per-server-settings-1352) below). It is an Inspector-specific extension; other MCP tools (Claude Desktop, Cursor, Cline) treat it as an unknown key and ignore it.
5657

5758
## First-run behavior
5859

@@ -187,6 +188,8 @@ Per `AGENTS.md`'s "test new or modified code" rule plus the UI-changes guidance:
187188

188189
## Per-server settings (#1352)
189190

191+
Our new UI design separates the basic server configuration (transport, URL or command + args + env) from settings (custom headers, connect/request timeout, global request metadata, client id/secret) into two dialogs. The latter is stored in an optional `settings` node of the server config. The reason they're separated in the UI is that custom settings are less likely to be needed than basic config, so a simpler, friendlier form greets most users.
192+
190193
Each server entry may carry an optional `settings` node alongside the transport config:
191194

192195
```jsonc
@@ -217,9 +220,17 @@ Each server entry may carry an optional `settings` node alongside the transport
217220
- `settings.connectionTimeout``Promise.race` wrapper around `InspectorClient.connect()` in the web client.
218221
- `settings.oauthClientId` / `oauthClientSecret` / `oauthScopes` → pre-seeded OAuth client credentials via `InspectorClientOptions.oauth`.
219222
- **First-connect contract**: settings apply on the *first* outbound request after the entry loads from disk — no need to open the settings form. The browser sends `settings` to the backend in the `/api/mcp/connect` body; the backend reads it from `RemoteConnectRequest` and threads it into `createTransportNode`.
220-
- **Secret storage**: `oauthClientSecret` is persisted in `mcp.json` alongside stdio `env` values, both protected by the file's `0o600` permission. OS-keychain integration is out of scope; a follow-up may switch the layout if a stronger secret store is needed.
223+
- **Secret storage**: `oauthClientSecret` is persisted in `mcp.json` alongside stdio `env` values, both protected by the file's `0o600` permission. OS-keychain integration is tracked separately in #1356 — when that lands, the secret will be lifted out of the file and replaced with a keychain reference.
221224
- **Removed**: `MCPServerConfig.headers` (previously on `SseServerConfig` / `StreamableHttpServerConfig`) has been deleted. The headers textarea in `ServerConfigModal` is gone; HTTP headers are entered only in `ServerSettingsForm`. v2 has not shipped a stable release with the old shape, so no on-read migration is included.
222-
- **UI**: `ServerSettingsModal` is opened from the server card's settings affordance; saving routes through `useServers.updateServerSettings(id, settings)` which calls `PUT /api/servers/:id` with `{ id, config, settings }`. `useServers.updateServer` re-sends the existing settings whenever it issues a PUT so the config-modal save does not clobber persisted settings.
225+
- **UI**: `ServerSettingsModal` is opened from the server card's settings affordance. Saving routes through `useServers.updateServerSettings(id, settings)` which issues a settings-only `PUT /api/servers/:id` with `{ id, settings }` — the route preserves the on-disk transport config inside its write lock. Conversely, `useServers.updateServer` (driven by the basic-config modal) issues a config-only PUT with `{ id, config }` and the route preserves the on-disk settings node. Edits in either modal cannot silently wipe the other half.
226+
- **Save cadence**: the form fires `onSettingsChange` on every keystroke. `App.tsx` debounces 300 ms and flushes on modal close so a burst of edits coalesces into a single PUT. If the close-flush PUT fails (network hiccup, server 500), a red `@mantine/notifications` toast surfaces the failure — the modal has already closed so a silent failure would leave the user thinking the last edits saved.
227+
- **`PUT /api/servers/:id` patch semantics**: both `config` and `settings` are independent patches.
228+
- Field omitted → preserve the on-disk value.
229+
- Explicit `null` on `settings` → clear the settings node. (`config` may not be `null`; a body that wants to update only settings should omit `config` entirely.)
230+
- Field present and well-formed → validate and apply.
231+
- A bare `PUT { id: "renamed" }` is a pure rename preserving both halves.
232+
- **Write-path gates**: `validateSettings` rejects malformed shapes (non-object, wrong-typed `headers` / `metadata`, non-numeric timeouts) with `400` + descriptive message and picks-and-builds the validated value so unknown stowaway keys silently drop. `buildStoredEntry` strips any `settings` key nested inside the incoming `config` (and logs a `warn` with the server id) so `validateSettings` remains the only path settings reach disk on the write side.
233+
- **Read-path gates**: `normalizeMcpServers` re-runs `validateSettings` on the on-disk settings node when loading; a malformed hand-edited `settings` node is silently dropped (lenient-on-read pattern matching `normalizeServerType`'s unknown→stdio fallback). A subsequent preserve-on-PUT cannot propagate the broken node, and `mcp.json` files written by future versions with unknown stowaway keys won't poison the in-memory shape.
223234

224235
## Out of scope (follow-ups)
225236

0 commit comments

Comments
 (0)