call method

Future call({
  1. required Client client,
  2. required String tool,
  3. required Map<String, dynamic> params,
  4. String? scope,
})

Implementation

Future<dynamic> call({
  required Client client,
  required String tool,
  required Map<String, dynamic> params,
  String? scope,
}) async {
  _logger.debug('Tool call', {'tool': tool, 'params': params});

  // Try in-process first. If the tool is registered we skip the
  // external MCP forward and resolve it directly.
  if (resolveInProcess(tool, scope: scope) != null) {
    return _callInProcessForRuntime(tool, params, scope: scope);
  }

  final List<Tool> tools;
  try {
    tools = await client.listTools();
  } catch (e, st) {
    _logger.logError('listTools failed', e, st, {'tool': tool});
    throw ToolExecutionException(tool, cause: e);
  }

  if (!tools.any((t) => t.name == tool)) {
    throw ToolNotFoundException(
      tool,
      tools.map((t) => t.name).toList(),
    );
  }

  final CallToolResult result;
  try {
    result = await client.callTool(tool, params);
  } catch (e, st) {
    _logger.logError('callTool failed', e, st, {'tool': tool});
    throw ToolExecutionException(tool, cause: e);
  }

  _logger
      .debug('Tool result', {'tool': tool, 'items': result.content.length});

  // The server refused the call (a rate limit, a rule): the runtime must see
  // a failure and fire the document's `onError`. Read as a payload, the
  // refusal text is not JSON, came back null, and the runtime took the call
  // for a success — the button did nothing and said nothing.
  if (result.isError == true) {
    final text = result.content.whereType<TextContent>().map((c) => c.text);
    return <String, dynamic>{
      'content': <Map<String, dynamic>>[
        <String, dynamic>{'type': 'text', 'text': text.join('\n')},
      ],
      'isError': true,
    };
  }

  if (result.content.isEmpty) return null;
  final first = result.content.first;
  if (first is! TextContent) return null;

  try {
    return jsonDecode(first.text);
  } catch (e) {
    _logger.warn(
        'Failed to parse tool response',
        {
          'tool': tool,
          'text': first.text,
        },
        e);
    return null;
  }
}