| íëĒŠ | ë´ėŠ |
|---|---|
| Tools | Read, Glob, Grep |
| Model | haiku |
| Skills | flutter-inspector |
Flutter Inspector - Network Agent#
A specialized agent for logging and analyzing HTTP requests/responses at runtime.
Triggers#
Automatically activated when @flutter-inspector-network is invoked or the following keywords are detected:
- API call, HTTP request
- Network, response
- Error code, timeout
MCP Tools#
network_get_logs#
Returns recent HTTP request/response logs.
{
" name " : " network_get_logs " ,
" description " : " HTTP request/response logs " ,
" inputSchema " : {
" type " : " object " ,
" properties " : {
" limit " : {
" type " : " integer " ,
" description " : " Maximum count " ,
" default " : 50
},
" method " : {
" type " : " string " ,
" description " : " Filter: HTTP method (GET, POST, PUT, DELETE) "
},
" path " : {
" type " : " string " ,
" description " : " Filter: URL path substring "
}
}
}
}
Response example:
{
" logs " : [
{
" id " : " req_001 " ,
" timestamp " : " 2024-01-01T10:00:00Z " ,
" method " : " GET " ,
" url " : " https://api.example.com/users/123 " ,
" statusCode " : 200,
" duration " : 245,
" requestHeaders " : { " Authorization " : " Bearer *** " },
" responseSize " : 1024
},
{
" id " : " req_002 " ,
" timestamp " : " 2024-01-01T10:00:05Z " ,
" method " : " POST " ,
" url " : " https://api.example.com/posts " ,
" statusCode " : 201,
" duration " : 320,
" requestBody " : { " title " : " ... " },
" responseSize " : 512
}
],
" totalCount " : 2
}
network_get_errors#
Filters and returns only failed requests.
{
" name " : " network_get_errors " ,
" description " : " Failed request logs " ,
" inputSchema " : {
" type " : " object " ,
" properties " : {
" limit " : {
" type " : " integer " ,
" description " : " Maximum count " ,
" default " : 20
}
}
}
}
Response example:
{
" errors " : [
{
" id " : " req_003 " ,
" timestamp " : " 2024-01-01T10:01:00Z " ,
" method " : " GET " ,
" url " : " https://api.example.com/data " ,
" statusCode " : 500,
" duration " : 150,
" errorType " : " ServerError " ,
" errorMessage " : " Internal Server Error " ,
" responseBody " : { " error " : " Database connection failed " }
},
{
" id " : " req_004 " ,
" timestamp " : " 2024-01-01T10:02:00Z " ,
" method " : " POST " ,
" url " : " https://api.example.com/upload " ,
" statusCode " : null,
" duration " : 30000,
" errorType " : " Timeout " ,
" errorMessage " : " Connection timed out "
}
],
" errorCount " : 2
}
network_get_stats#
Returns network statistics.
{
" name " : " network_get_stats " ,
" description " : " Network statistics " ,
" inputSchema " : {
" type " : " object " ,
" properties " : {}
}
}
Response example:
{
" stats " : {
" totalRequests " : 150,
" successfulRequests " : 145,
" failedRequests " : 5,
" averageDuration " : 180,
" totalBytesReceived " : 524288,
" totalBytesSent " : 10240,
" requestsByMethod " : {
" GET " : 100,
" POST " : 40,
" PUT " : 8,
" DELETE " : 2
},
" requestsByStatus " : {
" 2xx " : 140,
" 4xx " : 3,
" 5xx " : 2,
" timeout " : 5
}
},
" period " : " session "
}
network_clear_logs#
Clears network logs.
{
" name " : " network_clear_logs " ,
" description " : " Clear logs " ,
" inputSchema " : {
" type " : " object " ,
" properties " : {}
}
}
App Integration Code#
// lib/debug/mcp_network_tools.dart
import 'package:mcp_toolkit/mcp_toolkit.dart';
import 'package:dio/dio.dart';
class NetworkLogger extends Interceptor {
final _logs = <Map<String, dynamic>>[];
static final instance = NetworkLogger._();
NetworkLogger._();
@override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
_logs.add({
'id': 'req_${DateTime.now().millisecondsSinceEpoch}',
'timestamp': DateTime.now().toIso8601String(),
'method': options.method,
'url': options.uri.toString(),
'requestHeaders': _sanitizeHeaders(options.headers),
'requestBody': options.data,
'startTime': DateTime.now(),
});
handler.next(options);
}
@override
void onResponse(Response response, ResponseInterceptorHandler handler) {
final log = _logs.lastWhere(
(l) => l['url'] == response.requestOptions.uri.toString(),
orElse: () => {},
);
if (log.isNotEmpty) {
log['statusCode'] = response.statusCode;
log['duration'] = DateTime.now()
.difference(log['startTime'] as DateTime)
.inMilliseconds;
log['responseSize'] = response.data?.toString().length ?? 0;
log.remove('startTime');
}
handler.next(response);
}
@override
void onError(DioException err, ErrorInterceptorHandler handler) {
final log = _logs.lastWhere(
(l) => l['url'] == err.requestOptions.uri.toString(),
orElse: () => {},
);
if (log.isNotEmpty) {
log['statusCode'] = err.response?.statusCode;
log['duration'] = DateTime.now()
.difference(log['startTime'] as DateTime)
.inMilliseconds;
log['errorType'] = err.type.name;
log['errorMessage'] = err.message;
log['responseBody'] = err.response?.data;
log.remove('startTime');
}
handler.next(err);
}
Map<String, String> _sanitizeHeaders(Map<String, dynamic> headers) {
return headers.map((key, value) {
if (key.toLowerCase() == 'authorization') {
return MapEntry(key, '***');
}
return MapEntry(key, value.toString());
});
}
List<Map<String, dynamic>> getLogs({int? limit, String? method, String? path}) {
var filtered = _logs.toList();
if (method != null) {
filtered = filtered.where((l) => l['method'] == method).toList();
}
if (path != null) {
filtered = filtered.where((l) => (l['url'] as String).contains(path)).toList();
}
return filtered.reversed.take(limit ?? 50).toList();
}
List<Map<String, dynamic>> getErrors({int? limit}) {
return _logs
.where((l) => l['statusCode'] == null || (l['statusCode'] as int) >= 400)
.toList()
.reversed
.take(limit ?? 20)
.toList();
}
Map<String, dynamic> getStats() {
final successful = _logs.where((l) =>
l['statusCode'] != null && (l['statusCode'] as int) < 400).length;
return {
'totalRequests': _logs.length,
'successfulRequests': successful,
'failedRequests': _logs.length - successful,
// ... additional statistics
};
}
void clear() => _logs.clear();
}
void registerNetworkTools() {
if (!kDebugMode) return;
addMcpTool(MCPCallEntry.tool(
handler: (params) => MCPCallResult(
message: 'Network logs',
parameters: {
'logs': NetworkLogger.instance.getLogs(
limit: params['limit'] as int?,
method: params['method'] as String?,
path: params['path'] as String?,
),
},
),
definition: MCPToolDefinition(
name: 'network_get_logs',
description: 'HTTP logs',
inputSchema: {
'type': 'object',
'properties': {
'limit': {'type': 'integer'},
'method': {'type': 'string'},
'path': {'type': 'string'},
},
},
),
));
// network_get_errors, network_get_stats, network_clear_logs are implemented similarly
}
Usage Examples#
Check recent API calls#
Q: Show me recent API call history
A: Run network_get_logs limit=10
- > 10 requests (GET /users 200, POST /posts 201, ...)
Filter specific endpoint#
Q: Show me only /users related requests
A: Run network_get_logs path= " /users "
- > Filtered to requests containing /users
Check error requests#
Q: Are there any failed requests?
A: Run network_get_errors
- > 500 error: /api/data, Timeout: /api/upload
Network statistics#
Q: Summarize the network status
A: Run network_get_stats
- > 150 requests, 145 successful, 5 failed, avg 180ms
Common Problem Diagnosis#
Slow API response#
1. Check duration with network_get_logs
2. Identify slow requests
3. Determine server performance or network issue
401 Unauthorized#
1. Check 401 errors with network_get_errors
2. Check Authorization in requestHeaders
3. Check token status with auth_get_status
500 Server Error#
1. Check details with network_get_errors
2. Check error message in responseBody
3. Review request parameters
Timeout#
1. Check timeout errors with network_get_errors
2. Check network connection status
3. Review timeout configuration values
Related Agents#
@flutter-inspector: Master inspector@flutter-inspector-auth: Authentication token issues@flutter-inspector-log: Network related app logs