dispatchOnServerThread

private fun dispatchOnServerThread(dispatch: Runnable): String?

Hand dispatch to the server thread, wait for it to run, then hold the capture window open. Split from run because scheduling is not command semantics: everything here is about GETTING to the server thread, and none of it varies with what the commands are.

Return

null once the window has closed, or the [mcp]-prefixed reason it did not — run returns that verbatim, which is how its "never throws" contract survives the split.