initial file integrations for dart tooling daemon
diff --git a/pkgs/dart_mcp/example/dash_client.dart b/pkgs/dart_mcp/example/dash_client.dart
index d0f6960..df8c096 100644
--- a/pkgs/dart_mcp/example/dash_client.dart
+++ b/pkgs/dart_mcp/example/dash_client.dart
@@ -25,12 +25,20 @@
 
   final parsedArgs = argParser.parse(args);
   final serverCommands = parsedArgs['server'] as List<String>;
-  DashClient(
-    serverCommands,
-    geminiApiKey: geminiApiKey,
-    auto: parsedArgs.flag('auto'),
-    raw: parsedArgs.flag('raw'),
-    verbose: parsedArgs.flag('verbose'),
+  runZonedGuarded(
+    () {
+      DashClient(
+        serverCommands,
+        geminiApiKey: geminiApiKey,
+        model: parsedArgs.option('model')!,
+        auto: parsedArgs.flag('auto'),
+        raw: parsedArgs.flag('raw'),
+        verbose: parsedArgs.flag('verbose'),
+      );
+    },
+    (e, s) {
+      stderr.writeln('$e\n$s');
+    },
   );
 }
 
@@ -41,6 +49,17 @@
         abbr: 's',
         help: 'A command to run to start an MCP server',
       )
