@tmhs/mobile-mcp 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,303 @@
1
+ import { z } from "zod";
2
+ import { writeFileSync, mkdirSync, existsSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { textResponse, errorResponse } from "../types.js";
5
+ const inputSchema = {
6
+ project_path: z
7
+ .string()
8
+ .optional()
9
+ .describe("Absolute path to the project root. Defaults to cwd."),
10
+ framework: z
11
+ .enum(["expo", "flutter"])
12
+ .optional()
13
+ .default("expo")
14
+ .describe("Framework (default: expo)."),
15
+ output_directory: z
16
+ .string()
17
+ .optional()
18
+ .default("lib")
19
+ .describe("Output directory relative to project root (default: lib)."),
20
+ };
21
+ function generateExpoTokens() {
22
+ return `export const colors = {
23
+ light: {
24
+ primary: "#0A84FF",
25
+ primaryContainer: "#D6E4FF",
26
+ background: "#FFFFFF",
27
+ surface: "#F2F2F7",
28
+ surfaceVariant: "#E5E5EA",
29
+ text: "#000000",
30
+ textSecondary: "#3C3C43",
31
+ textTertiary: "#8E8E93",
32
+ border: "#C6C6C8",
33
+ error: "#FF3B30",
34
+ success: "#34C759",
35
+ warning: "#FF9500",
36
+ },
37
+ dark: {
38
+ primary: "#0A84FF",
39
+ primaryContainer: "#003A70",
40
+ background: "#000000",
41
+ surface: "#1C1C1E",
42
+ surfaceVariant: "#2C2C2E",
43
+ text: "#FFFFFF",
44
+ textSecondary: "#EBEBF5",
45
+ textTertiary: "#8E8E93",
46
+ border: "#38383A",
47
+ error: "#FF453A",
48
+ success: "#30D158",
49
+ warning: "#FF9F0A",
50
+ },
51
+ } as const;
52
+
53
+ export const spacing = {
54
+ xs: 4,
55
+ sm: 8,
56
+ md: 16,
57
+ lg: 24,
58
+ xl: 32,
59
+ xxl: 48,
60
+ } as const;
61
+
62
+ export const typography = {
63
+ largeTitle: { fontSize: 34, lineHeight: 41, fontWeight: "700" as const },
64
+ title1: { fontSize: 28, lineHeight: 34, fontWeight: "700" as const },
65
+ title2: { fontSize: 22, lineHeight: 28, fontWeight: "700" as const },
66
+ title3: { fontSize: 20, lineHeight: 25, fontWeight: "600" as const },
67
+ headline: { fontSize: 17, lineHeight: 22, fontWeight: "600" as const },
68
+ body: { fontSize: 17, lineHeight: 22, fontWeight: "400" as const },
69
+ callout: { fontSize: 16, lineHeight: 21, fontWeight: "400" as const },
70
+ subheadline: { fontSize: 15, lineHeight: 20, fontWeight: "400" as const },
71
+ footnote: { fontSize: 13, lineHeight: 18, fontWeight: "400" as const },
72
+ caption1: { fontSize: 12, lineHeight: 16, fontWeight: "400" as const },
73
+ caption2: { fontSize: 11, lineHeight: 13, fontWeight: "400" as const },
74
+ } as const;
75
+
76
+ export const radii = {
77
+ sm: 4,
78
+ md: 8,
79
+ lg: 12,
80
+ xl: 16,
81
+ full: 9999,
82
+ } as const;
83
+
84
+ export type ColorScheme = "light" | "dark";
85
+ export type Colors = typeof colors.light;
86
+ `;
87
+ }
88
+ function generateExpoThemeProvider() {
89
+ return `import React, { createContext, useContext, useEffect, useState } from "react";
90
+ import { useColorScheme as useSystemColorScheme } from "react-native";
91
+ import AsyncStorage from "@react-native-async-storage/async-storage";
92
+ import { colors, spacing, typography, radii, type ColorScheme, type Colors } from "./tokens";
93
+
94
+ const THEME_STORAGE_KEY = "@app_theme_preference";
95
+
96
+ interface ThemeContextValue {
97
+ colorScheme: ColorScheme;
98
+ colors: Colors;
99
+ spacing: typeof spacing;
100
+ typography: typeof typography;
101
+ radii: typeof radii;
102
+ setColorScheme: (scheme: ColorScheme | "system") => void;
103
+ isSystem: boolean;
104
+ }
105
+
106
+ const ThemeContext = createContext<ThemeContextValue | null>(null);
107
+
108
+ export function ThemeProvider({ children }: { children: React.ReactNode }) {
109
+ const systemScheme = useSystemColorScheme() ?? "light";
110
+ const [preference, setPreference] = useState<ColorScheme | "system">("system");
111
+ const [loaded, setLoaded] = useState(false);
112
+
113
+ useEffect(() => {
114
+ AsyncStorage.getItem(THEME_STORAGE_KEY).then((stored) => {
115
+ if (stored === "light" || stored === "dark" || stored === "system") {
116
+ setPreference(stored);
117
+ }
118
+ setLoaded(true);
119
+ });
120
+ }, []);
121
+
122
+ const colorScheme: ColorScheme = preference === "system" ? systemScheme : preference;
123
+
124
+ const setColorScheme = (scheme: ColorScheme | "system") => {
125
+ setPreference(scheme);
126
+ AsyncStorage.setItem(THEME_STORAGE_KEY, scheme);
127
+ };
128
+
129
+ if (!loaded) return null;
130
+
131
+ return (
132
+ <ThemeContext.Provider
133
+ value={{
134
+ colorScheme,
135
+ colors: colors[colorScheme],
136
+ spacing,
137
+ typography,
138
+ radii,
139
+ setColorScheme,
140
+ isSystem: preference === "system",
141
+ }}
142
+ >
143
+ {children}
144
+ </ThemeContext.Provider>
145
+ );
146
+ }
147
+
148
+ export function useTheme(): ThemeContextValue {
149
+ const ctx = useContext(ThemeContext);
150
+ if (!ctx) throw new Error("useTheme must be used within a ThemeProvider");
151
+ return ctx;
152
+ }
153
+ `;
154
+ }
155
+ function generateFlutterTokens() {
156
+ return `import 'package:flutter/material.dart';
157
+
158
+ abstract class AppColors {
159
+ static const light = _LightColors();
160
+ static const dark = _DarkColors();
161
+ }
162
+
163
+ class _LightColors {
164
+ const _LightColors();
165
+ Color get primary => const Color(0xFF0A84FF);
166
+ Color get primaryContainer => const Color(0xFFD6E4FF);
167
+ Color get background => const Color(0xFFFFFFFF);
168
+ Color get surface => const Color(0xFFF2F2F7);
169
+ Color get surfaceVariant => const Color(0xFFE5E5EA);
170
+ Color get onSurface => const Color(0xFF000000);
171
+ Color get onSurfaceVariant => const Color(0xFF3C3C43);
172
+ Color get outline => const Color(0xFFC6C6C8);
173
+ Color get error => const Color(0xFFFF3B30);
174
+ Color get success => const Color(0xFF34C759);
175
+ Color get warning => const Color(0xFFFF9500);
176
+ }
177
+
178
+ class _DarkColors {
179
+ const _DarkColors();
180
+ Color get primary => const Color(0xFF0A84FF);
181
+ Color get primaryContainer => const Color(0xFF003A70);
182
+ Color get background => const Color(0xFF000000);
183
+ Color get surface => const Color(0xFF1C1C1E);
184
+ Color get surfaceVariant => const Color(0xFF2C2C2E);
185
+ Color get onSurface => const Color(0xFFFFFFFF);
186
+ Color get onSurfaceVariant => const Color(0xFFEBEBF5);
187
+ Color get outline => const Color(0xFF38383A);
188
+ Color get error => const Color(0xFFFF453A);
189
+ Color get success => const Color(0xFF30D158);
190
+ Color get warning => const Color(0xFFFF9F0A);
191
+ }
192
+
193
+ abstract class AppSpacing {
194
+ static const double xs = 4;
195
+ static const double sm = 8;
196
+ static const double md = 16;
197
+ static const double lg = 24;
198
+ static const double xl = 32;
199
+ static const double xxl = 48;
200
+ }
201
+
202
+ abstract class AppRadii {
203
+ static const double sm = 4;
204
+ static const double md = 8;
205
+ static const double lg = 12;
206
+ static const double xl = 16;
207
+ static const double full = 9999;
208
+ }
209
+ `;
210
+ }
211
+ function generateFlutterTheme() {
212
+ return `import 'package:flutter/material.dart';
213
+ import 'tokens.dart';
214
+
215
+ class AppTheme {
216
+ static ThemeData light() {
217
+ final colors = AppColors.light;
218
+ return ThemeData(
219
+ useMaterial3: true,
220
+ brightness: Brightness.light,
221
+ colorScheme: ColorScheme.light(
222
+ primary: colors.primary,
223
+ primaryContainer: colors.primaryContainer,
224
+ surface: colors.surface,
225
+ surfaceContainerHighest: colors.surfaceVariant,
226
+ onSurface: colors.onSurface,
227
+ onSurfaceVariant: colors.onSurfaceVariant,
228
+ outline: colors.outline,
229
+ error: colors.error,
230
+ ),
231
+ scaffoldBackgroundColor: colors.background,
232
+ );
233
+ }
234
+
235
+ static ThemeData dark() {
236
+ final colors = AppColors.dark;
237
+ return ThemeData(
238
+ useMaterial3: true,
239
+ brightness: Brightness.dark,
240
+ colorScheme: ColorScheme.dark(
241
+ primary: colors.primary,
242
+ primaryContainer: colors.primaryContainer,
243
+ surface: colors.surface,
244
+ surfaceContainerHighest: colors.surfaceVariant,
245
+ onSurface: colors.onSurface,
246
+ onSurfaceVariant: colors.onSurfaceVariant,
247
+ outline: colors.outline,
248
+ error: colors.error,
249
+ ),
250
+ scaffoldBackgroundColor: colors.background,
251
+ );
252
+ }
253
+ }
254
+ `;
255
+ }
256
+ export function register(server) {
257
+ server.tool("mobile_setupTheming", "Initialize a design token system with light/dark themes, semantic colors, spacing, typography, and persistent theme preference.", inputSchema, async (args) => {
258
+ try {
259
+ const root = args.project_path || process.cwd();
260
+ const outDir = join(root, args.output_directory, "theme");
261
+ mkdirSync(outDir, { recursive: true });
262
+ const isFlutter = args.framework === "flutter";
263
+ const ext = isFlutter ? "dart" : "ts";
264
+ const files = [];
265
+ if (isFlutter) {
266
+ files.push({ name: `tokens.${ext}`, content: generateFlutterTokens() }, { name: `theme.${ext}`, content: generateFlutterTheme() });
267
+ }
268
+ else {
269
+ files.push({ name: `tokens.${ext}`, content: generateExpoTokens() }, { name: `theme-provider.${ext}x`, content: generateExpoThemeProvider() });
270
+ }
271
+ const created = [];
272
+ const skipped = [];
273
+ for (const file of files) {
274
+ const filePath = join(outDir, file.name);
275
+ if (existsSync(filePath)) {
276
+ skipped.push(filePath);
277
+ }
278
+ else {
279
+ writeFileSync(filePath, file.content, "utf-8");
280
+ created.push(filePath);
281
+ }
282
+ }
283
+ const nextSteps = [];
284
+ if (isFlutter) {
285
+ nextSteps.push("Use AppTheme.light() and AppTheme.dark() in MaterialApp's theme and darkTheme", "Access colors via Theme.of(context).colorScheme", "Use AppSpacing and AppRadii constants for consistent layout", "Add shared_preferences for persistent theme preference");
286
+ }
287
+ else {
288
+ nextSteps.push("Install @react-native-async-storage/async-storage for theme persistence", "Wrap your root layout with <ThemeProvider>", "Use const { colors, spacing } = useTheme() in components", "Use colorScheme for StatusBar and NavigationBar styling");
289
+ }
290
+ return textResponse(JSON.stringify({
291
+ success: true,
292
+ framework: args.framework,
293
+ files_created: created,
294
+ files_skipped: skipped,
295
+ next_steps: nextSteps,
296
+ }, null, 2));
297
+ }
298
+ catch (err) {
299
+ return errorResponse(err);
300
+ }
301
+ });
302
+ }
303
+ //# sourceMappingURL=setupTheming.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setupTheming.js","sourceRoot":"","sources":["../../src/tools/setupTheming.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tmhs/mobile-mcp",
3
- "version": "0.9.0",
4
- "description": "MCP server for mobile app development - 26 tools for environment checks, project scaffolding, device deployment, screen/component generation, dependency installation, permissions, AI integration, build health, push notifications, deep links, dev environment reset, store builds, metadata validation, App Store submission, Play Store submission, screenshot capture, bundle analysis, OTA update configuration, test execution, CI/CD setup, test file generation, i18n setup, map integration, form generation, and real-time client setup.",
3
+ "version": "0.11.0",
4
+ "description": "MCP server for mobile app development - 33 tools for environment checks, project scaffolding, device deployment, screen/component generation, dependency installation, permissions, AI integration, build health, push notifications, deep links, dev environment reset, store builds, metadata validation, App Store submission, Play Store submission, screenshot capture, bundle analysis, OTA update configuration, test execution, CI/CD setup, test file generation, i18n setup, map integration, form generation, real-time client setup, security auditing, performance profiling, offline readiness checks, APM monitoring setup, theming setup, accessibility auditing, and feature flag configuration.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "bin": {