reattach method
Future<ReattachResult>
reattach({
- required Client client,
- required MCPUIRuntime runtime,
- required String ownerKey,
Re-issues every subscription recorded for ownerKey on a NEW client.
A subscription belongs to the CONNECTION. After a background round trip the connection is torn down and rebuilt, and the server has no memory of what the previous link had subscribed — the stream simply stops. The runtime bindings, on the other hand, live in the runtime and survive, so nothing on screen looks wrong and pressing Subscribe again is a no-op from the runtime's point of view. Only the wire call has to be redone.
Bindings are therefore NOT re-registered here; the initial read is, so the first value after a resume is current rather than whatever was on screen when the app went away.
Implementation
Future<ReattachResult> reattach({
required Client client,
required MCPUIRuntime runtime,
required String ownerKey,
}) async {
final uris = _active[ownerKey];
if (uris == null || uris.isEmpty) return const ReattachResult();
var resubscribed = 0;
var failed = 0;
for (final uri in List<String>.from(uris)) {
try {
await SharedResourceSubscriptions.subscribe(client, uri);
resubscribed++;
} catch (e, st) {
failed++;
_logger.logError('resubscribe after reconnect failed', e, st,
{'uri': uri, 'ownerKey': ownerKey});
continue;
}
try {
final resource = await client.readResource(uri);
if (resource.contents.isEmpty) continue;
final text = resource.contents.first.text;
if (text == null) continue;
final decoded = jsonDecode(text);
if (decoded is Map<String, dynamic>) {
decoded.forEach(runtime.stateManager.set);
}
} catch (e) {
_logger.warn('resubscribe initial read failed', {'uri': uri}, e);
}
}
// Successes and failures, separately. This used to report the number of
// URIs ATTEMPTED under the name `count`, so a reattach where every
// resource was refused still printed `resubscribed … count: 2` with the
// failures on their own earlier lines — which reads as success and was
// misread as success by someone debugging a device that had gone quiet.
_logger.info('resubscribed after reconnect', {
'ownerKey': ownerKey,
'resubscribed': resubscribed,
'failed': failed,
});
return ReattachResult(resubscribed: resubscribed, failed: failed);
}