{"record":{"id":"4161c27f87d68b48","repo":"charmbracelet/crush","slug":"failed-to-get-mcp-auth-url-status-code-d","errorCode":null,"errorMessage":"failed to get MCP auth URL: status code %d","messagePattern":"failed to get MCP auth URL: status code (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/client/proto.go","lineNumber":359,"sourceCode":"\t}\n\tvar pending []proto.MCPPendingAuthServer\n\tif err := json.NewDecoder(rsp.Body).Decode(&pending); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to decode MCP pending auth: %w\", err)\n\t}\n\treturn pending, nil\n}\n\n// MCPAuthURL retrieves the current OAuth authorization URL for a named MCP\n// server, if a flow is in progress.\nfunc (c *Client) MCPAuthURL(ctx context.Context, id, name string) (string, error) {\n\tq := url.Values{\"name\": []string{name}}\n\trsp, err := c.get(ctx, fmt.Sprintf(\"/workspaces/%s/mcp/auth-url\", id), q, nil)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to get MCP auth URL: %w\", err)\n\t}\n\tdefer rsp.Body.Close()\n\tif rsp.StatusCode != http.StatusOK {\n\t\treturn \"\", fmt.Errorf(\"failed to get MCP auth URL: status code %d\", rsp.StatusCode)\n\t}\n\tvar resp proto.MCPAuthResponse\n\tif err := json.NewDecoder(rsp.Body).Decode(&resp); err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to decode MCP auth URL: %w\", err)\n\t}\n\treturn resp.AuthURL, nil\n}\n\n// MCPAuthenticate runs the OAuth flow for a named MCP server. The server's\n// local browser is suppressed; the caller is responsible for surfacing the\n// authorization URL (via polling [Client.MCPPendingAuth] / state events)\n// and opening it on the user's machine. The call blocks until the flow\n// completes, fails, or ctx is cancelled.\nfunc (c *Client) MCPAuthenticate(ctx context.Context, id, name string) error {\n\trsp, err := c.post(ctx, fmt.Sprintf(\"/workspaces/%s/mcp/auth\", id), nil,\n\t\tjsonBody(proto.MCPNameRequest{Name: name}),\n\t\thttp.Header{\"Content-Type\": []string{\"application/json\"}})\n\tif err != nil {","sourceCodeStart":341,"sourceCodeEnd":377,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/client/proto.go#L341-L377","documentation":"MCPAuthURL expects HTTP 200 from GET /workspaces/{id}/mcp/auth-url. Any other status (401 unauthorized, 404 unknown server name or workspace, 409 no flow in progress, 5xx server fault) is reported as this error with the numeric status embedded. It means the auth-URL lookup was rejected by the server, not that the request failed to send.","triggerScenarios":"Calling MCPAuthURL(ctx, id, name) when the response status is not 200 — e.g. the named MCP server does not exist on the workspace (404), auth token is invalid (401), no OAuth flow has been started for that server (409/4xx), or the server returns 500.","commonSituations":"Querying auth-url for an MCP server name that was never registered or was renamed; calling before MCPAuthenticate initiated a flow; expired session token; hitting a server build that doesn't yet expose this route (404) after a version upgrade.","solutions":["Check the status code in the message: 404 → verify the MCP server name is registered on this workspace; 401 → refresh credentials; 5xx → server-side fault, retry later.","Confirm the exact server name string matches the registered MCP server (names are case-sensitive identifiers).","Start the OAuth flow (authenticate) before polling the auth URL if the server only exposes a URL mid-flow.","Verify client and server versions both support the /mcp/auth-url endpoint."],"exampleFix":"// before\nname := \"GitHub\" // registered as \"github\"\nurl, err := client.MCPAuthURL(ctx, wsID, name) // 404\n\n// after\nservers, err := client.MCPPendingAuth(ctx, wsID)\nif err != nil {\n    return err\n}\nfor _, s := range servers {\n    if s.Name == \"github\" {\n        url, err := client.MCPAuthURL(ctx, wsID, s.Name)\n        ...\n    }\n}","handlingStrategy":"retry","validationCode":"// Ensure the MCP server name exists before asking for its auth URL.\nservers, err := client.MCPPendingAuth(ctx, wsID)\nif err != nil {\n    return err\n}\nknown := false\nfor _, s := range servers {\n    if s.Name == name {\n        known = true\n        break\n    }\n}\nif !known {\n    return fmt.Errorf(\"mcp server %q not registered on workspace %s\", name, wsID)","typeGuard":null,"tryCatchPattern":"url, err := client.MCPAuthURL(ctx, id, name)\nif err != nil {\n    if strings.Contains(err.Error(), \"status code 404\") {\n        return \"\", fmt.Errorf(\"mcp server %q unknown on this workspace: %w\", name, err)\n    }\n    if strings.Contains(err.Error(), \"status code 409\") {\n        return \"\", fmt.Errorf(\"no auth flow in progress for %q; start the flow first: %w\", name, err)\n    }\n    if strings.Contains(err.Error(), \"status code 5\") {\n        return retryWithBackoff(ctx, func() (string, error) { return client.MCPAuthURL(ctx, id, name) })\n    }\n    return \"\", err\n}","preventionTips":["Copy MCP server names from the registration/manifest rather than typing them by hand.","Only poll auth-url while an OAuth flow is active for that server.","Pin compatible client/server versions; new endpoints 404 on older servers.","Differentiate retry policy by status class: retry 5xx, never retry 4xx."],"tags":["http","status-code","mcp","oauth","client"],"backgroundTag":"http-non-200-response","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}