Skip to content

Commit 63314cb

Browse files
committed
Limit log output
1 parent 5713879 commit 63314cb

File tree

2 files changed

+103
-3
lines changed

2 files changed

+103
-3
lines changed

pkgs/dart_mcp_server/lib/src/mixins/flutter_launcher.dart

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ library;
88
import 'dart:async';
99
import 'dart:convert';
1010
import 'dart:io';
11+
import 'dart:math' as math;
1112

1213
import 'package:dart_mcp/server.dart';
1314

@@ -171,10 +172,15 @@ base mixin FlutterLauncherSupport
171172
'Flutter application ${process!.pid} exited with code $exitCode.',
172173
);
173174
if (!completer.isCompleted) {
174-
final logOutput = _runningApps[process.pid]?.logs.join('\n');
175+
final logs = _runningApps[process.pid]?.logs ?? [];
176+
// Only output the last 500 lines of logs.
177+
final startPos = math.max(0, logs.length - 500);
178+
final logOutput = logs.sublist(startPos).join('\n');
175179
completer.completeError(
176180
'Flutter application exited with code $exitCode before the DTD '
177-
'URI was found, with log output:\n$logOutput',
181+
'URI was found, with log output:\n'
182+
'''${startPos > 0 ? '[showing only the last 500 lines of a log with ${logs.length} lines]...' : ''}'''
183+
'$logOutput',
178184
);
179185
}
180186
_runningApps.remove(process.pid);
@@ -362,6 +368,11 @@ base mixin FlutterLauncherSupport
362368
'The process ID of the flutter run process running the '
363369
'application.',
364370
),
371+
'maxLines': Schema.int(
372+
description:
373+
'The maximum number of log lines to return from the end of the '
374+
'logs. Defaults to 500. If set to -1, all logs will be returned.',
375+
),
365376
},
366377
required: ['pid'],
367378
),
@@ -378,8 +389,9 @@ base mixin FlutterLauncherSupport
378389

379390
Future<CallToolResult> _getAppLogs(CallToolRequest request) async {
380391
final pid = request.arguments!['pid'] as int;
392+
var maxLines = request.arguments!['maxLines'] as int? ?? 500;
381393
log(LoggingLevel.info, 'Getting logs for application with PID: $pid');
382-
final logs = _runningApps[pid]?.logs;
394+
var logs = _runningApps[pid]?.logs;
383395

384396
if (logs == null) {
385397
log(
@@ -396,6 +408,17 @@ base mixin FlutterLauncherSupport
396408
);
397409
}
398410

411+
if (maxLines == -1) {
412+
maxLines = logs.length;
413+
}
414+
if (maxLines > 0 && maxLines <= logs.length) {
415+
final startLine = logs.length - maxLines;
416+
logs = [
417+
if (startLine > 0) '[skipping $startLine log lines]...',
418+
...logs.sublist(startLine),
419+
];
420+
}
421+
399422
return CallToolResult(
400423
content: [TextContent(text: logs.join('\n'))],
401424
structuredContent: {'logs': logs},

pkgs/dart_mcp_server/test/tools/flutter_launcher_test.dart

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,83 @@ void main() {
337337
await client.shutdown();
338338
});
339339

340+
test.test('get_app_logs tool respects maxLines', () async {
341+
final dtdUri = 'ws://127.0.0.1:12345/abcdefg=';
342+
final processPid = 54321;
343+
final mockProcessManager = MockProcessManager();
344+
mockProcessManager.addCommand(
345+
Command(
346+
[
347+
Platform.isWindows
348+
? r'C:\path\to\flutter\sdk\bin\cache\dart-sdk\bin\dart.exe'
349+
: '/path/to/flutter/sdk/bin/cache/dart-sdk/bin/dart',
350+
'language-server',
351+
'--protocol',
352+
'lsp',
353+
],
354+
stdout:
355+
'''Content-Length: 145\r\n\r\n{"jsonrpc":"2.0","id":0,"result":{"capabilities":{"workspace":{"workspaceFolders":{"supported":true,"changeNotifications":true}},"workspaceSymbolProvider":true}}}''',
356+
),
357+
);
358+
mockProcessManager.addCommand(
359+
Command(
360+
[
361+
Platform.isWindows
362+
? r'C:\path\to\flutter\sdk\bin\flutter.bat'
363+
: '/path/to/flutter/sdk/bin/flutter',
364+
'run',
365+
'--print-dtd',
366+
'--device-id',
367+
'test-device',
368+
],
369+
stdout:
370+
'line 1\nline 2\nline 3\n'
371+
'The Dart Tooling Daemon is available at: $dtdUri\n',
372+
pid: processPid,
373+
),
374+
);
375+
final serverAndClient = await createServerAndClient(
376+
processManager: mockProcessManager,
377+
fileSystem: fileSystem,
378+
);
379+
final server = serverAndClient.server;
380+
final client = serverAndClient.client;
381+
382+
// Initialize and launch the app
383+
await client.initialize(
384+
InitializeRequest(
385+
protocolVersion: ProtocolVersion.latestSupported,
386+
capabilities: ClientCapabilities(),
387+
clientInfo: Implementation(name: 'test_client', version: '1.0.0'),
388+
),
389+
);
390+
client.notifyInitialized();
391+
await client.callTool(
392+
CallToolRequest(
393+
name: 'launch_app',
394+
arguments: {'root': projectRoot, 'device': 'test-device'},
395+
),
396+
);
397+
398+
// Get the logs
399+
final result = await client.callTool(
400+
CallToolRequest(
401+
name: 'get_app_logs',
402+
arguments: {'pid': processPid, 'maxLines': 2},
403+
),
404+
);
405+
406+
test.expect(result.isError, test.isNot(true));
407+
test.expect(result.structuredContent, {
408+
'logs': [
409+
'[stdout] line 3',
410+
'[stdout] The Dart Tooling Daemon is available at: $dtdUri',
411+
],
412+
});
413+
await server.shutdown();
414+
await client.shutdown();
415+
});
416+
340417
test.test('list_devices tool returns available devices', () async {
341418
final mockProcessManager = MockProcessManager();
342419
mockProcessManager.addCommand(

0 commit comments

Comments
 (0)