notifyResourceUpdated method
Send notifications/resources/updated for uri to the sessions
subscribed to it (on 2026-07-28, to the subscriptions/listen streams
that watch it).
content, when given, is added as a content key — not part of the
specification, read by clients that want the new contents without a
resources/read (mcp_client onResourceContentUpdated). It is one
resource-contents entry: a ResourceContents, an EmbeddedResource
(its resource is sent) or the JSON of either.
Implementation
void notifyResourceUpdated(String uri, {Object? content}) {
final Map<String, dynamic>? contentJson = switch (content) {
null => null,
EmbeddedResource() => Map<String, dynamic>.from(
content.resource.toJson(),
),
ResourceContents() => Map<String, dynamic>.from(content.toJson()),
Map() => Map<String, dynamic>.from(content),
_ =>
throw ArgumentError.value(
content,
'content',
'is not resource contents',
),
};
// Invalidate cache
_resourceCache.remove(uri);
if (!isConnected || !capabilities.hasResources) return;
// Build notification based on whether content is provided
final Map<String, dynamic> notification;
if (contentJson != null) {
// Extended format: one resource-contents entry
notification = {'uri': uri, 'content': contentJson};
} else {
// Standard MCP 2025-03-26 format (URI only)
notification = {'uri': uri};
}
// 2026-07-28 (SEP-2577): fan out to `subscriptions/listen` streams whose
// filter opted into this URI (independent of the legacy subscribe registry).
_fanOutToSubscriptions(
'notifications/resources/updated',
notification,
uri: uri,
);
final subscribers = _resourceSubscriptions[uri];
if (subscribers == null || subscribers.isEmpty) return;
for (final sessionId in subscribers) {
if (_sessions.containsKey(sessionId)) {
_sendNotification(
sessionId,
'notifications/resources/updated',
notification,
);
}
}
}