Repository navigation
Description
Problem
Raised by Copilot on the v2.9.0 milestone-merge PR (#2536, review comment), and confirmed in the code.
#2481 moved acquired OAuth tokens out of the OAuth state file (oauth.json) and into the secret store. The secret store is not scoped by which state file is in use:
- Its location ignores
MCP_INSPECTOR_OAUTH_STATE_PATH. It is the OS keychain by default, one per user. The file backend isMCP_INSPECTOR_SECRET_FILE, else$MCP_STORAGE_DIR/secrets.json, else~/.mcp-inspector/secrets.json(core/auth/node/secret-store-selection.ts:126-142). - Its entries are keyed only by server URL or issuer (
oauthSecretServerId→oauth+<encoded url>,core/auth/node/oauth-secrets.ts:78).
So two CLI/TUI profiles that point MCP_INSPECTOR_OAUTH_STATE_PATH at different state files, but connect to the same server, now share one secret-store entry. When profile B logs in, it overwrites profile A's tokens. Profile A's state file is then joined with B's credentials, so A silently acts as B's identity. Before #2481, tokens lived inside each state file, so profiles were isolated. This is a regression from #2481.
Who is affected
Only people who set MCP_INSPECTOR_OAUTH_STATE_PATH to keep separate OAuth profiles against the same server. The default single-profile setup is unaffected.
docs/cli-smoke-testing.md recommends MCP_STORAGE_DIR + MCP_INSPECTOR_OAUTH_STATE_PATH for isolation. With the default keychain backend, that setup now shares tokens with the user's real keychain entries for the same server URL.
Workaround (until fixed)
Give each profile its own secret store as well as its own state file:
export MCP_INSPECTOR_SECRET_STORE=file
export MCP_INSPECTOR_SECRET_FILE=/path/to/profile-a/secrets.json # distinct per profileA per-profile MCP_STORAGE_DIR, with MCP_INSPECTOR_SECRET_STORE=file, also works.
Fix
Namespace secret IDs by the persistence context, for example a stable hash of the resolved state-file path when it is not the default, with a migration for existing unscoped IDs, or otherwise restore per-state-file isolation. Constraints:
- IDs must stay colon-free and must not prefix-collide. The keyring store's
deleteAllForServerparses accounts at the first colon (see the comment aboveoauthSecretServerId). - The default single-profile path must keep its current IDs, or migrate them transparently, so existing users are not logged out.
Update docs/cli-smoke-testing.md (and docs/environment-variables.md) in the same change so that the documented isolation recipe is isolated again.
Acceptance
- Two profiles with different
MCP_INSPECTOR_OAUTH_STATE_PATHvalues against one server keep separate tokens, on both the keyring and file backends - Existing default-profile tokens survive the upgrade (migration tested)
- The smoke-testing isolation recipe is correct as documented
Activity
cliffhall commentedon Sep 30, 2026
Triage: Priority High (total 9)
buglabel, +1 milestoned (v2.10.0)Board: #28, Status
Todo.BobDickinson commentedon Oct 2, 2026
Shipped in #2556 (merged to v2/main as 4930bb1).
Scope secret-store entries by a per-state-file namespace