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

  1. Read the server message after the colon — it states the exact application-level rejection reason
  2. Verify the MCP name matches a server configured on that workspace and the workspace id is valid
  3. Check/refresh the client's authentication credentials and permissions for the workspace
  4. 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

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

Related errors


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/6da226d860cae3b7. Report an issue: GitHub.