Background
clients/web/src/App.tsx:47 currently hardcodes two seed servers (filesystem-server-default, everything-server-default) in SEED_SERVERS, with a note that "Persistence + an 'Add server' UI are explicitly out of scope for #1244." The four onServerAdd / onServerEdit / onServerClone / onServerRemove callbacks at App.tsx:639–646 are wired to todoNoop. Time to fix that.
The v1.5 port already brought us most of the building blocks:
core/storage/store-io.ts — atomic R/W, ENOENT handling, mkdir -p, 0o600. Plus getDefaultStorageDir() resolving ~/.mcp-inspector/storage/.
core/mcp/node/config.ts — loadMcpServersConfig parses the canonical { mcpServers: { ... } } format and normalizes server types (missing → stdio, http → streamable-http).
core/mcp/remote/node/server.ts — Hono backend with the /api/storage/:storeId route as a clear template for new endpoints.
Scope
Persist the server list to ~/.mcp-inspector/mcp.json (Windows: %USERPROFILE%\.mcp-inspector\mcp.json), in the canonical { mcpServers: { ... } } format so the file is interoperable with Claude Desktop / Cursor / Cline and hand-editable. Full CRUD in one issue (read → write → UI dialogs).
Full design and rationale in specification/v2_servers_file.md.
Backend (core)
- Add
getDefaultMcpConfigPath() to core/storage/store-io.ts (sibling of getDefaultStorageDir()).
- Add
core/mcp/serverList.ts: pure converters mcpConfigToServerEntries / serverEntriesToMcpConfig, plus DEFAULT_SEED_CONFIG (the two current seeds). Re-export normalizeServerType from core/mcp/node/config.ts.
- Add to
core/mcp/remote/node/server.ts:
GET /api/servers — returns MCPConfig; writes DEFAULT_SEED_CONFIG to disk if file absent.
POST /api/servers — body { id, config }; 409 on duplicate id.
PUT /api/servers/:id — body { id?, config }; supports id rename.
DELETE /api/servers/:id — idempotent.
- All ids validated with
validateStoreId (alphanum + hyphen + underscore).
RemoteServerOptions.mcpConfigPath?: string defaulting to getDefaultMcpConfigPath().
Frontend
- Add
core/react/useServers.ts: returns { servers, loading, error, refresh, addServer, updateServer, removeServer }. Calls the new endpoints with the existing x-mcp-remote-auth token.
clients/web/src/App.tsx: drop SEED_SERVERS; wire to useServers; connect the four todo-noop server callbacks to the hook's mutators. On remove of the active server, disconnect first. On rename of the active server, update activeServerId.
- Add minimal Add / Edit Mantine
Modal dialogs per AGENTS.md's React rules (subcomponent constants via .withProps(), theme variants in src/theme/, no inline styles, no hex literals).
Tests
- Unit:
clients/web/src/test/core/mcp/serverList.test.ts, clients/web/src/test/core/storage/getDefaultMcpConfigPath.test.ts.
- Integration:
clients/web/src/test/integration/mcp/remote/servers-route.test.ts (real createRemoteApp against a tmp mcpConfigPath), clients/web/src/test/integration/react/useServers.test.tsx (hook against a real Hono instance).
- Per-file 90% gate applies. Cover network / 4xx / 5xx error paths on the hook.
- Manual smoke: first-launch seed-write, hand-edit + reload, add/edit/remove + hard reload, delete-active-server disconnect.
Out of scope (separate follow-ups)
- Import-from-Claude-Desktop button.
fs.watch-based hot reload of external edits.
- Wiring CLI/TUI to default to
getDefaultMcpConfigPath() when no --config is passed — touch when those clients are wired up to v2.
Background
clients/web/src/App.tsx:47currently hardcodes two seed servers (filesystem-server-default,everything-server-default) inSEED_SERVERS, with a note that "Persistence + an 'Add server' UI are explicitly out of scope for #1244." The fouronServerAdd/onServerEdit/onServerClone/onServerRemovecallbacks atApp.tsx:639–646are wired totodoNoop. Time to fix that.The v1.5 port already brought us most of the building blocks:
core/storage/store-io.ts— atomic R/W, ENOENT handling,mkdir -p, 0o600. PlusgetDefaultStorageDir()resolving~/.mcp-inspector/storage/.core/mcp/node/config.ts—loadMcpServersConfigparses the canonical{ mcpServers: { ... } }format and normalizes server types (missing →stdio,http→streamable-http).core/mcp/remote/node/server.ts— Hono backend with the/api/storage/:storeIdroute as a clear template for new endpoints.Scope
Persist the server list to
~/.mcp-inspector/mcp.json(Windows:%USERPROFILE%\.mcp-inspector\mcp.json), in the canonical{ mcpServers: { ... } }format so the file is interoperable with Claude Desktop / Cursor / Cline and hand-editable. Full CRUD in one issue (read → write → UI dialogs).Full design and rationale in
specification/v2_servers_file.md.Backend (core)
getDefaultMcpConfigPath()tocore/storage/store-io.ts(sibling ofgetDefaultStorageDir()).core/mcp/serverList.ts: pure convertersmcpConfigToServerEntries/serverEntriesToMcpConfig, plusDEFAULT_SEED_CONFIG(the two current seeds). Re-exportnormalizeServerTypefromcore/mcp/node/config.ts.core/mcp/remote/node/server.ts:GET /api/servers— returnsMCPConfig; writesDEFAULT_SEED_CONFIGto disk if file absent.POST /api/servers— body{ id, config }; 409 on duplicate id.PUT /api/servers/:id— body{ id?, config }; supports id rename.DELETE /api/servers/:id— idempotent.validateStoreId(alphanum + hyphen + underscore).RemoteServerOptions.mcpConfigPath?: stringdefaulting togetDefaultMcpConfigPath().Frontend
core/react/useServers.ts: returns{ servers, loading, error, refresh, addServer, updateServer, removeServer }. Calls the new endpoints with the existingx-mcp-remote-authtoken.clients/web/src/App.tsx: dropSEED_SERVERS; wire touseServers; connect the four todo-noop server callbacks to the hook's mutators. On remove of the active server, disconnect first. On rename of the active server, updateactiveServerId.Modaldialogs perAGENTS.md's React rules (subcomponent constants via.withProps(), theme variants insrc/theme/, no inline styles, no hex literals).Tests
clients/web/src/test/core/mcp/serverList.test.ts,clients/web/src/test/core/storage/getDefaultMcpConfigPath.test.ts.clients/web/src/test/integration/mcp/remote/servers-route.test.ts(realcreateRemoteAppagainst a tmpmcpConfigPath),clients/web/src/test/integration/react/useServers.test.tsx(hook against a real Hono instance).Out of scope (separate follow-ups)
fs.watch-based hot reload of external edits.getDefaultMcpConfigPath()when no--configis passed — touch when those clients are wired up to v2.