Removing a tool definition is a one-line diff. The risk isn't in the diff, it's in the sessions that don't exist at diff-review time: a long-running or resumed session that, three steps ago, planned a sequence of actions around a tool that has since disappeared. A fresh session started after the removal never sees the problem — it plans with the current toolset. A session that's mid-task when the removal lands can reference a tool name that no longer resolves, and fail in a way no eval suite catches, because the eval suite runs fresh sessions against the current tool list by construction.
Adding a tool is close to risk-free — nothing that already exists depends on it. Renaming or removing one is the opposite: everything that was already relying on the old name is retroactively affected, and none of it gets a chance to opt in first. This is the same asymmetry that makes tool contract drift dangerous when a schema changes underneath a running agent — deprecation is that problem's scheduled, deliberate cousin. The difference is that drift is usually accidental and deprecation is planned, which means it's also preventable in a way drift often isn't: there's no excuse for a planned removal to strand a live session, because you control the timeline.
| Stage | Tool still callable | What changes | Who's affected |
|---|---|---|---|
| Mark deprecated | Yes, unchanged behavior | Tool description gains a deprecation notice and a pointer to the replacement | Nobody yet — pure signal |
| Warn on use | Yes, plus a warning | A successful call still succeeds, but returns a visible deprecation warning alongside the result | Sessions still calling the old tool, now on notice |
| Redirect | Yes, but forwards | The old tool name silently calls the new implementation and maps the result back to the old shape | Nobody breaks; behavior converges |
| Remove | No | The tool is gone; only sessions planned entirely after this point ever see the toolset without it | Should be nobody, if the earlier stages did their job |
The mark stage costs nothing and catches the easiest-to-fix case: a session that's about to plan a multi-step task can see the deprecation notice in the tool description (see writing tool descriptions an agent will actually follow) and choose the replacement unprompted, before ever calling the old tool. The warn stage catches the next easiest case — a session already using the old tool gets told on every call, so even a long-running session gets repeated chances to notice and adapt mid-task rather than finding out only when the tool disappears entirely.
Marking and warning only help a session that reads the signal and acts on it — nothing forces that. Redirect is the stage with no failure mode for the caller: the old tool name keeps working, silently forwards to the new implementation, and reshapes the response to match what the old tool used to return. A session that never noticed any warning still succeeds, because from its point of view nothing changed. This is the stage to hold the longest, because its cost is low — one thin forwarding function — and it's the only stage that protects sessions that don't cooperate with the deprecation at all.
The removal is safe exactly to the degree the redirect stage was long enough to cover every session lifetime the system actually has, including checkpointed sessions resumed after a long pause — a session paused for a week and resumed after the redirect window closed is exactly the case a fixed calendar deadline misses. Prefer a removal condition tied to observed usage — no calls to the old tool for N consecutive days — over a fixed date picked in advance, because a fixed date is a guess about session lifetimes and observed usage is a measurement of them.
The test to apply: find the last tool your agent stopped supporting. Was there a stage where the old name kept working but pointed at the new implementation, or did support end at the same moment the new one began? If it was the same moment, any session mid-task at that instant had no path forward except failing — and the number of sessions that were mid-task at that exact instant is never zero for anything actually in production.
Related: an agent that hits a removed tool cold should fail the way a well-designed tool error message fails — telling it what changed and where the replacement is — not with a generic "unknown tool" that gives it nothing to recover with.