pi-mcp-adapter stores OAuth tokens in plaintext — is there a keychain fix?
pi-mcp-adapter writes OAuth tokens to a plaintext JSON file under the configured OAuth directory, so any process running as the same user can read them — including access/refresh tokens and dynamic client secrets. On macOS, Claude Code itself uses the encrypted keychain, which makes the plaintext file approach a security gap. Is there a supported way to store these credentials in the OS keychain, and what happens to existing tokens.json files after upgrading?
Originally asked on GitHub: https://github.com/nicobailon/pi-mcp-adapter/issues/180
4 answers
Fixed. The maintainer confirmed the report and shipped the fix in commit
fc5a86a8b9b6ca00026d1d80d09f309c802a1815: OAuth entries now persist through the OS credential store via@napi-rs/keyringinstead of plaintexttokens.jsonfiles. If the OS credential store is unavailable, OAuth storage fails closed with a clear error — there is no plaintext fallback.What the fix does:
- Stored payload covers tokens, dynamic client info, legacy PKCE verifier/state fields when present, and the server URL binding.
- Existing plaintext entries under the old
oauthDir/MCP_OAUTH_DIRpaths are imported once and then removed, sooauthDiris now a legacy import location, not a credential namespace. - Docs were updated in
README.md,OAUTH.md, and the TypeScript setting docs.
Validation performed by the maintainer:
shnpx tsc --noEmit npm test npm run test:oauth npm run test:oauth-provider npm run test:conformance node --import tsx <secure-store smoke test> npm pack --dry-run git diff --checkA follow-up security/correctness review found no blockers. Security note: before this fix, access/refresh tokens and any dynamic client secret were readable by any process running as the current user — treat any old
tokens.jsonfile as a secret; the fix removes it after the one-time import. As general advice, if you run a build older than that commit, rotate tokens after upgrading.Did this work for you?What a GitHub user tried on GitHub (Outcome unclear)
The reporter described how OAuth tokens are always persisted as plaintext JSON under the OAuth directory, with file/directory permissions as the only protection, and cited Claude Code's macOS keychain storage as a more secure model.
Source: https://github.com/nicobailon/pi-mcp-adapter/issues/180 (opens external site)
Did this work for you?What @nicobailon tried on GitHub (Outcome unclear)
The maintainer traced the code path and confirmed the plaintext behavior, including access/refresh tokens and dynamic client secrets. He proposed a storage design covering macOS Keychain, Windows Credential Manager, Linux Secret Service/libsecret, headless/SSH/CI behavior, migration of existing tokens.json files, and fail-closed policy, explicitly rejecting a quick 'wrap the existing JSON file' patch.
Did this work for you?What @nicobailon tried on GitHub (Worked)
The maintainer implemented the fix in commit fc5a86a8b9b6ca00026d1d80d09f309c802a1815 using @napi-rs/keyring: OS credential store persistence, one-time import and removal of legacy plaintext entries, and fail-closed behavior when the OS store is unavailable. It passed tsc, npm test, the OAuth/conformance suites, a secure-store smoke test, and
npm pack --dry-run.Did this work for you?
Related questions
- pi-mcp-adapter: per-server override in ~/.pi/agent/mcp.json wipes imported command/url
- How do I hot-load MCP direct tools in pi-mcp-adapter without reloading Pi?
- pi-mcp-adapter: compact TUI rendering for direct MCP tools?
- pi-mcp-adapter: huge MCP output hangs session, Esc can't abort — fixed upstream?
- Show which MCP tool is actually called when pi-mcp-adapter outputs only `mcp OK`