Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/skills/breaking-changes/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Look for both categories of breaking changes:
- **API (compile-time)** — changes to public type signatures, parameter types, return types, removed members, sealed types, new obsoletion attributes, etc.
- **Behavioral (runtime)** — new/changed exceptions, altered return values, changed defaults, modified event ordering, serialization changes, etc.

See [references/classification.md](references/classification.md) for the full classification guide, including SDK-specific versioning policies (pre-1.0 preview flexibility, experimental APIs, obsoletion lifecycle, and spec-driven changes) that influence how breaks are assessed.
See [references/classification.md](references/classification.md) for the full classification guide, including SDK-specific versioning policies (experimental APIs, obsoletion lifecycle, and spec-driven changes) that influence how breaks are assessed.

### Step 2: Assess Impact

Expand Down Expand Up @@ -63,7 +63,7 @@ Present the final list of confirmed breaking changes, sorted from most impactful

## Output

The audit produces a structured list of breaking changes that can be consumed by other skills (e.g. the release-notes skill) or presented directly to the user.
The audit produces a structured list of breaking changes that can be consumed by other skills (e.g. the **prepare-release** and **publish-release** skills) or presented directly to the user.

Each entry contains:
- PR number and description
Expand Down
46 changes: 37 additions & 9 deletions .github/skills/breaking-changes/references/classification.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,21 @@ Changes to internal surface or behavior (e.g., internal APIs, private reflection

The classification rules above are derived from the dotnet/runtime breaking change guidelines, but the MCP SDK has its own versioning policy (see `docs/versioning.md`) that provides additional context for classification decisions.

### Pre-1.0 Preview Status

Prior to a stable 1.0.0 release, the SDK is in preview and breaking changes can be introduced without prior notice. This does **not** change how breaks are classified — they should still be flagged, labeled, and documented — but it affects the **severity assessment**. Preview consumers expect breaks, so migration guidance matters more than avoidance.

### Experimental APIs

APIs annotated with `[Experimental]` (using `MCP`-prefixed diagnostic codes) can change at any time, including within PATCH or MINOR updates. Changes to experimental APIs should still be **noted** in the audit, but classified as **Bucket 3 (Unlikely Grey Area)** or lower unless the API has been widely adopted despite its experimental status.

#### APIs exclusively reachable through `[Experimental]` gates

A change to a non-experimental public API is **not considered breaking** if it only affects consumers who have already opted into an `[Experimental]` code path. The key question is: *can a consumer reach the breaking impact without suppressing an experimental diagnostic?*

For example, adding an abstract member to a public abstract class is normally a Bucket 1 break (anyone deriving from the class must implement the new member). However, if the class's only accessible constructor is marked `[Experimental]`, then deriving from it already requires suppressing the experimental diagnostic — meaning the consumer has explicitly accepted that the API is subject to change.

**How to identify this pattern:**
1. A change would normally be classified as a breaking change (e.g., CP0005 — adding abstract member to abstract type)
2. Trace the code path a consumer must follow to be affected by the break
3. If **every** such path requires the consumer to use an `[Experimental]`-annotated API (constructor, method, type, etc.), the break is dismissed

### Obsoletion Lifecycle

The SDK follows a three-step obsoletion process:
Expand All @@ -88,7 +95,7 @@ When auditing, classify each step appropriately:
- Step 2 (escalating to error) → API breaking change (previously working code now fails)
- Step 3 (removal) → API breaking change; migration guidance should note prior deprecation

In exceptional circumstances — especially during the pre-1.0 preview period — the obsoletion lifecycle may be compressed (e.g., marking obsolete and removing in the same MINOR release). This should still be flagged as a breaking change but the migration guidance should explain the rationale.
In exceptional circumstances, the obsoletion lifecycle may be compressed (e.g., marking obsolete and removing in the same MINOR release). This should still be flagged as a breaking change but the migration guidance should explain the rationale.

### Spec-Driven Changes

Expand All @@ -102,19 +109,40 @@ When a breaking change includes an `AppContext` switch or other opt-in/opt-out m
* Compat switch: `ModelContextProtocol.AspNetCore.AllowNewSessionForNonInitializeRequests` = `true` restores previous behavior
```

## Dismissing Potential Breaking Changes

When a change appears to be breaking but is dismissed (e.g., as a bug fix, clearly non-public, or exclusively gated by `[Experimental]` APIs), the audit must present the full rationale to the user for verification.

### Gathering Supporting Evidence

Before dismissing a potential break, review the PR description and all PR comments (both review comments and general comments) for discussion about the breaking change. Authors and reviewers often explain *why* a change is acceptable — for example, noting that the affected type is gated by an experimental constructor, that the previous behavior was incorrect per the spec, that no external consumers exist yet, or that compatibility suppressions were added intentionally. This discussion serves as supporting evidence for the dismissal and should be cited in the audit findings.

### Presenting Dismissals

Every dismissed potential break must be reported to the user with enough detail for them to verify the conclusion. The audit must:

1. **Identify what would normally be breaking and why** (e.g., "CP0005 — adding abstract member `Completion` to abstract class `McpClient`")
2. **Explain the specific reason for dismissal** (e.g., "Bug fix correcting incorrect behavior per the MCP spec" or "`McpClient`'s only accessible constructor is `protected` and marked `[Experimental(MCPEXP002)]` with message 'Subclassing McpClient and McpServer is experimental and subject to change.'")
3. **Cite any supporting discussion** from the PR description or comments (e.g., "Reviewers discussed the addition and did not flag it as a breaking concern; compatibility suppressions were added for CP0005")
4. **Conclude with the dismissal and its category** (e.g., "Dismissed — bug fix correcting spec-non-compliant behavior" or "Dismissed — exclusively gated by `[Experimental]` API. Do not apply the `breaking-change` label.")

This transparency allows the user to verify each dismissal rationale and override it if the justification is insufficient.

## What to Study for Each PR

For every PR in the range, examine:

1. **PR description** — Authors often describe breaking changes here
1. **PR description** — Authors often describe breaking changes here, or explain why a potentially breaking change is acceptable
2. **Linked issues** — May contain discussion about breaking impact
3. **Review comments** — Reviewers may have flagged breaking concerns
4. **Code diff** — Look at changes to:
3. **Review comments** — Reviewers may have flagged breaking concerns or discussed why a change is acceptable despite appearing breaking (e.g., experimental gates, no external consumers, compatibility suppressions). These discussions are critical evidence when dismissing potential breaks.
4. **General comments** — Authors and reviewers sometimes discuss breaking change justification in the PR conversation thread rather than in review comments
5. **Code diff** — Look at changes to:
- Public type/member signatures
- Exception throwing patterns
- Default values and constants
- Return value changes
- Parameter validation changes
- Attribute changes (`[Obsolete]`, `[Experimental]`, etc.)
- `AppContext.TryGetSwitch` or environment variable compat switches
5. **Labels** — Check if `breaking-change` is already applied
- Compatibility suppressions (e.g., `CompatibilitySuppressions.xml` for ApiCompat CP0005 etc.)
6. **Labels** — Check if `breaking-change` is already applied
38 changes: 31 additions & 7 deletions .github/skills/bump-version/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,39 +1,63 @@
---
name: bump-version
description: Bump the SDK version after publishing a release. Reads the current version from src/Directory.Build.props, suggests the next minor version, and creates a pull request with the change. Use when asked to bump the version, prepare for the next release, or increment the version number.
compatibility: Requires gh CLI with repo access for creating branches and pull requests.
description: Assess and bump the SDK version using Semantic Versioning 2.0.0. Evaluates queued changes to recommend PATCH/MINOR/MAJOR, updates src/Directory.Build.props, and creates a pull request. Owns the SemVer assessment logic shared by prepare-release and publish-release. Use when asked to bump the version, assess the version, or determine what the next version should be.
compatibility: Requires gh CLI with repo access for creating branches and pull requests. GitHub API access for PR details when performing SemVer-informed assessment.
---

# Bump Version

Bump the SDK version in `src/Directory.Build.props` after publishing a release and create a pull request with the change.
Assess and bump the SDK version in `src/Directory.Build.props` to prepare for the next release. This skill owns the [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html) assessment logic — the [SemVer assessment guide](references/semver-assessment.md) is the single source of truth for version assessment criteria used across the release workflow by both the **prepare-release** and **publish-release** skills.

> **Note**: For comprehensive release preparation — including ApiCompat/ApiDiff, documentation review, and release notes — use the **prepare-release** skill, which incorporates version assessment as part of its broader workflow.

## Process

### Step 1: Read Current Version
### Step 1: Read Current Version and Previous Release

Read `src/Directory.Build.props` on the default branch and extract:
- `<VersionPrefix>` — the `MAJOR.MINOR.PATCH` version
- `<VersionSuffix>` — the prerelease suffix (e.g. `preview.1`), if present

Display the current version to the user: `{VersionPrefix}-{VersionSuffix}` or `{VersionPrefix}` if no suffix.

### Step 2: Determine Next Version
Determine the previous release tag from `gh release list` (most recent **published** release). Draft releases must be ignored — they represent a pending release that has not yet shipped. Use `--exclude-drafts` or filter to only published releases when querying.

### Step 2: Assess and Determine Next Version

If the user provided a target version in their prompt, use it directly. Otherwise, determine the next version using one of two approaches:

#### SemVer-Informed Assessment (Preferred)

If the user provided a target version in their prompt, use it. Otherwise, suggest the next **minor** version with the same suffix pattern:
When context about queued changes is available or can be gathered, assess the version following the [SemVer assessment guide](references/semver-assessment.md):

1. Get the list of PRs merged between the previous release tag and the target commit (typically HEAD).
2. Classify the release level:
- **MAJOR** — if any confirmed breaking changes (API or behavioral), excluding `[Experimental]` APIs
- **MINOR** — if new public API surface, features, or obsoletion warnings are present
- **PATCH** — if only backward-compatible bug fixes, documentation, tests, or infrastructure changes
3. Compute the recommended version from the previous release tag (see the assessment guide for increment rules and pre-release suffix handling).
4. Compare against the current version in `Directory.Build.props` and flag any discrepancy.
5. Present the assessment with a summary table and rationale, then get user confirmation.

#### Default Suggestion (Fallback)

When a quick bump is needed without full change analysis, suggest the next **minor** version with the same suffix pattern:

- Current `0.9.0` with suffix `preview.1` → suggest `0.10.0-preview.1`
- Current `1.0.0` with no suffix → suggest `1.1.0`
- Current `1.2.3` with suffix `rc.1` → suggest `1.3.0-rc.1`

Present the suggestion and let the user confirm or provide an alternative. Parse the confirmed version into its `VersionPrefix` and `VersionSuffix` components.
Present the suggestion and let the user confirm or provide an alternative.

In either case, parse the confirmed version into its `VersionPrefix` and `VersionSuffix` components.

### Step 3: Create Pull Request

1. Create a new branch named `bump-version-to-{version}` (e.g. `bump-version-to-0.10.0-preview.1`) from the default branch
2. Update `src/Directory.Build.props`:
- Set `<VersionPrefix>` to the new prefix
- Set `<VersionSuffix>` to the new suffix, or remove the element if there is no suffix
- Update `<PackageValidationBaselineVersion>` if the MAJOR version has changed
3. Commit with message: `Bump version to {version}`
4. Push the branch and create a pull request:
- **Title**: `Bump version to {version}`
Expand Down
126 changes: 126 additions & 0 deletions .github/skills/bump-version/references/semver-assessment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# Semantic Versioning Assessment Guide

This reference describes how to assess the appropriate [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html) (SemVer) release level for the C# MCP SDK based on the changes queued since the previous release.

## SemVer Summary

The SDK follows SemVer 2.0.0 as documented in the [C# SDK Versioning](https://modelcontextprotocol.github.io/csharp-sdk/versioning.html) documentation. Given a version `MAJOR.MINOR.PATCH`:

- **MAJOR**: Increment when incompatible API changes are included
- **MINOR**: Increment when functionality is added in a backward-compatible manner
- **PATCH**: Increment when only backward-compatible bug fixes are included

When incrementing:
- MAJOR resets MINOR and PATCH to 0 (e.g., `1.2.3` → `2.0.0`)
- MINOR resets PATCH to 0 (e.g., `1.2.3` → `1.3.0`)
- PATCH increments only the PATCH component (e.g., `1.2.3` → `1.2.4`)

## Assessment Criteria

Evaluate every PR in the release range against these criteria, ordered from highest to lowest precedence.

### MAJOR — Incompatible API Changes

Recommend a MAJOR version increment if **any** of the following are present:

- Confirmed breaking changes from the breaking change audit (API or behavioral)
- Removal of public types, members, or interfaces
- Changes to parameter types, order, or count on public methods
- Return type changes on public methods or properties
- Sealing of previously unsealed types (with accessible constructors)
- Escalation of `[Obsolete]` from warning to error
- Removal of previously obsolete APIs

**Exception — Experimental APIs**: Changes to APIs annotated with `[Experimental]` do not require a MAJOR increment, even if they would otherwise be considered breaking. This includes changes that only affect consumers who have opted into an `[Experimental]` code path — for example, adding an abstract member to a public abstract class whose only accessible constructor is `[Experimental]`. If every path to the breaking impact requires suppressing an experimental diagnostic, the change is not considered breaking for versioning purposes. Note these changes but classify the release level based on the non-experimental changes.

### MINOR — Backward-Compatible New Functionality

Recommend a MINOR version increment if no MAJOR criteria are met but **any** of the following are present:

- New public types, methods, properties, or events
- New overloads or extension methods
- New configuration options or parameters with default values
- New MCP capabilities or protocol features
- Addition of `[Obsolete]` attributes producing build warnings (step 1 of obsoletion lifecycle)
- Addition of `[Experimental]` attributes on new APIs
- New interfaces implemented by existing types
- Performance improvements that introduce new API surface
- Changes to `[Experimental]` APIs (regardless of whether they would be breaking outside the experimental surface)

### PATCH — Backward-Compatible Bug Fixes Only

Recommend a PATCH version increment if no MAJOR or MINOR criteria are met. PATCH-level changes include:

- Bug fixes that correct incorrect behavior
- Spec compliance corrections
- Security fixes
- Performance improvements with no API surface changes
- Documentation changes (XML doc comments shipped in packages)
- Test-only changes (no impact on shipped packages)
- Infrastructure-only changes (CI, build system, dependencies)

**Note**: Releases that contain _only_ documentation, test, or infrastructure changes may not warrant a release at all. Flag this to the user if no shipped-package changes are present.

## Computing the Recommended Version

1. Parse the previous release tag to extract `MAJOR.MINOR.PATCH` and any pre-release suffix.
2. Apply the assessed level:
- MAJOR: `(MAJOR+1).0.0`
- MINOR: `MAJOR.(MINOR+1).0`
- PATCH: `MAJOR.MINOR.(PATCH+1)`
3. If the previous release had a pre-release suffix, carry the same suffix pattern forward (e.g., `-preview.1`).

**Examples** from previous release `v1.2.0`:

| Level | Recommended |
|-------|-------------|
| PATCH | `v1.2.1` |
| MINOR | `v1.3.0` |
| MAJOR | `v2.0.0` |

**Examples** from previous release `v1.0.0-preview.1`:

| Level | Recommended |
|-------|-------------|
| PATCH | `v1.0.1-preview.1` |
| MINOR | `v1.1.0-preview.1` |
| MAJOR | `v2.0.0-preview.1` |

## Comparing Against the Candidate Version

After computing the recommended version:

1. Compare it against the candidate version (from `src/Directory.Build.props` or an existing draft release tag).
2. Present one of three outcomes:
- **Match**: The candidate aligns with the assessment. Proceed with confidence.
- **Under-versioned**: The candidate uses a lower increment level than the changes warrant (e.g., candidate is `1.2.1` but changes include new APIs requiring `1.3.0`). Flag this as a concern — the version should be corrected.
- **Over-versioned**: The candidate uses a higher increment level than strictly required (e.g., candidate is `2.0.0` but no breaking changes). This is permitted by SemVer but worth noting for the user's awareness.

## Presentation Format

Present the assessment as a summary table followed by a rationale:

```
### Version Assessment

| Aspect | Finding |
|--------|---------|
| Previous release | v1.0.0 |
| Breaking changes | None confirmed |
| New API surface | Yes — 3 PRs add new public APIs |
| Bug fixes | Yes — 2 PRs fix runtime behavior |
| Recommended level | **MINOR** |
| Recommended version | `v1.1.0` |
| Candidate version | `1.1.0` ✅ matches |

**Rationale**: Three PRs introduce new public API surface (#101, #105, #112)
including new extension methods and configuration options. No confirmed breaking
changes. The candidate version in Directory.Build.props aligns with the MINOR
assessment.
```

When the candidate does not match, flag the discrepancy:

```
| Candidate version | `1.0.1` ⚠️ under-versioned (PATCH < MINOR) |
```
Loading