generateSearchHandler function

String generateSearchHandler()

Generates the _search handler method.

Implementation

String generateSearchHandler() {
  return '''
  FutureOr<CallToolResult> _search(CallToolRequest request) async {
    try {
      final query = (request.arguments?['query'] as String?) ?? '';
      final detailLevel = (request.arguments?['detail_level'] as String?) ?? 'brief';

      // Validate query length
      if (query.length > $maxSearchQueryLength) {
        return CallToolResult(
          content: [TextContent(text: 'Search query exceeds maximum length of $maxSearchQueryLength characters.')],
          isError: true,
        );
      }

      final terms = query.toLowerCase().split(' ').where((t) => t.isNotEmpty).toList();

      if (terms.isEmpty) {
        final results = _codeModeToolSpecs.map((tool) =>
            _formatSearchResult(tool, detailLevel)).toList();
        return CallToolResult(
          content: [TextContent(text: jsonEncode(results))],
        );
      }

      // Phase 1: strict AND match — all terms must appear in name or description
      final andMatches = _codeModeToolSpecs.where((tool) {
        final name = (tool['name'] as String).toLowerCase();
        final desc = (tool['description'] as String).toLowerCase();
        return terms.every((term) => name.contains(term) || desc.contains(term));
      }).toList();

      List<Map<String, dynamic>> matches;
      if (andMatches.isNotEmpty) {
        matches = andMatches;
      } else {
        // Phase 2: ranked OR match — score each tool by how many terms it matches
        final scored = _codeModeToolSpecs.map((tool) {
          final name = (tool['name'] as String).toLowerCase();
          final desc = (tool['description'] as String).toLowerCase();
          int score = 0;
          for (final term in terms) {
            if (name.contains(term) || desc.contains(term)) score++;
          }
          return MapEntry(tool, score);
        }).where((e) => e.value > 0).toList();

        scored.sort((a, b) => b.value.compareTo(a.value));
        matches = scored.map((e) => e.key).toList();
      }

      final results = matches.map((tool) =>
          _formatSearchResult(tool, detailLevel)).toList();

      return CallToolResult(
        content: [TextContent(text: jsonEncode(results))],
      );
    } catch (e, st) {
      if (_logErrors) {
        io.stderr.writeln('[easy_api] _search: \$e');
        io.stderr.writeln(st);
        await io.stderr.flush();
      }
      return CallToolResult(
        content: [TextContent(text: 'An error occurred while processing the request.')],
        isError: true,
      );
    }
  }

  Map<String, dynamic> _formatSearchResult(
    Map<String, dynamic> tool,
    String detailLevel,
  ) {
    final name = tool['name'] as String;
    final desc = tool['description'] as String;
    final params = tool['parameters'] as List<Map<String, dynamic>>;

    if (detailLevel == 'brief') {
      return {'name': name, 'description': desc};
    } else if (detailLevel == 'detailed') {
      final paramInfo = params.map((p) => {
        'name': p['name'],
        'type': p['type'],
        'required': p['required'],
      }).toList();
      return {'name': name, 'description': desc, 'parameters': paramInfo};
    } else {
      final paramInfo = params.map((p) {
        final map = <String, dynamic>{
          'name': p['name'],
          'type': p['type'],
          'required': p['required'],
        };
        return map;
      }).toList();
      return {'name': name, 'description': desc, 'parameters': paramInfo};
    }
  }
  // ignore: prefer_adjacent_string_concatenation
  ''';
}