Skip to content
Draft
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
2 changes: 1 addition & 1 deletion .github/workflows/test_core.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:

- name: Run official MCP 2025 client conformance
run: >
npx -y @modelcontextprotocol/conformance@0.2.0-alpha.1 client
npx -y @modelcontextprotocol/conformance@0.2.0-alpha.2 client
--command "dart run test/conformance/mcp_2026_rc_client.dart"
--suite all
--spec-version 2025-11-25
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## Unreleased

### Conformance and release readiness

- Updated official conformance gates to
`@modelcontextprotocol/conformance@0.2.0-alpha.2`, with 2026 RC runs pinned
to `DRAFT-2026-v1` and the current upstream draft fixture gap tracked as an
expected failure.

## 2.3.0-dev.0

This is a dev preview for MCP `2026-07-28` draft/RC support. MCP
Expand Down
30 changes: 21 additions & 9 deletions doc/mcp-2026-rc.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,22 @@ behavior.
## Dev Release Checklist

Use dev releases for MCP `2026-07-28` draft/RC testing until the official spec
is released. Dev versions must include a prerelease suffix such as
`2.3.0-dev.0` so pub.dev and GitHub treat them as preview builds.
is released. The initial SDK dev release, `mcp_dart 2.3.0-dev.0`, is already
published on pub.dev. Follow-up dev versions must increment the prerelease
suffix, such as `2.3.0-dev.1`, so pub.dev and GitHub treat them as preview
builds.

Before creating tags from `dev/2026-07-28-rc`, run:
Before creating follow-up dev tags from `dev/2026-07-28-rc`, run:

```sh
dart analyze
dart run test/conformance/run_2025_server_conformance.dart
npx -y @modelcontextprotocol/conformance@0.2.0-alpha.2 client \
--command "dart run test/conformance/mcp_2026_rc_client.dart" \
--suite all \
--spec-version 2025-11-25
dart run test/conformance/run_2026_rc_server_conformance.dart
dart run test/conformance/run_2026_rc_client_conformance.dart
dart pub publish --dry-run
dart pub global run pana --no-warning
dart run tool/validate_cli_publish.dart
Expand All @@ -132,10 +141,11 @@ For dev packages, keep package documentation links pointed at
`dev/2026-07-28-rc` until the draft work is ready to merge back to `main`.
Restore those links to `main` as part of the final spec release prep.

Publish the SDK package first by running the `Create Release` workflow for
`mcp_dart` from `dev/2026-07-28-rc`. The publish workflow runs a dry-run check
before `dart pub publish --force`, and prerelease versions are marked as GitHub
prereleases rather than repository latest releases.
For follow-up dev releases, publish the SDK package first by running the
`Create Release` workflow for `mcp_dart` from `dev/2026-07-28-rc`. The publish
workflow runs a dry-run check before `dart pub publish --force`, and prerelease
versions are marked as GitHub prereleases rather than repository latest
releases.

After `mcp_dart` is available on pub.dev, validate the CLI against the published
SDK package:
Expand All @@ -145,8 +155,10 @@ dart run tool/validate_cli_publish.dart --published-sdk
```

Then run the `Create Release` workflow for `mcp_dart_cli` from
`dev/2026-07-28-rc`. The CLI publish workflow removes the local SDK override
before publishing so users receive the published SDK dependency.
`dev/2026-07-28-rc` when a matching CLI dev release is needed. The initial CLI
dev release, `mcp_dart_cli 0.2.0-dev.0`, is already published. The CLI publish
workflow removes the local SDK override before publishing so users receive the
published SDK dependency.

Install the dev CLI explicitly by version:

Expand Down
7 changes: 4 additions & 3 deletions packages/mcp_dart_cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -464,9 +464,10 @@ exported tree outside the monorepo git/.pubignore context:
dart run tool/validate_cli_publish.dart
```

Before the matching `mcp_dart` SDK dev package is published, this uses
`pubspec_overrides.yaml` so the CLI can validate against the local SDK checkout.
After publishing the SDK package, validate the CLI against the pub.dev SDK
For follow-up CLI dev releases whose matching `mcp_dart` SDK dev package is not
published yet, this uses `pubspec_overrides.yaml` so the CLI can validate
against the local SDK checkout. The initial `mcp_dart 2.3.0-dev.0` SDK package
is already published, so release validation should also cover the pub.dev SDK
version:

```bash
Expand Down
7 changes: 5 additions & 2 deletions test/conformance/2026_rc_client_expected_failures.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
# Expected failures for @modelcontextprotocol/conformance@0.2.0-alpha.1
# Expected failures for @modelcontextprotocol/conformance@0.2.0-alpha.2
# against the 2026 RC/DRAFT client suite.
#
# Keep this list scenario-based so the baseline is easy to review. When a
# scenario turns green, remove it from this file in the same PR as the fix.
#
# No expected client failures are currently tracked.
# Upstream alpha.2 fixture gap: this scenario's mock server rejects
# DRAFT-2026-v1 with HTTP 400 and advertises only stable protocol versions.
# Keep it expected-fail until the conformance fixture is draft-capable.
json-schema-ref-no-deref
2 changes: 1 addition & 1 deletion test/conformance/2026_rc_expected_failures.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Expected failures for @modelcontextprotocol/conformance@0.2.0-alpha.1
# Expected failures for @modelcontextprotocol/conformance@0.2.0-alpha.2
# against the 2026 RC/DRAFT server suite.
#
# Keep this list scenario-based so the baseline is easy to review. When a
Expand Down
9 changes: 5 additions & 4 deletions test/conformance/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ dart run test/conformance/run_2025_server_conformance.dart
```

The runner starts `mcp_2025_server.dart`, runs
`@modelcontextprotocol/conformance@0.2.0-alpha.1 server --suite all
`@modelcontextprotocol/conformance@0.2.0-alpha.2 server --suite all
--spec-version 2025-11-25`, and writes artifacts under
`.dart_tool/conformance/2025_server/`.

