I'm working on an engine that implements the UCI protocol that is intended to play against other engines. Since the UCI protocol has no commands for offering or accepting draws, how does this work when games run into the 50-move or threefold repetition rules? Do engines just never offer a draw? Then, would it not be possible for the game to just never end because there is no way to force a mate and neither engine will offer a draw?
Your engine doesn't need to offer a draw. This is the responsibility for GUI. A good GUI should stop the game for 50 moves or three-repetition.
If you want to run game matches, try: https://github.com/cutechess/cutechess