@tmhs/mobile-mcp 0.6.0 → 0.9.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.
Files changed (58) hide show
  1. package/dist/index.js +23 -1
  2. package/dist/index.js.map +1 -1
  3. package/dist/tools/addMap.d.ts +3 -0
  4. package/dist/tools/addMap.d.ts.map +1 -0
  5. package/dist/tools/addMap.js +223 -0
  6. package/dist/tools/addMap.js.map +1 -0
  7. package/dist/tools/analyzeBundle.d.ts +3 -0
  8. package/dist/tools/analyzeBundle.d.ts.map +1 -0
  9. package/dist/tools/analyzeBundle.js +122 -0
  10. package/dist/tools/analyzeBundle.js.map +1 -0
  11. package/dist/tools/checkDevEnvironment.js +1 -1
  12. package/dist/tools/checkDevEnvironment.js.map +1 -1
  13. package/dist/tools/configureOTA.d.ts +3 -0
  14. package/dist/tools/configureOTA.d.ts.map +1 -0
  15. package/dist/tools/configureOTA.js +118 -0
  16. package/dist/tools/configureOTA.js.map +1 -0
  17. package/dist/tools/generateForm.d.ts +3 -0
  18. package/dist/tools/generateForm.d.ts.map +1 -0
  19. package/dist/tools/generateForm.js +337 -0
  20. package/dist/tools/generateForm.js.map +1 -0
  21. package/dist/tools/generateScreenshots.d.ts +3 -0
  22. package/dist/tools/generateScreenshots.d.ts.map +1 -0
  23. package/dist/tools/generateScreenshots.js +128 -0
  24. package/dist/tools/generateScreenshots.js.map +1 -0
  25. package/dist/tools/generateTestFile.d.ts +3 -0
  26. package/dist/tools/generateTestFile.d.ts.map +1 -0
  27. package/dist/tools/generateTestFile.js +184 -0
  28. package/dist/tools/generateTestFile.js.map +1 -0
  29. package/dist/tools/runOnDevice.d.ts.map +1 -1
  30. package/dist/tools/runOnDevice.js +45 -40
  31. package/dist/tools/runOnDevice.js.map +1 -1
  32. package/dist/tools/runTests.d.ts +3 -0
  33. package/dist/tools/runTests.d.ts.map +1 -0
  34. package/dist/tools/runTests.js +165 -0
  35. package/dist/tools/runTests.js.map +1 -0
  36. package/dist/tools/scaffoldProject.js +1 -1
  37. package/dist/tools/scaffoldProject.js.map +1 -1
  38. package/dist/tools/setupCI.d.ts +3 -0
  39. package/dist/tools/setupCI.d.ts.map +1 -0
  40. package/dist/tools/setupCI.js +202 -0
  41. package/dist/tools/setupCI.js.map +1 -0
  42. package/dist/tools/setupI18n.d.ts +3 -0
  43. package/dist/tools/setupI18n.d.ts.map +1 -0
  44. package/dist/tools/setupI18n.js +155 -0
  45. package/dist/tools/setupI18n.js.map +1 -0
  46. package/dist/tools/setupRealtime.d.ts +3 -0
  47. package/dist/tools/setupRealtime.d.ts.map +1 -0
  48. package/dist/tools/setupRealtime.js +311 -0
  49. package/dist/tools/setupRealtime.js.map +1 -0
  50. package/dist/tools/submitToAppStore.js +1 -1
  51. package/dist/tools/submitToAppStore.js.map +1 -1
  52. package/dist/tools/submitToPlayStore.d.ts +3 -0
  53. package/dist/tools/submitToPlayStore.d.ts.map +1 -0
  54. package/dist/tools/submitToPlayStore.js +92 -0
  55. package/dist/tools/submitToPlayStore.js.map +1 -0
  56. package/dist/tools/validateStoreMetadata.js +1 -1
  57. package/dist/tools/validateStoreMetadata.js.map +1 -1
  58. package/package.json +2 -2
