listToolsPage method

Future<ListToolsResult> listToolsPage({
  1. String? cursor,
  2. Map<String, dynamic>? meta,
  3. RequestOptions? options,
})

One page of tools/list: pass the previous page's nextCursor as cursor for the next one.

Implementation

Future<ListToolsResult> listToolsPage({String? cursor, Map<String, dynamic>? meta, RequestOptions? options}) async {
  if (!_initialized) {
    throw McpError('Client is not initialized');
  }

  if (!_statelessMode && _serverCapabilities?.tools != true) {
    throw McpError('Server does not support tools');
  }

  final response = await _sendRequest(
      'tools/list', _withMeta({if (cursor != null) 'cursor': cursor}, meta), options);
  var result = ListToolsResult.fromJson(Map<String, dynamic>.from(response as Map));
  // 2026-07-28 Streamable HTTP: a tool whose x-mcp-header annotations are
  // invalid is left out of the list (with a warning), the rest stay usable.
  if (_mirrorsParamHeaders) {
    final kept = <Tool>[];
    for (final tool in result.tools) {
      final annotations = mcpParamHeaders(tool.toJson()['inputSchema']);
      if (annotations.problem != null) {
        _logger.warning('Tool "${tool.name}" rejected: ${annotations.problem}');
        continue;
      }
      _toolParamHeaders[tool.name] = annotations.headers;
      kept.add(tool);
    }
    if (kept.length != result.tools.length) {
      result = ListToolsResult.fromJson({
        ...result.toJson(),
        'tools': [for (final t in kept) t.toJson()],
      });
    }
  }
  for (final tool in result.tools) {
    _rememberOutputSchema(tool);
    final execution = tool.toJson()['execution'];
    _toolTaskSupport[tool.name] =
        (execution is Map ? execution['taskSupport'] as String? : null) ?? 'forbidden';
  }
  return result;
}