MCP tool drift
MCP tool drift is any change to a tool's contract, as a Model Context Protocol server serves it, after a client has already seen that contract.
The contract is what tools/list returns for the tool: its name, description, input schema, output schema and annotations. Drift is found by comparing the contract a client recorded when it first saw the tool with the one the server returns now. Nothing in the protocol makes a server announce the change: the notifications/tools/list_changed message is optional, and a tool contract carries no signature.
Related terms
- Silent drift
- Drift that ships while the server's declared version stays the same. The word is about version evidence only. It says nothing about intent.
- Rug pull
- What security writing calls drift done on purpose, after a tool has passed review. The term asserts intent. Drift does not, and a contract diff cannot tell a rug pull from routine maintenance.
- Registry drift
- A change to the server's entry in the MCP registry. The entry is what a server says about itself and the contract is what it serves, and either can move while the other stays put.
- Safety-relevant drift
- Drift of a kind marked safety-relevant in the table below: a change a person should look at before the tool runs again.
What counts
Each change the crawler observes is classified into a fixed set of kinds. These are the tool-level kinds published on the drift ledger.
| Kind | What changed | Safety-relevant |
|---|---|---|
| added-required-param | new required input | yes |
| added-optional-param | new optional input | no |
| removed-param | input removed | yes |
| type-changed | input type changed | yes |
| enum-values-removed | allowed values removed | yes |
| constraint-narrowed | input constraint tightened | yes |
| required-set-expanded | more inputs now required | yes |
| output-schema-changed | output shape changed | yes |
| output-schema-added | output shape added | no |
| annotation-flip-to-destructive | now marked destructive | yes |
| param-mirrored-to-header | a parameter value is now sent in an HTTP header | yes |
| tool-removed | tool removed | yes |
| deep-schema-undiffable | schema too nested to diff | yes |
The ledger also publishes 3 server-level kinds, all safety-relevant: server now injects instructions into agent context (instructions-added); injected server instructions changed (instructions-changed); prompt argument contract changed (prompt-args-changed).
More kinds are recorded and kept off the ledger, among them a tool being added and an edit to description text with no structural change. A description edit is still drift under the definition above, and a poisoned description is one way an attack arrives, but on its own the classifier grades it cosmetic.
What drift is not
A contract diff says the contract changed. Whether the new contract is safe is a separate question, and this page makes no safety call.
A change in behavior under an unchanged contract is outside the definition. A server that keeps its contract byte for byte and changes what the tool does is invisible to any contract comparison, including ours.
How it is measured
Live contracts: a daily crawl of the reachable remote servers in the official MCP registry calls tools/list on each and compares every snapshot with the one before it, so it also records changes that are later reverted. Only servers reachable in both snapshots are compared, so a server going offline never counts as its tools being removed. The frozen edition below covers 23 snapshots across 40 days, 2026-06-09 to 2026-07-19, including a 21-day crawler outage. Changes that happened and were undone entirely inside the outage were never observed.
Registry entries: 120 observations of the official registry over 88.6 days, to 2026-07-28. For each starting observation, take every server listed then and count the share whose entry differed from its starting value at any observation within N days; a change that later reverts still counts. Each rate is the mean over starting observations: 60 for 30 days, 36 for 60 and 9 for 89, the last using windows of at least 80 days.
The numbers
Live contracts, Drift Report Edition v1
| Safety-relevant contract changes, deduped to one per server, tool and kind | 2,503 |
| Shipped with the declared version unchanged | 62.4% (1,561) |
| The same share with the 76 unstable tools left out | 63.4% of 2,401 |
| Annotation flipped toward destructive | 123, on 24 servers |
The 2,503 changes came from 291 servers and span 9 of the safety-relevant kinds above. Of the 123 flips to destructive, 68 were a tool declaring annotations for the first time and 55 changed an annotation the tool already had. No single publisher accounts for the same-version share: the largest supplied 10% of those changes (156 of 1,561) and the top five together 37.1%.
Registry entries, MCP Registry Drift Panel
| Share of servers that changed | 30 days | 60 days | 89 days |
|---|---|---|---|
| Description text, the part a reviewer reads | 3.3% | 5.5% | 6.9% |
| Any field of the entry, an upper bound that also counts bare version bumps | 11.9% | 16.3% | 19.1% |
Of the 18,748 servers seen in at least 10 observations, 75.2% never changed their entry in the window, and the tenth of them that changed most account for 78.7% of all changes.
These figures are frozen and will not move. The running count is on the drift ledger, and the full live-contract analysis is The MCP Drift Report.
Cite this
Cite the definition when you use the term, and the paper or datasets when you use a number. The datasets are on Zenodo under CC-BY-4.0 and the figures recompute from their files. The paper is a preprint and has not been peer reviewed.
@misc{bharti2026tooldrift,
author = {Bharti, Gautam},
title = {{MCP} Tool Drift: Definition, Kinds and Measured Rates},
year = {2026},
howpublished = {mcpindex.ai},
url = {https://mcpindex.ai/drift}
}@misc{bharti2026drift,
author = {Bharti, Gautam},
title = {Registry Descriptions Go Stale Unevenly: An 89-Day Measurement of
Model Context Protocol Drift, and Why Drift-Ranked Re-Auditing Under-Covers It},
year = {2026},
eprint = {2608.00997},
archivePrefix= {arXiv},
primaryClass = {cs.SE},
doi = {10.48550/arXiv.2608.00997},
url = {https://arxiv.org/abs/2608.00997}
}@dataset{bharti2026driftreport,
author = {Bharti, Gautam},
title = {mcpindex Drift Report -- Edition v1 dataset},
year = {2026},
publisher = {Zenodo},
doi = {10.5281/zenodo.21449150},
url = {https://doi.org/10.5281/zenodo.21449150},
note = {Frozen Edition v1 (2026-07-19). Concept DOI 10.5281/zenodo.21449149 resolves to the latest version. CC-BY-4.0. Live: https://mcpindex.ai/drift-report}
}@dataset{bharti2026panel,
author = {Bharti, Gautam},
title = {MCP Registry Drift Panel v1},
year = {2026},
publisher = {Zenodo},
doi = {10.5281/zenodo.21709945},
url = {https://doi.org/10.5281/zenodo.21709945},
note = {Concept DOI; resolves to the latest version. CC-BY-4.0}
}Guides that apply this: silent contract drift and how to hold it, how to monitor for it. Corrections: hello@mcpindex.ai.