Run the stable client suite from the repository root:

```bash
npx -y @modelcontextprotocol/conformance@0.2.0-alpha.1 client \
npx -y @modelcontextprotocol/conformance@0.2.0-alpha.2 client \
--command "dart run test/conformance/mcp_2026_rc_client.dart" \
--suite all \
--spec-version 2025-11-25 \
Expand All @@ -55,8 +55,9 @@ dart run test/conformance/run_2026_rc_server_conformance.dart

The runner starts a local `StreamableMcpServer` with JSON stateless responses
enabled, runs the draft server scenarios from
`@modelcontextprotocol/conformance@0.2.0-alpha.1` one by one, and writes per-run
artifacts under `.dart_tool/conformance/2026_rc/`.
`@modelcontextprotocol/conformance@0.2.0-alpha.2` one by one with
`--spec-version DRAFT-2026-v1`, and writes per-run artifacts under
`.dart_tool/conformance/2026_rc/`.

Expected failures live in `2026_rc_expected_failures.txt`. When a scenario is
fixed, remove it from that file so the baseline remains useful.
Expand Down
118 changes: 86 additions & 32 deletions test/conformance/mcp_2026_rc_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,37 @@ Future<void> main(List<String> args) async {

switch (scenario) {
case 'initialize':
await _withClient(serverUrl, protocolVersion: latestProtocolVersion);
await _withClient(serverUrl, protocolVersion: protocolVersion);
case 'tools_call':
await _withClient(
serverUrl,
protocolVersion: latestProtocolVersion,
action: (client) async {
if (isStatelessProtocolVersion(protocolVersion)) {
final client = _RawStatelessClient(serverUrl, protocolVersion);
try {
await client.request(Method.serverDiscover, const {});
await client.listTools();
await client.callTool(
const CallToolRequest(
name: 'add_numbers',
arguments: {'a': 2, 'b': 3},
),
'add_numbers',
arguments: const {'a': 2, 'b': 3},
);
},
);
} finally {
client.close();
}
} else {
await _withClient(
serverUrl,
protocolVersion: protocolVersion,
action: (client) async {
await client.listTools();
await client.callTool(
const CallToolRequest(
name: 'add_numbers',
arguments: {'a': 2, 'b': 3},
),
);
},
);
}
case 'elicitation-sep1034-client-defaults':
await _runElicitationDefaults(serverUrl);
await _runElicitationDefaults(serverUrl, protocolVersion);
case 'request-metadata':
await _runRequestMetadata(serverUrl, protocolVersion);
case 'sep-2322-client-request-state':
Expand All @@ -52,11 +66,11 @@ Future<void> main(List<String> args) async {
case 'http-invalid-tool-headers':
await _runInvalidToolHeaders(serverUrl, protocolVersion);
case 'json-schema-ref-no-deref':
await _runSchemaRefNoDeref(serverUrl, latestProtocolVersion);
await _runSchemaRefNoDeref(serverUrl, protocolVersion);
case 'sse-retry':
await _withClient(
serverUrl,
protocolVersion: latestProtocolVersion,
protocolVersion: protocolVersion,
action: (client) async {
await client.listTools();
await client.callTool(
Expand Down Expand Up @@ -141,10 +155,13 @@ Future<void> _withClient(
}
}

Future<void> _runElicitationDefaults(Uri serverUrl) async {
Future<void> _runElicitationDefaults(
Uri serverUrl,
String protocolVersion,
) async {
await _withClient(
serverUrl,
protocolVersion: latestProtocolVersion,
protocolVersion: protocolVersion,
capabilities: const ClientCapabilities(
elicitation: ClientElicitation(
form: ClientElicitationForm(applyDefaults: true),
Expand Down Expand Up @@ -357,7 +374,7 @@ Future<void> _runAuthScenario(
Map<String, dynamic> context,
) async {
final provider = _ConformanceOAuthProvider(scenario, context);
final client = _RawOAuthClient(serverUrl, latestProtocolVersion, provider);
final client = _RawOAuthClient(serverUrl, protocolVersion, provider);
const allowClientErrorScenarios = {
'auth/resource-mismatch',
'auth/scope-retry-limit',
Expand All @@ -370,7 +387,9 @@ Future<void> _runAuthScenario(

try {
await client.start();
await client.initialize();
await client.initialize(
maxAuthAttempts: scenario == 'auth/scope-retry-limit' ? 1 : 4,
);

switch (scenario) {
case 'auth/authorization-server-migration':
Expand All @@ -381,7 +400,7 @@ Future<void> _runAuthScenario(
await client.callTool('test-tool');
case 'auth/scope-retry-limit':
try {
await client.listTools(maxAuthAttempts: 2);
await client.listTools(maxAuthAttempts: 1);
} catch (_) {
// The scenario only needs to observe a bounded number of auth
// retries; the server intentionally never grants the scope.
Expand Down Expand Up @@ -417,6 +436,14 @@ class _RawStatelessClient {

_RawStatelessClient(this.serverUrl, this.protocolVersion);

void close() {
_httpClient.close(force: true);
}

Future<Map<String, dynamic>> listTools() {
return request(Method.toolsList, const {});
}

Future<Map<String, dynamic>> callTool(
String name, {
Map<String, dynamic> arguments = const {},
Expand Down Expand Up @@ -679,26 +706,41 @@ class _RawOAuthClient {

Future<void> close() => transport.close();

Future<void> initialize() async {
final id = _nextId++;
await _request(
JsonRpcInitializeRequest(
id: id,
initParams: InitializeRequest(
protocolVersion: protocolVersion,
capabilities: _draftCapabilities,
clientInfo: _clientInfo,
Future<void> initialize({
int maxAuthAttempts = 4,
}) async {
if (isStatelessProtocolVersion(protocolVersion)) {
await _request(
JsonRpcRequest(
id: _nextId++,
method: Method.serverDiscover,
meta: _requestMeta(),
),
),
);
await transport.send(const JsonRpcInitializedNotification());
maxAuthAttempts: maxAuthAttempts,
);
} else {
await _request(
JsonRpcInitializeRequest(
id: _nextId++,
initParams: InitializeRequest(
protocolVersion: protocolVersion,
capabilities: _draftCapabilities,
clientInfo: _clientInfo,
),
),
);
await transport.send(const JsonRpcInitializedNotification());
}
}

Future<Map<String, dynamic>> listTools({
int maxAuthAttempts = 4,
}) {
return _request(
JsonRpcListToolsRequest(id: _nextId++),
JsonRpcListToolsRequest(
id: _nextId++,
meta: _requestMeta(),
),
maxAuthAttempts: maxAuthAttempts,
);
}
Expand All @@ -711,11 +753,23 @@ class _RawOAuthClient {
JsonRpcCallToolRequest(
id: _nextId++,
params: CallToolRequest(name: name).toJson(),
meta: _requestMeta(),
),
maxAuthAttempts: maxAuthAttempts,
);
}

Map<String, dynamic>? _requestMeta() {
if (!isStatelessProtocolVersion(protocolVersion)) {
return null;
}
return buildProtocolRequestMeta(
protocolVersion: protocolVersion,
clientInfo: _clientInfo,
clientCapabilities: _draftCapabilities,
);
}

Future<Map<String, dynamic>> _request(
JsonRpcRequest request, {
int maxAuthAttempts = 4,
Expand Down
2 changes: 1 addition & 1 deletion test/conformance/run_2025_server_conformance.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'dart:convert';
import 'dart:io';

const _defaultConformancePackage =
'@modelcontextprotocol/conformance@0.2.0-alpha.1';
'@modelcontextprotocol/conformance@0.2.0-alpha.2';
const _defaultTimeout = Duration(seconds: 60);

Future<void> main(List<String> args) async {
Expand Down
2 changes: 1 addition & 1 deletion test/conformance/run_2026_rc_client_conformance.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'dart:convert';
import 'dart:io';

const _defaultConformancePackage =
'@modelcontextprotocol/conformance@0.2.0-alpha.1';
'@modelcontextprotocol/conformance@0.2.0-alpha.2';
const _defaultTimeout = Duration(seconds: 30);

const _draftClientScenarios = [
Expand Down
4 changes: 3 additions & 1 deletion test/conformance/run_2026_rc_server_conformance.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'dart:convert';
import 'dart:io';

const _defaultConformancePackage =
'@modelcontextprotocol/conformance@0.2.0-alpha.1';
'@modelcontextprotocol/conformance@0.2.0-alpha.2';
const _defaultTimeout = Duration(seconds: 25);

const _draftServerScenarios = [
Expand Down Expand Up @@ -229,6 +229,8 @@ Future<_ScenarioResult> _runScenario({
serverUrl.toString(),
'--suite',
'draft',
'--spec-version',
'DRAFT-2026-v1',
'--scenario',
scenario,
'--verbose',
Expand Down