@@ -25,15 +25,25 @@ void main(List<String> args) {
25
25
26
26
final parsedArgs = argParser.parse (args);
27
27
final serverCommands = parsedArgs['server' ] as List <String >;
28
- DashClient (serverCommands, geminiApiKey: geminiApiKey);
28
+ DashClient (
29
+ serverCommands,
30
+ geminiApiKey: geminiApiKey,
31
+ verbose: parsedArgs['verbose' ] == true ,
32
+ );
29
33
}
30
34
31
35
final argParser =
32
- ArgParser ()..addMultiOption (
33
- 'server' ,
34
- abbr: 's' ,
35
- help: 'A command to run to start an MCP server' ,
36
- );
36
+ ArgParser ()
37
+ ..addMultiOption (
38
+ 'server' ,
39
+ abbr: 's' ,
40
+ help: 'A command to run to start an MCP server' ,
41
+ )
42
+ ..addOption (
43
+ 'verbose' ,
44
+ abbr: 'v' ,
45
+ help: 'Enables verbose logging for logs from servers.' ,
46
+ );
37
47
38
48
final class DashClient extends MCPClient with RootsSupport {
39
49
final StreamQueue <String > stdinQueue;
@@ -42,20 +52,24 @@ final class DashClient extends MCPClient with RootsSupport {
42
52
final Map <String , ServerConnection > connectionForFunction = {};
43
53
final List <gemini.Content > chatHistory = [];
44
54
final gemini.GenerativeModel model;
55
+ final bool verbose;
45
56
46
- DashClient (this .serverCommands, {required String geminiApiKey})
47
- : model = gemini.GenerativeModel (
48
- // model: 'gemini-2.5-pro-exp-03-25',
49
- model: 'gemini-2.0-flash' ,
50
- apiKey: geminiApiKey,
51
- systemInstruction: systemInstructions,
52
- ),
53
- stdinQueue = StreamQueue (
54
- stdin.transform (utf8.decoder).transform (const LineSplitter ()),
55
- ),
56
- super (
57
- ClientImplementation (name: 'Example gemini client' , version: '0.1.0' ),
58
- ) {
57
+ DashClient (
58
+ this .serverCommands, {
59
+ required String geminiApiKey,
60
+ this .verbose = false ,
61
+ }) : model = gemini.GenerativeModel (
62
+ // model: 'gemini-2.5-pro-exp-03-25',
63
+ model: 'gemini-2.0-flash' ,
64
+ apiKey: geminiApiKey,
65
+ systemInstruction: systemInstructions,
66
+ ),
67
+ stdinQueue = StreamQueue (
68
+ stdin.transform (utf8.decoder).transform (const LineSplitter ()),
69
+ ),
70
+ super (
71
+ ClientImplementation (name: 'Example gemini client' , version: '0.1.0' ),
72
+ ) {
59
73
addRoot (
60
74
Root (
61
75
uri: Directory .current.absolute.uri.toString (),
@@ -71,6 +85,7 @@ final class DashClient extends MCPClient with RootsSupport {
71
85
await _connectToServers ();
72
86
}
73
87
await _initializeServers ();
88
+ _listenToLogs ();
74
89
final serverTools = await _listServerCapabilities ();
75
90
76
91
// If assigned then it is used as the next input from the user
@@ -144,7 +159,7 @@ final class DashClient extends MCPClient with RootsSupport {
144
159
response.writeln (content.text);
145
160
case final ImageContent content when content.isImage:
146
161
chatHistory.add (
147
- gemini.Content .data ('image/png' , base64Decode (content.data)),
162
+ gemini.Content .data (content.mimeType , base64Decode (content.data)),
148
163
);
149
164
response.writeln ('Image added to context' );
150
165
default :
@@ -227,6 +242,27 @@ final class DashClient extends MCPClient with RootsSupport {
227
242
}
228
243
}
229
244
245
+ /// Listens for log messages on all [serverConnections] that support logging.
246
+ void _listenToLogs () {
247
+ for (var connection in serverConnections) {
248
+ if (connection.serverCapabilities.logging == null ) {
249
+ continue ;
250
+ }
251
+
252
+ connection.setLogLevel (
253
+ SetLevelRequest (
254
+ level: verbose ? LoggingLevel .debug : LoggingLevel .warning,
255
+ ),
256
+ );
257
+ connection.onLog.listen ((event) {
258
+ print (
259
+ 'Server Log(${event .level .name }): '
260
+ '${event .logger != null ? '[${event .logger }] ' : '' }${event .data }' ,
261
+ );
262
+ });
263
+ }
264
+ }
265
+
230
266
/// Lists all the tools available the [serverConnections] .
231
267
Future <List <gemini.Tool >> _listServerCapabilities () async {
232
268
final functions = < gemini.FunctionDeclaration > [];
@@ -321,7 +357,11 @@ final class DashChatBotServer extends MCPServer with ToolsSupport {
321
357
322
358
registerTool (removeImagesTool, (_) async {
323
359
final oldLength = client.chatHistory.length;
324
- client.chatHistory.removeWhere ((content) => content is ImageContent );
360
+ // TODO: Something more robust than this, maybe just remove them by object
361
+ // reference.
362
+ client.chatHistory.removeWhere (
363
+ (content) => content.parts.first is gemini.DataPart ,
364
+ );
325
365
return CallToolResult (
326
366
content: [
327
367
TextContent (
0 commit comments