registerMcpOAuthClient function

Future<McpOAuthDynamicClientRegistration> registerMcpOAuthClient({
  1. required McpAuthorizationServerMetadata authorizationServer,
  2. required McpOAuthDynamicClientRegistrationRequest registration,
  3. String? initialAccessToken,
  4. HttpClient? httpClient,
  5. Map<String, String> headers = const <String, String>{},
  6. Duration timeout = _defaultRegistrationTimeout,
  7. int maxResponseBytes = _defaultRegistrationResponseBytes,
  8. void onRequestOpened(
    1. HttpClientRequest request
    )?,
})

Registers a public OAuth client through the advertised RFC 7591 endpoint.

onRequestOpened observes the request before it is sent so an owning client can bind it to a larger lifecycle.

Implementation

Future<McpOAuthDynamicClientRegistration> registerMcpOAuthClient({
  required McpAuthorizationServerMetadata authorizationServer,
  required McpOAuthDynamicClientRegistrationRequest registration,
  String? initialAccessToken,
  HttpClient? httpClient,
  Map<String, String> headers = const <String, String>{},
  Duration timeout = _defaultRegistrationTimeout,
  int maxResponseBytes = _defaultRegistrationResponseBytes,
  void Function(HttpClientRequest request)? onRequestOpened,
}) async {
  final endpoint = authorizationServer.registrationEndpoint;
  if (endpoint == null) {
    throw McpOAuthClientRegistrationException(
      'Authorization server does not advertise a dynamic client '
      'registration endpoint.',
      endpoint: authorizationServer.issuer,
    );
  }
  _validateRegistrationInputs(
    endpoint: endpoint,
    initialAccessToken: initialAccessToken,
    headers: headers,
    timeout: timeout,
    maxResponseBytes: maxResponseBytes,
  );

  final encodedBody = utf8.encode(jsonEncode(registration.toJson()));
  final client = httpClient ?? HttpClient();
  final ownsClient = httpClient == null;
  final deadline = DateTime.now().add(timeout);
  HttpClientRequest? request;

  try {
    request = await client
        .postUrl(endpoint)
        .timeout(_remaining(deadline, endpoint));
    try {
      onRequestOpened?.call(request);
    } catch (error) {
      request.abort(error);
      rethrow;
    }
    request.followRedirects = false;
    request.headers.contentType = ContentType.json;
    request.headers.set(HttpHeaders.acceptHeader, 'application/json');
    for (final entry in headers.entries) {
      request.headers.set(entry.key, entry.value);
    }
    if (initialAccessToken != null) {
      request.headers.set(
        HttpHeaders.authorizationHeader,
        'Bearer $initialAccessToken',
      );
    }
    request.contentLength = encodedBody.length;
    request.add(encodedBody);

    final response = await request.close().timeout(
      _remaining(deadline, endpoint),
    );
    final body = await _readRegistrationResponse(
      response,
      endpoint: endpoint,
      maxResponseBytes: maxResponseBytes,
    ).timeout(_remaining(deadline, endpoint));
    final registrationResponse = _ClientRegistrationResponse(
      statusCode: response.statusCode,
      mimeType: response.headers.contentType?.mimeType,
      body: body,
    );
    return _parseRegistrationResponse(
      registrationResponse,
      endpoint: endpoint,
      authorizationServer: authorizationServer,
      request: registration,
    );
  } on McpOAuthClientRegistrationException {
    rethrow;
  } on TimeoutException {
    request?.abort();
    throw McpOAuthClientRegistrationException(
      'Dynamic client registration request timed out.',
      endpoint: endpoint,
    );
  } on Object {
    request?.abort();
    throw McpOAuthClientRegistrationException(
      'Dynamic client registration request failed.',
      endpoint: endpoint,
    );
  } finally {
    if (ownsClient) {
      client.close(force: true);
    }
  }
}