Tool drift is one of those problems that feels like it should not exist in a protocol with a handshake, and yet here we are. The two-gate approach you built makes sense. Pinning the definition hash and authorizing the call separately catches the cases where the description alone would be taken as instruction.
One thing worth considering: the drift problem gets harder when the server is itself an agent that can mutate its own tool list based on context. When the hash changes legitimately, the diff turns into noise. That shifts the problem from tool verification to identity verification, proving the entity presenting the tool list is the same one you approved across sessions. There is work on verifiable agent identity that pairs well with this kind of policy enforcement, and the combination feels like the right shape of the answer for the hard cases.