charmbracelet/crush · error

failed to refresh MCP tools: status code %d

Error message

failed to refresh MCP tools: status code %d

What it means

RefreshMCPTools expects the server to answer 200 OK after refreshing MCP tools. Any other status (4xx/5xx) produces 'failed to refresh MCP tools: status code %d'. The body is not read, so the status number is the only diagnostic the client surfaces.

Source

Thrown at internal/client/config.go:299

	}
	defer rsp.Body.Close()
	if rsp.StatusCode != http.StatusOK {
		return fmt.Errorf("failed to disable docker MCP: status code %d", rsp.StatusCode)
	}
	return nil
}

// RefreshMCPTools refreshes tools for a named MCP server.
func (c *Client) RefreshMCPTools(ctx context.Context, id, name string) error {
	rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/mcp/refresh-tools", 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 tools: %w", err)
	}
	defer rsp.Body.Close()
	if rsp.StatusCode != http.StatusOK {
		return fmt.Errorf("failed to refresh MCP tools: status code %d", rsp.StatusCode)
	}
	return nil
}

// ReadMCPResource reads a resource from a named MCP server.
func (c *Client) ReadMCPResource(ctx context.Context, id, name, uri string) ([]MCPResourceContents, error) {
	rsp, err := c.post(ctx, fmt.Sprintf("/workspaces/%s/mcp/read-resource", id), nil, jsonBody(struct {
		Name string `json:"name"`
		URI  string `json:"uri"`
	}{Name: name, URI: uri}), http.Header{"Content-Type": []string{"application/json"}})
	if err != nil {
		return nil, fmt.Errorf("failed to read MCP resource: %w", err)
	}
	defer rsp.Body.Close()
	if rsp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("failed to read MCP resource: status code %d", rsp.StatusCode)
	}
	var contents []MCPResourceContents

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Check the status code: 401/403 → re-authenticate; 404 → verify workspace ID and server version; 5xx → check server logs.
  2. Ensure the workspace is running before refreshing tools.
  3. Upgrade/downgrade the client to match the server's API version.
  4. Retry after the MCP server is confirmed healthy.

Example fix

// before
err := client.RefreshMCPTools(ctx, id, name)
// after
if err := client.RefreshMCPTools(ctx, id, name); err != nil {
    if strings.Contains(err.Error(), "status code 4") {
        // re-authenticate then retry
        if err := login(ctx); err != nil { return err }
        err = client.RefreshMCPTools(ctx, id, name)
    }
    if err != nil { return err }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// confirm workspace is running and auth is valid beforehand
ws, err := client.Workspace(ctx, workspaceID)
if err != nil || ws.LatestBuild.Status != "running" {
    return errors.New("workspace must be running before refreshing MCP tools")
}

Try / catch

if err := client.RefreshMCPTools(ctx, id, name); err != nil {
    var code int
    if n, _ := fmt.Sscanf(err.Error(), "failed to refresh MCP tools: status code %d", &code); n == 1 {
        switch {
        case code == 401 || code == 403:
            // re-authenticate and retry
        case code == 404:
            // wrong workspace/name — surface to user
        default:
            // retry server-side (5xx) with backoff
        }
    }
    return err
}

Prevention

When it happens

Trigger: Server returns non-200 to POST /workspaces/{id}/mcp/refresh-tools: 401/403 (bad or expired session token), 404 (wrong workspace ID or server version lacking the endpoint), 500 (server failed to reach the MCP server), 409/423 (workspace not running).

Common situations: Session cookie/API key expired; workspace stopped or building when tools were refreshed; older Coder server without the refresh-tools route; MCP server offline so the backend returns 500.

Related errors


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