Agents and hosts cache assumptions about tool names and input schemas. A container can restart cleanly while every agent in the company still calls create_ticket with last week’s arguments. Versioning for MCP is therefore about contracts, not only binaries.
What changes break clients
| Change | Risk | Typical symptom |
|---|---|---|
| Rename / remove tool | High | Model keeps calling old name |
| New required argument | High | Validation errors / isError spikes |
| Auth scheme change | High | Mass 401/403 |
| Tighter rate limits | Medium | Agent loops fail differently |
| Additive optional fields | Low | Usually fine if documented |
| Bugfix, same schema | Low | If tested |
Discovery and invocation use tools/list and tools/call per the Tools specification (2025-11-25).
Protocol support for dynamic tools
From the tools specification:
- Servers that support tools MUST declare the
toolscapability listChanged: trueindicates the server will notify when the list changes- When the list changes, such servers SHOULD send
notifications/tools/list_changed
Official client best practices tell hosts using progressive discovery to refresh search catalogs on list_changed. If you change tools at runtime without notifications, clients may serve stale definitions until restart.
Versioning strategies that work
1. Immutable artifacts (baseline)
Always ship a git SHA or image digest. Production should never be “whatever main builds to tonight.”
deploy: sha-a1b2c3d → good
rollback: redeploy sha-9f8e7d6 → known good
2. Parallel deployments (canary / dual-run)
Keep billing-mcp stable while billing-mcp-v2 takes canary traffic. Hosts opt in by URL. This is the safest way to rename tools or change auth.
3. Schema discipline
- Prefer additive optional fields
- For renames: ship
create_ticket_v2, markcreate_ticketdeprecated in description, dual-run for a deprecation window - Document breaking changes in release notes your agent platform team actually reads
4. Registry / package versions
If you publish public servers, follow official registry versioning and pin packages in consumer configs. The MCP Registry is metadata pointing at packages — versioning still lives in npm/PyPI/Docker tags.
Rollout playbook
1. CI: unit + contract snapshots + protocol smoke
2. Deploy immutable artifact → staging
3. Smoke: initialize, tools/list diff, tools/call happy + auth fail
4. Canary: subset of API keys / internal dogfood host configs
5. Watch tool error rate + auth failures 30–60 min
6. Promote prod pointer
7. Keep previous artifact hot for rollback
Load tests should include:
- Realistic concurrency of
tools/call - Failure paths (
isError) - At least one oversized/invalid payload
Diffing tools/list before promote
Capture staging and previous prod:
tools/list → names + required fields
diff against last release snapshot
require human approval on BREAKING
If only descriptions change, risk is low. If required fields change, treat as major.
Rollback
| Layer | Action |
|---|---|
| Artifact | Redeploy previous SHA/digest |
| Config | Revert env/auth only if the change was intentional |
| Clients | Usually unchanged if URL stable |
Rule: know the previous artifact before you need it. “We’ll rebuild last week’s main” is not a rollback plan.
Communication template
Subject: billing-mcp v1.4.0 (additive)
- New tool: list_invoices_v2 (optional date range)
- No renames; auth unchanged
- Staging URL smoked; canary 10% eng keys
- Rollback: redeploy sha-9f8e7d6
Checklist
- Immutable artifact IDs in the release ticket
- Staging parity for transport + auth
- Before/after
tools/listsnapshot -
listChangedcapability if tools are dynamic - Canary plan and success metrics
- Alert on tool error-rate spike post-deploy
- Named rollback owner
- Deprecation window for any renamed tools
Anti-patterns
- Deploying
:latestto production - Silent tool renames “because the new name is cleaner”
- Changing OAuth scopes and tool schemas in the same release
- No staging because “it’s just an MCP server”
Worked scenario: Dual-run instead of a hard rename
Billing agents depend on create_invoice. A cleanup PR renames it to invoice_create. Error rates explode; the model keeps choosing the old name from earlier turns and docs.
You revert, then ship invoice_create alongside create_invoice. The old tool’s description says deprecated and points to the new name. After two weeks of dual-run analytics (calls to old vs new), you remove the old tool in a dated release note with a rollback digest ready.
For risky auth changes, you stand up billing-mcp-v2 as a parallel deployment and move hosts by URL, not by surprise schema change.
Checklist for this topic
- Treat tool renames/required fields as breaking
- Prefer dual-run or parallel deployment URLs
- Diff tools/list before promote
- Emit list_changed if tools change at runtime
- Canary a subset of keys/users first
- Document previous digest before release
Topic-specific failure modes
| Failure | Likely cause | Fix |
|---|---|---|
| Silent agent breakage | Contract change without dual-run | Additive first, then deprecate |
| Clients keep old tools | No list_changed / no refresh | Declare listChanged; refresh hosts |
| Cannot canary | Single global deploy only | Parallel URL or key cohorts |
| Rollback rebuilds wrong code | :latest tags | Pin digests |
Related guides
Testing · CI/CD · Observability