Skip to content

Commit c1743c2

Browse files
authored
Merge pull request #312 from leehack/fix/2026-result-identity-metadata-main
Align MCP 2026 result identity metadata
2 parents c961c33 + 0ea9bec commit c1743c2

38 files changed

Lines changed: 2948 additions & 286 deletions

.github/workflows/interop_2026_07_28.yml

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ on:
2020
- 'tool/testing/run_ts_2026_07_28_interop.dart'
2121
- 'tool/testing/run_python_2026_07_28_interop.dart'
2222
- 'tool/testing/run_browser_2026_07_28_interop.dart'
23+
- 'tool/testing/bounded_response_body.dart'
24+
- 'tool/testing/mcp_2026_07_28_discovery_wire_probe.dart'
2325
- '.github/workflows/interop_2026_07_28.yml'
2426
- 'pubspec.yaml'
2527
- 'pubspec.lock'
@@ -34,6 +36,8 @@ on:
3436
- 'tool/testing/run_ts_2026_07_28_interop.dart'
3537
- 'tool/testing/run_python_2026_07_28_interop.dart'
3638
- 'tool/testing/run_browser_2026_07_28_interop.dart'
39+
- 'tool/testing/bounded_response_body.dart'
40+
- 'tool/testing/mcp_2026_07_28_discovery_wire_probe.dart'
3741
- '.github/workflows/interop_2026_07_28.yml'
3842
- 'pubspec.yaml'
3943
- 'pubspec.lock'
@@ -62,8 +66,16 @@ jobs:
6266
working-directory: test/interop/ts_2026_07_28
6367
run: npm ci
6468

65-
- name: Run TypeScript 2026-07-28 interop fixture
66-
run: dart run tool/testing/run_ts_2026_07_28_interop.dart
69+
- name: Run Dart client against published TypeScript beta server
70+
run: >-
71+
dart run tool/testing/run_ts_2026_07_28_interop.dart
72+
--direction=dart-to-ts
73+
74+
- name: Verify known published TypeScript beta client gap
75+
run: >-
76+
dart run tool/testing/run_ts_2026_07_28_interop.dart
77+
--direction=ts-to-dart
78+
--expect-published-ts-client-gap
6779
6880
python-2026-07-28-interop:
6981
runs-on: ubuntu-latest
@@ -90,10 +102,20 @@ jobs:
90102
python -m pip install
91103
-r test/interop/python_2026_07_28/requirements.txt
92104
93-
- name: Run Python 2026-07-28 interop fixture
105+
- name: Run Dart client against published Python beta server
106+
env:
107+
MCP_PYTHON: python
108+
run: >-
109+
dart run tool/testing/run_python_2026_07_28_interop.dart
110+
--direction=dart-to-python
111+
112+
- name: Verify known published Python beta client gap
94113
env:
95114
MCP_PYTHON: python
96-
run: dart run tool/testing/run_python_2026_07_28_interop.dart
115+
run: >-
116+
dart run tool/testing/run_python_2026_07_28_interop.dart
117+
--direction=python-to-dart
118+
--expect-published-python-client-gap
97119
98120
browser-2026-07-28-interop:
99121
runs-on: ubuntu-latest

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
## Unreleased
2+
3+
### Changed
4+
5+
- Aligned MCP `2026-07-28` identity with spec PR #3002: client identity is
6+
optional, and servers stamp validated identity in successful stateless result
7+
`_meta` by default instead of the discovery body. A handler `null` omits the
8+
optional key; received canonical `null` or malformed identities are rejected.
9+
10+
### Breaking and compatibility notes
11+
12+
- `DiscoverResult.serverInfo` is now nullable because MCP `2026-07-28` permits
13+
anonymous servers. Check for `null` before reading identity fields. MCP
14+
`2025-11-25` behavior is unchanged.
15+
116
## 2.3.0-dev.2
217

318
This preview makes MCP `2026-07-28` the preferred protocol while preserving