@@ -0,0 +1,337 @@
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 fieldSchema = z.object({
6
+ name: z.string().describe("Field name in camelCase (e.g. 'email', 'firstName')."),
7
+ type: z
8
+ .enum(["text", "email", "password", "number", "phone", "textarea", "select"])
9
+ .describe("Field input type."),
10
+ label: z.string().describe("Display label for the field."),
11
+ required: z.boolean().optional().default(true).describe("Whether the field is required."),
12
+ placeholder: z.string().optional().describe("Placeholder text."),
13
+ });
14
+ const inputSchema = {
15
+ form_name: z
16
+ .string()
17
+ .describe("Form component name in PascalCase (e.g. 'LoginForm', 'ContactForm')."),
18
+ fields: z
19
+ .array(fieldSchema)
20
+ .min(1)
21
+ .describe("Array of field definitions."),
22
+ project_path: z
23
+ .string()
24
+ .optional()
25
+ .describe("Absolute path to the project root. Defaults to cwd."),
26
+ framework: z
27
+ .enum(["expo", "flutter"])
28
+ .optional()
29
+ .default("expo")
30
+ .describe("Project framework (default: expo)."),
31
+ output_directory: z
32
+ .string()
33
+ .optional()
34
+ .default("components")
35
+ .describe("Output directory relative to project root (default: components)."),
36
+ };
37
+ function zodValidation(field) {
38
+ let chain = "z.string()";
39
+ if (field.required)
40
+ chain += '.min(1, "Required")';
41
+ if (field.type === "email")
42
+ chain += '.email("Invalid email")';
43
+ if (field.type === "number")
44
+ chain = "z.coerce.number()";
45
+ if (field.type === "phone")
46
+ chain += '.regex(/^\\+?[0-9\\s-()]+$/, "Invalid phone number")';
47
+ if (field.type === "password")
48
+ chain += '.min(8, "At least 8 characters")';
49
+ return chain;
50
+ }
51
+ function rnInputType(type) {
52
+ switch (type) {
53
+ case "email": return "email-address";
54
+ case "number": return "numeric";
55
+ case "phone": return "phone-pad";
56
+ default: return "default";
57
+ }
58
+ }
59
+ function generateExpoForm(name, fields) {
60
+ const schemaFields = fields
61
+ .map((f) => ` ${f.name}: ${zodValidation(f)},`)
62
+ .join("\n");
63
+ const formFields = fields
64
+ .map((f) => {
65
+ const secureEntry = f.type === "password" ? "\n secureTextEntry" : "";
66
+ const multiline = f.type === "textarea" ? "\n multiline\n numberOfLines={4}" : "";
67
+ const keyboard = f.type !== "text" && f.type !== "password" && f.type !== "textarea" && f.type !== "select"
68
+ ? `\n keyboardType="${rnInputType(f.type)}"`
69
+ : "";
70
+ return ` <View style={styles.field}>
71
+ <Text style={styles.label}>${f.label}</Text>
72
+ <Controller
73
+ control={control}
74
+ name="${f.name}"
75
+ render={({ field: { onChange, onBlur, value } }) => (
76
+ <TextInput
77
+ style={[styles.input, errors.${f.name} && styles.inputError]}
78
+ onBlur={onBlur}
79
+ onChangeText={onChange}
80
+ value={value}
81
+ placeholder="${f.placeholder || f.label}"${secureEntry}${multiline}${keyboard}
82
+ />
83
+ )}
84
+ />
85
+ {errors.${f.name} && (
86
+ <Text style={styles.error}>{errors.${f.name}?.message}</Text>
87
+ )}
88
+ </View>`;
89
+ })
90
+ .join("\n\n");
91
+ const defaultValues = fields
92
+ .map((f) => ` ${f.name}: "",`)
93
+ .join("\n");
94
+ return `import { View, Text, TextInput, Pressable, StyleSheet } from "react-native";
95
+ import { useForm, Controller } from "react-hook-form";
96
+ import { zodResolver } from "@hookform/resolvers/zod";
97
+ import { z } from "zod";
98
+
99
+ const schema = z.object({
100
+ ${schemaFields}
101
+ });
102
+
103
+ type FormData = z.infer<typeof schema>;
104
+
105
+ interface ${name}Props {
106
+ onSubmit: (data: FormData) => void;
107
+ }
108
+
109
+ export function ${name}({ onSubmit }: ${name}Props) {
110
+ const {
111
+ control,
112
+ handleSubmit,
113
+ formState: { errors, isSubmitting },
114
+ } = useForm<FormData>({
115
+ resolver: zodResolver(schema),
116
+ defaultValues: {
117
+ ${defaultValues}
118
+ },
119
+ });
120
+
121
+ return (
122
+ <View style={styles.container}>
123
+ ${formFields}
124
+
125
+ <Pressable
126
+ style={[styles.button, isSubmitting && styles.buttonDisabled]}
127
+ onPress={handleSubmit(onSubmit)}
128
+ disabled={isSubmitting}
129
+ >
130
+ <Text style={styles.buttonText}>
131
+ {isSubmitting ? "Submitting..." : "Submit"}
132
+ </Text>
133
+ </Pressable>
134
+ </View>
135
+ );
136
+ }
137
+
138
+ const styles = StyleSheet.create({
139
+ container: {
140
+ padding: 16,
141
+ gap: 12,
142
+ },
143
+ field: {
144
+ gap: 4,
145
+ },
146
+ label: {
147
+ fontSize: 14,
148
+ fontWeight: "600",
149
+ },
150
+ input: {
151
+ borderWidth: 1,
152
+ borderColor: "#ccc",
153
+ borderRadius: 8,
154
+ padding: 12,
155
+ fontSize: 16,
156
+ },
157
+ inputError: {
158
+ borderColor: "#dc2626",
159
+ },
160
+ error: {
161
+ color: "#dc2626",
162
+ fontSize: 12,
163
+ },
164
+ button: {
165
+ backgroundColor: "#0A84FF",
166
+ padding: 16,
167
+ borderRadius: 8,
168
+ alignItems: "center",
169
+ marginTop: 8,
170
+ },
171
+ buttonDisabled: {
172
+ opacity: 0.6,
173
+ },
174
+ buttonText: {
175
+ color: "#fff",
176
+ fontSize: 16,
177
+ fontWeight: "600",
178
+ },
179
+ });
180
+ `;
181
+ }
182
+ function generateFlutterForm(name, fields) {
183
+ const className = name.charAt(0).toUpperCase() + name.slice(1);
184
+ const controllers = fields
185
+ .map((f) => ` final _${f.name}Controller = TextEditingController();`)
186
+ .join("\n");
187
+ const disposeControllers = fields
188
+ .map((f) => ` _${f.name}Controller.dispose();`)
189
+ .join("\n");
190
+ const formFields = fields
191
+ .map((f) => {
192
+ const obscure = f.type === "password" ? "\n obscureText: true," : "";
193
+ const maxLines = f.type === "textarea" ? "\n maxLines: 4," : "";
194
+ const keyboard = f.type === "email"
195
+ ? "\n keyboardType: TextInputType.emailAddress,"
196
+ : f.type === "number"
197
+ ? "\n keyboardType: TextInputType.number,"
198
+ : f.type === "phone"
199
+ ? "\n keyboardType: TextInputType.phone,"
200
+ : "";
201
+ const validator = f.required
202
+ ? `\n validator: (value) {\n if (value == null || value.isEmpty) return '${f.label} is required';\n return null;\n },`
203
+ : "";
204
+ return ` TextFormField(
205
+ controller: _${f.name}Controller,
206
+ decoration: const InputDecoration(
207
+ labelText: '${f.label}',
208
+ hintText: '${f.placeholder || f.label}',
209
+ ),${obscure}${maxLines}${keyboard}${validator}
210
+ ),
211
+ const SizedBox(height: 16),`;
212
+ })
213
+ .join("\n");
214
+ return `import 'package:flutter/material.dart';
215
+
216
+ class ${className} extends StatefulWidget {
217
+ final void Function(Map<String, String> data) onSubmit;
218
+
219
+ const ${className}({super.key, required this.onSubmit});
220
+
221
+ @override
222
+ State<${className}> createState() => _${className}State();
223
+ }
224
+
225
+ class _${className}State extends State<${className}> {
226
+ final _formKey = GlobalKey<FormState>();
227
+ bool _isSubmitting = false;
228
+
229
+ ${controllers}
230
+
231
+ @override
232
+ void dispose() {
233
+ ${disposeControllers}
234
+ super.dispose();
235
+ }
236
+
237
+ Future<void> _handleSubmit() async {
238
+ if (!_formKey.currentState!.validate()) return;
239
+
240
+ setState(() => _isSubmitting = true);
241
+ try {
242
+ widget.onSubmit({
243
+ ${fields.map((f) => ` '${f.name}': _${f.name}Controller.text,`).join("\n")}
244
+ });
245
+ } finally {
246
+ if (mounted) setState(() => _isSubmitting = false);
247
+ }
248
+ }
249
+
250
+ @override
251
+ Widget build(BuildContext context) {
252
+ return Form(
253
+ key: _formKey,
254
+ child: Padding(
255
+ padding: const EdgeInsets.all(16),
256
+ child: Column(
257
+ crossAxisAlignment: CrossAxisAlignment.stretch,
258
+ children: [
259
+ ${formFields}
260
+ ElevatedButton(
261
+ onPressed: _isSubmitting ? null : _handleSubmit,
262
+ child: Text(_isSubmitting ? 'Submitting...' : 'Submit'),
263
+ ),
264
+ ],
265
+ ),
266
+ ),
267
+ );
268
+ }
269
+ }
270
+ `;
271
+ }
272
+ export function register(server) {
273
+ server.tool("mobile_generateForm", "Scaffold a validated form component with typed fields, validation rules, and error handling. Uses React Hook Form + Zod for Expo or Form + TextFormField for Flutter.", inputSchema, async (args) => {
274
+ try {
275
+ const root = args.project_path || process.cwd();
276
+ const outDir = join(root, args.output_directory);
277
+ mkdirSync(outDir, { recursive: true });
278
+ const fields = args.fields.map((f) => ({
279
+ name: f.name,
280
+ type: f.type,
281
+ label: f.label,
282
+ required: f.required,
283
+ placeholder: f.placeholder,
284
+ }));
285
+ if (args.framework === "flutter") {
286
+ const fileName = args.form_name
287
+ .replace(/([A-Z])/g, "_$1")
288
+ .toLowerCase()
289
+ .replace(/^_/, "");
290
+ const filePath = join(outDir, `${fileName}.dart`);
291
+ if (existsSync(filePath)) {
292
+ return errorResponse(new Error(`File already exists: ${filePath}`));
293
+ }
294
+ writeFileSync(filePath, generateFlutterForm(args.form_name, fields), "utf-8");
295
+ return textResponse(JSON.stringify({
296
+ success: true,
297
+ framework: "flutter",
298
+ file_created: filePath,
299
+ form_name: args.form_name,
300
+ fields: fields.map((f) => f.name),
301
+ next_steps: [
302
+ "Import and use the form widget in your screen",
303
+ "Add email/phone validators if needed",
304
+ "Add keyboard avoidance with SingleChildScrollView",
305
+ ],
306
+ }, null, 2));
307
+ }
308
+ const filePath = join(outDir, `${args.form_name}.tsx`);
309
+ if (existsSync(filePath)) {
310
+ return errorResponse(new Error(`File already exists: ${filePath}`));
311
+ }
312
+ writeFileSync(filePath, generateExpoForm(args.form_name, fields), "utf-8");
313
+ return textResponse(JSON.stringify({
314
+ success: true,
315
+ framework: "expo",
316
+ file_created: filePath,
317
+ form_name: args.form_name,
318
+ fields: fields.map((f) => f.name),
319
+ dependencies_needed: [
320
+ "react-hook-form",
321
+ "@hookform/resolvers",
322
+ "zod",
323
+ ],
324
+ next_steps: [
325
+ "Install dependencies: npx expo install react-hook-form @hookform/resolvers zod",
326
+ "Import and render the form in your screen",
327
+ "Implement the onSubmit handler with your API call",
328
+ "Add KeyboardAvoidingView wrapper if fields are below the fold",
329
+ ],
330
+ }, null, 2));
331
+ }
332
+ catch (err) {
333
+ return errorResponse(err);
334
+ }
335
+ });
336
+ }
337
+ //# sourceMappingURL=generateForm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generateForm.js","sourceRoot":"","sources":["../../src/tools/generateForm.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function register(server: McpServer): void;
3
+ //# sourceMappingURL=generateScreenshots.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generateScreenshots.d.ts","sourceRoot":"","sources":["../../src/tools/generateScreenshots.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA4EzE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA8EhD"}
@@ -0,0 +1,128 @@
1
+ import { z } from "zod";
2
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { textResponse, errorResponse } from "../types.js";
5
+ const IOS_DEVICES = [
6
+ { name: '6.7" Display', width: 1290, height: 2796, label: "iPhone 15 Pro Max / 16 Pro Max" },
7
+ { name: '6.5" Display', width: 1284, height: 2778, label: "iPhone 14 Plus / 15 Plus" },
8
+ { name: '5.5" Display', width: 1242, height: 2208, label: "iPhone 8 Plus (legacy)" },
9
+ { name: '12.9" iPad Pro', width: 2048, height: 2732, label: "iPad Pro 12.9-inch" },
10
+ ];
11
+ const ANDROID_DEVICES = [
12
+ { name: "Phone", width: 1080, height: 1920, label: "Standard phone (1080x1920)" },
13
+ { name: "Phone Hi-Res", width: 1440, height: 3120, label: "Flagship phone (1440x3120)" },
14
+ { name: '7" Tablet', width: 1200, height: 1920, label: "7-inch tablet" },
15
+ { name: '10" Tablet', width: 1600, height: 2560, label: "10-inch tablet" },
16
+ ];
17
+ const HELPER_SCRIPT = `#!/usr/bin/env node
18
+ /**
19
+ * Screenshot helper - captures screenshots at store-required dimensions.
20
+ * Run from your project root with a running dev server.
21
+ *
22
+ * Usage:
23
+ * node scripts/capture-screenshots.js --platform ios
24
+ * node scripts/capture-screenshots.js --platform android
25
+ *
26
+ * Prerequisites:
27
+ * - Simulator/emulator running with your app loaded
28
+ * - For iOS: Xcode command line tools (xcrun simctl)
29
+ * - For Android: adb in PATH
30
+ */
31
+
32
+ const { execSync } = require("child_process");
33
+ const { mkdirSync, existsSync } = require("fs");
34
+ const path = require("path");
35
+
36
+ const platform = process.argv.includes("--android") ? "android" : "ios";
37
+ const outDir = path.join("screenshots", platform);
38
+
39
+ if (!existsSync(outDir)) mkdirSync(outDir, { recursive: true });
40
+
41
+ if (platform === "ios") {
42
+ const timestamp = Date.now();
43
+ const file = path.join(outDir, \`screenshot_\${timestamp}.png\`);
44
+ try {
45
+ execSync(\`xcrun simctl io booted screenshot "\${file}"\`, { stdio: "pipe" });
46
+ console.log("Saved:", file);
47
+ } catch (e) {
48
+ console.error("Failed. Is the iOS Simulator running?");
49
+ process.exit(1);
50
+ }
51
+ } else {
52
+ const timestamp = Date.now();
53
+ const file = path.join(outDir, \`screenshot_\${timestamp}.png\`);
54
+ try {
55
+ execSync(\`adb exec-out screencap -p > "\${file}"\`, { stdio: "pipe", shell: true });
56
+ console.log("Saved:", file);
57
+ } catch (e) {
58
+ console.error("Failed. Is an Android device/emulator connected?");
59
+ process.exit(1);
60
+ }
61
+ }
62
+ `;
63
+ const inputSchema = {
64
+ project_path: z
65
+ .string()
66
+ .optional()
67
+ .describe("Absolute path to the Expo project root. Defaults to cwd."),
68
+ platform: z
69
+ .enum(["ios", "android", "both"])
70
+ .optional()
71
+ .default("both")
72
+ .describe("Target platform for screenshot dimensions (default: both)"),
73
+ };
74
+ export function register(server) {
75
+ server.tool("mobile_generateScreenshots", "Generate a screenshot capture helper script and list required App Store and Play Store screenshot dimensions. Creates scripts/capture-screenshots.js.", inputSchema, async (args) => {
76
+ try {
77
+ const root = args.project_path || process.cwd();
78
+ const scriptsDir = join(root, "scripts");
79
+ if (!existsSync(scriptsDir)) {
80
+ mkdirSync(scriptsDir, { recursive: true });
81
+ }
82
+ const scriptPath = join(scriptsDir, "capture-screenshots.js");
83
+ writeFileSync(scriptPath, HELPER_SCRIPT, "utf-8");
84
+ const devices = [];
85
+ if (args.platform === "ios" || args.platform === "both") {
86
+ devices.push(...IOS_DEVICES.map((d) => ({ platform: "ios", ...d })));
87
+ }
88
+ if (args.platform === "android" || args.platform === "both") {
89
+ devices.push(...ANDROID_DEVICES.map((d) => ({ platform: "android", ...d })));
90
+ }
91
+ return textResponse(JSON.stringify({
92
+ success: true,
93
+ message: "Screenshot helper script created and dimension reference generated",
94
+ script: "scripts/capture-screenshots.js",
95
+ usage: [
96
+ "node scripts/capture-screenshots.js --ios",
97
+ "node scripts/capture-screenshots.js --android",
98
+ ],
99
+ required_dimensions: devices,
100
+ guidelines: {
101
+ ios: [
102
+ "Minimum 3 screenshots, maximum 10 per device size",
103
+ "6.7-inch and 6.5-inch displays are required for current iPhones",
104
+ "5.5-inch is required if supporting older iPhones",
105
+ "No device frames, status bars, or alpha transparency",
106
+ "PNG or JPEG, RGB color space",
107
+ ],
108
+ android: [
109
+ "Minimum 2 screenshots, maximum 8",
110
+ "JPEG or 24-bit PNG, minimum 320px, maximum 3840px per side",
111
+ "16:9 aspect ratio recommended for phones",
112
+ "At least one phone and one 7-inch or 10-inch tablet screenshot",
113
+ ],
114
+ },
115
+ next_steps: [
116
+ "Navigate to each key screen in your app",
117
+ "Run the capture script for each screen",
118
+ "Resize captures to required dimensions if needed",
119
+ "Upload to App Store Connect / Play Console",
120
+ ],
121
+ }, null, 2));
122
+ }
123
+ catch (err) {
124
+ return errorResponse(err);
125
+ }
126
+ });
127
+ }
128
+ //# sourceMappingURL=generateScreenshots.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generateScreenshots.js","sourceRoot":"","sources":["../../src/tools/generateScreenshots.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function register(server: McpServer): void;
3
+ //# sourceMappingURL=generateTestFile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generateTestFile.d.ts","sourceRoot":"","sources":["../../src/tools/generateTestFile.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAwHzE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAyGhD"}