By the end of this lesson, you'll apply semantic versioning to agent skills, maintain changelogs that communicate impact clearly, manage dependencies across LLM model versions, and implement lifecycle management that keeps your agent ecosystem healthy over time.
After this lesson, you'll have a versioning strategy that lets consumers of your skills upgrade confidently — knowing exactly what changed, what might break, and when deprecated features disappear.
Agent skills aren't static artifacts. Models evolve, prompts get refined, tool APIs change, and new capabilities emerge. Without disciplined versioning, consumers of your skill face a grim choice: pin to an old version and miss improvements, or upgrade blindly and risk breakage in production.
The MCP specification treats servers as versioned components with explicit capability negotiation. Your skills deserve the same rigor. A version number is a contract — it tells consumers exactly how much risk an upgrade carries.
Apply Semantic Versioning (SemVer) to agent skills with domain-specific interpretations. The format is MAJOR.MINOR.PATCH, where each segment communicates a different level of change impact:
Increment MAJOR when consumers must modify their code or configuration to continue working. In the agent skill context, these are breaking changes:
# skill-spec.yaml — MAJOR version bump (2.x → 3.0.0)
# Breaking: renamed 'query' parameter to 'prompt', removed 'verbose' flag
name: research-agent
version: 3.0.0
interface:
inputs:
- name: prompt # was 'query' in v2.x — BREAKING
type: string
required: true
- name: depth # was 'verbose' boolean in v2.x — BREAKING
type: integer # changed type from boolean to integer
required: false
default: 2
Increment MINOR when you add functionality that existing consumers can ignore. Their code continues to work without changes:
# skill-spec.yaml — MINOR version bump (3.0.0 → 3.1.0)
# Added: optional 'sources' parameter, new 'confidence' field in output
name: research-agent
version: 3.1.0
interface:
inputs:
- name: prompt
type: string
required: true
- name: depth
type: integer
required: false
default: 2
- name: sources # NEW — optional, defaults to "all"
type: string
required: false
default: "all"
outputs:
- name: result
type: string
- name: confidence # NEW — consumers can ignore this field
type: float
Increment PATCH for fixes that don't change the interface or add features. Consumers should always feel safe upgrading patches:
# CHANGELOG entry for PATCH (3.1.0 → 3.1.1)
# Fixed: prompt truncation when input exceeds 50k tokens
# Fixed: timeout not respected when API returns partial response
# No interface changes — safe to upgrade without code modifications
When in doubt, bump MAJOR. A false-alarm major version bump costs consumers a few minutes reading the changelog. A stealth breaking change in a minor bump costs them hours debugging production failures.
A changelog is the human-readable story of your skill's evolution. The Keep a Changelog format, adapted for agent skills, uses three categories that map directly to SemVer:
| Category | Maps to | What belongs here |
|---|---|---|
| Added | MINOR+ | New features, tools, parameters, model support |
| Changed | MAJOR or MINOR | Modified behavior, renamed fields, altered defaults. Mark breaking changes explicitly. |
| Fixed | PATCH | Bug fixes, documentation corrections, edge-case handling |
Additional categories when needed: Deprecated (will be removed in a future MAJOR), Removed (MAJOR only), Security (vulnerability patches).
# Changelog — research-agent
## [3.1.1] - 2026-07-18
### Fixed
- Prompt truncation when input exceeds 50k tokens now chunks correctly
- Timeout handler now respects partial API responses instead of hanging
- Token counting uses cl100k_base encoding (was incorrectly using p50k)
## [3.1.0] - 2026-07-10
### Added
- Optional `sources` parameter to restrict search scope (web, internal, academic)
- `confidence` field in output (0.0–1.0) indicating result reliability
- Support for Claude 3.5 Sonnet as execution model
### Changed
- Default search depth increased from 1 to 2 (backward compatible — existing
behavior unchanged for consumers not using the parameter)
## [3.0.0] - 2026-06-28
### Changed
- **BREAKING:** Renamed `query` parameter to `prompt` for consistency with
MCP naming conventions. Update your invocations.
- **BREAKING:** `verbose` boolean replaced by `depth` integer (1-5).
Migration: `verbose: true` → `depth: 3`, `verbose: false` → `depth: 1`
### Removed
- **BREAKING:** Dropped support for Claude 2.x models (EOL upstream)
- Removed deprecated `raw_mode` parameter (deprecated since 2.3.0)
### Added
- Migration guide: docs/migration-v2-to-v3.md
## [2.4.0] - 2026-06-15
### Deprecated
- `verbose` parameter — will be replaced by `depth` in v3.0.0 (target: June 28)
- Claude 2.x model support — upstream EOL announced, removal in v3.0.0
Each entry answers three questions: What changed? Why should consumers care? What do they need to do (if anything)? For breaking changes, always include migration instructions or a link to a migration guide.
Agent skills depend on models, tool APIs, framework libraries, and other skills. Each dependency is a potential break point. Manage them proactively rather than reactively.
Document which combinations of dependencies are tested and supported. This prevents the "works on my machine" problem at scale:
# compatibility.yaml — research-agent v3.1.x
dependencies:
models:
- name: claude-3.5-sonnet
versions: ["20241022", "20250101"]
status: supported
notes: "Primary target. All tests run against this."
- name: claude-3-opus
versions: ["20240229"]
status: supported
notes: "Higher quality, higher latency. Use for complex research."
- name: claude-3-haiku
versions: ["20240307"]
status: compatible
notes: "Works but quality degrades on multi-step tasks."
- name: claude-2.1
versions: ["*"]
status: deprecated
removal_date: "2026-06-28"
notes: "Upstream EOL. Migrate to claude-3.5-sonnet."
tools:
- name: web-search-api
versions: [">=2.0.0", "<4.0.0"]
pinned: "3.2.1"
notes: "v4.0 changes response schema — blocked until adapter written."
- name: document-retriever
versions: [">=1.5.0"]
pinned: "1.8.0"
frameworks:
- name: mcp-sdk
versions: [">=0.9.0", "<2.0.0"]
pinned: "1.2.3"
- name: tokenizer
versions: [">=0.4.0"]
pinned: "0.5.1"
skills:
- name: summarization-skill
versions: [">=2.0.0", "<3.0.0"]
notes: "Used for final output compression."
Not all dependencies are equal. Pin the ones where version drift causes silent behavior changes:
# requirements.lock — pinned for reproducibility
# CRITICAL: These affect output quality. Do not upgrade without running eval suite.
anthropic==0.34.2 # Model API client — response format can change
tiktoken==0.7.0 # Token counting — different versions count differently
mcp-sdk==1.2.3 # Protocol layer — breaking changes affect all tools
# FLEXIBLE: These can float within compatible ranges
httpx>=0.27.0,<1.0.0 # HTTP client — stable interface
pydantic>=2.5.0,<3.0.0 # Validation — breaking changes are MAJOR only
structlog>=24.1.0 # Logging — additive changes only
Model providers release new versions that change behavior subtly. Your CI must test against every supported model version:
# ci/test-matrix.yaml
test_matrix:
model_versions:
- claude-3.5-sonnet-20241022
- claude-3.5-sonnet-20250101
- claude-3-opus-20240229
test_suites:
- name: regression
description: "Core behavior hasn't changed"
threshold: { accuracy: 0.92, latency_p95: 5000 }
- name: quality
description: "Output quality meets bar"
threshold: { accuracy: 0.88 }
# Lower threshold for older models — acceptable degradation
overrides:
claude-3-haiku-20240307: { accuracy: 0.80 }
schedule:
on_commit: [regression] # fast — runs on every PR
nightly: [regression, quality] # full matrix — runs overnight
on_model_release: [regression, quality] # triggered by provider announcements
Deprecation without a timeline is a lie. Give consumers concrete dates and migration paths:
# deprecation-policy.md
## Active Deprecations
| Feature | Deprecated | Removal Target | Migration Path |
|---------|-----------|----------------|----------------|
| `verbose` param | v2.4.0 (Jun 15) | v3.0.0 (Jun 28) | Use `depth: 3` for verbose, `depth: 1` for concise |
| Claude 2.x support | v2.4.0 (Jun 15) | v3.0.0 (Jun 28) | Switch to claude-3.5-sonnet in config |
| `raw_mode` param | v2.3.0 (May 20) | v3.0.0 (Jun 28) | Use structured output with `format: json` |
| Python 3.9 support | v3.0.0 (Jun 28) | v4.0.0 (Q4 2026) | Upgrade to Python 3.11+ |
## Deprecation Rules
1. Minimum 30 days between deprecation notice and removal
2. Deprecated features emit runtime warnings with migration instructions
3. Removal only happens in MAJOR version bumps
4. Each deprecation includes a migration guide or one-line fix
5. Breaking removal is announced in the previous MINOR's changelog
Deprecation is a communication tool, not a punishment. The goal is giving consumers enough time and information to migrate smoothly. A surprise removal in a patch version destroys trust permanently.
Skills and their model dependencies have lifecycles. Governance requires tracking what's approved, what's aging, and what needs proactive replacement before it breaks.
Maintain a catalog of approved models organized by use case. This prevents teams from independently adopting untested models and provides a single source of truth for what's production-ready:
# model-catalog.yaml — Approved models by use case
catalog:
text_generation:
primary:
model: claude-3.5-sonnet-20250101
status: approved
approved_date: 2025-01-15
eval_score: 0.94
cost_per_1k_tokens: 0.003
notes: "Best balance of quality, speed, and cost for general tasks."
alternatives:
- model: claude-3-opus-20240229
status: approved
use_when: "Task requires highest quality; latency budget > 10s"
eval_score: 0.97
cost_per_1k_tokens: 0.015
- model: claude-3-haiku-20240307
status: approved
use_when: "High volume, low complexity; latency budget < 1s"
eval_score: 0.82
cost_per_1k_tokens: 0.00025
code_generation:
primary:
model: claude-3.5-sonnet-20250101
status: approved
approved_date: 2025-01-20
eval_score: 0.91
notes: "Strongest code quality across languages."
summarization:
primary:
model: claude-3.5-sonnet-20250101
status: approved
eval_score: 0.93
classification:
primary:
model: claude-3-haiku-20240307
status: approved
eval_score: 0.89
notes: "Classification doesn't need heavy reasoning — haiku is cost-effective."
retired:
- model: claude-2.1
status: end_of_life
eol_date: 2026-06-01
replacement: claude-3.5-sonnet-20250101
migration_guide: docs/migrate-claude2-to-3.5.md
Don't wait for a model to stop working. When a provider announces end-of-life, trigger migration immediately:
# lifecycle-automation.yaml
lifecycle_rules:
- trigger: model_eol_announced
actions:
- create_migration_ticket:
priority: high
deadline: "eol_date - 30 days"
assignee: skill_owner
template: |
## Model EOL Migration Required
**Model:** {{model_name}}
**EOL Date:** {{eol_date}}
**Replacement:** {{recommended_replacement}}
### Steps
1. Update compatibility.yaml to add replacement model
2. Run full eval suite against replacement
3. Update prompts if quality degrades (score delta > 3%)
4. Publish MINOR version with new model support
5. Deprecate old model in changelog
6. Publish MAJOR version removing old model (after 30-day window)
- notify_consumers:
channels: [email, slack]
message: |
⚠️ {{skill_name}} v{{version}} uses {{model_name}} which reaches
EOL on {{eol_date}}. Upgrade to v{{next_version}} (available
{{available_date}}) which supports {{replacement}}.
- schedule_eval_run:
model: "{{recommended_replacement}}"
test_suite: full
notify_on_failure: skill_owner
- trigger: eval_score_drops_below_threshold
conditions:
score_delta: ">5%"
duration: "3 consecutive runs"
actions:
- alert:
severity: high
message: "{{skill_name}} quality degraded on {{model_name}}"
- create_investigation_ticket:
priority: high
- trigger: dependency_security_advisory
actions:
- create_patch_ticket:
priority: critical
deadline: "advisory_date + 7 days"
Every skill version moves through a defined lifecycle. Track the state explicitly:
# Track skill versions and their lifecycle state
skill_registry:
research-agent:
versions:
- version: "3.1.1"
state: active
released: "2026-07-18"
supported_until: null # current — no planned EOL
- version: "3.0.0"
state: deprecated
released: "2026-06-28"
deprecated_since: "2026-07-10"
removal_target: "2026-09-10"
reason: "Superseded by 3.1.x with confidence scoring"
- version: "2.4.0"
state: end_of_life
released: "2026-06-15"
eol_since: "2026-06-28"
reason: "MAJOR version released — v2.x no longer maintained"
Here's how versioning, changelogs, dependencies, and lifecycle management connect in practice:
# Maintenance workflow — triggered by upstream model release
1. Provider announces new model version
└─▶ Lifecycle automation creates eval ticket
2. Run eval suite against new model
├─▶ Pass (score within 3%): Add to compatibility matrix → MINOR bump
└─▶ Fail (score drops >3%): Investigate → fix prompts → re-eval → MINOR bump
3. Update compatibility.yaml
└─▶ Add new model to "supported" list
4. Update CHANGELOG.md
└─▶ "Added: Support for claude-X.Y-YYYYMMDD"
5. If old model approaching EOL:
├─▶ Add "Deprecated" entry to changelog (current MINOR)
├─▶ Emit runtime deprecation warning
├─▶ Notify consumers with timeline
└─▶ Schedule MAJOR bump to remove (30+ days out)
6. Publish new version
└─▶ Consumers upgrade at their own pace within the support window
Automate steps 1-3. Model releases are predictable events. A scheduled CI job that runs your eval suite against newly released models and opens a PR with updated compatibility data eliminates the most common maintenance bottleneck: nobody noticed the new version was available.
You add a new optional parameter to your skill with a sensible default value. Existing consumers don't need to change their code. What version bump is correct?
A model provider announces that Claude 3 Haiku reaches end-of-life in 60 days. Your skill currently lists it as "supported." What's the correct sequence of actions?
Your compatibility matrix shows a skill tested against two model versions. A third model version is released by the provider. Your eval suite shows quality drops 8% on the new version. What should you do?
Pick one of your skills and create a CHANGELOG.md retroactively. Document at least three versions (even if you've been informally iterating). Then create a compatibility.yaml listing every model and tool dependency with its tested version range. If you discover untested combinations, add them to your CI matrix.
Read the Anthropic MCP Specification — the protocol's approach to capability negotiation and version handshakes between clients and servers demonstrates how versioning enables safe evolution of distributed agent systems without breaking existing integrations.