A few of you said the same thing in different words. Wasilis: “a tool description is basically a prompt. Change one sentence and the model behaves differently, with no code diff to catch it.” Aurel: “remember what I approved, check it on every start, hold anything new until I look at the diff.” So I built it, and then I tested it on a real client. Here’s what happened, including the parts that didn’t work. What it is A small proxy that sits between your client and a local MCP server. You pin the tool list you approved, name plus a hash of each description and schema. It re-checks every tool list. In enforce mode it hides new or changed tools and refuses calls to them, until you approve them on a review page that highlights the changed words and shows hidden characters. What I tested A planted demo server (four harmless tools I wrote myself) in front of Claude Code 2.1.325 on Windows, interactive mode. One or two runs per case, so treat these as observations and not as conclusions. 🔹 A tool dropped mid-session, announced: the client re-listed within about 10 ms. The model never called the gone tool and rerouted. 🔹 A description reworded mid-session, announced: the client kept its cached list. I confirmed in the gate’s journal that it never re-listed. The model called the tool anyway, the gate refused the call, and the model said (paraphrasing) “retrying won’t help, approval is yours to grant.” It didn’t retry, didn’t try to approve anything, and did the sum itself, labelled as its own. Both refusal styles reached the model. 🔹 A tool renamed mid-session, announced: same thing. The client kept the old name, the model called it, the gate refused, and nothing told the model what replaced it. 🔹 A description reworded with no notification: the call went through. The gate only learns what a tool looks like from a tool list, and in the 75 real server sessions I recorded, only 2 ever sent a change notice. So it’s strongest at session start and blind to a silent mid-session change unless it polls. I haven’t built polling.