{"record":{"id":"c1a432963e612acc","repo":"charmbracelet/crush","slug":"oauth-client-secret-w","errorCode":null,"errorMessage":"oauth_client_secret: %w","messagePattern":"oauth_client_secret: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/agent/tools/mcp/init.go","lineNumber":1097,"sourceCode":"\t\t\t\t\tslog.Warn(\"Failed to persist MCP OAuth token\", \"name\", name, \"error\", err)\n\t\t\t\t} else {\n\t\t\t\t\tslog.Info(\"Persisted MCP OAuth token\", \"name\", name)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// A pre-registered client is required for servers that do not\n\t\t\t// support dynamic client registration (e.g. GitHub, Slack).\n\t\t\t// Resolve the credentials through the shell like other config\n\t\t\t// values so $VAR and $(cmd) work.\n\t\t\tvar preregistered *oauth.OAuthClient\n\t\t\tif strings.TrimSpace(m.OAuthClientID) != \"\" {\n\t\t\t\tclientID, err := resolver.ResolveValue(m.OAuthClientID)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, nil, fmt.Errorf(\"oauth_client_id: %w\", err)\n\t\t\t\t}\n\t\t\t\tclientSecret, err := resolver.ResolveValue(m.OAuthClientSecret)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, nil, fmt.Errorf(\"oauth_client_secret: %w\", err)\n\t\t\t\t}\n\t\t\t\tpreregistered = &oauth.OAuthClient{\n\t\t\t\t\tClientID:     strings.TrimSpace(clientID),\n\t\t\t\t\tClientSecret: strings.TrimSpace(clientSecret),\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Normalize trailing slash for PRM discovery compatibility.\n\t\t\tnormalizedURL := strings.TrimSuffix(url, \"/\")\n\t\t\toauthHandler, oauthErr := mcpoauth.NewHandler(name, normalizedURL, m.OAuthToken, preregistered, tokenSaver, mcpoauth.IsInteractive(ctx), m.OAuthCallbackPort)\n\t\t\tif oauthErr != nil {\n\t\t\t\treturn nil, nil, fmt.Errorf(\"failed to create OAuth handler for mcp %q: %w\", name, oauthErr)\n\t\t\t}\n\t\t\tauthURLs.Set(name, oauthHandler)\n\t\t\treturn &mcp.StreamableClientTransport{\n\t\t\t\tEndpoint:     url,\n\t\t\t\tOAuthHandler: oauthHandler,\n\t\t\t}, oauthHandler, nil","sourceCodeStart":1079,"sourceCodeEnd":1115,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/agent/tools/mcp/init.go#L1079-L1115","documentation":"Same resolution path as the client ID but for oauth_client_secret: the value is resolved through the shell resolver and failures are wrapped as 'oauth_client_secret: <cause>'.","triggerScenarios":"OAuth MCP config where OAuthClientSecret contains an unresolvable $VAR or a failing $(cmd) substitution when resolver.ResolveValue is called.","commonSituations":"Secret not present in the environment (CI missing secret injection); secret-manager CLI (e.g. op read, 1password) not installed; permission errors making the fetch command fail.","solutions":["Inspect the wrapped cause for the failing variable or command","Inject the secret into the environment (export/CI secrets) before running","Test the $(cmd) secret command standalone to confirm it outputs the secret","If using dynamic client registration, drop the pre-registered secret entirely"],"exampleFix":"// before\nmcp api type http url '...' oauth oauth_client_secret '$(op read vault/secret)'  # op not on PATH\n// after\ninstall the 1Password CLI or use: oauth_client_secret '$SECRET_VALUE'","handlingStrategy":"validation","validationCode":"sec := os.Getenv(\"OAUTH_CLIENT_SECRET\")\nif strings.TrimSpace(sec) == \"\" {\n    return errors.New(\"OAUTH_CLIENT_SECRET must be exported before starting crush\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Inject secrets via the environment (never commit them to config)","Test the secret-fetch command standalone and confirm exit code 0","Ensure CI secret injection runs before launching crush","Fall back to dynamic client registration to avoid static secrets"],"tags":["mcp","oauth","secrets","variable-resolution"],"backgroundTag":"unresolved-config-variable","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}