Summary
InspectorServerSettings.oauthClientSecret currently persists in plaintext inside ~/.mcp-inspector/mcp.json. The file is 0o600, which is appropriate for a local dev tool, but the secret is now mixed into a file users may:
- Symlink to / from Claude Desktop's
claude_desktop_config.json (the spec calls this out as an interop benefit; the file is meant to be tool-shareable).
cat for debugging, paste into bug reports, sync via dotfile managers, or commit by accident.
Goal: lift oauthClientSecret out of the on-disk JSON and store it in the OS keychain (macOS Keychain Services, Windows Credential Manager, Linux libsecret) keyed by (servers map key, "oauth-client-secret"). The mcp.json settings.oauthClientSecret field becomes a reference (or is absent) once a value has been promoted to the keychain.
Files
core/mcp/types.ts — InspectorServerSettings shape; may need a discriminated oauthClientSecret: { stored: "keychain" } or similar.
core/mcp/remote/node/server.ts — /api/servers POST/PUT/DELETE need to read/write the keychain alongside the on-disk JSON.
core/mcp/serverList.ts — converter that hydrates the secret from the keychain into ServerEntry.settings.oauthClientSecret for in-memory use (browser never sees the raw value; it only sees a flag "secret configured").
clients/web/src/components/groups/ServerSettingsForm/ServerSettingsForm.tsx — the Client Secret field becomes write-only (show "Configured" placeholder, "Clear" button) rather than echoing the value.
specification/v2_servers_file.md — document the keychain integration; explain that other tools reading the file won't see the secret.
Proposal
Recommended library: keytar (mature, cross-platform). Adds a native dep.
- New helper
core/auth/secret-store.ts with getSecret(serverId, field) / setSecret(serverId, field, value) / deleteSecret(serverId, field) backed by keytar.
/api/servers POST/PUT: when body contains settings.oauthClientSecret, write to keychain and drop the field from disk.
/api/servers GET: rehydrate from keychain on read so useServers sees the secret on the active session.
/api/servers DELETE: tombstone keychain entries for that server id.
- Migration: on first read of a
mcp.json that contains a oauthClientSecret value, lift to keychain and overwrite the file with the field removed.
Out of scope / open questions
- Linux without libsecret installed: keytar falls back to in-memory. Document.
- Tests in CI: keytar requires platform native bindings; integration tests need a mock backend (keytar provides
setPassword/getPassword interfaces that are mockable).
- Whether to extend the same treatment to stdio
env values (which are also secrets in many setups). Argue separately.
Acceptance criteria
- A
oauthClientSecret entered in ServerSettingsForm is persisted to the OS keychain, not to mcp.json.
cat ~/.mcp-inspector/mcp.json does not show the secret.
- Re-opening the settings modal shows "Configured" rather than the raw value.
- Removing the server (DELETE) also removes the keychain entry.
- Existing
mcp.json files with oauthClientSecret in plaintext are migrated to the keychain on first read.
Summary
InspectorServerSettings.oauthClientSecretcurrently persists in plaintext inside~/.mcp-inspector/mcp.json. The file is0o600, which is appropriate for a local dev tool, but the secret is now mixed into a file users may:claude_desktop_config.json(the spec calls this out as an interop benefit; the file is meant to be tool-shareable).catfor debugging, paste into bug reports, sync via dotfile managers, or commit by accident.Goal: lift
oauthClientSecretout of the on-disk JSON and store it in the OS keychain (macOS Keychain Services, Windows Credential Manager, Linux libsecret) keyed by(servers map key, "oauth-client-secret"). The mcp.jsonsettings.oauthClientSecretfield becomes a reference (or is absent) once a value has been promoted to the keychain.Files
core/mcp/types.ts—InspectorServerSettingsshape; may need a discriminatedoauthClientSecret: { stored: "keychain" }or similar.core/mcp/remote/node/server.ts—/api/serversPOST/PUT/DELETE need to read/write the keychain alongside the on-disk JSON.core/mcp/serverList.ts— converter that hydrates the secret from the keychain intoServerEntry.settings.oauthClientSecretfor in-memory use (browser never sees the raw value; it only sees a flag "secret configured").clients/web/src/components/groups/ServerSettingsForm/ServerSettingsForm.tsx— theClient Secretfield becomes write-only (show "Configured" placeholder, "Clear" button) rather than echoing the value.specification/v2_servers_file.md— document the keychain integration; explain that other tools reading the file won't see the secret.Proposal
Recommended library:
keytar(mature, cross-platform). Adds a native dep.core/auth/secret-store.tswithgetSecret(serverId, field)/setSecret(serverId, field, value)/deleteSecret(serverId, field)backed by keytar./api/serversPOST/PUT: when body containssettings.oauthClientSecret, write to keychain and drop the field from disk./api/serversGET: rehydrate from keychain on read souseServerssees the secret on the active session./api/serversDELETE: tombstone keychain entries for that server id.mcp.jsonthat contains aoauthClientSecretvalue, lift to keychain and overwrite the file with the field removed.Out of scope / open questions
setPassword/getPasswordinterfaces that are mockable).envvalues (which are also secrets in many setups). Argue separately.Acceptance criteria
oauthClientSecretentered inServerSettingsFormis persisted to the OS keychain, not tomcp.json.cat ~/.mcp-inspector/mcp.jsondoes not show the secret.mcp.jsonfiles withoauthClientSecretin plaintext are migrated to the keychain on first read.