It kind of depends on the lifespan and impact you want to have.
Since you mention starting a server, I guess we can assume that your control process can live outside of that. If you wish to write it in (asynchronous) python, you can take full control of the lifecycle of the server, and keep it in the same event loop with techniques like this dask blog or the deeper gist. That says
NotebookApp all over, but one can replace it with
LabApp and pretty much get the same experience: it’s probably worth revisiting… but maybe I’ll wait until
nbclassic isn’t in there complicating matters.
Once you have a server running, then the REST API is the most predictable, and will correctly fire all the hooks: it takes a bit of messing about to figure out which Manager instances contain the specific things you’d want, from watching the browser traffic.
There aren’t a lot of ways to command a browser to do a lot of things… unless you actually take control of the browser… while not a “real” online solution, there are some examples in e.g. robotframework-jupyterlibrary, raw selenium, or even
galata… but all of these are going to be far more brittle than real APIs.