Sometimes the best debugging sessions end not with a triumphant commit, but with a quiet git reset --hard.
Tonight was one of those nights.
The Dream
The vision was simple: unlock lifelog from the desktop. Let Claude on mobile—on the train, in the kitchen while the sourdough proofs, in bed before sleep—whisper tasks into the void, have them land in the timeline. No laptop required. Just voice to Claude to lifelog to memory.
The path seemed clear: OAuth 2.1 for claude.ai’s MCP connector. We already had OAuth working for Claude Desktop. How different could the web client be?
Very, it turns out. Very different.
The Descent
We began with confidence. The OAuth server was there—authorization endpoint, token endpoint, PKCE support, dynamic client registration. All the RFC-compliant machinery humming along.
But claude.ai spoke a slightly different dialect.
First clue: the logs showed requests to /api/mcp. — note the trailing dot. A typo? No, just me being careless with the URL input. Fixed.
Second clue: requests to /.well-known/oauth-protected-resource/api/mcp. Claude was concatenating the resource metadata URL with the MCP path. Not wrong per se—creative, even—but unexpected. We adapted. Added handlers for the weird paths.
Third clue, and this was the one that mattered: the OAuth flow completed. Tokens were issued. Beautiful, valid, 7200-second-lived tokens. We could see them in the logs:
[OAuth] Token response (status=200): {"access_token":"N2JMNDY1ND...","token_type":"Bearer"}
[AUTH] Found bearer token (prefix: N2JMNDY1ND...)
[OAuth] Token valid for user: [email protected]
[AUTH] OAuth access token authenticated: [email protected]
Three authenticated requests. The handshake happened. And then—
Silence.
Fifteen seconds later, a new request. Without a token. The cycle began again.
The Wall
We compared our implementation with mcp-oauth2-proxy, the reference implementation that claims to work with claude.ai. Aligned our metadata formats. Added the resource field. Added scopes_supported. Added response_modes_supported. Matched their authorization_servers structure exactly.
We searched GitHub issues. Found discussions about OAuth not working. Found bugs about infinite loops. Found a pattern: the MCP OAuth support in claude.ai is… young. Still finding its legs.
The tokens were valid. Our server was correct. But somewhere in the client—in the machinery we cannot see or debug—the token was obtained and then… not used. Or used once and forgotten. Or sent to the wrong endpoint in a parallel universe where /.well-known/oauth-protected-resource/mcp is a thing.
The Wisdom
There’s a moment in every debugging session where you have to ask: is this mine to fix?
Tonight, the answer was no.
The OAuth server is better for the journey—cleaner metadata, proper WWW-Authenticate headers, handlers for claude.ai’s URL creativity. The groundwork is laid. When Anthropic fixes their client, we’ll be ready.
But tonight, the fermentation experiments will not be logged as Greek tragedies. Oscar’s pouts will not become Divine Comedy. The sourdough starter’s heroic rise and fall will go unwitnessed by Claude on mobile.
Tonight, we git stash and we sleep.
What Remains
The good news: Claude Code CLI still works. The bearer token authentication is untouched. The MCP server responds to tools/list and tools/call with the same reliability it always has.
The dream isn’t dead—just deferred. Sometimes the APIs are buggier than we wish. Sometimes the documentation is a treasure map where X marks a spot that moved. Sometimes the reference implementation works for reasons that aren’t in the code.
And sometimes, you write it down. Because even the failures are part of becoming.
Next time on Becoming Lifelog: we either wait for Anthropic to fix their OAuth client, or we find another path to mobile. The sourdough demands documentation.
See also:
The Saga (in which authentication humbles us all):
- The Phantom Users - When multi-user auth worked, before OAuth got ambitious
- The V3 Saga Continues Credentials & The Navigation Puzzle - Another credentials journey, different labyrinth
- The Schrödinger IP - Sometimes the API has the answer, just not where you expect
The References (OAuth rabbit holes validated):
- MCP OAuth Discussion #587 - Where others found the same wall
- OAuth 2.1 Spec - The standard we implemented correctly
- PKCE RFC 7636 - The proof key that proved nothing when the client forgets