doc/interoperability.md

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ For MCP 2026-07-28 coverage, see the
2626
| Dart client -> TypeScript SDK server | Streamable HTTP | MCP 2025-11-25 | [`test/interop/dart_client_with_ts_server_test.dart`](../test/interop/dart_client_with_ts_server_test.dart), [`test/interop/ts/`](../test/interop/ts/) | Verified | Covers tool calls and stale preconfigured session-id recovery. |
2727
| TypeScript SDK client -> Dart server | stdio | MCP 2025-11-25 | [`test/interop/ts_client_with_dart_server_test.dart`](../test/interop/ts_client_with_dart_server_test.dart), [`test/interop/test_dart_server.dart`](../test/interop/test_dart_server.dart) | Verified | Runs the compiled TypeScript client fixture against a Dart server process and checks that an official TS client can list tools immediately after the lifecycle handshake. |
2828
| TypeScript SDK client -> Dart server | Streamable HTTP | MCP 2025-11-25 | [`test/interop/ts_client_with_dart_server_test.dart`](../test/interop/ts_client_with_dart_server_test.dart), [`test/interop/test_dart_server.dart`](../test/interop/test_dart_server.dart) | Verified | Includes official TS Streamable HTTP client lifecycle coverage, pre-`initialized` operation rejection, GET SSE streams, and `Last-Event-ID` replay behavior. |
29-
| TypeScript SDK beta client -> Dart server | Streamable HTTP | MCP 2026-07-28 | [`test/interop/ts_2026_07_28/`](../test/interop/ts_2026_07_28/), [`tool/testing/run_ts_2026_07_28_interop.dart`](../tool/testing/run_ts_2026_07_28_interop.dart), [`interop_2026_07_28.yml`](../.github/workflows/interop_2026_07_28.yml) | Verified | Uses published `@modelcontextprotocol/client@2.0.0-beta.4` and `@modelcontextprotocol/server@2.0.0-beta.4` packages. Covers modern negotiation, cache metadata, `tools/list`, `tools/call`, routing-header validation and retry, removed core RPC rejection, progress, `subscriptions/listen`, `AbortController` response-stream cancellation observed by the Dart server, and follow-up recovery. |
30-
| Dart MCP 2026-07-28 client -> TypeScript SDK beta server | Streamable HTTP | MCP 2026-07-28 | [`test/interop/ts_2026_07_28/src/server.mjs`](../test/interop/ts_2026_07_28/src/server.mjs), [`tool/testing/run_ts_2026_07_28_interop.dart`](../tool/testing/run_ts_2026_07_28_interop.dart), [`interop_2026_07_28.yml`](../.github/workflows/interop_2026_07_28.yml) | Verified | Uses the published TypeScript SDK beta server through its `createMcpHandler` entry and covers `server/discover` negotiation, `tools/list`, `tools/call`, one-time `HeaderMismatch` metadata refresh and retry, an MCP 2026-07-28 `input_required` elicitation retry, request-stream cancellation observed through the server's Web Request `AbortSignal`, and post-cancellation recovery. |
29+
| TypeScript SDK beta client -> Dart server | Streamable HTTP | MCP 2026-07-28 | [`test/interop/ts_2026_07_28/`](../test/interop/ts_2026_07_28/), [`tool/testing/run_ts_2026_07_28_interop.dart`](../tool/testing/run_ts_2026_07_28_interop.dart), [`interop_2026_07_28.yml`](../.github/workflows/interop_2026_07_28.yml) | Known published-beta gap | Published `@modelcontextprotocol/client@2.0.0-beta.4` predates spec PR #3002 and rejects discovery without obsolete body `serverInfo`. CI asserts the exact negotiation failure as temporary expected drift; a TypeScript SDK PR #2513 preview validates the final result metadata identity shape. |
30+
| Dart MCP 2026-07-28 client -> TypeScript SDK beta server | Streamable HTTP | MCP 2026-07-28 | [`test/interop/ts_2026_07_28/src/server.mjs`](../test/interop/ts_2026_07_28/src/server.mjs), [`tool/testing/run_ts_2026_07_28_interop.dart`](../tool/testing/run_ts_2026_07_28_interop.dart), [`interop_2026_07_28.yml`](../.github/workflows/interop_2026_07_28.yml) | Verified | Uses the published TypeScript SDK beta server through its `createMcpHandler` entry and a temporary read-only fallback for legacy body `serverInfo`; covers `server/discover`, `tools/list`, `tools/call`, one-time `HeaderMismatch` metadata refresh and retry, MCP 2026-07-28 `input_required`, request-stream cancellation, and post-cancellation recovery. |
3131
| Dart client -> Python MCP server | stdio | Server-dependent | [`doc/transports.md`](transports.md#connect-to-python-server) | Documented recipe | The transport can spawn Python servers over stdio; the stable recipe remains separate from the MCP 2026-07-28 beta fixture. |
32-
| Python SDK beta client -> Dart server | Streamable HTTP | MCP 2026-07-28 | [`test/interop/python_2026_07_28/`](../test/interop/python_2026_07_28/), [`tool/testing/run_python_2026_07_28_interop.dart`](../tool/testing/run_python_2026_07_28_interop.dart), [`interop_2026_07_28.yml`](../.github/workflows/interop_2026_07_28.yml) | Verified | Uses official Python SDK `mcp==2.0.0b1` and covers automatic `server/discover` negotiation, `tools/list`, and `tools/call` against the Dart conformance server. |
33-
| Dart MCP 2026-07-28 client -> Python SDK beta server | Streamable HTTP | MCP 2026-07-28 | [`test/interop/python_2026_07_28/server.py`](../test/interop/python_2026_07_28/server.py), [`tool/testing/run_python_2026_07_28_interop.dart`](../tool/testing/run_python_2026_07_28_interop.dart), [`interop_2026_07_28.yml`](../.github/workflows/interop_2026_07_28.yml) | Verified | Uses the official Python SDK beta MCP server and covers discovery, protocol selection, `tools/list`, and `tools/call`. |
32+
| Python SDK beta client -> Dart server | Streamable HTTP | MCP 2026-07-28 | [`test/interop/python_2026_07_28/`](../test/interop/python_2026_07_28/), [`tool/testing/run_python_2026_07_28_interop.dart`](../tool/testing/run_python_2026_07_28_interop.dart), [`interop_2026_07_28.yml`](../.github/workflows/interop_2026_07_28.yml) | Known published-beta gap | Published `mcp==2.0.0b2` predates spec PR #3002 and falls back to MCP 2025-11-25 when canonical discovery omits obsolete body `serverInfo`. CI asserts that exact fallback as temporary expected drift. |
33+
| Dart MCP 2026-07-28 client -> Python SDK beta server | Streamable HTTP | MCP 2026-07-28 | [`test/interop/python_2026_07_28/server.py`](../test/interop/python_2026_07_28/server.py), [`tool/testing/run_python_2026_07_28_interop.dart`](../tool/testing/run_python_2026_07_28_interop.dart), [`interop_2026_07_28.yml`](../.github/workflows/interop_2026_07_28.yml) | Verified | Uses the official Python SDK beta server through the temporary read-only fallback for legacy body `serverInfo`; covers discovery, protocol selection, `tools/list`, and `tools/call`. |
3434
| Dart browser client -> Dart server | Streamable HTTP | MCP 2025-11-25 and MCP 2026-07-28 | [`test/browser/mcp_2026_07_28_streamable_http_test.dart`](../test/browser/mcp_2026_07_28_streamable_http_test.dart), [`tool/testing/run_browser_2026_07_28_interop.dart`](../tool/testing/run_browser_2026_07_28_interop.dart) | Verified | A real Chrome client completes 12 tool-list requests and 12 tool calls in each profile over cross-origin Streamable HTTP. It also proves MCP 2026-07-28 request-stream cancellation and recovery. The MCP 2025-11-25 case waits for response-stream reconnect timers and guards against browser connection-slot exhaustion. |
3535
| Flutter Web example -> Dart server | Streamable HTTP | MCP 2026-07-28 | [`example/flutter_http_client/test/browser_e2e_test.dart`](../example/flutter_http_client/test/browser_e2e_test.dart), [`tool/testing/run_flutter_web_example_e2e.dart`](../tool/testing/run_flutter_web_example_e2e.dart), [`test_core.yml`](../.github/workflows/test_core.yml) | Verified | The example's real service layer runs in Chrome and completes connection, 12 tool-list requests, 12 tool calls, expected RPC-error recovery, reconnect, a post-reconnect request, and disconnect. Deterministic widget tests cover the UI separately. Flutter Web cannot spawn stdio servers. |
3636
| MCP Apps host/client metadata | stdio or Streamable HTTP | MCP 2026-07-28 plus `io.modelcontextprotocol/ui` extension | [`doc/mcp-apps.md`](mcp-apps.md), [`example/mcp_apps_helpers_server.dart`](../example/mcp_apps_helpers_server.dart), [`test/types/mcp_ui_test.dart`](../test/types/mcp_ui_test.dart), [`test/server/mcp_ui_test.dart`](../test/server/mcp_ui_test.dart) | Verified | Verified coverage is limited to SDK metadata helpers, serialization, and checked-in examples; host rendering behavior varies by host, so verify UI metadata against your target host. |
@@ -60,35 +60,50 @@ packages:
6060
cd test/interop/ts_2026_07_28
6161
npm ci
6262
cd ../../..
63-
dart run tool/testing/run_ts_2026_07_28_interop.dart
63+
dart run tool/testing/run_ts_2026_07_28_interop.dart \
64+
--direction=dart-to-ts
6465
```
6566

66-
This starts the Dart MCP 2026-07-28 conformance server, runs the pinned TypeScript
67-
SDK beta client against it, then runs the reverse Dart MCP 2026-07-28 client smoke check
68-
against the TypeScript SDK beta server.
67+
That direction verifies the compatible Dart client -> published TypeScript beta
68+
server path. The published beta.4 client still expects obsolete body
69+
`serverInfo`; CI keeps that reverse direction visible without weakening Dart's
70+
wire output by asserting the specific pre-#3002 negotiation failure:
6971

70-
`@modelcontextprotocol/client@2.0.0-beta.4` and
71-
`@modelcontextprotocol/server@2.0.0-beta.4` expose the required modern path and
72-
pass in both directions. The reverse path includes an `input_required`
73-
elicitation retry, bidirectional request-stream cancellation observed by each
74-
server, and successful post-cancellation tool calls in addition to discovery
75-
and normal tool calls.
72+
```bash
73+
dart run tool/testing/run_ts_2026_07_28_interop.dart \
74+
--direction=ts-to-dart \
75+
--expect-published-ts-client-gap
76+
```
77+
78+
An unexpected pass or a different failure is an error, so the workaround cannot
79+
silently become stale. To validate a TypeScript SDK #2513 preview installation,
80+
run the same `ts-to-dart` direction without the expected-gap flag.
81+
82+
The Dart-client path includes an `input_required` elicitation retry,
83+
request-stream cancellation observed by the TypeScript server, and successful
84+
post-cancellation tool calls in addition to discovery and normal tool calls.
7685

7786
The `Run MCP 2026-07-28 Interop` workflow covers relevant PRs, manual dispatch,
7887
and a daily schedule on `main`.
7988

80-
The official Python SDK beta fixture runs independently in both directions:
89+
The official Python SDK beta fixture runs each direction independently:
8190

8291
```bash
8392
python3 -m venv .dart_tool/python-2026-interop
8493
.dart_tool/python-2026-interop/bin/python -m pip install \
8594
-r test/interop/python_2026_07_28/requirements.txt
8695
MCP_PYTHON=.dart_tool/python-2026-interop/bin/python \
87-
dart run tool/testing/run_python_2026_07_28_interop.dart
96+
dart run tool/testing/run_python_2026_07_28_interop.dart \
97+
--direction=dart-to-python
98+
MCP_PYTHON=.dart_tool/python-2026-interop/bin/python \
99+
dart run tool/testing/run_python_2026_07_28_interop.dart \
100+
--direction=python-to-dart \
101+
--expect-published-python-client-gap
88102
```
89103

90-
It verifies automatic MCP 2026-07-28 discovery plus tool listing and execution for both
91-
Python client -> Dart server and Dart client -> Python server.
104+
The Dart client -> Python beta server path remains required. The reverse path
105+
asserts the published beta's exact pre-#3002 fallback; an unexpected pass or a
106+
different failure is an error so the exception cannot silently become stale.
92107

93108
The browser fixture runs the web implementation in Chrome against the same
94109
Dart conformance server:
@@ -146,4 +161,6 @@ When adding a new interoperability claim:
146161
- A broader compatibility table once additional SDKs expose stable MCP
147162
2025-11-25 fixtures.
148163
- Request-scoped cancellation against Python SDK beta and additional peer
149-
implementations; both TypeScript SDK beta directions are verified.
164+
implementations. The published TypeScript beta.4 server direction is
165+
verified, its client direction remains the documented pre-#3002 gap, and the
166+
TypeScript SDK #2513 preview verifies the final reverse path.

doc/mcp-2026-07-28-release-runbook.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,21 @@ dart run test/conformance/run_2026_07_28_server_conformance.dart \
9898
dart run test/conformance/run_2026_07_28_client_conformance.dart \
9999
--timeout-seconds 90
100100
cd test/interop/ts_2026_07_28 && npm ci && cd ../../..
101-
dart run tool/testing/run_ts_2026_07_28_interop.dart
101+
dart run tool/testing/run_ts_2026_07_28_interop.dart \
102+
--direction=dart-to-ts
103+
dart run tool/testing/run_ts_2026_07_28_interop.dart \
104+
--direction=ts-to-dart \
105+
--expect-published-ts-client-gap
102106
python3 -m venv .dart_tool/python-2026-interop
103107
.dart_tool/python-2026-interop/bin/python -m pip install \
104108
-r test/interop/python_2026_07_28/requirements.txt
105109
MCP_PYTHON=.dart_tool/python-2026-interop/bin/python \
106-
dart run tool/testing/run_python_2026_07_28_interop.dart
110+
dart run tool/testing/run_python_2026_07_28_interop.dart \
111+
--direction=dart-to-python
112+
MCP_PYTHON=.dart_tool/python-2026-interop/bin/python \
113+
dart run tool/testing/run_python_2026_07_28_interop.dart \
114+
--direction=python-to-dart \
115+
--expect-published-python-client-gap
107116
dart run tool/testing/run_browser_2026_07_28_interop.dart
108117
dart run tool/testing/run_flutter_web_example_e2e.dart
109118
dart pub publish --dry-run

doc/mcp-2026-07-28.md

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,40 @@ Use `McpServerOptions(protocol: McpProtocol.legacy)` to advertise only legacy
6363
MCP versions, or `McpServerOptions(protocol: McpProtocol.require2026)` to
6464
reject legacy initialization.
6565

66+
## Stateless identity metadata
67+
68+
MCP `2026-07-28` carries peer identity in stateless metadata:
69+
70+
- Clients normally include `_meta["io.modelcontextprotocol/clientInfo"]` on
71+
every request, but the field is optional. Servers accept it when omitted and
72+
reject a value that is present but is not a valid `Implementation` object.
73+
- `McpServer` stamps its configured identity in
74+
`_meta["io.modelcontextprotocol/serverInfo"]` on successful stateless results
75+
by default. A valid handler-authored `Implementation` value at that key wins;
76+
`null` configures an anonymous result by omitting the key, while malformed
77+
non-null values fail before serialization. `server/discover` no longer emits
78+
a body `serverInfo` field.
79+
- `DiscoverResult.serverInfo` is nullable and reads result metadata for
80+
convenience. Missing canonical identity is anonymous; a present malformed or
81+
`null` canonical identity is rejected. The temporary pre-#3002 body fallback
82+
ignores malformed legacy identity. Applications must not use self-reported
83+
identity for security or protocol behavior.
84+
85+
Code that previously dereferenced discovery identity must now handle an
86+
anonymous server:
87+
88+
```dart
89+
final discovery = await client.discoverServer();
90+
final serverInfo = discovery.serverInfo;
91+
if (serverInfo != null) {
92+
print('${serverInfo.name} ${serverInfo.version}');
93+
}
94+
```
95+
96+
The parser temporarily accepts the pre-spec top-level discovery identity sent by
97+
published TypeScript and Python SDK beta releases. Dart never emits that legacy
98+
shape; the fallback can be removed after corrected peer releases are pinned.
99+
66100
## Profile summary
67101

68102
| Profile | Default? | Client behavior | Server behavior |
@@ -162,10 +196,11 @@ matching dev.2 SDK dependency.
162196
while `dev/2026-07-28-rc` remains a read-only archive for links embedded in the
163197
earlier dev.0 and dev.1 packages.
164198

165-
CI runs official MCP 2025-11-25 and MCP 2026-07-28 conformance, pinned
166-
spec-example audits, bidirectional TypeScript and Python interop, real-browser
167-
transport tests, a Flutter Web service integration in Chrome, and widget
168-
tests. See
199+
CI runs official MCP 2025-11-25 and MCP 2026-07-28 conformance plus pinned
200+
spec-example audits, published TypeScript and Python server interop, exact
201+
published-beta client expected-gap checks for both SDKs, real-browser transport
202+
tests, a Flutter Web service integration in Chrome, and widget tests.
203+
TypeScript SDK #2513 preview interop is validated separately before release. See
169204
the [interoperability guide](interoperability.md#running-interop-checks-locally)
170205
for local commands and the
171206
[day-0 release runbook](mcp-2026-07-28-release-runbook.md) for the final tag,

0 commit comments

Comments
 (0)