+      ..addOption(
+        'model',
+        abbr: 'm',
+        help: 'The model to use',
+        defaultsTo: 'models/gemini-2.5-pro-preview-03-25',
+        allowed: [
+          'models/gemini-2.5-pro-preview-03-25',
+          'models/gemini-2.5-pro-exp-03-25',
+          'gemini-2.0-flash',
+        ],
+      )
       ..addFlag(
         'auto',
         help:
@@ -65,7 +84,8 @@
   final List<ServerConnection> serverConnections = [];
   final Map<String, ServerConnection> connectionForFunction = {};
   final List<gemini.Content> chatHistory = [];
-  final gemini.GenerativeModel model;
+  final gemini.GenerativeModel proModel;
+  final gemini.GenerativeModel flashModel;
   final bool auto;
   final bool raw;
   final bool verbose;
@@ -73,11 +93,16 @@
   DashClient(
     this.serverCommands, {
     required String geminiApiKey,
+    required String model,
     this.auto = false,
     this.raw = false,
     this.verbose = false,
-  }) : model = gemini.GenerativeModel(
-         // model: 'gemini-2.5-pro-exp-03-25',
+  }) : proModel = gemini.GenerativeModel(
+         model: model,
+         apiKey: geminiApiKey,
+         systemInstruction: systemInstructions,
+       ),
+       flashModel = gemini.GenerativeModel(
          model: 'gemini-2.0-flash',
          apiKey: geminiApiKey,
          systemInstruction: systemInstructions,
@@ -114,8 +139,9 @@
       final nextMessage = continuation ?? await stdinQueue.next;
       continuation = null;
       chatHistory.add(gemini.Content.text(nextMessage));
+      print('thinking...');
       final modelResponse =
-          (await model.generateContent(
+          (await proModel.generateContent(
             chatHistory,
             tools: serverTools,
           )).candidates.single.content;
@@ -139,7 +165,7 @@
     final dashSpeakResponse =
         raw
             ? currentText
-            : (await model.generateContent([
+            : (await flashModel.generateContent([
               gemini.Content.text(
                 'Please rewrite the following message in your own voice',
               ),
@@ -195,7 +221,8 @@
       }
     }
     await _chatToUser(response.toString());
-    return null;
+    // After a function call, always ask gemini to keep going.
+    return 'Continue';
   }
 
   /// Analyzes a user [message] to see if it looks like they approved of the
@@ -203,7 +230,7 @@
   Future<bool> _analyzeSentiment(String message) async {
     if (message == 'y' || message == 'yes') return true;
     final sentimentResult =
-        (await model.generateContent([
+        (await flashModel.generateContent([
           gemini.Content.text(
             'Analyze the sentiment of the following response. If you are '
             'highly confident that the user approves of running the previous '
diff --git a/pkgs/dart_tooling_mcp_server/lib/src/mixins/dtd.dart b/pkgs/dart_tooling_mcp_server/lib/src/mixins/dtd.dart
index 19bf087..358980b 100644
--- a/pkgs/dart_tooling_mcp_server/lib/src/mixins/dtd.dart
+++ b/pkgs/dart_tooling_mcp_server/lib/src/mixins/dtd.dart
@@ -101,6 +101,8 @@
   FutureOr<InitializeResult> initialize(InitializeRequest request) async {
     registerTool(connectTool, _connect);
     registerTool(getRuntimeErrorsTool, runtimeErrors);
+    registerTool(readFileTool, _readFile);
+    registerTool(writeFileTool, _writeFile);
 
     // TODO: these tools should only be registered for Flutter applications, or
     // they should return an error when used against a pure Dart app (or a
@@ -449,6 +451,38 @@
     );
   }
 
+  /// Reads a file by uri using DTD.
+  Future<CallToolResult> _readFile(CallToolRequest request) async {
+    final dtd = _dtd;
+    if (dtd == null) return _dtdNotConnected;
+    final uriString = request.arguments?['uri'] as String?;
+    if (uriString == null) {
+      return _requiredUriParamMissing;
+    }
+
+    final uri = Uri.parse(uriString);
+    final contents = await dtd.readFileAsString(uri);
+    return CallToolResult(content: [TextContent(text: contents.content!)]);
+  }
+
+  /// Reads a file by uri using DTD.
+  Future<CallToolResult> _writeFile(CallToolRequest request) async {
+    final dtd = _dtd;
+    if (dtd == null) return _dtdNotConnected;
+    final uriString = request.arguments?['uri'] as String?;
+    if (uriString == null) {
+      return _requiredUriParamMissing;
+    }
+    final content = request.arguments?['content'] as String?;
+    if (content == null) {
+      return _requiredContentParamMissing;
+    }
+
+    final uri = Uri.parse(uriString);
+    await dtd.writeFileAsString(uri, content);
+    return CallToolResult(content: [TextContent(text: 'Success')]);
+  }
+
   /// Calls [callback] on the first active debug session, if available.
   Future<CallToolResult> _callOnVmService({
     required Future<CallToolResult> Function(VmService) callback,
@@ -530,6 +564,39 @@
     inputSchema: ObjectSchema(),
   );
 
+  static final readFileTool = Tool(
+    name: 'read_file',
+    description:
+        'Reads a file by URI as a utf8 String. Requires "${connectTool.name}" '
+        'to be successfully called first.',
+    inputSchema: Schema.object(
+      properties: {
+        'uri': Schema.string(title: 'uri', description: 'The file uri to read'),
+      },
+      required: ['uri'],
+    ),
+  );
+
+  static final writeFileTool = Tool(
+    name: 'write_file',
+    description:
+        'Writes a file by URI. Requires "${connectTool.name}" to be '
+        'successfully called first.',
+    inputSchema: Schema.object(
+      properties: {
+        'uri': Schema.string(
+          title: 'uri',
+          description: 'The file uri to write',
+        ),
+        'content': Schema.string(
+          title: 'content',
+          description: 'The utf8 String contents to write to the file',
+        ),
+      },
+      required: ['uri', 'content'],
+    ),
+  );
+
   static final _dtdNotConnected = CallToolResult(
     isError: true,
     content: [
@@ -569,6 +636,16 @@
       ),
     ],
   );
+
+  static final _requiredUriParamMissing = CallToolResult(
+    isError: true,
+    content: [TextContent(text: 'Missing required parameter `uri`.')],
+  );
+
+  static final _requiredContentParamMissing = CallToolResult(
+    isError: true,
+    content: [TextContent(text: 'Missing required parameter `content`.')],
+  );
 }
 
 /// Adds the [getDebugSessions] method to [DartToolingDaemon], so that calling