feat: default MCP off and unify MCP settings page
Disable MCP by default on a fresh install so the feature stays opt-in until the user explicitly enables it. Existing installs keep their saved preference so the upgrade does not silently flip the toggle. Gate every new-chat code path on the global mcpEnabled flag so that flipping it off always wins over the newChatMcpEnabled default and the per-chat toggle cannot leak enabled state past a disabled global toggle. Rewrite the MCP tools screen to share the visual conventions used by SettingsViews (section cards, panel rows, toggle widget, layout breakpoint, system-inset padding, status/error panels) so the page reads as a first-class settings surface instead of an ad-hoc screen. Co-Authored-By: Opus 4.8 <noreply@puku.sh>
abdulmominsakib committed
Jul 27, 2026 at 14:52 UTC
99bb12207395b195e9ef42169bfd72146c7c0c38
4 files changed
+378
-199
lib/features/chat/providers/chat_mcp_providers.dart
+5
-2
@@ -11,7 +11,8 @@ class ChatMcpConfigNotifier extends Notifier<ChatMcpConfig> {
11
ChatMcpConfig build() {
12
// We no longer watch settingsProvider here to avoid resetting per-chat state
13
// when global settings change. Initial state is set via ChatNotifier.
14
- return const ChatMcpConfig(enabled: true);
14
+ // Default to disabled so a fresh app launch does not silently enable MCP.
15
+ return const ChatMcpConfig(enabled: false);
16
}
17
18
void setConfig(ChatMcpConfig config) {
@@ -84,7 +85,9 @@ class ChatMcpConfigNotifier extends Notifier<ChatMcpConfig> {
85
86
void clearAll() {
87
final settings = ref.read(settingsProvider);
87
- state = ChatMcpConfig(enabled: settings.newChatMcpEnabled);
88
+ state = ChatMcpConfig(
89
+ enabled: settings.mcpEnabled && settings.newChatMcpEnabled,
90
+ );
91
ref.read(mcpServerManagerProvider).clear();
92
}
93
}
lib/features/chat/providers/chat_notifier.dart
+11
-5
@@ -2,6 +2,7 @@ import 'dart:async';
2
import 'dart:io';
3
import 'dart:math';
4
5
+import 'package:flutter/services.dart';
6
import 'package:flutter_riverpod/flutter_riverpod.dart';
7
import 'package:localmind/core/logger/app_logger.dart';
8
import 'package:localmind/core/models/enums.dart';
@@ -371,7 +372,7 @@ class ChatNotifier extends Notifier<ChatState> {
372
373
ref
374
.read(chatMcpConfigProvider.notifier)
374
- .setEnabled(conversation.mcpEnabled ?? true);
375
+ .setEnabled(conversation.mcpEnabled ?? false);
376
} catch (e, stackTrace) {
377
Log.fatal(error: e, stackTrace: stackTrace);
378
state = state.copyWith(
@@ -491,7 +492,7 @@ class ChatNotifier extends Notifier<ChatState> {
492
final settings = ref.read(settingsProvider);
493
ref
494
.read(chatMcpConfigProvider.notifier)
494
- .setEnabled(settings.newChatMcpEnabled);
495
+ .setEnabled(settings.mcpEnabled && settings.newChatMcpEnabled);
496
}
497
498
void _invalidateStreamCallbacks() {
@@ -698,7 +699,7 @@ class ChatNotifier extends Notifier<ChatState> {
699
preselected.map((p) => p.id).toList(),
700
),
701
systemPrompt: newConversationSystemPrompt,
701
- mcpEnabled: settings.newChatMcpEnabled,
702
+ mcpEnabled: settings.mcpEnabled && settings.newChatMcpEnabled,
703
isTemporary: false,
704
folderId: ref.read(pendingNewChatFolderIdProvider.notifier).consume(),
705
);
@@ -715,7 +716,9 @@ class ChatNotifier extends Notifier<ChatState> {
716
_useFreshConversationSystemPrompt = true;
717
_freshConversationSystemPrompt = newConversationSystemPrompt;
718
718
- ref.read(chatMcpConfigProvider.notifier).setEnabled(settings.newChatMcpEnabled);
719
+ ref
720
+ .read(chatMcpConfigProvider.notifier)
721
+ .setEnabled(settings.mcpEnabled && settings.newChatMcpEnabled);
722
723
if (!settings.keepPersonaOnNewChat) {
724
ref.read(selectedPersonasProvider.notifier).clear();
@@ -959,6 +962,9 @@ class ChatNotifier extends Notifier<ChatState> {
962
.copyWith(isProcessing: true);
963
_latestStreamingMessage = streamingAssistantMessage;
964
stateNeedsUpdate = true;
965
+ if (ref.read(settingsProvider).hapticFeedbackEnabled) {
966
+ HapticFeedback.lightImpact();
967
+ }
968
break;
969
case ChatResponseType.timeoutError:
970
case ChatResponseType.error:
@@ -2269,7 +2275,7 @@ class ChatNotifier extends Notifier<ChatState> {
2275
systemPrompt: preselected.isEmpty
2276
? null
2277
: PersonaPromptUtils.combineSystemPrompts(preselected),
2272
- mcpEnabled: settings.newChatMcpEnabled,
2278
+ mcpEnabled: settings.mcpEnabled && settings.newChatMcpEnabled,
2279
isTemporary: false,
2280
);
2281
lib/features/mcp/views/mcp_tools_screen.dart
+358
-188
@@ -1,4 +1,3 @@
1
-import "package:localmind/core/theme/colors.dart";
1
import 'package:flutter/material.dart';
2
import 'package:flutter_riverpod/flutter_riverpod.dart';
3
import 'package:hugeicons/hugeicons.dart';
@@ -8,6 +7,8 @@ import 'package:shadcn_ui/shadcn_ui.dart';
7
import '../../chat/data/mcp_server_manager.dart';
8
import '../../chat/data/tools/tool_definition.dart';
9
import '../../chat/providers/tooling_providers.dart';
10
+import '../../../core/providers/app_providers.dart';
11
+import '../../../core/utils/system_insets.dart';
12
13
class McpToolsScreen extends ConsumerStatefulWidget {
14
const McpToolsScreen({super.key});
@@ -49,11 +50,12 @@ class _McpToolsScreenState extends ConsumerState<McpToolsScreen> {
50
@override
51
Widget build(BuildContext context) {
52
final l10n = AppLocalizations.of(context)!;
52
- final theme = Theme.of(context);
53
- final isDark = theme.brightness == Brightness.dark;
53
+ final settings = ref.watch(settingsProvider);
54
final manager = ref.watch(mcpServerManagerProvider);
55
final hasExampleServer = manager.hasExampleServer();
56
+ final isDark = Theme.of(context).brightness == Brightness.dark;
57
final topPadding = MediaQuery.of(context).padding.top;
58
+ final bottomInset = bottomSystemInset(context);
59
60
return Column(
61
children: [
@@ -95,142 +97,267 @@ class _McpToolsScreenState extends ConsumerState<McpToolsScreen> {
97
),
98
),
99
Expanded(
98
- child: RefreshIndicator(
99
- onRefresh: () async => _refreshTools(),
100
- child: ListView(
101
- padding: const EdgeInsets.all(16),
102
- children: [
103
- _ExampleServerPanel(
104
- enabled: hasExampleServer,
105
- onToggle: _toggleExampleServer,
100
+ child: LayoutBuilder(
101
+ builder: (context, constraints) {
102
+ final useTwoColumns = constraints.maxWidth >= 1080;
103
+ final contentMaxWidth = useTwoColumns ? 1120.0 : 720.0;
104
+ final horizontalPadding = constraints.maxWidth >= 720
105
+ ? 20.0
106
+ : 12.0;
107
+
108
+ final mcpCard = _McpSectionCard(
109
+ title: l10n.mcp_tools_title,
110
+ children: [
111
+ _McpToggleSetting(
112
+ label: l10n.enable_mcp,
113
+ value: settings.mcpEnabled,
114
+ badges: [_FeatureBadge(label: l10n.experimental_label)],
115
+ onChanged: (value) => ref
116
+ .read(settingsProvider.notifier)
117
+ .setMcpEnabled(value),
118
+ ),
119
+ if (settings.mcpEnabled) ...[
120
+ _McpToggleSetting(
121
+ label: l10n.new_chat_mcp_default,
122
+ value: settings.newChatMcpEnabled,
123
+ onChanged: (value) => ref
124
+ .read(settingsProvider.notifier)
125
+ .setNewChatMcpEnabled(value),
126
+ ),
127
+ _McpToggleSetting(
128
+ label: l10n.enable_example_server,
129
+ description: l10n.example_mcp_server_desc,
130
+ value: hasExampleServer,
131
+ onChanged: (_) => _toggleExampleServer(),
132
+ ),
133
+ ],
134
+ ],
135
+ );
136
+
137
+ final toolsCard = _McpSectionCard(
138
+ title: l10n.available_tools,
139
+ accent: const Color(0xFF22C55E),
140
+ icon: HugeIcons.strokeRoundedPuzzle,
141
+ children: [_buildToolsContent(l10n, settings.mcpEnabled)],
142
+ );
143
+
144
+ return ListView(
145
+ padding: EdgeInsets.fromLTRB(
146
+ horizontalPadding,
147
+ 12,
148
+ horizontalPadding,
149
+ 24 + bottomInset,
150
),
107
- const SizedBox(height: 20),
108
- Text(
109
- l10n.available_tools,
110
- style: theme.textTheme.titleMedium?.copyWith(
111
- fontWeight: FontWeight.w700,
151
+ children: [
152
+ Center(
153
+ child: ConstrainedBox(
154
+ constraints: BoxConstraints(maxWidth: contentMaxWidth),
155
+ child: Column(
156
+ crossAxisAlignment: CrossAxisAlignment.stretch,
157
+ children: [
158
+ mcpCard,
159
+ const SizedBox(height: 16),
160
+ toolsCard,
161
+ ],
162
+ ),
163
+ ),
164
),
165
+ ],
166
+ );
167
+ },
168
+ ),
169
+ ),
170
+ ],
171
+ );
172
+ }
173
+
174
+ Widget _buildToolsContent(AppLocalizations l10n, bool mcpEnabled) {
175
+ if (!mcpEnabled) {
176
+ return _StatusPanel(
177
+ icon: HugeIcons.strokeRoundedAlertCircle,
178
+ title: l10n.mcp_disabled_warning,
179
+ body: l10n.no_tools_registered_desc,
180
+ );
181
+ }
182
+
183
+ return FutureBuilder<List<ToolDefinition>>(
184
+ future: _toolsFuture,
185
+ builder: (context, snapshot) {
186
+ if (snapshot.connectionState == ConnectionState.waiting) {
187
+ return _McpPanel(
188
+ child: Padding(
189
+ padding: const EdgeInsets.symmetric(vertical: 24),
190
+ child: Center(
191
+ child: SizedBox(
192
+ width: 22,
193
+ height: 22,
194
+ child: const CircularProgressIndicator(strokeWidth: 2),
195
),
114
- const SizedBox(height: 12),
115
- FutureBuilder<List<ToolDefinition>>(
116
- future: _toolsFuture,
117
- builder: (context, snapshot) {
118
- if (snapshot.connectionState == ConnectionState.waiting) {
119
- return const Center(
120
- child: Padding(
121
- padding: EdgeInsets.all(32),
122
- child: CircularProgressIndicator(),
196
+ ),
197
+ ),
198
+ );
199
+ }
200
+
201
+ if (snapshot.hasError) {
202
+ return _StatusPanel(
203
+ icon: HugeIcons.strokeRoundedInformationCircle,
204
+ title: l10n.unable_load_tools,
205
+ body: snapshot.error.toString(),
206
+ );
207
+ }
208
+
209
+ final tools = snapshot.data ?? const <ToolDefinition>[];
210
+ if (tools.isEmpty) {
211
+ return _StatusPanel(
212
+ icon: HugeIcons.strokeRoundedPuzzle,
213
+ title: l10n.no_tools_registered,
214
+ body: l10n.no_tools_registered_desc,
215
+ );
216
+ }
217
+
218
+ return Column(
219
+ children: tools
220
+ .map((tool) => _ToolRow(tool: tool))
221
+ .toList(growable: false),
222
+ );
223
+ },
224
+ );
225
+ }
226
+}
227
+
228
+class _McpSectionCard extends StatelessWidget {
229
+ const _McpSectionCard({
230
+ required this.title,
231
+ required this.children,
232
+ this.icon = HugeIcons.strokeRoundedMcpServer,
233
+ this.accent = const Color(0xFF8B5CF6),
234
+ });
235
+
236
+ final String title;
237
+ final List<List<dynamic>> icon;
238
+ final Color accent;
239
+ final List<Widget> children;
240
+
241
+ @override
242
+ Widget build(BuildContext context) {
243
+ return Container(
244
+ decoration: BoxDecoration(
245
+ color: _surfaceColor(context),
246
+ borderRadius: BorderRadius.circular(18),
247
+ border: Border.all(color: _outlineColor(context, alpha: 0.9)),
248
+ ),
249
+ child: Padding(
250
+ padding: const EdgeInsets.all(16),
251
+ child: Column(
252
+ crossAxisAlignment: CrossAxisAlignment.start,
253
+ children: [
254
+ Row(
255
+ children: [
256
+ Container(
257
+ width: 34,
258
+ height: 34,
259
+ decoration: BoxDecoration(
260
+ color: accent.withValues(alpha: 0.10),
261
+ borderRadius: BorderRadius.circular(10),
262
+ ),
263
+ child: Center(
264
+ child: HugeIcon(icon: icon, color: accent, size: 16),
265
+ ),
266
+ ),
267
+ const SizedBox(width: 10),
268
+ Expanded(
269
+ child: Text(
270
+ title,
271
+ style: Theme.of(context).textTheme.titleMedium?.copyWith(
272
+ fontWeight: FontWeight.w700,
273
),
124
- );
125
- }
126
-
127
- if (snapshot.hasError) {
128
- return _StatusPanel(
129
- icon: HugeIcons.strokeRoundedInformationCircle,
130
- title: l10n.unable_load_tools,
131
- body: snapshot.error.toString(),
132
- );
133
- }
134
-
135
- final tools = snapshot.data ?? const <ToolDefinition>[];
136
- if (tools.isEmpty) {
137
- return _StatusPanel(
138
- icon: HugeIcons.strokeRoundedPuzzle,
139
- title: l10n.no_tools_registered,
140
- body: l10n.no_tools_registered_desc,
141
- );
142
- }
143
-
144
- return Column(
145
- children: tools
146
- .map((tool) => _ToolRow(tool: tool))
147
- .toList(growable: false),
148
- );
149
- },
274
+ ),
275
),
276
],
277
),
153
- ),
278
+ const SizedBox(height: 12),
279
+ ..._withVerticalSpacing(children, gap: 10),
280
+ ],
281
),
155
- ],
282
+ ),
283
);
284
}
285
}
286
160
-class _ExampleServerPanel extends StatelessWidget {
161
- const _ExampleServerPanel({required this.enabled, required this.onToggle});
287
+class _McpPanel extends StatelessWidget {
288
+ const _McpPanel({required this.child});
289
163
- final bool enabled;
164
- final VoidCallback onToggle;
290
+ final Widget child;
291
292
@override
293
Widget build(BuildContext context) {
168
- final l10n = AppLocalizations.of(context)!;
169
- final theme = Theme.of(context);
170
- final isDark = theme.brightness == Brightness.dark;
171
-
294
return Container(
173
- padding: const EdgeInsets.all(16),
295
+ padding: const EdgeInsets.all(12),
296
decoration: BoxDecoration(
175
- color: isDark ? const Color(0xFF141414) : const Color(0xFFF8FAFC),
176
- borderRadius: BorderRadius.circular(8),
177
- border: Border.all(
178
- color: isDark ? AppColors.darkSurfaceCard : AppColors.lightSurface,
179
- ),
297
+ color: _panelColor(context),
298
+ borderRadius: BorderRadius.circular(14),
299
+ border: Border.all(color: _outlineColor(context)),
300
),
181
- child: Column(
182
- crossAxisAlignment: CrossAxisAlignment.start,
301
+ child: child,
302
+ );
303
+ }
304
+}
305
+
306
+class _McpToggleSetting extends StatelessWidget {
307
+ const _McpToggleSetting({
308
+ required this.label,
309
+ required this.value,
310
+ required this.onChanged,
311
+ this.description,
312
+ this.badges = const [],
313
+ });
314
+
315
+ final String label;
316
+ final String? description;
317
+ final bool value;
318
+ final ValueChanged<bool> onChanged;
319
+ final List<Widget> badges;
320
+
321
+ @override
322
+ Widget build(BuildContext context) {
323
+ final theme = Theme.of(context);
324
+
325
+ return _McpPanel(
326
+ child: Row(
327
+ crossAxisAlignment: CrossAxisAlignment.center,
328
children: [
184
- Row(
185
- crossAxisAlignment: CrossAxisAlignment.start,
186
- children: [
187
- Container(
188
- width: 40,
189
- height: 40,
190
- alignment: Alignment.center,
191
- decoration: BoxDecoration(
192
- color: theme.colorScheme.primary.withValues(alpha: 0.12),
193
- borderRadius: BorderRadius.circular(8),
194
- ),
195
- child: HugeIcon(
196
- icon: HugeIcons.strokeRoundedMcpServer,
197
- color: theme.colorScheme.primary,
198
- size: 22,
199
- ),
200
- ),
201
- const SizedBox(width: 12),
202
- Expanded(
203
- child: Column(
204
- crossAxisAlignment: CrossAxisAlignment.start,
329
+ Expanded(
330
+ child: Column(
331
+ crossAxisAlignment: CrossAxisAlignment.start,
332
+ children: [
333
+ Wrap(
334
+ spacing: 8,
335
+ runSpacing: 6,
336
+ crossAxisAlignment: WrapCrossAlignment.center,
337
children: [
338
Text(
207
- l10n.example_mcp_server_title,
208
- style: theme.textTheme.titleMedium?.copyWith(
339
+ label,
340
+ style: theme.textTheme.titleSmall?.copyWith(
341
fontWeight: FontWeight.w700,
342
),
343
),
212
- const SizedBox(height: 4),
213
- Text(
214
- l10n.example_mcp_server_desc,
215
- style: theme.textTheme.bodyMedium?.copyWith(
216
- color: theme.colorScheme.onSurfaceVariant,
217
- ),
218
- ),
344
+ ...badges,
345
],
346
),
221
- ),
222
- ],
223
- ),
224
- const SizedBox(height: 16),
225
- ShadButton(
226
- onPressed: onToggle,
227
- leading: HugeIcon(icon: enabled ? HugeIcons.strokeRoundedPower : HugeIcons.strokeRoundedAdd01),
228
- child: Text(
229
- enabled
230
- ? l10n.disable_example_server
231
- : l10n.enable_example_server,
347
+ if (description != null) ...[
348
+ const SizedBox(height: 4),
349
+ Text(
350
+ description!,
351
+ style: theme.textTheme.bodySmall?.copyWith(
352
+ color: theme.colorScheme.onSurfaceVariant,
353
+ ),
354
+ ),
355
+ ],
356
+ ],
357
),
358
),
359
+ const SizedBox(width: 8),
360
+ Switch.adaptive(value: value, onChanged: onChanged),
361
],
362
),
363
);
@@ -247,70 +374,65 @@ class _ToolRow extends StatelessWidget {
374
final theme = Theme.of(context);
375
final l10n = AppLocalizations.of(context)!;
376
final isMcp = tool.providerType == ToolProviderType.mcp;
250
- final isDark = theme.brightness == Brightness.dark;
377
252
- return Container(
253
- margin: const EdgeInsets.only(bottom: 10),
254
- padding: const EdgeInsets.all(14),
255
- decoration: BoxDecoration(
256
- color: isDark ? const Color(0xFF111111) : Colors.white,
257
- borderRadius: BorderRadius.circular(8),
258
- border: Border.all(
259
- color: isDark ? AppColors.darkSurfaceCard : AppColors.lightSurface,
260
- ),
261
- ),
262
- child: Row(
263
- crossAxisAlignment: CrossAxisAlignment.start,
264
- children: [
265
- HugeIcon(icon:
266
- isMcp ? HugeIcons.strokeRoundedShare01 : HugeIcons.strokeRoundedCalculate,
267
- size: 20,
268
- color: isMcp
269
- ? theme.colorScheme.primary
270
- : theme.colorScheme.outline,
271
- ),
272
- const SizedBox(width: 12),
273
- Expanded(
274
- child: Column(
275
- crossAxisAlignment: CrossAxisAlignment.start,
276
- children: [
277
- Text(
278
- tool.name,
279
- style: theme.textTheme.bodyLarge?.copyWith(
280
- fontWeight: FontWeight.w700,
281
- ),
282
- ),
283
- if (tool.description.isNotEmpty) ...[
284
- const SizedBox(height: 4),
378
+ return Padding(
379
+ padding: const EdgeInsets.only(bottom: 10),
380
+ child: _McpPanel(
381
+ child: Row(
382
+ crossAxisAlignment: CrossAxisAlignment.start,
383
+ children: [
384
+ HugeIcon(
385
+ icon: isMcp
386
+ ? HugeIcons.strokeRoundedShare01
387
+ : HugeIcons.strokeRoundedCalculate,
388
+ size: 18,
389
+ color: isMcp
390
+ ? theme.colorScheme.primary
391
+ : theme.colorScheme.outline,
392
+ ),
393
+ const SizedBox(width: 12),
394
+ Expanded(
395
+ child: Column(
396
+ crossAxisAlignment: CrossAxisAlignment.start,
397
+ children: [
398
Text(
286
- tool.description,
287
- style: theme.textTheme.bodyMedium?.copyWith(
288
- color: theme.colorScheme.onSurfaceVariant,
399
+ tool.name,
400
+ style: theme.textTheme.bodyLarge?.copyWith(
401
+ fontWeight: FontWeight.w700,
402
),
403
),
291
- ],
292
- const SizedBox(height: 8),
293
- Wrap(
294
- spacing: 8,
295
- runSpacing: 8,
296
- children: [
297
- _ToolBadge(
298
- label: isMcp ? 'MCP' : l10n.built_in_label,
299
- color: isMcp
300
- ? theme.colorScheme.primary
301
- : theme.colorScheme.outline,
404
+ if (tool.description.isNotEmpty) ...[
405
+ const SizedBox(height: 4),
406
+ Text(
407
+ tool.description,
408
+ style: theme.textTheme.bodyMedium?.copyWith(
409
+ color: theme.colorScheme.onSurfaceVariant,
410
+ ),
411
),
303
- if (tool.providerRef != null)
412
+ ],
413
+ const SizedBox(height: 8),
414
+ Wrap(
415
+ spacing: 8,
416
+ runSpacing: 8,
417
+ children: [
418
_ToolBadge(
305
- label: tool.providerRef!,
306
- color: theme.colorScheme.secondary,
419
+ label: isMcp ? 'MCP' : l10n.built_in_label,
420
+ color: isMcp
421
+ ? theme.colorScheme.primary
422
+ : theme.colorScheme.outline,
423
),
308
- ],
309
- ),
310
- ],
424
+ if (tool.providerRef != null)
425
+ _ToolBadge(
426
+ label: tool.providerRef!,
427
+ color: theme.colorScheme.secondary,
428
+ ),
429
+ ],
430
+ ),
431
+ ],
432
+ ),
433
),
312
- ),
313
- ],
434
+ ],
435
+ ),
436
),
437
);
438
}
@@ -356,33 +478,81 @@ class _StatusPanel extends StatelessWidget {
478
@override
479
Widget build(BuildContext context) {
480
final theme = Theme.of(context);
481
+ return _McpPanel(
482
+ child: Padding(
483
+ padding: const EdgeInsets.symmetric(vertical: 12),
484
+ child: Column(
485
+ children: [
486
+ HugeIcon(icon: icon, size: 28, color: theme.colorScheme.outline),
487
+ const SizedBox(height: 12),
488
+ Text(
489
+ title,
490
+ style: theme.textTheme.titleMedium?.copyWith(
491
+ fontWeight: FontWeight.w700,
492
+ ),
493
+ textAlign: TextAlign.center,
494
+ ),
495
+ const SizedBox(height: 6),
496
+ Text(
497
+ body,
498
+ style: theme.textTheme.bodyMedium?.copyWith(
499
+ color: theme.colorScheme.onSurfaceVariant,
500
+ ),
501
+ textAlign: TextAlign.center,
502
+ ),
503
+ ],
504
+ ),
505
+ ),
506
+ );
507
+ }
508
+}
509
+
510
+class _FeatureBadge extends StatelessWidget {
511
+ const _FeatureBadge({required this.label});
512
+
513
+ final String label;
514
+
515
+ @override
516
+ Widget build(BuildContext context) {
517
return Container(
360
- padding: const EdgeInsets.all(24),
518
+ padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
519
decoration: BoxDecoration(
362
- borderRadius: BorderRadius.circular(8),
363
- border: Border.all(color: theme.colorScheme.outline),
520
+ color: Colors.amber.withValues(alpha: 0.10),
521
+ borderRadius: BorderRadius.circular(999),
522
+ border: Border.all(color: Colors.amber.withValues(alpha: 0.22)),
523
),
365
- child: Column(
366
- children: [
367
- HugeIcon(icon: icon, size: 32, color: theme.colorScheme.outline),
368
- const SizedBox(height: 12),
369
- Text(
370
- title,
371
- style: theme.textTheme.titleMedium?.copyWith(
372
- fontWeight: FontWeight.w700,
373
- ),
374
- textAlign: TextAlign.center,
375
- ),
376
- const SizedBox(height: 6),
377
- Text(
378
- body,
379
- style: theme.textTheme.bodyMedium?.copyWith(
380
- color: theme.colorScheme.onSurfaceVariant,
524
+ child: Text(
525
+ label.toUpperCase(),
526
+ style: Theme.of(context).textTheme.labelSmall?.copyWith(
527
+ color: const Color(0xFFB45309),
528
+ fontWeight: FontWeight.w800,
529
+ letterSpacing: 0.45,
530
),
382
- textAlign: TextAlign.center,
383
- ),
384
- ],
531
),
532
);
533
}
534
}
535
+
536
+// Visual helpers, kept in sync with settings_screen.dart to ensure the
537
+// MCP screen matches the rest of the app's settings surfaces.
538
+List<Widget> _withVerticalSpacing(List<Widget> children, {double gap = 12}) {
539
+ if (children.isEmpty) return const [];
540
+ return [
541
+ for (var index = 0; index < children.length; index++) ...[
542
+ children[index],
543
+ if (index != children.length - 1) SizedBox(height: gap),
544
+ ],
545
+ ];
546
+}
547
+
548
+Color _panelColor(BuildContext context) {
549
+ return ShadTheme.of(context).colorScheme.secondary;
550
+}
551
+
552
+Color _surfaceColor(BuildContext context) {
553
+ return ShadTheme.of(context).colorScheme.card;
554
+}
555
+
556
+Color _outlineColor(BuildContext context, {double alpha = 0.6}) {
557
+ return ShadTheme.of(context).colorScheme.border.withValues(alpha: alpha);
558
+}
\ No newline at end of file
lib/features/settings/data/models/app_settings.dart
+4
-4
@@ -71,8 +71,8 @@ class AppSettings {
71
this.defaultPersonaId,
72
this.hasCompletedOnboarding = false,
73
this.hasAskedForNotifications = false,
74
- this.mcpEnabled = true,
75
- this.newChatMcpEnabled = true,
74
+ this.mcpEnabled = false,
75
+ this.newChatMcpEnabled = false,
76
this.codeThemeDark = SyntaxThemeName.dark,
77
this.codeThemeLight = SyntaxThemeName.light,
78
this.preferredBackend = PreferredBackend.cpu,
@@ -268,8 +268,8 @@ class AppSettings {
268
defaultPersonaId: map['defaultPersonaId'],
269
hasCompletedOnboarding: map['hasCompletedOnboarding'] ?? false,
270
hasAskedForNotifications: map['hasAskedForNotifications'] ?? false,
271
- mcpEnabled: map['mcpEnabled'] ?? true,
272
- newChatMcpEnabled: map['newChatMcpEnabled'] ?? true,
271
+ mcpEnabled: map['mcpEnabled'] ?? false,
272
+ newChatMcpEnabled: map['newChatMcpEnabled'] ?? false,
273
codeThemeDark: SyntaxThemeName.values[map['codeThemeDark'] ?? 0],
274
codeThemeLight: SyntaxThemeName.values[map['codeThemeLight'] ?? 1],
275
preferredBackend: _parsePreferredBackend(map['preferredBackend']),