Skip to content

Commit 0b22b67

Browse files
authored
Update to new MCP spec version (#193)
1 parent a74efc3 commit 0b22b67

19 files changed

+846
-90
lines changed

pkgs/dart_mcp/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
- Added error checking to required fields of all `Request` subclasses so that
44
they will throw helpful errors when accessed and not set.
55
- Added enum support to Schema.
6+
- Updates to the latest MCP spec, [2025-06-08](https://modelcontextprotocol.io/specification/2025-06-18/changelog)
7+
- Adds support for Elicitations to allow the server to ask the user questions.
8+
- Adds `ResourceLink` as a tool return content type.
9+
- Adds support for structured tool output.
610

711
## 0.2.2
812

pkgs/dart_mcp/lib/src/api/api.dart

Lines changed: 184 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5-
/// Interfaces are based on https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.json
5+
/// Interfaces are based on
6+
/// https://github.com/modelcontextprotocol/specification/blob/main/schema/2025-06-18/schema.ts
67
library;
78

89
import 'dart:collection';
@@ -11,6 +12,7 @@ import 'package:collection/collection.dart';
1112
import 'package:json_rpc_2/json_rpc_2.dart';
1213

1314
part 'completions.dart';
15+
part 'elicitation.dart';
1416
part 'initialization.dart';
1517
part 'logging.dart';
1618
part 'prompts.dart';
@@ -22,7 +24,8 @@ part 'tools.dart';
2224
/// Enum of the known protocol versions.
2325
enum ProtocolVersion {
2426
v2024_11_05('2024-11-05'),
25-
v2025_03_26('2025-03-26');
27+
v2025_03_26('2025-03-26'),
28+
v2025_06_18('2025-06-18');
2629

2730
const ProtocolVersion(this.versionString);
2831

@@ -35,7 +38,7 @@ enum ProtocolVersion {
3538
static const oldestSupported = ProtocolVersion.v2024_11_05;
3639

3740
/// The most recent version supported by the current API.
38-
static const latestSupported = ProtocolVersion.v2025_03_26;
41+
static const latestSupported = ProtocolVersion.v2025_06_18;
3942

4043
/// The version string used over the wire to identify this version.
4144
final String versionString;
@@ -58,9 +61,65 @@ extension type ProgressToken( /*String|int*/ Object _) {}
5861
/// An opaque token used to represent a cursor for pagination.
5962
extension type Cursor(String _) {}
6063

61-
/// Generic metadata passed with most requests, can be anything.
64+
/// Generic metadata passed with most requests.
65+
///
66+
/// Metadata reserved by MCP to allow clients and servers to attach additional
67+
/// metadata to their interactions.
68+
///
69+
/// Certain key names are reserved by MCP for protocol-level metadata, as
70+
/// specified below; implementations MUST NOT make assumptions about values at
71+
/// these keys.
72+
///
73+
/// Additionally, definitions in the schema may reserve particular names for
74+
/// purpose-specific metadata, as declared in those definitions.
75+
///
76+
/// Key name format: valid `_meta` key names have two segments: an optional
77+
/// prefix, and a name.
78+
///
79+
/// - Prefix: If specified, MUST be a series of labels separated by dots
80+
/// (`.`), followed by a slash (`/`). Labels MUST start with a letter and
81+
/// end with a letter or digit; interior characters can be letters, digits,
82+
/// or hyphens (`-`). Any prefix beginning with zero or more valid labels,
83+
/// followed by `modelcontextprotocol` or `mcp`, followed by any valid
84+
/// label, is reserved for MCP use. For example: `modelcontextprotocol.io/`,
85+
/// `mcp.dev/`, `api.modelcontextprotocol.org/`, and `tools.mcp.com/` are
86+
/// all reserved.
87+
/// - Name: Unless empty, MUST begin and end with an alphanumeric character
88+
/// (`[a-z0-9A-Z]`). MAY contain hyphens (`-`), underscores (`_`), dots
89+
/// (`.`), and alphanumerics in between.
6290
extension type Meta.fromMap(Map<String, Object?> _value) {}
6391

92+
/// Basic metadata required by multiple types.
93+
///
94+
/// Not to be confused with the `_meta` property in the spec, which has a
95+
/// different purpose.
96+
extension type BaseMetadata.fromMap(Map<String, Object?> _value)
97+
implements Meta {
98+
factory BaseMetadata({required String name, String? title}) =>
99+
BaseMetadata.fromMap({'name': name, 'title': title});
100+
101+
/// Intended for programmatic or logical use, but used as a display name in
102+
/// past specs for fallback (if title isn't present).
103+
String get name {
104+
final name = _value['name'] as String?;
105+
if (name == null) {
106+
throw ArgumentError('Missing name field in $runtimeType');
107+
}
108+
return name;
109+
}
110+
111+
/// A short title for this object.
112+
///
113+
/// Intended for UI and end-user contexts — optimized to be human-readable and
114+
/// easily understood, even by those unfamiliar with domain-specific
115+
/// terminology.
116+
///
117+
/// If not provided, the name should be used for display (except for Tool,
118+
/// where `annotations.title` should be given precedence over using `name`, if
119+
/// present).
120+
String? get title => _value['title'] as String?;
121+
}
122+
64123
/// A "mixin"-like extension type for any extension type that might contain a
65124
/// [ProgressToken] at the key "progressToken".
66125
///
@@ -78,10 +137,22 @@ extension type MetaWithProgressToken.fromMap(Map<String, Object?> _value)
78137
MetaWithProgressToken.fromMap({'progressToken': progressToken});
79138
}
80139

140+
/// Base interface for all types that can have arbitrary metadata attached.
141+
///
142+
/// Should not be constructed directly, and has no public constructor.
143+
extension type WithMetadata._fromMap(Map<String, Object?> _value) {
144+
/// The `_meta` property/parameter is reserved by MCP to allow clients and
145+
/// servers to attach additional metadata to their interactions.
146+
///
147+
/// See [Meta] for more information about the format of these values.
148+
Meta? get meta => _value['_meta'] as Meta?;
149+
}
150+
81151
/// Base interface for all request types.
82152
///
83153
/// Should not be constructed directly, and has no public constructor.
84-
extension type Request._fromMap(Map<String, Object?> _value) {
154+
extension type Request._fromMap(Map<String, Object?> _value)
155+
implements WithMetadata {
85156
/// If specified, the caller is requesting out-of-band progress notifications
86157
/// for this request (as represented by notifications/progress).
87158
///
@@ -273,15 +344,19 @@ extension type Content._(Map<String, Object?> _value) {
273344

274345
/// Text provided to or from an LLM.
275346
extension type TextContent.fromMap(Map<String, Object?> _value)
276-
implements Content, Annotated {
347+
implements Content, Annotated, WithMetadata {
277348
static const expectedType = 'text';
278349

279-
factory TextContent({required String text, Annotations? annotations}) =>
280-
TextContent.fromMap({
281-
'text': text,
282-
'type': expectedType,
283-
if (annotations != null) 'annotations': annotations,
284-
});
350+
factory TextContent({
351+
required String text,
352+
Annotations? annotations,
353+
Meta? meta,
354+
}) => TextContent.fromMap({
355+
'text': text,
356+
'type': expectedType,
357+
if (annotations != null) 'annotations': annotations,
358+
if (meta != null) '_meta': meta,
359+
});
285360

286361
String get type {
287362
final type = _value['type'] as String;
@@ -295,18 +370,20 @@ extension type TextContent.fromMap(Map<String, Object?> _value)
295370

296371
/// An image provided to or from an LLM.
297372
extension type ImageContent.fromMap(Map<String, Object?> _value)
298-
implements Content, Annotated {
373+
implements Content, Annotated, WithMetadata {
299374
static const expectedType = 'image';
300375

301376
factory ImageContent({
302377
required String data,
303378
required String mimeType,
304379
Annotations? annotations,
380+
Meta? meta,
305381
}) => ImageContent.fromMap({
306382
'data': data,
307383
'mimeType': mimeType,
308384
'type': expectedType,
309385
if (annotations != null) 'annotations': annotations,
386+
if (meta != null) '_meta': meta,
310387
});
311388

312389
String get type {
@@ -328,18 +405,20 @@ extension type ImageContent.fromMap(Map<String, Object?> _value)
328405
///
329406
/// Only supported since version [ProtocolVersion.v2025_03_26].
330407
extension type AudioContent.fromMap(Map<String, Object?> _value)
331-
implements Content, Annotated {
408+
implements Content, Annotated, WithMetadata {
332409
static const expectedType = 'audio';
333410

334411
factory AudioContent({
335412
required String data,
336413
required String mimeType,
337414
Annotations? annotations,
415+
Meta? meta,
338416
}) => AudioContent.fromMap({
339417
'data': data,
340418
'mimeType': mimeType,
341419
'type': expectedType,
342420
if (annotations != null) 'annotations': annotations,
421+
if (meta != null) '_meta': meta,
343422
});
344423

345424
String get type {
@@ -362,16 +441,18 @@ extension type AudioContent.fromMap(Map<String, Object?> _value)
362441
/// It is up to the client how best to render embedded resources for the benefit
363442
/// of the LLM and/or the user.
364443
extension type EmbeddedResource.fromMap(Map<String, Object?> _value)
365-
implements Content, Annotated {
444+
implements Content, Annotated, WithMetadata {
366445
static const expectedType = 'resource';
367446

368447
factory EmbeddedResource({
369448
required Content resource,
370449
Annotations? annotations,
450+
Meta? meta,
371451
}) => EmbeddedResource.fromMap({
372452
'resource': resource,
373453
'type': expectedType,
374454
if (annotations != null) 'annotations': annotations,
455+
if (meta != null) '_meta': meta,
375456
});
376457

377458
String get type {
@@ -386,6 +467,76 @@ extension type EmbeddedResource.fromMap(Map<String, Object?> _value)
386467
String? get mimeType => _value['mimeType'] as String?;
387468
}
388469

470+
/// A resource link returned from a tool.
471+
///
472+
/// Resource links returned by tools are not guaranteed to appear in the results
473+
/// of a `resources/list` request.
474+
extension type ResourceLink.fromMap(Map<String, Object?> _value)
475+
implements Content, Annotated, WithMetadata, BaseMetadata {
476+
static const expectedType = 'resource_link';
477+
478+
factory ResourceLink({
479+
required String name,
480+
String? title,
481+
required String description,
482+
required String uri,
483+
required String mimeType,
484+
Annotations? annotations,
485+
Meta? meta,
486+
}) => ResourceLink.fromMap({
487+
'name': name,
488+
if (title != null) 'title': title,
489+
'description': description,
490+
'uri': uri,
491+
'mimeType': mimeType,
492+
'type': expectedType,
493+
if (annotations != null) 'annotations': annotations,
494+
if (meta != null) '_meta': meta,
495+
});
496+
497+
String get type {
498+
final type = _value['type'] as String;
499+
assert(type == expectedType);
500+
return type;
501+
}
502+
503+
/// The name of the resource.
504+
String get name {
505+
final name = _value['name'] as String?;
506+
if (name == null) {
507+
throw ArgumentError('Missing name field in $ResourceLink.');
508+
}
509+
return name;
510+
}
511+
512+
/// The description of the resource.
513+
String get description {
514+
final description = _value['description'] as String?;
515+
if (description == null) {
516+
throw ArgumentError('Missing description field in $ResourceLink.');
517+
}
518+
return description;
519+
}
520+
521+
/// The URI of the resource.
522+
String get uri {
523+
final uri = _value['uri'] as String?;
524+
if (uri == null) {
525+
throw ArgumentError('Missing uri field in $ResourceLink.');
526+
}
527+
return uri;
528+
}
529+
530+
/// The MIME type of the resource.
531+
String get mimeType {
532+
final mimeType = _value['mimeType'] as String?;
533+
if (mimeType == null) {
534+
throw ArgumentError('Missing mimeType field in $ResourceLink.');
535+
}
536+
return mimeType;
537+
}
538+
}
539+
389540
/// Base type for objects that include optional annotations for the client.
390541
///
391542
/// The client can use annotations to inform how objects are used or displayed.
@@ -396,10 +547,15 @@ extension type Annotated._fromMap(Map<String, Object?> _value) {
396547

397548
/// The annotations for an [Annotated] object.
398549
extension type Annotations.fromMap(Map<String, Object?> _value) {
399-
factory Annotations({List<Role>? audience, double? priority}) {
550+
factory Annotations({
551+
List<Role>? audience,
552+
DateTime? lastModified,
553+
double? priority,
554+
}) {
400555
assert(priority == null || (priority >= 0 && priority <= 1));
401556
return Annotations.fromMap({
402557
if (audience != null) 'audience': [for (var role in audience) role.name],
558+
if (lastModified != null) 'lastModified': lastModified.toIso8601String(),
403559
if (priority != null) 'priority': priority,
404560
});
405561
}
@@ -416,6 +572,18 @@ extension type Annotations.fromMap(Map<String, Object?> _value) {
416572
];
417573
}
418574

575+
/// Describes when this data was last modified.
576+
///
577+
/// The moment the resource was last modified.
578+
///
579+
/// Examples: last activity timestamp in an open file, timestamp when the
580+
/// resource was attached, etc.
581+
DateTime? get lastModified {
582+
final lastModified = _value['lastModified'] as String?;
583+
if (lastModified == null) return null;
584+
return DateTime.parse(lastModified);
585+
}
586+
419587
/// Describes how important this data is for operating the server.
420588
///
421589
/// A value of 1 means "most important," and indicates that the data is

0 commit comments

Comments
 (0)