In order for the mux to work, each TSR had to have its own identifier code. Other than some reserved ranges, no one organized such a namespace, meaning it was possible for two or more TSRs to intercept the same request, leading to the same collision problem.
This note from Ralf Brown's Interrupt List has the gory details:
http://www.ctyme.com/intr/rb-4251.htm
Incomplete list of TSRs and drivers relying on it:
Int 2F is initially handled by DOS as a stub, but additional programs (like drivers and TSRs) can override INT 2F, put their bucket of functionality and then fallback to whatever the previous installed handler was (called chaining) for whatever they don't handle.
This gives a glimpse into how much various crap could end up installed as an Int 2F handler: https://www.minuszerodegrees.net/websitecopies/Linux.old/doc...
It was often used for feature/presence checks and usually nothing time critical as that chaining setup was most definitely not timing friendly.
Lot's of crap in INT 21 too: https://fd.lod.bz/rbil/zint/index_21.html (on the whole I like this HTMLified version of Ralf Brown's Interrupt List better)
But I suppose they invented INT 2F to discourage people from doing that to INT 21.
And then Ralf Brown also proposed an alternative multiplex interrupt, INT 2D: https://fd.lod.bz/rbil/interrup/tsr/2d.html#4258
Overriding software interrupt handlers was a common mechanism, applied to BIOS interrupts as well (e.g. there was a BIOS timer interrupt that was there just for that).
The idea was that programs would take over the interrupt vector then chain-call the previous handlers. One can still see something similar at play in certain scenarios with e.g. callbacks. The system/library doesn't have to maintain a list of clients.