flutter-inspector-log
Log debugging specialist. Use for app log collection and error tracking
| 항목 | 내용 |
|---|---|
| 모델 | haiku |
| 사용 도구 | Read, Glob, Grep |
| 연계 스킬 | flutter-inspector |
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