{"record":{"id":"7b28c575b20df631","repo":"charmbracelet/crush","slug":"oauth-token-source-w","errorCode":null,"errorMessage":"oauth token source: %w","messagePattern":"oauth token source: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/agent/tools/mcp/init.go","lineNumber":1241,"sourceCode":"\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden {\n\t\tif authErr := rt.handler.Authorize(req.Context(), req, resp); authErr != nil {\n\t\t\treturn resp, nil\n\t\t}\n\t\tresp.Body.Close()\n\t\treturn rt.doRequestWithToken(req.Clone(req.Context()))\n\t}\n\n\treturn resp, nil\n}\n\nfunc (rt *oauthRoundTripper) doRequestWithToken(req *http.Request) (*http.Response, error) {\n\tts, err := rt.handler.TokenSource(req.Context())\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"oauth token source: %w\", err)\n\t}\n\tif ts != nil {\n\t\ttoken, err := ts.Token()\n\t\tif err == nil && token != nil {\n\t\t\treq.Header.Set(\"Authorization\", \"Bearer \"+token.AccessToken)\n\t\t}\n\t}\n\treturn rt.base.RoundTrip(req)\n}\n\nfunc mcpTimeout(m config.MCPConfig) time.Duration {\n\tif m.Timeout > 0 {\n\t\treturn time.Duration(m.Timeout) * time.Second\n\t}\n\t// OAuth flows require user interaction in a browser, so use a\n\t// generous default to avoid timing out mid-auth.\n\tif m.OAuth {\n\t\treturn 30 * time.Second","sourceCodeStart":1223,"sourceCodeEnd":1259,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/agent/tools/mcp/init.go#L1223-L1259","documentation":"The oauthRoundTripper injects bearer tokens into SSE HTTP requests. Before sending, it asks the OAuth handler for a TokenSource; if that call errors (handler closed, no authorization performed, discovery failure), the error is wrapped as `oauth token source: ...` and the request is not sent.","triggerScenarios":"An SSE MCP request flows through the round-tripper while the OAuth handler cannot produce a token source: the MCP server was never authorized (`/mcp` login not done), the handler's authorization metadata is missing, or the context is cancelled mid-flow.","commonSituations":"Server requires OAuth but Crush was started non-interactively so no browser flow ran; saved token revoked server-side leaving the handler unable to build a source; rapid requests racing handler shutdown during session teardown.","solutions":["Run the MCP OAuth login flow (e.g. `/mcp` in the TUI) to authorize the server, then retry","Clear the stale `mcp.<name>.oauth_token` from global config and re-authenticate","Verify the server advertises OAuth metadata (/.well-known/oauth-authorization-server) if you expect token-based auth","Alternatively disable oauth for the server and pass an Authorization header directly via headers"],"exampleFix":"// before: no token available\nmcp acme {\n  type sse\n  url \"https://acme.com/sse\"\n  oauth true\n}\n// after: either login first, or use a static header\nmcp acme {\n  type sse\n  url \"https://acme.com/sse\"\n  headers {\n    Authorization \"Bearer $ACME_TOKEN\"\n  }\n}","handlingStrategy":"try-catch","validationCode":"// ensure the server has been authorized before issuing calls\ntok, _ := loadSavedToken(name)\nif tok == nil || tok.AccessToken == \"\" {\n    return fmt.Errorf(\"run the OAuth login for MCP server %q first\", name)\n}","typeGuard":"func hasUsableToken(tok *oauth.Token) bool {\n    return tok != nil && tok.AccessToken != \"\"\n}","tryCatchPattern":"resp, err := runMCPTool(ctx, name, tool, args)\nif err != nil && strings.Contains(err.Error(), \"oauth token source\") {\n    if reauthErr := mcpLogin(ctx, name); reauthErr == nil {\n        resp, err = runMCPTool(ctx, name, tool, args) // retry once after auth\n    }\n}","preventionTips":["Complete the /mcp OAuth login before scripted use of OAuth servers","Fall back to static Authorization headers when OAuth metadata is absent","Monitor for 401s and trigger re-auth proactively"],"tags":["mcp","oauth","sse","http"],"backgroundTag":"oauth-token-unavailable","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}