generateSearchHandler function
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
''';
}