Version 2.0.0 brings the C# SDK into stable alignment with the MCP 2026-07-28 specification.
This major release introduces discovery-first negotiation, multi-round-trip requests, stateless-by-default HTTP, caching hints, standardized headers, stronger OAuth and token-cache safety, and dedicated MCP Apps and Tasks extension packages, with down-level interoperability for peers negotiating 2025-11-25 and earlier. Review the migration guidance below.
Breaking Changes
Refer to the C# SDK Versioning documentation for details on versioning and breaking-change policies.
- Default to stateless HTTP and discovery-first negotiation #1610
HttpServerTransportOptions.Statelessnow defaults totrue. Stateless servers do not create transport sessions, expose the standalone SSEGET/DELETEendpoints, or support unsolicited server-to-client requests.- Set
Stateless = falsewhen an existing server requires legacy stateful behavior. Stateful-only options now produceMCP9006warnings and apply only to down-level initialize-handshake connections. - Clients probe
server/discoverfirst and automatically fall back to the legacyinitializehandshake for down-level servers.
- Deprecate Roots, Sampling, and Logging APIs #1651
- The stable Roots, Sampling, and Logging API surfaces now produce
MCP9005warnings because these features are deprecated by the 2026-07-28 specification. - Existing down-level connections can continue using these APIs. Suppress
MCP9005temporarily if continued use is required while planning migration.
- The stable Roots, Sampling, and Logging API surfaces now produce
- Move Tasks into
ModelContextProtocol.Extensions.Tasks#1693- The v1.4.x Tasks implementation is replaced by a dedicated extension package and has no API or wire compatibility with the earlier experimental implementation.
- Add a reference to
ModelContextProtocol.Extensions.Tasks, import its namespace, register Tasks withWithTasks(...), and replace CoreRequestMethods.Tasks*constants withTasksProtocolmembers.
- Strengthen OAuth callback and issuer validation #1605
AuthorizationRedirectDelegateandClientOAuthOptions.AuthorizationRedirectDelegatenow produceMCP9007warnings. Migrate toClientOAuthOptions.AuthorizationCallbackHandlerso callbacks can return the authorization code, state, and issuer.- OAuth authorization now rejects issuer mismatches required by RFC 9207 and RFC 8414. Correct inconsistent authorization-server metadata rather than bypassing validation.
- Emit non-object structured tool results directly #1568
- Tools with
UseStructuredContent = trueand a non-object return type now emit the raw value and matching schema, such asstructuredContent: 72, instead of wrapping it as{ "result": 72 }. - Update clients to read the value according to the advertised output schema rather than assuming a
resultproperty.
- Tools with
- Require
Tool.inputSchemaduring deserialization #1600- Deserializing a
Toolpayload withoutinputSchemanow throwsJsonExceptioninstead of silently defaulting the schema. - Custom servers, proxies, and test fixtures that produce tool JSON must include
inputSchema; an empty{}is sufficient.
- Deserializing a
- Require explicit PKCE S256 support in OAuth metadata #1700
- OAuth authorization now fails when authorization-server metadata does not advertise
S256incode_challenge_methods_supported. - Update the authorization server metadata to declare PKCE S256 support.
- OAuth authorization now fails when authorization-server metadata does not advertise
- Send
application_typeduring dynamic client registration #1613- Dynamic client registration requests now include an inferred
application_type. - Authorization servers that validate the request shape must accept this standard field; set
DynamicClientRegistrationOptions.ApplicationTypeexplicitly when the inferred value is not appropriate.
- Dynamic client registration requests now include an inferred
- Propagate underlying SSE connection exceptions #1432
- Explicit SSE connections now surface the underlying
HttpRequestException,TimeoutException, or genuine I/O exception instead of always wrapping failures inIOException. - Update connection error handling that depends on the old
IOException("Failed to connect transport.")wrapper. In AutoDetect mode, inspect the outerHttpRequestExceptionand its inner SSE failure.
- Explicit SSE connections now surface the underlying
- Fail OAuth step-up when a challenge makes no progress #1591
- A repeated
insufficient_scopechallenge that introduces no new scopes now throwsMcpExceptioninstead of retrying indefinitely. - Handle the exception as an authorization failure and ensure repeated challenges add scopes when another step-up attempt is expected.
- A repeated
What's Changed
- Add
InheritEnvironmentVariablestoStdioClientTransportOptions#1563 by @halter73 - Stop logging stdio transport environment variables #1538 by @halter73 (co-authored by @Copilot)
- Implement SEP-2243 HTTP header standardization #1553 by @mikekistler (co-authored by @tarekgh @Copilot)
- Append
offline_accessto authorization scope when advertised (SEP-2207) #1479 by @stephentoub (co-authored by @Copilot) - Deprecate
McpErrorCode.ResourceNotFoundper SEP-2164 #1558 by @jayaraman-venkatesan - Add APIs for pre-populating the
McpClienttool cache #1590 by @tarekgh - Add
ScopeSelectorDelegateto OAuth options #1596 by @halllo - Validate the authenticated user on Streamable HTTP session deletion #1604 by @halter73
- Relax resource URI validation to accept a base URL #1517 by @JBallan (co-authored by @tarekgh)
- Rename
MinVersionForStandardHeaderstoDraftProtocolVersion#1603 by @halter73 - Add Enterprise Managed Authorization support (SEP-990) #1305 by @aniket-okta
- Add multi-round-trip requests (MRTR) #1458 by @halter73
- Align
x-mcp-headerbehavior with SEP-2243 clarifications #1619 by @tarekgh - Skip the idle-tracking timer in stateless mode #1531 by @MukundaKatta
- Fix
$refpointer resolution after output-schema wrapping #1435 by @weinong - Add
McpClientOptions.InitializeMeta#1599 by @adityasingh2400 - Remove
charset=utf-8from HTTPapplication/jsoncontent types #1528 by @jayaraman-venkatesan - Require Tool
inputSchemaduring deserialization #1600 by @DragonFSKY - Release the SSE response stream reference when a GET request ends #1519 by @joelmforsyth
- Diagnose messages dropped from the GET SSE stream #1609 by @halter73
- Implement the SEP-2663 Tasks extension #1579 by @PranavSenthilnathan (co-authored by @Copilot)
- Add SEP-2549 caching hints to cacheable results #1623 by @tarekgh
- Accept any JSON Schema 2020-12 document as an output schema (SEP-2106) #1568 by @jayaraman-venkatesan (co-authored by @mikekistler)
- Add MCP Apps extension support #1484 by @mikekistler (co-authored by @Copilot)
- Accumulate client scopes during step-up authorization (SEP-2350) #1591 by @mikekistler (co-authored by @Copilot @tarekgh)
- Obsolete Roots, Sampling, and Logging APIs per SEP-2577 #1651 by @tarekgh
- Enable .NET Framework UI MCP clients #1639 by @KirillOsenkov
- Use
TimeToLiveinstead ofTtlMsin public APIs #1644 by @PranavSenthilnathan (co-authored by @halter73 @Copilot) - Add sessionless and handshake-less draft protocol support #1610 by @halter73
- Stabilize official Extensions, Tasks, and MRTR APIs #1642 by @PranavSenthilnathan (co-authored by @Copilot)
- Finalize 2026-07-28 terminology, error codes, and protocol-version floors #1671 by @halter73
- Document caching behavior and warn on non-conformant cacheable results #1669 by @tarekgh
- Stabilize protocol properties #1686 by @eiriktsarpalis (co-authored by @Copilot)
- Echo the request ID in post-parse Streamable HTTP errors #1687 by @tarekgh
- Add
DeferChangedEvents()for batched primitive-change notifications #1689 by @jeffhandley (co-authored by @Copilot) - Fix request-scoped client capabilities #1685 by @PranavSenthilnathan (co-authored by @Copilot @tarekgh)
- Tighten protocol-negotiation boundaries #1692 by @halter73 (co-authored by @Copilot @tarekgh)
- Add the
completeresult discriminator #1684 by @PranavSenthilnathan (co-authored by @Copilot @tarekgh) - Ignore empty Streamable HTTP JSON responses #1626 by @jstar0
- Extract Tasks into
ModelContextProtocol.Extensions.Tasks#1693 by @jeffhandley (co-authored by @Copilot @tarekgh) - Add an opt-out for the Streamable HTTP standalone GET stream #1699 by @jstar0 (co-authored by @jeffhandley @Copilot)
- Enforce PKCE metadata compliance in the OAuth client #1700 by @PranavSenthilnathan
- Make authorization-provider token caches thread-safe #1708 by @tarekgh
- Add
application_typeto dynamic client registration #1613 by @jayaraman-venkatesan - Preserve the underlying status code in AutoDetect probes #1530 by @MukundaKatta
- Return a JSON-RPC parse error for over-nested stdio requests #1629 by @adityasingh2400 (co-authored by @tarekgh)
- Implement RFC 9207 issuer validation in
ClientOAuthProvider#1605 by @mikekistler - Reject mismatched initialize protocol versions #1724 by @halter73 (co-authored by @Copilot)
- Fix
ClientOAuthProviderclient ID availability on cold start #1705 by @halter73 (co-authored by @Copilot) - Fix HTTP Task filter composition #1722 by @PranavSenthilnathan
- Rename MCPEXP002 extensibility constants #1739 by @arimu1
- Propagate underlying SSE connection exceptions #1432 by @xue-cai (co-authored by @Copilot @jeffhandley)
- Validate OAuth authorization state #1726 by @halter73 (co-authored by @Copilot)
- Gate 2026-07-28 result fields on the negotiated protocol version #1753 by @tarekgh
- Align the 2026-07-28 wire with specification PR #3002 and SEP-2575 HTTP statuses #1752 by @pcarleton (co-authored by @halter73 @Copilot @tarekgh)
- Reauthorize tools changed by call-tool filters #1737 by @PranavSenthilnathan
- Enable additional client conformance scenarios #1763 by @halter73 (co-authored by @Copilot)
Documentation Updates
- Fix broken links to the MCP Tasks specification #1594 by @tarekgh
- Fix a broken Enterprise Managed Authorization specification link #1663 by @PranavSenthilnathan
- Update package READMEs as part of the release process #1683 by @jeffhandley (co-authored by @Copilot)
- Add Docker-based MCP server deployment guidance #1423 by @john-mckillip
- Complete the v2 documentation edit pass #1728 by @gewarren
- Refine the 2.0.0 conceptual documentation #1742 by @jeffhandley (co-authored by @Copilot)
Test Improvements
- Fix the flaky
AddIncomingMessageFilter_Multiple_Filters_Execute_In_Ordertest #1627 by @tarekgh - Add a synchronization point to
ReadEventsAsync_InStreamingMode_YieldsNewlyWrittenEvents#1491 by @ericstj (co-authored by @Copilot) - Stabilize
DiagnosticTests.Session_TracksActivities#1495 by @ericstj (co-authored by @Copilot) - Reduce absolute time dependencies in tests #1649 by @PranavSenthilnathan
- Re-enable the
http-custom-headersconformance scenario #1691 by @tarekgh - Run stateful and stateless conformance endpoints in one server process #1672 by @halter73
- Expand npm conformance coverage #1725 by @halter73
- Extend the in-memory server discovery timeout for Windows Debug CI #1702 by @PranavSenthilnathan (co-authored by @Copilot @halter73)
- Add initial Tasks extension conformance coverage #1734 by @PranavSenthilnathan (co-authored by @Copilot)
- Resolve test-server paths deterministically #1735 by @PranavSenthilnathan
- Add MCP Apps to NativeAOT coverage #1711 by @lntutor (co-authored by @jeffhandley)
- Add MCP Apps
_meta.uiserialization round-trip tests #1698 by @yayayouyou (co-authored by @jeffhandley) - Enable complete Tasks extension conformance coverage #1741 by @PranavSenthilnathan
Repository Infrastructure Updates
- Support release servicing branches in the release process #1620 by @jeffhandley (co-authored by @Copilot)
- Bump the version to 2.0.0-preview.1 #1621 by @jeffhandley (co-authored by @Copilot)
- Include release branches in CodeQL workflows #1624 by @jeffhandley
- Remove a dangling
Tool.Executionreference from the main build #1660 by @halter73 - Release v2.0.0-preview.2 #1695 by @jeffhandley (co-authored by @Copilot)
- Release v2.0.0-preview.3 #1706 by @jeffhandley (co-authored by @Copilot)
- Publish the multi-version documentation site #1727 by @jeffhandley (co-authored by @Copilot)
- Release v2.0.0-rc.1 #1738 by @jeffhandley (co-authored by @Copilot)
- Bump actions/setup-node from 6.4.0 to 7.0.0 #1717
- Bump coverlet.collector from 10.0.0 to 10.0.1 #1582
- Bump qs from 6.14.2 to 6.15.2 #1597
- Bump actions/setup-dotnet from 5.2.0 to 6.0.0 #1716
- Bump danielpalme/ReportGenerator-GitHub-Action from 5.5.5 to 5.5.10 #1569
- Bump actions/checkout from 6 to 7 #1665
- Bump the testing-frameworks dependency group #1746
- Bump the serilog-testing dependency group #1748
- Bump actions/checkout from 7.0.0 to 7.0.1 #1745
- Bump Anthropic from 12.17.0 to 12.39.0 #1750
- Bump OpenTelemetry and five other dependencies #1747
- Bump JsonSchema.Net and System.Linq.AsyncEnumerable #1749
- Update Microsoft.Extensions.AI dependencies #1755 by @jeffhandley
- Release v2.0.0-rc.2 #1757 by @jeffhandley (co-authored by @Copilot)
- Add AI disclosure guidance #1756 by @PranavSenthilnathan
Acknowledgements
Community Stability Contributions
- @cclabadmin conducted detailed 2026-07-28 conformance testing against preview builds, reporting five issues with wire captures, exact repros, specification citations, and root-cause analysis: #1580, #1614, #1676, #1677, and #1680. The resulting fixes landed in #1724, #1629, #1684, #1687, and #1692.
- @KirillOsenkov reported and helped diagnose three v2 stability issues with deterministic repros and source-level analysis: console-less .NET Framework clients failing over stdio (#1638, fixed by #1639), standalone SSE GET deadlocks under connection-pool pressure (#1637, fixed by #1699), and OAuth client ID availability on cold start (#1658, fixed by #1705).
- @KubaZ2 found the preview.3 regression that broke servers combining Tasks with HTTP transport (#1720), then validated the #1722 fix against rc.1 before release.
- @p-ob found and documented the preview.3 wire-compatibility regression that emitted 2026-07-28 result fields to down-level clients (#1721), directly enabling the release-blocking fix in #1753.
- @shmed reported both the AutoDetect status-code issue (#1526, fixed by #1530) and a production interoperability failure where the SDK's HTTP content type was rejected by GitHub Copilot's MCP endpoint (#1527, fixed by #1528).
- @jwoo-msft provided a minimal repro and root-cause analysis showing that
Tool.InputSchemasilently masked malformed payloads (#1575), leading to the required-field fix in #1600. - @mirusser pinpointed the missing path for setting
_metaon initialize requests (#1593), leading toMcpClientOptions.InitializeMetain #1599. - @DmitryLukyanov, with independent confirmation from @dimable, documented the empty HTTP response deserialization failure (#1132), fixed by #1626.
- @mllab-nl identified the OAuth client's non-compliant PKCE metadata fallback (#730), ultimately resolved by the strict S256 validation in #1700.
- @k8x10 supplied a self-contained repro and source-level root-cause analysis for double-wrapped Task results (#1480), informing the v2 Tasks redesign.
- @aaronpk corrected Enterprise Managed Authorization terminology in #1305 to keep the SDK aligned with the authoritative OAuth specification work.
New Contributors
- @JBallan made their first contribution in #1517.
- @aniket-okta made their first contribution in #1305.
- @MukundaKatta made their first contribution in #1531.
- @weinong made their first contribution in #1435.
- @adityasingh2400 made their first contribution in #1599.
- @DragonFSKY made their first contribution in #1600.
- @joelmforsyth made their first contribution in #1519.
- @KirillOsenkov made their first contribution in #1639.
- @jstar0 made their first contribution in #1626.
- @john-mckillip made their first contribution in #1423.
- @lntutor made their first contribution in #1711.
- @arimu1 made their first contribution in #1739.
- @yayayouyou made their first contribution in #1698.
- @xue-cai made their first contribution in #1432.
- @pcarleton made their first contribution in #1752.
Additional Issue Reporters
- @hans1512 submitted issue #1119 (resolved by #1517).
- @felixweinberger submitted issue #949 (resolved by #1305).
- @chrisdoopboop submitted issue #1574 (resolved by #1600).
Reviewers
- @halter73 @jeffhandley @tarekgh @PranavSenthilnathan @mikekistler @eiriktsarpalis @ericstj @jozkee @stephentoub @gewarren @jstar0 @luisquintanilla reviewed pull requests.
Full Changelog: v1.4.1...v2.0.0