charmbracelet/crush · error
failed to authenticate MCP: %s
Error message
failed to authenticate MCP: %s
What it means
This error is returned by Client.MCPAuthenticate when the server responds with a non-200 status code AND the response body successfully decodes into proto.Error with a non-empty Message. The server's error message is surfaced verbatim: "failed to authenticate MCP: <server message>". It indicates the request reached the server but the server rejected the MCP authentication request for an application-level reason.
Source
Thrown at internal/client/proto.go:384
}
// MCPAuthenticate runs the OAuth flow for a named MCP server. The server's
// local browser is suppressed; the caller is responsible for surfacing the
// authorization URL (via polling [Client.MCPPendingAuth] / state events)
// and opening it on the user's machine. The call blocks until the flow
// completes, fails, or ctx is cancelled.
func (c *Client) MCPAuthenticate(ctx context.Context, id, name string) error {
rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/mcp/auth", id), nil,
jsonBody(proto.MCPNameRequest{Name: name}),
http.Header{"Content-Type": []string{"application/json"}})
if err != nil {
return fmt.Errorf("failed to authenticate MCP: %w", err)
}
defer rsp.Body.Close()
if rsp.StatusCode != http.StatusOK {
var e proto.Error
if err := json.NewDecoder(rsp.Body).Decode(&e); err == nil && e.Message != "" {
return fmt.Errorf("failed to authenticate MCP: %s", e.Message)
}
return fmt.Errorf("failed to authenticate MCP: status code %d", rsp.StatusCode)
}
return nil
}
// MCPRefreshPrompts refreshes prompts for a named MCP client.
func (c *Client) MCPRefreshPrompts(ctx context.Context, id, name string) error {
rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/mcp/refresh-prompts", id), nil,
jsonBody(struct {
Name string `json:"name"`
}{Name: name}),
http.Header{"Content-Type": []string{"application/json"}})
if err != nil {
return fmt.Errorf("failed to refresh MCP prompts: %w", err)
}
defer rsp.Body.Close()
if rsp.StatusCode != http.StatusOK {View on GitHub (pinned to 7944b8e522)
Solutions
- Read the server message after the colon — it states the exact application-level rejection reason
- Verify the MCP name matches a server configured on that workspace and the workspace id is valid
- Check/refresh the client's authentication credentials and permissions for the workspace
- Retry after correcting the configuration; if it is a 401/403, re-authenticate or request access
Example fix
// before name := "filesytem" // typo, MCP not registered on workspace err := client.MCPAuthenticate(ctx, workspaceID, name) // after name := "filesystem" // must match the MCP server name registered on the workspace err := client.MCPAuthenticate(ctx, workspaceID, name)
Defensive patterns
Strategy: validation
Validate before calling
// Validate inputs and session before calling MCPAuthenticate
func canAuthenticate(id, name, token string) error {
if id == "" { return errors.New("workspace id is required") }
if name == "" { return errors.New("mcp name is required") }
if token == "" { return errors.New("api token missing; log in first") }
if expiry, err := tokenExpiry(token); err == nil && time.Now().After(expiry) {
return errors.New("api token expired; re-authenticate")
}
return nil
} Type guard
// Distinguish a server-rejected auth from other failures by message shape
func isServerAuthRejection(err error) bool {
if err == nil { return false }
msg := err.Error()
if !strings.HasPrefix(msg, "failed to authenticate MCP: ") { return false }
rest := strings.TrimPrefix(msg, "failed to authenticate MCP: ")
return !strings.HasPrefix(rest, "status code ") && !strings.Contains(rest, ": ")
} Try / catch
// Inspect the surfaced server message and branch on the cause
if err := client.MCPAuthenticate(ctx, id, name); err != nil {
msg := err.Error()
switch {
case strings.Contains(msg, "401") || strings.Contains(strings.ToLower(msg), "unauthorized"):
return reauthenticateAndRetry(ctx, id, name)
case strings.Contains(strings.ToLower(msg), "not found"):
return fmt.Errorf("mcp %q not registered on workspace %s: %w", name, id, err)
default:
return err
}
} Prevention
- Confirm the MCP name matches exactly a server registered on the workspace (watch for typos/case)
- Log in or refresh the API token before batch MCP operations
- Verify workspace ids against a listing call instead of hardcoding them
- Check workspace permissions for the authenticated user before attempting MCP auth
When it happens
Trigger: Calling MCPAuthenticate(ctx, id, name) and receiving e.g. 400 Bad Request (unknown MCP name or malformed request), 401/403 (missing/invalid credentials or insufficient permissions on the workspace), or 404 (workspace id or named MCP client not found), with the server including a JSON error body {"message": "..."}.
Common situations: MCP client name not registered on the workspace; user token lacking admin rights to authenticate MCP servers; workspace id pointing to a deleted or foreign workspace; stale API token after rotation.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- failed to authenticate MCP: status code %d
- failed to refresh MCP prompts: status code %d
- unexpected status code: %d
- mcp http config requires a non-empty 'url' field
- failed to create OAuth handler for mcp %q: %w
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/6da226d860cae3b7.
Report an issue: GitHub.