Fix `--log-file` option so it doesn't crash the server. (#207)
diff --git a/.vscode/launch.json b/.vscode/launch.json index 9c60be5..fffc49b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json
@@ -1,28 +1,33 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "counter_app", - "cwd": "pkgs/dart_tooling_mcp_server/test_fixtures/counter_app", - "request": "launch", - "type": "dart" - }, - { - "name": "counter_app (profile mode)", - "cwd": "pkgs/dart_tooling_mcp_server/test_fixtures/counter_app", - "request": "launch", - "type": "dart", - "flutterMode": "profile" - }, - { - "name": "counter_app (release mode)", - "cwd": "pkgs/dart_tooling_mcp_server/test_fixtures/counter_app", - "request": "launch", - "type": "dart", - "flutterMode": "release" - } - ] + "version": "0.2.0", + "configurations": [ + { + "name": "test_mcp", + "program": "pkgs/dart_mcp_server/bin/main.dart", + "cwd": "${workspaceFolder}", + "args": ["--log-file", "pkgs/dart_mcp_server/log.txt"], + "request": "launch", + "type": "dart" + }, + { + "name": "counter_app", + "cwd": "pkgs/dart_tooling_mcp_server/test_fixtures/counter_app", + "request": "launch", + "type": "dart" + }, + { + "name": "counter_app (profile mode)", + "cwd": "pkgs/dart_tooling_mcp_server/test_fixtures/counter_app", + "request": "launch", + "type": "dart", + "flutterMode": "profile" + }, + { + "name": "counter_app (release mode)", + "cwd": "pkgs/dart_tooling_mcp_server/test_fixtures/counter_app", + "request": "launch", + "type": "dart", + "flutterMode": "release" + } + ] }
diff --git a/pkgs/dart_mcp/CHANGELOG.md b/pkgs/dart_mcp/CHANGELOG.md index 28118c8..59240c6 100644 --- a/pkgs/dart_mcp/CHANGELOG.md +++ b/pkgs/dart_mcp/CHANGELOG.md
@@ -20,6 +20,8 @@ and stdout streams instead of starting processes itself. This enables custom process spawning (such as using package:process), and also enables the client to run in browser environments. +- Fixed a problem where specifying `--log-file` would cause the server to stop + working. ## 0.2.2
diff --git a/pkgs/dart_mcp/lib/src/api/initialization.dart b/pkgs/dart_mcp/lib/src/api/initialization.dart index 81d7b6b..bcbd8f4 100644 --- a/pkgs/dart_mcp/lib/src/api/initialization.dart +++ b/pkgs/dart_mcp/lib/src/api/initialization.dart
@@ -28,7 +28,7 @@ /// /// May be `null` if the version is not recognized. ProtocolVersion? get protocolVersion => - ProtocolVersion.tryParse(_value['protocolVersion'] as String); + ProtocolVersion.tryParse(_value['protocolVersion'] as String? ?? ''); ClientCapabilities get capabilities { final capabilities = _value['capabilities'] as ClientCapabilities?;
diff --git a/pkgs/dart_mcp_server/lib/src/mixins/analyzer.dart b/pkgs/dart_mcp_server/lib/src/mixins/analyzer.dart index 10ae558..52cf80d 100644 --- a/pkgs/dart_mcp_server/lib/src/mixins/analyzer.dart +++ b/pkgs/dart_mcp_server/lib/src/mixins/analyzer.dart
@@ -24,10 +24,10 @@ on ToolsSupport, LoggingSupport, RootsTrackingSupport implements SdkSupport { /// The LSP server connection for the analysis server. - late final Peer _lspConnection; + Peer? _lspConnection; /// The actual process for the LSP server. - late final Process _lspServer; + Process? _lspServer; /// The current diagnostics for a given file. Map<Uri, List<lsp.Diagnostic>> diagnostics = {}; @@ -91,7 +91,7 @@ /// /// On failure, returns a reason for the failure. Future<String?> _initializeAnalyzerLspServer() async { - _lspServer = await Process.start(sdk.dartExecutablePath, [ + final lspServer = await Process.start(sdk.dartExecutablePath, [ 'language-server', // Required even though it is documented as the default. // https://github.com/dart-lang/sdk/issues/60574 @@ -101,7 +101,8 @@ // '--protocol-traffic-log', // 'language-server-protocol.log', ]); - _lspServer.stderr + _lspServer = lspServer; + lspServer.stderr .transform(utf8.decoder) .transform(const LineSplitter()) .listen((line) async { @@ -109,8 +110,8 @@ log(LoggingLevel.warning, line, logger: 'DartLanguageServer'); }); - _lspConnection = - Peer(lspChannel(_lspServer.stdout, _lspServer.stdin)) + final lspConnection = + Peer(lspChannel(lspServer.stdout, lspServer.stdin)) ..registerMethod( lsp.Method.textDocument_publishDiagnostics.toString(), _handleDiagnostics, @@ -122,8 +123,9 @@ () => 'Unhandled LSP message: ${params.method} - ${params.asMap}', ); }); + _lspConnection = lspConnection; - unawaited(_lspConnection.listen()); + unawaited(lspConnection.listen()); log(LoggingLevel.debug, 'Connecting to analyzer lsp server'); lsp.InitializeResult? initializeResult; @@ -131,7 +133,7 @@ try { // Initialize with the server. initializeResult = lsp.InitializeResult.fromJson( - (await _lspConnection.sendRequest( + (await lspConnection.sendRequest( lsp.Method.initialize.toString(), lsp.InitializeParams( capabilities: lsp.ClientCapabilities( @@ -221,10 +223,10 @@ } if (error != null) { - _lspServer.kill(); - await _lspConnection.close(); + lspServer.kill(); + await lspConnection.close(); } else { - _lspConnection.sendNotification( + lspConnection.sendNotification( lsp.Method.initialized.toString(), lsp.InitializedParams().toJson(), ); @@ -235,8 +237,8 @@ @override Future<void> shutdown() async { await super.shutdown(); - _lspServer.kill(); - await _lspConnection.close(); + _lspServer?.kill(); + await _lspConnection?.close(); } /// Implementation of the [analyzeFilesTool], analyzes all the files in all @@ -270,7 +272,7 @@ if (errorResult != null) return errorResult; final query = request.arguments![ParameterNames.query] as String; - final result = await _lspConnection.sendRequest( + final result = await _lspConnection!.sendRequest( lsp.Method.workspace_symbol.toString(), lsp.WorkspaceSymbolParams(query: query).toJson(), ); @@ -288,7 +290,7 @@ line: request.arguments![ParameterNames.line] as int, character: request.arguments![ParameterNames.column] as int, ); - final result = await _lspConnection.sendRequest( + final result = await _lspConnection!.sendRequest( lsp.Method.textDocument_signatureHelp.toString(), lsp.SignatureHelpParams( textDocument: lsp.TextDocumentIdentifier(uri: uri), @@ -309,7 +311,7 @@ line: request.arguments![ParameterNames.line] as int, character: request.arguments![ParameterNames.column] as int, ); - final result = await _lspConnection.sendRequest( + final result = await _lspConnection!.sendRequest( lsp.Method.textDocument_hover.toString(), lsp.HoverParams( textDocument: lsp.TextDocumentIdentifier(uri: uri), @@ -396,7 +398,7 @@ () => 'Notifying of workspace root change: ${event.toJson()}', ); - _lspConnection.sendNotification( + _lspConnection!.sendNotification( lsp.Method.workspace_didChangeWorkspaceFolders.toString(), lsp.DidChangeWorkspaceFoldersParams(event: event).toJson(), );
diff --git a/pkgs/dart_mcp_server/lib/src/server.dart b/pkgs/dart_mcp_server/lib/src/server.dart index 04af79d..e900dd6 100644 --- a/pkgs/dart_mcp_server/lib/src/server.dart +++ b/pkgs/dart_mcp_server/lib/src/server.dart
@@ -252,12 +252,15 @@ StreamSinkTransformer.fromHandlers( handleData: (data, innerSink) { innerSink.add(utf8.encode(data)); - // It's a log, so we want to make sure it's always up-to-date. - fileByteSink.flush(); }, - handleDone: (innerSink) { + handleDone: (innerSink) async { innerSink.close(); }, + handleError: (Object e, StackTrace s, _) { + io.stderr.writeln( + 'Error in writing to log file ${logFile.path}: $e\n$s', + ); + }, ), ); }
diff --git a/pkgs/dart_mcp_server/test/dart_tooling_mcp_server_test.dart b/pkgs/dart_mcp_server/test/dart_tooling_mcp_server_test.dart index 606be7f..2857be0 100644 --- a/pkgs/dart_mcp_server/test/dart_tooling_mcp_server_test.dart +++ b/pkgs/dart_mcp_server/test/dart_tooling_mcp_server_test.dart
@@ -123,7 +123,7 @@ // Wait for the process to release the file. await doWithRetries(() => File(logDescriptor.io.path).delete()); - }, skip: 'https://github.com/dart-lang/ai/issues/181'); + }); }); }