Suppose the client software is trustworthy (after all, if it isn't, how do you know it doesn't send a copy of all your messages somewhere).
Everyone installs the client, it generates a new key pair each time, sends the public key to a directory service with the user's identifier (phone number, FB uid, whatever).
You want to send an encrypted message to me. You ask Signal's directory service (or Whatsapp, or whatever) for my public key. The directory service gives you a public key and says it's mine. You encrypt your message, send it to the chat service and I receive it and decrypt it using my private key. It works!
But, the directory service could have generated a new key pair, given you the public key, then when the chat service received the encrypted message, decrypted it, then encrypted it using my real public key (for which the private really only exists on my device), and sent it to me. We would not notice the small additional latency.
The way to check is to make sure the public key your client thinks is mine is the same one my client thinks is mine and to check that the public key my client thinks is yours is the same as the public key your client thinks is yours. If that is true (and the client software is itself trustworthy), you know the server does not do the MitM attack.
The way this verification is done differs between applications: Signal "verify safety numbers", Whatsapp "Verify Security Code", Wire "compare key fingerprints", etc.
There is a related attack, for example on iMessage, where I can be logged in on multiple devices, and when you encrypt your message to me, you actually encrypt it to multiple public keys, one for each of my devices. You get the list of all my public keys from the directory service. They can add a new key, as if I logged in on a new device, and your device will encrypt your messages to me to this key in addition to my other keys. But they control the new key. The way to protect would be to ensure new devices "joining the group chat" are visible to all other participants, but you can't. This is a UX tradeoff - the more secure the app is, the harder it is to use.