connect method
Connect the client to a transport.
When statelessMode is true, the client speaks the 2026-07-28 stateless
core: it skips the initialize handshake, marks the transport to stamp
MCP-Protocol-Version: 2026-07-28 on every request, and attaches the
reverse-DNS _meta keys (client info + per-request capabilities) to each
outbound request. Server capabilities are fetched on demand via
discover. Default false (dormant) — the handshake path is unchanged.
Implementation
Future<void> connect(
ClientTransport transport, {
bool statelessMode = false,
}) async {
if (_transport != null) {
throw McpError('Client is already connected to a transport');
}
if (_connecting) {
throw McpError('Client is already connecting to a transport');
}
_connecting = true;
_statelessMode = statelessMode;
_transport = transport;
_transport!.onMessage.listen(_handleMessage);
_transport!.onClose
.then((_) {
// Only send disconnect event if we're still connected
if (_transport != null) {
_disconnectStreamController.add(DisconnectReason.transportClosed);
_onDisconnect();
}
})
.catchError((error) {
// Only handle error if we're still connected
if (_transport != null) {
_errorStreamController.add(McpError('Transport error: $error'));
_disconnectStreamController.add(DisconnectReason.transportError);
_onDisconnect();
}
});
// Set up message handling
_messageController.stream.listen((message) async {
try {
await _processMessage(message);
} catch (e) {
_logger.debug('Error processing message: $e');
_errorStreamController.add(McpError('Error processing message: $e'));
}
});
// Initialize the connection
try {
if (_statelessMode) {
// Stateless core: no handshake. Record the version so the HTTP
// transport stamps `MCP-Protocol-Version: 2026-07-28`, and mark the
// client ready to send requests immediately. Server capabilities are
// fetched on demand via [discover].
_negotiatedProtocolVersion = McpProtocol.v2026_07_28;
final tx = _transport;
if (tx is StreamableHttpClientTransport) {
tx.setProtocolVersion(McpProtocol.v2026_07_28);
}
_initialized = true;
} else {
await initialize();
}
_connecting = false;
// Emit connection event after successful initialization
if (_initialized && _serverInfo != null && _serverCapabilities != null) {
_connectStreamController.add(
ServerInfo(
name: _serverInfo!['name'] as String? ?? 'Unknown',
version: _serverInfo!['version'] as String? ?? 'Unknown',
capabilities: _serverCapabilities!.toJson(),
protocolVersion: protocolVersion,
),
);
}
} catch (e) {
_connecting = false;
_errorStreamController.add(McpError('Initialization error: $e'));
disconnect();
rethrow;
}
}