reduce macOS keychain prompts for OAuth MCP servers#2580
Merged
dgageot merged 3 commits intodocker:mainfrom Apr 28, 2026
Merged
Conversation
Verifies that the mutex protection correctly handles concurrent reads, writes, and deletes without data races. The test exercises 10 goroutines performing 100 operations each across 3 different resource URLs.
aheritier
reviewed
Apr 28, 2026
Contributor
aheritier
left a comment
There was a problem hiding this comment.
LGTM. Tokens will be stored in. a different location after restart but not really worth documenting it
aheritier
approved these changes
Apr 28, 2026
Contributor
aheritier
left a comment
There was a problem hiding this comment.
LGTM. Tokens will be stored in. a different location after restart but not really worth documenting it
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When using OAuth-protected MCP servers, macOS prompted the user for keychain access many times — once per server on first launch, again on every HTTP request that needed to read a token, and yet again on every token refresh. Even after clicking Always Allow, a rebuild (which changes the binary's signature) brought the prompts back.
The root cause was the storage layout: every token was a separate keychain item, and every keychain item carries its own ACL on macOS.
Fix
Store all OAuth tokens for all MCP servers in a single bundled keychain item, cached in memory after the first read.
The store is exposed as a process-wide singleton via
sync.OnceValue, so multiple OAuth-capable toolsets share the same in-memory cache and never reopen the keyring on construction.Backwards compatibility
On first load, any tokens written by the previous one-item-per-token layout are migrated into the bundle, and the legacy entries (including the index) are removed. Migration is best-effort: failures are logged but never block startup.
Public API
Unchanged.
NewKeyringTokenStore,OAuthTokenEntry,ListOAuthTokens, andRemoveOAuthTokenkeep the same signatures, somcp.go,oauth_login.go, andcmd/root/debug_oauth.gowork untouched.Tests
New tests cover:
Getregardless of how many resource URLs are looked up.Setcalls targeting the single bundle item.Validation
go build ./...✓go vet ./...✓go test ./...✓ (full suite, including-race)golangci-lint run ./...✓ (0 issues)Possible follow-up
Code-sign release builds with a stable Developer ID so macOS recognises the binary across upgrades and never re-prompts after Always Allow. Today's dev/ad-hoc-signed builds change identity on every rebuild, which forces another prompt no matter how good the storage layout is. That's outside this PR's scope.