@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,266 @@
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
+ provider: z
16
+ .enum(["posthog", "launchdarkly", "firebase"])
17
+ .optional()
18
+ .default("posthog")
19
+ .describe("Feature flag provider (default: posthog)."),
20
+ output_directory: z
21
+ .string()
22
+ .optional()
23
+ .default("lib")
24
+ .describe("Output directory relative to project root (default: lib)."),
25
+ };
26
+ function generatePostHogExpo() {
27
+ return `import PostHog from "posthog-react-native";
28
+
29
+ const posthog = new PostHog(process.env.EXPO_PUBLIC_POSTHOG_KEY!, {
30
+ host: process.env.EXPO_PUBLIC_POSTHOG_HOST ?? "https://us.i.posthog.com",
31
+ });
32
+
33
+ export type FeatureFlags = {
34
+ new_onboarding: boolean;
35
+ premium_features: boolean;
36
+ experimental_ui: boolean;
37
+ };
38
+
39
+ const FLAG_DEFAULTS: FeatureFlags = {
40
+ new_onboarding: false,
41
+ premium_features: false,
42
+ experimental_ui: false,
43
+ };
44
+
45
+ export function getFlag<K extends keyof FeatureFlags>(key: K): FeatureFlags[K] {
46
+ const value = posthog.getFeatureFlag(key);
47
+ if (value === undefined || value === null) return FLAG_DEFAULTS[key];
48
+ return value as FeatureFlags[K];
49
+ }
50
+
51
+ export function useFeatureFlag<K extends keyof FeatureFlags>(key: K): FeatureFlags[K] {
52
+ const value = posthog.useFeatureFlag(key);
53
+ if (value === undefined || value === null) return FLAG_DEFAULTS[key];
54
+ return value as FeatureFlags[K];
55
+ }
56
+
57
+ export function identifyUser(userId: string, properties?: Record<string, unknown>): void {
58
+ posthog.identify(userId, properties);
59
+ }
60
+
61
+ export function resetUser(): void {
62
+ posthog.reset();
63
+ }
64
+
65
+ export { posthog };
66
+ `;
67
+ }
68
+ function generateLaunchDarklyExpo() {
69
+ return `import { LDClient, LDConfig, LDContext } from "@launchdarkly/react-native-client-sdk";
70
+
71
+ const config: LDConfig = {
72
+ mobileKey: process.env.EXPO_PUBLIC_LAUNCHDARKLY_KEY!,
73
+ debug: __DEV__,
74
+ };
75
+
76
+ const client = new LDClient();
77
+
78
+ export type FeatureFlags = {
79
+ new_onboarding: boolean;
80
+ premium_features: boolean;
81
+ experimental_ui: boolean;
82
+ };
83
+
84
+ const FLAG_DEFAULTS: FeatureFlags = {
85
+ new_onboarding: false,
86
+ premium_features: false,
87
+ experimental_ui: false,
88
+ };
89
+
90
+ let initialized = false;
91
+
92
+ export async function initFeatureFlags(userId: string): Promise<void> {
93
+ if (initialized) return;
94
+ const context: LDContext = { kind: "user", key: userId };
95
+ await client.configure(config, context);
96
+ initialized = true;
97
+ }
98
+
99
+ export function getFlag<K extends keyof FeatureFlags>(key: K): FeatureFlags[K] {
100
+ return (client.boolVariation(key, FLAG_DEFAULTS[key]) as FeatureFlags[K]);
101
+ }
102
+
103
+ export function onFlagChange(key: keyof FeatureFlags, callback: (value: boolean) => void): () => void {
104
+ const listener = (flagKey: string, value: unknown) => {
105
+ if (flagKey === key) callback(value as boolean);
106
+ };
107
+ client.registerFeatureFlagListener(key, listener);
108
+ return () => client.unregisterFeatureFlagListener(key, listener);
109
+ }
110
+
111
+ export { client as ldClient };
112
+ `;
113
+ }
114
+ function generateFirebaseExpo() {
115
+ return `import remoteConfig from "@react-native-firebase/remote-config";
116
+
117
+ export type FeatureFlags = {
118
+ new_onboarding: boolean;
119
+ premium_features: boolean;
120
+ experimental_ui: boolean;
121
+ };
122
+
123
+ const FLAG_DEFAULTS: FeatureFlags = {
124
+ new_onboarding: false,
125
+ premium_features: false,
126
+ experimental_ui: false,
127
+ };
128
+
129
+ export async function initFeatureFlags(): Promise<void> {
130
+ await remoteConfig().setDefaults(FLAG_DEFAULTS as Record<string, boolean>);
131
+ await remoteConfig().setConfigSettings({
132
+ minimumFetchIntervalMillis: __DEV__ ? 0 : 3600000,
133
+ });
134
+ await remoteConfig().fetchAndActivate();
135
+ }
136
+
137
+ export function getFlag<K extends keyof FeatureFlags>(key: K): FeatureFlags[K] {
138
+ return remoteConfig().getBoolean(key) as FeatureFlags[K];
139
+ }
140
+
141
+ export function getAllFlags(): FeatureFlags {
142
+ return {
143
+ new_onboarding: remoteConfig().getBoolean("new_onboarding"),
144
+ premium_features: remoteConfig().getBoolean("premium_features"),
145
+ experimental_ui: remoteConfig().getBoolean("experimental_ui"),
146
+ };
147
+ }
148
+
149
+ export async function refreshFlags(): Promise<void> {
150
+ await remoteConfig().fetchAndActivate();
151
+ }
152
+ `;
153
+ }
154
+ function generateFlutterFeatureFlags(provider) {
155
+ const providerImport = provider === "firebase"
156
+ ? "import 'package:firebase_remote_config/firebase_remote_config.dart';"
157
+ : "// Add your feature flag provider package import here";
158
+ return `${providerImport}
159
+
160
+ enum FeatureFlag {
161
+ newOnboarding('new_onboarding', false),
162
+ premiumFeatures('premium_features', false),
163
+ experimentalUi('experimental_ui', false);
164
+
165
+ const FeatureFlag(this.key, this.defaultValue);
166
+ final String key;
167
+ final bool defaultValue;
168
+ }
169
+
170
+ class FeatureFlagService {
171
+ static final FeatureFlagService _instance = FeatureFlagService._();
172
+ factory FeatureFlagService() => _instance;
173
+ FeatureFlagService._();
174
+
175
+ final Map<String, bool> _overrides = {};
176
+
177
+ Future<void> init() async {
178
+ ${provider === "firebase" ? ` final rc = FirebaseRemoteConfig.instance;
179
+ await rc.setDefaults({
180
+ for (final flag in FeatureFlag.values) flag.key: flag.defaultValue,
181
+ });
182
+ await rc.setConfigSettings(RemoteConfigSettings(
183
+ fetchTimeout: const Duration(seconds: 10),
184
+ minimumFetchInterval: const Duration(hours: 1),
185
+ ));
186
+ await rc.fetchAndActivate();` : ` // Initialize your feature flag provider here
187
+ // Load remote flag values`}
188
+ }
189
+
190
+ bool isEnabled(FeatureFlag flag) {
191
+ if (_overrides.containsKey(flag.key)) return _overrides[flag.key]!;
192
+ ${provider === "firebase" ? ` return FirebaseRemoteConfig.instance.getBool(flag.key);` : ` return flag.defaultValue;`}
193
+ }
194
+
195
+ void setOverride(FeatureFlag flag, bool value) {
196
+ _overrides[flag.key] = value;
197
+ }
198
+
199
+ void clearOverrides() {
200
+ _overrides.clear();
201
+ }
202
+
203
+ Future<void> refresh() async {
204
+ ${provider === "firebase" ? ` await FirebaseRemoteConfig.instance.fetchAndActivate();` : ` // Refresh from remote provider`}
205
+ }
206
+ }
207
+ `;
208
+ }
209
+ export function register(server) {
210
+ server.tool("mobile_setupFeatureFlags", "Add a typed feature flag system with default values, remote sync, and provider integration (PostHog, LaunchDarkly, or Firebase Remote Config).", inputSchema, async (args) => {
211
+ try {
212
+ const root = args.project_path || process.cwd();
213
+ const outDir = join(root, args.output_directory);
214
+ mkdirSync(outDir, { recursive: true });
215
+ const isFlutter = args.framework === "flutter";
216
+ const ext = isFlutter ? "dart" : "ts";
217
+ const fileName = `feature-flags.${ext}`;
218
+ const filePath = join(outDir, fileName);
219
+ if (existsSync(filePath)) {
220
+ return errorResponse(new Error(`File already exists: ${filePath}`));
221
+ }
222
+ let content;
223
+ let dependencies;
224
+ const nextSteps = [];
225
+ if (isFlutter) {
226
+ content = generateFlutterFeatureFlags(args.provider);
227
+ dependencies = args.provider === "firebase"
228
+ ? ["firebase_remote_config"]
229
+ : [];
230
+ nextSteps.push("Call FeatureFlagService().init() in main() before runApp()", "Use FeatureFlagService().isEnabled(FeatureFlag.newOnboarding) to check flags", "Add new flags to the FeatureFlag enum as needed", `Configure ${args.provider} dashboard with matching flag keys`);
231
+ }
232
+ else {
233
+ switch (args.provider) {
234
+ case "launchdarkly":
235
+ content = generateLaunchDarklyExpo();
236
+ dependencies = ["@launchdarkly/react-native-client-sdk"];
237
+ nextSteps.push("Set EXPO_PUBLIC_LAUNCHDARKLY_KEY in .env", "Call initFeatureFlags(userId) after authentication", "Use getFlag('new_onboarding') to check flags");
238
+ break;
239
+ case "firebase":
240
+ content = generateFirebaseExpo();
241
+ dependencies = ["@react-native-firebase/remote-config", "@react-native-firebase/app"];
242
+ nextSteps.push("Ensure Firebase is configured (google-services.json / GoogleService-Info.plist)", "Call initFeatureFlags() in app root before rendering", "Use getFlag('new_onboarding') to check flags");
243
+ break;
244
+ default:
245
+ content = generatePostHogExpo();
246
+ dependencies = ["posthog-react-native"];
247
+ nextSteps.push("Set EXPO_PUBLIC_POSTHOG_KEY in .env", "Use useFeatureFlag('new_onboarding') in components", "Call identifyUser(userId) after authentication");
248
+ break;
249
+ }
250
+ }
251
+ writeFileSync(filePath, content, "utf-8");
252
+ return textResponse(JSON.stringify({
253
+ success: true,
254
+ provider: args.provider,
255
+ framework: args.framework,
256
+ file_created: filePath,
257
+ dependencies_needed: dependencies,
258
+ next_steps: nextSteps,
259
+ }, null, 2));
260
+ }
261
+ catch (err) {
262
+ return errorResponse(err);
263
+ }
264
+ });
265
+ }
266
+ //# sourceMappingURL=setupFeatureFlags.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setupFeatureFlags.js","sourceRoot":"","sources":["../../src/tools/setupFeatureFlags.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=setupMonitoring.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setupMonitoring.d.ts","sourceRoot":"","sources":["../../src/tools/setupMonitoring.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAsMzE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAoFhD"}
@@ -0,0 +1,250 @@
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
+ provider: z
11
+ .enum(["sentry", "datadog"])
12
+ .optional()
13
+ .default("sentry")
14
+ .describe("APM provider (default: sentry)."),
15
+ framework: z
16
+ .enum(["expo", "flutter"])
17
+ .optional()
18
+ .default("expo")
19
+ .describe("Framework (default: expo)."),
20
+ output_directory: z
21
+ .string()
22
+ .optional()
23
+ .default("lib")
24
+ .describe("Output directory relative to project root (default: lib)."),
25
+ };
26
+ function generateSentryExpo() {
27
+ return `import * as Sentry from "@sentry/react-native";
28
+
29
+ Sentry.init({
30
+ dsn: process.env.EXPO_PUBLIC_SENTRY_DSN!,
31
+ tracesSampleRate: __DEV__ ? 1.0 : 0.2,
32
+ profilesSampleRate: __DEV__ ? 1.0 : 0.1,
33
+ enableAutoSessionTracking: true,
34
+ environment: __DEV__ ? "development" : "production",
35
+ });
36
+
37
+ export function startSpan(name: string, op: string): Sentry.Span | undefined {
38
+ return Sentry.startInactiveSpan({ name, op });
39
+ }
40
+
41
+ export function captureError(error: unknown, context?: Record<string, unknown>): void {
42
+ Sentry.captureException(error, { extra: context });
43
+ }
44
+
45
+ export function setUser(id: string, email?: string): void {
46
+ Sentry.setUser({ id, email });
47
+ }
48
+
49
+ export function clearUser(): void {
50
+ Sentry.setUser(null);
51
+ }
52
+
53
+ export function addBreadcrumb(
54
+ category: string,
55
+ message: string,
56
+ level: Sentry.SeverityLevel = "info",
57
+ ): void {
58
+ Sentry.addBreadcrumb({ category, message, level });
59
+ }
60
+
61
+ export const SentryWrap = Sentry.wrap;
62
+ `;
63
+ }
64
+ function generateSentryFlutter() {
65
+ return `import 'package:sentry_flutter/sentry_flutter.dart';
66
+
67
+ Future<void> initMonitoring() async {
68
+ await SentryFlutter.init(
69
+ (options) {
70
+ options.dsn = const String.fromEnvironment('SENTRY_DSN');
71
+ options.tracesSampleRate = 0.2;
72
+ options.profilesSampleRate = 0.1;
73
+ options.environment =
74
+ const bool.fromEnvironment('dart.vm.product') ? 'production' : 'development';
75
+ },
76
+ );
77
+ }
78
+
79
+ Future<void> captureError(dynamic error, StackTrace stackTrace, {Map<String, dynamic>? extra}) async {
80
+ await Sentry.captureException(error, stackTrace: stackTrace, withScope: (scope) {
81
+ if (extra != null) {
82
+ for (final entry in extra.entries) {
83
+ scope.setExtra(entry.key, entry.value);
84
+ }
85
+ }
86
+ });
87
+ }
88
+
89
+ ISentrySpan startSpan(String operation, String description) {
90
+ final transaction = Sentry.startTransaction(operation, description);
91
+ return transaction;
92
+ }
93
+
94
+ void setUser(String id, {String? email}) {
95
+ Sentry.configureScope((scope) {
96
+ scope.setUser(SentryUser(id: id, email: email));
97
+ });
98
+ }
99
+
100
+ void clearUser() {
101
+ Sentry.configureScope((scope) {
102
+ scope.setUser(null);
103
+ });
104
+ }
105
+ `;
106
+ }
107
+ function generateDatadogExpo() {
108
+ return `import {
109
+ DdSdkReactNative,
110
+ DdSdkReactNativeConfiguration,
111
+ DdLogs,
112
+ DdTrace,
113
+ DdRum,
114
+ } from "@datadog/mobile-react-native";
115
+
116
+ const config = new DdSdkReactNativeConfiguration(
117
+ process.env.EXPO_PUBLIC_DD_CLIENT_TOKEN!,
118
+ __DEV__ ? "development" : "production",
119
+ process.env.EXPO_PUBLIC_DD_APPLICATION_ID!,
120
+ true, // track interactions
121
+ true, // track XHR
122
+ true, // track errors
123
+ );
124
+
125
+ config.site = "US1";
126
+ config.nativeCrashReportEnabled = true;
127
+ config.sessionSamplingRate = __DEV__ ? 100 : 20;
128
+
129
+ export async function initMonitoring(): Promise<void> {
130
+ await DdSdkReactNative.initialize(config);
131
+ }
132
+
133
+ export async function startSpan(name: string, resourceName: string): Promise<string> {
134
+ return DdTrace.startSpan(name, { "resource.name": resourceName });
135
+ }
136
+
137
+ export async function finishSpan(spanId: string): Promise<void> {
138
+ await DdTrace.finishSpan(spanId);
139
+ }
140
+
141
+ export function logError(message: string, context?: Record<string, unknown>): void {
142
+ DdLogs.error(message, context ?? {});
143
+ }
144
+
145
+ export function logInfo(message: string, context?: Record<string, unknown>): void {
146
+ DdLogs.info(message, context ?? {});
147
+ }
148
+
149
+ export function trackAction(name: string, context?: Record<string, unknown>): void {
150
+ DdRum.addAction("custom", name, context ?? {});
151
+ }
152
+
153
+ export function setUser(id: string, name?: string, email?: string): void {
154
+ DdSdkReactNative.setUser({ id, name, email });
155
+ }
156
+ `;
157
+ }
158
+ function generateDatadogFlutter() {
159
+ return `import 'package:datadog_flutter_plugin/datadog_flutter_plugin.dart';
160
+
161
+ Future<void> initMonitoring() async {
162
+ final configuration = DatadogConfiguration(
163
+ clientToken: const String.fromEnvironment('DD_CLIENT_TOKEN'),
164
+ env: const bool.fromEnvironment('dart.vm.product') ? 'production' : 'development',
165
+ site: DatadogSite.us1,
166
+ nativeCrashReportEnabled: true,
167
+ loggingConfiguration: DatadogLoggingConfiguration(),
168
+ rumConfiguration: DatadogRumConfiguration(
169
+ applicationId: const String.fromEnvironment('DD_APPLICATION_ID'),
170
+ sessionSamplingRate: 20,
171
+ traceSamplingRate: 20,
172
+ ),
173
+ );
174
+
175
+ await DatadogSdk.instance.initialize(configuration, TrackingConsent.granted);
176
+ }
177
+
178
+ void logError(String message, {Map<String, dynamic>? attributes}) {
179
+ DatadogSdk.instance.logs?.error(message, attributes: attributes ?? {});
180
+ }
181
+
182
+ void logInfo(String message, {Map<String, dynamic>? attributes}) {
183
+ DatadogSdk.instance.logs?.info(message, attributes: attributes ?? {});
184
+ }
185
+
186
+ void trackAction(String name, {Map<String, dynamic>? attributes}) {
187
+ DatadogSdk.instance.rum?.addAction(RumActionType.custom, name, attributes ?? {});
188
+ }
189
+
190
+ void setUser(String id, {String? name, String? email}) {
191
+ DatadogSdk.instance.setUserInfo(id: id, name: name, email: email);
192
+ }
193
+ `;
194
+ }
195
+ export function register(server) {
196
+ server.tool("mobile_setupMonitoring", "Configure application performance monitoring (APM) with Sentry Performance or Datadog RUM. Generates a monitoring module with error capture, tracing, user context, and breadcrumbs.", inputSchema, async (args) => {
197
+ try {
198
+ const root = args.project_path || process.cwd();
199
+ const outDir = join(root, args.output_directory);
200
+ mkdirSync(outDir, { recursive: true });
201
+ const isFlutter = args.framework === "flutter";
202
+ const ext = isFlutter ? "dart" : "ts";
203
+ const fileName = `monitoring.${ext}`;
204
+ const filePath = join(outDir, fileName);
205
+ if (existsSync(filePath)) {
206
+ return errorResponse(new Error(`File already exists: ${filePath}`));
207
+ }
208
+ let content;
209
+ let dependencies;
210
+ const nextSteps = [];
211
+ if (args.provider === "datadog") {
212
+ if (isFlutter) {
213
+ content = generateDatadogFlutter();
214
+ dependencies = ["datadog_flutter_plugin"];
215
+ nextSteps.push("Add DD_CLIENT_TOKEN and DD_APPLICATION_ID to --dart-define or .env", "Call initMonitoring() in main() before runApp()", "Wrap MaterialApp with DatadogNavigationObserver for auto route tracking");
216
+ }
217
+ else {
218
+ content = generateDatadogExpo();
219
+ dependencies = ["@datadog/mobile-react-native"];
220
+ nextSteps.push("Set EXPO_PUBLIC_DD_CLIENT_TOKEN and EXPO_PUBLIC_DD_APPLICATION_ID in .env", "Call initMonitoring() in your app root (_layout.tsx)", "Wrap navigation with DatadogProvider for auto view tracking");
221
+ }
222
+ }
223
+ else {
224
+ if (isFlutter) {
225
+ content = generateSentryFlutter();
226
+ dependencies = ["sentry_flutter"];
227
+ nextSteps.push("Add SENTRY_DSN to --dart-define or .env", "Call initMonitoring() in main() before runApp()", "Wrap runApp with Sentry's runZonedGuarded for automatic error capture");
228
+ }
229
+ else {
230
+ content = generateSentryExpo();
231
+ dependencies = ["@sentry/react-native"];
232
+ nextSteps.push("Set EXPO_PUBLIC_SENTRY_DSN in .env", "Wrap your root component with SentryWrap (e.g., export default SentryWrap(App))", "Run npx sentry-expo-upload-sourcemaps for production source maps", "Configure EAS Build to include Sentry plugin in app.config");
233
+ }
234
+ }
235
+ writeFileSync(filePath, content, "utf-8");
236
+ return textResponse(JSON.stringify({
237
+ success: true,
238
+ provider: args.provider,
239
+ framework: args.framework,
240
+ file_created: filePath,
241
+ dependencies_needed: dependencies,
242
+ next_steps: nextSteps,
243
+ }, null, 2));
244
+ }
245
+ catch (err) {
246
+ return errorResponse(err);
247
+ }
248
+ });
249
+ }
250
+ //# sourceMappingURL=setupMonitoring.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setupMonitoring.js","sourceRoot":"","sources":["../../src/tools/setupMonitoring.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=setupTheming.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setupTheming.d.ts","sourceRoot":"","sources":["../../src/tools/setupTheming.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAmQzE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA2EhD"}