Thanks for the clarifications. I read the document you added and it looks good to me. One thing I'm not sure about is the use of window/workDoneProgress/create and $/progress. I think the server should notify the client using build/taskStart, build/taskProgress, build/taskFinish. Where the type of tasks that can be reported is not limited to build-related tasks (see Build Server Protocol | Build Server Protocol). The LSP can then propagate these notifications to the editor using window/workDoneProgress/create and $/progress. See scala.meta.internal.metals.clients.language.ForwardingMetalsBuildClient in Scala Metals for an example.
Regarding file watching in Metals, I think I might have misunderstood their approach. I browsed the code and it looks like the client (Metals LSP) doesn't directly send file change notifications to the server. It merely invokes compilation for affected targets when scala.meta.internal.metals.MetalsLspService#didChangeWatchedFiles is invoked in the client. I think the approach you took with workspace/didChangeWatchedFiles is better.