blob: 2e699954e3a60f60c1a3818754587031cb92567b [file]
// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
part of 'api.dart';
/// A filter over the notifications a subscription carries.
///
/// A client sends one on a [SubscriptionsListenRequest] to opt in, and the
/// server sends one back on a [SubscriptionsAcknowledgedNotification] with the
/// types it agreed to. A type the server does not support is left out rather
/// than sent back as `false`.
///
/// From the 2026-07-28 revision.
extension type SubscriptionFilter.fromMap(Map<String, Object?> _value) {
factory SubscriptionFilter({
bool? toolsListChanged,
bool? promptsListChanged,
bool? resourcesListChanged,
List<String>? resourceSubscriptions,
}) => SubscriptionFilter.fromMap({
if (toolsListChanged != null) Keys.toolsListChanged: toolsListChanged,
if (promptsListChanged != null) Keys.promptsListChanged: promptsListChanged,
if (resourcesListChanged != null)
Keys.resourcesListChanged: resourcesListChanged,
if (resourceSubscriptions != null)
Keys.resourceSubscriptions: resourceSubscriptions,
});
/// If true, receive [ToolListChangedNotification]s.
bool? get toolsListChanged => _value[Keys.toolsListChanged] as bool?;
/// If true, receive [PromptListChangedNotification]s.
bool? get promptsListChanged => _value[Keys.promptsListChanged] as bool?;
/// If true, receive [ResourceListChangedNotification]s.
bool? get resourcesListChanged => _value[Keys.resourcesListChanged] as bool?;
/// The resource URIs to receive [ResourceUpdatedNotification]s for.
///
/// In the 2026-07-28 revision this field replaces the `resources/subscribe`
/// and `resources/unsubscribe` requests, which [SubscribeRequest] and
/// [UnsubscribeRequest] model.
List<String>? get resourceSubscriptions =>
(_value[Keys.resourceSubscriptions] as List?)?.cast<String>();
}
/// Sent from the client to open a long-lived stream for the notifications
/// which do not belong to a specific request.
///
/// This replaces the HTTP GET endpoint earlier revisions used, so stdio and
/// Streamable HTTP deliver those notifications the same way.
///
/// From the 2026-07-28 revision.
extension type SubscriptionsListenRequest.fromMap(Map<String, Object?> _value)
implements Request {
static const methodName = 'subscriptions/listen';
factory SubscriptionsListenRequest({
required SubscriptionFilter notifications,
MetaWithProgressToken? meta,
}) => SubscriptionsListenRequest.fromMap({
Keys.notifications: notifications,
if (meta != null) Keys.meta: meta,
});
/// The notification types this request opts in to.
SubscriptionFilter get notifications {
final notifications = _value[Keys.notifications] as SubscriptionFilter?;
if (notifications == null) {
throw ArgumentError(
'Missing ${Keys.notifications} field in $SubscriptionsListenRequest.',
);
}
return notifications;
}
}
/// A "mixin"-like extension type for any message on a `subscriptions/listen`
/// stream that carries a [MetaWithSubscriptionId] at the key "_meta".
///
/// Should be "mixed in" by implementing this type from other extension types.
///
/// This type is not intended to be constructed directly and thus has no public
/// constructor.
extension type WithSubscriptionId._fromMap(Map<String, Object?> _value) {
/// The metadata this message carries, including the [subscriptionId].
MetaWithSubscriptionId get meta {
final meta = _value[Keys.meta];
if (meta == null) {
throw ArgumentError('Missing ${Keys.meta} field in $WithSubscriptionId.');
}
return MetaWithSubscriptionId.fromMap(meta as Map<String, Object?>);
}
/// The JSON-RPC ID of the [SubscriptionsListenRequest] which opened the
/// stream this message belongs to.
///
/// Every message on the stream carries it under the
/// `io.modelcontextprotocol/subscriptionId` metadata key, so a client can
/// tell its streams apart.
RequestId get subscriptionId => meta.subscriptionId;
}
/// A [Meta] object carrying the [subscriptionId] every message on a
/// `subscriptions/listen` stream needs.
///
/// Has arbitrary other keys.
extension type MetaWithSubscriptionId.fromMap(Map<String, Object?> _value)
implements Meta {
factory MetaWithSubscriptionId({required RequestId subscriptionId}) =>
MetaWithSubscriptionId.fromMap({Keys.subscriptionIdMeta: subscriptionId});
/// The JSON-RPC ID of the [SubscriptionsListenRequest] which opened the
/// stream this metadata belongs to.
RequestId get subscriptionId {
final subscriptionId = _value[Keys.subscriptionIdMeta];
if (subscriptionId == null) {
throw ArgumentError(
'Missing ${Keys.subscriptionIdMeta} in $MetaWithSubscriptionId.',
);
}
return RequestId(subscriptionId);
}
}
/// The response to a [SubscriptionsListenRequest], sent when the server ends
/// the subscription gracefully, for example while shutting down.
///
/// The stream is long-lived, so there is no response while it is open, and an
/// abrupt transport close carries none at all. The result has no fields of its
/// own: the [subscriptionId] travels in its metadata.
///
/// From the 2026-07-28 revision.
extension type SubscriptionsListenResult.fromMap(Map<String, Object?> _value)
implements Result, WithSubscriptionId {
factory SubscriptionsListenResult({required MetaWithSubscriptionId meta}) =>
SubscriptionsListenResult.fromMap({Keys.meta: meta});
/// The metadata this message carries, including the [subscriptionId].
///
/// [Result] and [Notification] both declare a nullable `meta`, so each type
/// mixing in [WithSubscriptionId] narrows it here rather than inheriting it.
MetaWithSubscriptionId get meta => WithSubscriptionId._fromMap(_value).meta;
}
/// Sent by the server to acknowledge a [SubscriptionsListenRequest] and report
/// the notification types it agreed to send.
///
/// This is the first message carrying the subscription's ID. Over stdio every
/// subscription shares one channel, so messages from other subscriptions may
/// arrive before this acknowledgement.
///
/// From the 2026-07-28 revision.
extension type SubscriptionsAcknowledgedNotification.fromMap(
Map<String, Object?> _value
) implements Notification, WithSubscriptionId {
static const methodName = 'notifications/subscriptions/acknowledged';
factory SubscriptionsAcknowledgedNotification({
required SubscriptionFilter notifications,
required MetaWithSubscriptionId meta,
}) => SubscriptionsAcknowledgedNotification.fromMap({
Keys.notifications: notifications,
Keys.meta: meta,
});
/// The metadata this message carries, including the [subscriptionId].
///
/// [Result] and [Notification] both declare a nullable `meta`, so each type
/// mixing in [WithSubscriptionId] narrows it here rather than inheriting it.
MetaWithSubscriptionId get meta => WithSubscriptionId._fromMap(_value).meta;
/// The notification types the server agreed to send on this stream.
SubscriptionFilter get notifications {
final notifications = _value[Keys.notifications] as SubscriptionFilter?;
if (notifications == null) {
throw ArgumentError(
'Missing ${Keys.notifications} field in '
'$SubscriptionsAcknowledgedNotification.',
);
}
return notifications;
}
}