| íëĒŠ | ë´ėŠ |
|---|---|
| Tools | Read, Glob, Grep |
| Model | haiku |
| Skills | flutter-inspector |
Flutter Inspector - Log Agent#
A specialized agent for managing and analyzing app logs at runtime.
Triggers#
Automatically activated when @flutter-inspector-log is invoked or the following keywords are detected:
- Log, debug message
- Error log, warning
- Log search, filtering
MCP Tools#
log_get_recent#
Returns recent logs.
{
" name " : " log_get_recent " ,
" description " : " Retrieve recent logs " ,
" inputSchema " : {
" type " : " object " ,
" properties " : {
" limit " : {
" type " : " integer " ,
" description " : " Maximum count " ,
" default " : 50
},
" level " : {
" type " : " string " ,
" description " : " Filter: log level (debug, info, warning, error) " ,
" enum " : [ " debug " , " info " , " warning " , " error " ]
}
}
}
}
Response example:
{
" logs " : [
{
" timestamp " : " 2024-01-01T10:00:00Z " ,
" level " : " info " ,
" tag " : " AuthBloc " ,
" message " : " User logged in successfully " ,
" data " : { " userId " : 123}
},
{
" timestamp " : " 2024-01-01T10:00:05Z " ,
" level " : " error " ,
" tag " : " NetworkService " ,
" message " : " Request failed " ,
" data " : { " url " : " /api/users " , " statusCode " : 500}
}
],
" totalCount " : 2
}
log_get_errors#
Filters and returns only error logs.
{
" name " : " log_get_errors " ,
" description " : " Retrieve error logs " ,
" inputSchema " : {
" type " : " object " ,
" properties " : {
" limit " : {
" type " : " integer " ,
" description " : " Maximum count " ,
" default " : 20
},
" includeStackTrace " : {
" type " : " boolean " ,
" description " : " Whether to include stack trace " ,
" default " : true
}
}
}
}
Response example:
{
" errors " : [
{
" timestamp " : " 2024-01-01T10:00:05Z " ,
" tag " : " UserRepository " ,
" message " : " Failed to fetch user " ,
" error " : " SocketException: Connection refused " ,
" stackTrace " : " ... " ,
" context " : { " userId " : 123, " attempt " : 3}
}
],
" errorCount " : 1
}
log_search#
Searches for specific patterns in logs.
{
" name " : " log_search " ,
" description " : " Search logs " ,
" inputSchema " : {
" type " : " object " ,
" properties " : {
" query " : {
" type " : " string " ,
" description " : " Search string "
},
" tag " : {
" type " : " string " ,
" description " : " Filter: tag name "
},
" limit " : {
" type " : " integer " ,
" description " : " Maximum count " ,
" default " : 50
}
},
" required " : [ " query " ]
}
}
log_get_stats#
Returns log statistics.
{
" name " : " log_get_stats " ,
" description " : " Log statistics " ,
" inputSchema " : {
" type " : " object " ,
" properties " : {}
}
}
Response example:
{
" stats " : {
" totalLogs " : 500,
" byLevel " : {
" debug " : 200,
" info " : 250,
" warning " : 30,
" error " : 20
},
" topTags " : [
{ " tag " : " AuthBloc " , " count " : 50},
{ " tag " : " NetworkService " , " count " : 45},
{ " tag " : " HomeBloc " , " count " : 40}
],
" recentErrors " : 5
},
" period " : " session "
}
log_clear#
Clears logs.
{
" name " : " log_clear " ,
" description " : " Clear logs " ,
" inputSchema " : {
" type " : " object " ,
" properties " : {
" level " : {
" type " : " string " ,
" description " : " Delete only a specific level (omit for all) "
}
}
}
}
App Integration Code#
// lib/debug/mcp_log_tools.dart
import 'package:mcp_toolkit/mcp_toolkit.dart';
import 'package:logger/logger.dart';
class MCPLogOutput extends LogOutput {
final _logs = <Map<String, dynamic>>[];
static final instance = MCPLogOutput._();
MCPLogOutput._();
@override
void output(OutputEvent event) {
final log = {
'timestamp': DateTime.now().toIso8601String(),
'level': event.level.name,
'lines': event.lines,
};
_logs.add(log);
// Keep maximum 1000 entries
if (_logs.length > 1000) {
_logs.removeAt(0);
}
}
List<Map<String, dynamic>> getLogs({int? limit, String? level}) {
var filtered = _logs.toList();
if (level != null) {
filtered = filtered.where((l) => l['level'] == level).toList();
}
return filtered.reversed.take(limit ?? 50).toList();
}
List<Map<String, dynamic>> getErrors({int? limit}) {
return _logs
.where((l) => l['level'] == 'error' || l['level'] == 'wtf')
.toList()
.reversed
.take(limit ?? 20)
.toList();
}
List<Map<String, dynamic>> search(String query, {String? tag, int? limit}) {
return _logs
.where((l) => l['lines'].toString().contains(query))
.where((l) => tag == null || l['tag'] == tag)
.toList()
.reversed
.take(limit ?? 50)
.toList();
}
Map<String, dynamic> getStats() {
final byLevel = <String, int>{};
for (final log in _logs) {
final level = log['level'] as String;
byLevel[level] = (byLevel[level] ?? 0) + 1;
}
return {
'totalLogs': _logs.length,
'byLevel': byLevel,
};
}
void clear({String? level}) {
if (level != null) {
_logs.removeWhere((l) => l['level'] == level);
} else {
_logs.clear();
}
}
}
void registerLogTools() {
if (!kDebugMode) return;
addMcpTool(MCPCallEntry.tool(
handler: (params) => MCPCallResult(
message: 'Recent logs',
parameters: {
'logs': MCPLogOutput.instance.getLogs(
limit: params['limit'] as int?,
level: params['level'] as String?,
),
},
),
definition: MCPToolDefinition(
name: 'log_get_recent',
description: 'Retrieve recent logs',
inputSchema: {
'type': 'object',
'properties': {
'limit': {'type': 'integer'},
'level': {'type': 'string'},
},
},
),
));
// log_get_errors, log_search, log_get_stats, log_clear are implemented similarly
}
Usage Examples#
Check recent logs#
Q: Show me recent logs
A: Run log_get_recent limit=20
- > 20 logs (15 info, 3 warning, 2 error)
Check error logs#
Q: Show me only error logs
A: Run log_get_errors
- > 2 errors: SocketException, FormatException
Search specific keywords#
Q: Find authentication related logs
A: Run log_search query= " auth "
- > 15 logs related to AuthBloc, AuthRepository
Log statistics#
Q: Summarize the log status
A: Run log_get_stats
- > Total 500, 20 errors, top tags: AuthBloc, NetworkService
Common Problem Diagnosis#
Recurring errors#
1. Check error patterns with log_get_errors
2. Confirm repeated occurrences from the same tag
3. Analyze error context
Performance issue tracking#
1. log_search query= " slow " or " timeout "
2. Analyze logs around relevant timeframes
3. Identify bottleneck points
App crash cause#
1. Check last error with log_get_errors
2. Analyze stack trace
3. Trace logs immediately before the error
Related Agents#
@flutter-inspector: Master inspector@flutter-inspector-network: Network related logs@flutter-inspector-bloc: BLoC state change tracking