@tmhs/mobile-mcp 0.1.0 → 0.2.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.
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/tools/generateComponent.d.ts +3 -0
- package/dist/tools/generateComponent.d.ts.map +1 -0
- package/dist/tools/generateComponent.js +98 -0
- package/dist/tools/generateComponent.js.map +1 -0
- package/dist/tools/generateScreen.d.ts +3 -0
- package/dist/tools/generateScreen.d.ts.map +1 -0
- package/dist/tools/generateScreen.js +120 -0
- package/dist/tools/generateScreen.js.map +1 -0
- package/dist/tools/installDependency.d.ts +3 -0
- package/dist/tools/installDependency.d.ts.map +1 -0
- package/dist/tools/installDependency.js +79 -0
- package/dist/tools/installDependency.js.map +1 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -4,13 +4,19 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
4
4
|
import { register as registerCheckDevEnvironment } from "./tools/checkDevEnvironment.js";
|
|
5
5
|
import { register as registerScaffoldProject } from "./tools/scaffoldProject.js";
|
|
6
6
|
import { register as registerRunOnDevice } from "./tools/runOnDevice.js";
|
|
7
|
+
import { register as registerGenerateScreen } from "./tools/generateScreen.js";
|
|
8
|
+
import { register as registerGenerateComponent } from "./tools/generateComponent.js";
|
|
9
|
+
import { register as registerInstallDependency } from "./tools/installDependency.js";
|
|
7
10
|
const server = new McpServer({
|
|
8
11
|
name: "mobile-mcp",
|
|
9
|
-
version: "0.
|
|
12
|
+
version: "0.2.0",
|
|
10
13
|
});
|
|
11
14
|
registerCheckDevEnvironment(server);
|
|
12
15
|
registerScaffoldProject(server);
|
|
13
16
|
registerRunOnDevice(server);
|
|
17
|
+
registerGenerateScreen(server);
|
|
18
|
+
registerGenerateComponent(server);
|
|
19
|
+
registerInstallDependency(server);
|
|
14
20
|
async function main() {
|
|
15
21
|
const transport = new StdioServerTransport();
|
|
16
22
|
await server.connect(transport);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,OAAO,EAAE,QAAQ,IAAI,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AACzF,OAAO,EAAE,QAAQ,IAAI,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AACjF,OAAO,EAAE,QAAQ,IAAI,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,OAAO,EAAE,QAAQ,IAAI,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AACzF,OAAO,EAAE,QAAQ,IAAI,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AACjF,OAAO,EAAE,QAAQ,IAAI,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAE,QAAQ,IAAI,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAC/E,OAAO,EAAE,QAAQ,IAAI,yBAAyB,EAAE,MAAM,8BAA8B,CAAC;AACrF,OAAO,EAAE,QAAQ,IAAI,yBAAyB,EAAE,MAAM,8BAA8B,CAAC;AAErF,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,YAAY;IAClB,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,2BAA2B,CAAC,MAAM,CAAC,CAAC;AACpC,uBAAuB,CAAC,MAAM,CAAC,CAAC;AAChC,mBAAmB,CAAC,MAAM,CAAC,CAAC;AAC5B,sBAAsB,CAAC,MAAM,CAAC,CAAC;AAC/B,yBAAyB,CAAC,MAAM,CAAC,CAAC;AAClC,yBAAyB,CAAC,MAAM,CAAC,CAAC;AAElC,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generateComponent.d.ts","sourceRoot":"","sources":["../../src/tools/generateComponent.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA2DzE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAoDhD"}
|
|
@@ -0,0 +1,98 @@
|
|
|
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
|
+
name: z
|
|
7
|
+
.string()
|
|
8
|
+
.describe("Component name in PascalCase (e.g. 'Avatar', 'ProductCard')"),
|
|
9
|
+
directory: z
|
|
10
|
+
.string()
|
|
11
|
+
.optional()
|
|
12
|
+
.default("components")
|
|
13
|
+
.describe("Directory relative to project root (e.g. 'components/ui')"),
|
|
14
|
+
with_tests: z
|
|
15
|
+
.boolean()
|
|
16
|
+
.optional()
|
|
17
|
+
.default(false)
|
|
18
|
+
.describe("Generate a companion test file alongside the component"),
|
|
19
|
+
project_path: z
|
|
20
|
+
.string()
|
|
21
|
+
.optional()
|
|
22
|
+
.describe("Absolute path to the Expo project root. Defaults to cwd."),
|
|
23
|
+
};
|
|
24
|
+
function generateComponentContent(name) {
|
|
25
|
+
return `import { View, Text, StyleSheet } from "react-native";
|
|
26
|
+
|
|
27
|
+
interface ${name}Props {
|
|
28
|
+
// TODO: define props
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function ${name}({}: ${name}Props) {
|
|
32
|
+
return (
|
|
33
|
+
<View style={styles.container}>
|
|
34
|
+
<Text>${name}</Text>
|
|
35
|
+
</View>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const styles = StyleSheet.create({
|
|
40
|
+
container: {
|
|
41
|
+
// TODO: add styles
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
`;
|
|
45
|
+
}
|
|
46
|
+
function generateTestContent(name) {
|
|
47
|
+
return `import { render, screen } from "@testing-library/react-native";
|
|
48
|
+
import { ${name} } from "../${name}";
|
|
49
|
+
|
|
50
|
+
describe("${name}", () => {
|
|
51
|
+
it("renders without crashing", () => {
|
|
52
|
+
render(<${name} />);
|
|
53
|
+
expect(screen.getByText("${name}")).toBeTruthy();
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
`;
|
|
57
|
+
}
|
|
58
|
+
export function register(server) {
|
|
59
|
+
server.tool("mobile_generateComponent", "Create a React Native component file with typed props, StyleSheet, and optional test file.", inputSchema, async (args) => {
|
|
60
|
+
try {
|
|
61
|
+
const root = args.project_path || process.cwd();
|
|
62
|
+
const componentDir = join(root, args.directory);
|
|
63
|
+
mkdirSync(componentDir, { recursive: true });
|
|
64
|
+
const componentFile = join(componentDir, `${args.name}.tsx`);
|
|
65
|
+
if (existsSync(componentFile)) {
|
|
66
|
+
return errorResponse(new Error(`Component already exists: ${componentFile}`));
|
|
67
|
+
}
|
|
68
|
+
writeFileSync(componentFile, generateComponentContent(args.name));
|
|
69
|
+
const filesCreated = [componentFile];
|
|
70
|
+
if (args.with_tests) {
|
|
71
|
+
const testDir = join(componentDir, "__tests__");
|
|
72
|
+
mkdirSync(testDir, { recursive: true });
|
|
73
|
+
const testFile = join(testDir, `${args.name}.test.tsx`);
|
|
74
|
+
writeFileSync(testFile, generateTestContent(args.name));
|
|
75
|
+
filesCreated.push(testFile);
|
|
76
|
+
}
|
|
77
|
+
const result = {
|
|
78
|
+
success: true,
|
|
79
|
+
files_created: filesCreated,
|
|
80
|
+
component_name: args.name,
|
|
81
|
+
directory: args.directory,
|
|
82
|
+
has_tests: args.with_tests,
|
|
83
|
+
next_steps: [
|
|
84
|
+
`Define props in the ${args.name}Props interface`,
|
|
85
|
+
"Add styles to the StyleSheet",
|
|
86
|
+
args.with_tests
|
|
87
|
+
? "Update the test to match your final props"
|
|
88
|
+
: "Consider adding tests later with with_tests: true",
|
|
89
|
+
],
|
|
90
|
+
};
|
|
91
|
+
return textResponse(JSON.stringify(result, null, 2));
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
return errorResponse(err);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=generateComponent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generateComponent.js","sourceRoot":"","sources":["../../src/tools/generateComponent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generateScreen.d.ts","sourceRoot":"","sources":["../../src/tools/generateScreen.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA2EzE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA8DhD"}
|
|
@@ -0,0 +1,120 @@
|
|
|
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
|
+
name: z
|
|
7
|
+
.string()
|
|
8
|
+
.describe("Screen name (lowercase, used as filename e.g. 'profile')"),
|
|
9
|
+
type: z
|
|
10
|
+
.enum(["tab", "stack", "modal"])
|
|
11
|
+
.describe("Navigation type for this screen"),
|
|
12
|
+
layout_group: z
|
|
13
|
+
.string()
|
|
14
|
+
.optional()
|
|
15
|
+
.default("")
|
|
16
|
+
.describe("Route group directory (e.g. '(tabs)' or 'settings'). Empty for root-level screen."),
|
|
17
|
+
project_path: z
|
|
18
|
+
.string()
|
|
19
|
+
.optional()
|
|
20
|
+
.describe("Absolute path to the Expo project root. Defaults to cwd."),
|
|
21
|
+
};
|
|
22
|
+
function generateScreenContent(name, type) {
|
|
23
|
+
const componentName = name.charAt(0).toUpperCase() + name.slice(1) + "Screen";
|
|
24
|
+
return `import { View, Text, StyleSheet } from "react-native";
|
|
25
|
+
import { Stack } from "expo-router";
|
|
26
|
+
|
|
27
|
+
export default function ${componentName}() {
|
|
28
|
+
return (
|
|
29
|
+
<View style={styles.container}>
|
|
30
|
+
<Stack.Screen options={{ title: "${name.charAt(0).toUpperCase() + name.slice(1)}" }} />
|
|
31
|
+
<Text style={styles.title}>${name.charAt(0).toUpperCase() + name.slice(1)}</Text>
|
|
32
|
+
</View>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const styles = StyleSheet.create({
|
|
37
|
+
container: {
|
|
38
|
+
flex: 1,
|
|
39
|
+
alignItems: "center",
|
|
40
|
+
justifyContent: "center",
|
|
41
|
+
padding: 16,
|
|
42
|
+
},
|
|
43
|
+
title: {
|
|
44
|
+
fontSize: 24,
|
|
45
|
+
fontWeight: "600",
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
`;
|
|
49
|
+
}
|
|
50
|
+
function generateLayoutContent(group, type) {
|
|
51
|
+
if (type === "tab") {
|
|
52
|
+
return `import { Tabs } from "expo-router";
|
|
53
|
+
|
|
54
|
+
export default function ${group.replace(/[()]/g, "")}Layout() {
|
|
55
|
+
return (
|
|
56
|
+
<Tabs
|
|
57
|
+
screenOptions={{
|
|
58
|
+
tabBarActiveTintColor: "#007AFF",
|
|
59
|
+
}}
|
|
60
|
+
/>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
`;
|
|
64
|
+
}
|
|
65
|
+
return `import { Stack } from "expo-router";
|
|
66
|
+
|
|
67
|
+
export default function ${group.replace(/[()]/g, "")}Layout() {
|
|
68
|
+
return <Stack />;
|
|
69
|
+
}
|
|
70
|
+
`;
|
|
71
|
+
}
|
|
72
|
+
export function register(server) {
|
|
73
|
+
server.tool("mobile_generateScreen", "Create a new Expo Router screen file with navigation wiring and boilerplate.", inputSchema, async (args) => {
|
|
74
|
+
try {
|
|
75
|
+
const root = args.project_path || process.cwd();
|
|
76
|
+
const appDir = join(root, "app");
|
|
77
|
+
const screenDir = args.layout_group
|
|
78
|
+
? join(appDir, args.layout_group)
|
|
79
|
+
: appDir;
|
|
80
|
+
mkdirSync(screenDir, { recursive: true });
|
|
81
|
+
const screenFile = join(screenDir, `${args.name}.tsx`);
|
|
82
|
+
if (existsSync(screenFile)) {
|
|
83
|
+
return errorResponse(new Error(`Screen already exists: ${screenFile}`));
|
|
84
|
+
}
|
|
85
|
+
writeFileSync(screenFile, generateScreenContent(args.name, args.type));
|
|
86
|
+
const layoutFile = join(screenDir, "_layout.tsx");
|
|
87
|
+
let layoutCreated = false;
|
|
88
|
+
if (!existsSync(layoutFile) && args.layout_group) {
|
|
89
|
+
writeFileSync(layoutFile, generateLayoutContent(args.layout_group, args.type));
|
|
90
|
+
layoutCreated = true;
|
|
91
|
+
}
|
|
92
|
+
const result = {
|
|
93
|
+
success: true,
|
|
94
|
+
screen_file: screenFile,
|
|
95
|
+
layout_file: layoutCreated ? layoutFile : null,
|
|
96
|
+
layout_created: layoutCreated,
|
|
97
|
+
name: args.name,
|
|
98
|
+
type: args.type,
|
|
99
|
+
layout_group: args.layout_group || "(root)",
|
|
100
|
+
next_steps: [
|
|
101
|
+
layoutCreated
|
|
102
|
+
? `Review the generated _layout.tsx in ${args.layout_group}`
|
|
103
|
+
: null,
|
|
104
|
+
`Edit ${screenFile} to add your screen content`,
|
|
105
|
+
args.type === "tab"
|
|
106
|
+
? "Add a tabBarIcon to the Tabs.Screen options in the layout"
|
|
107
|
+
: null,
|
|
108
|
+
args.type === "modal"
|
|
109
|
+
? 'Add presentation: "modal" to the Stack.Screen options in the parent layout'
|
|
110
|
+
: null,
|
|
111
|
+
].filter(Boolean),
|
|
112
|
+
};
|
|
113
|
+
return textResponse(JSON.stringify(result, null, 2));
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
return errorResponse(err);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=generateScreen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generateScreen.js","sourceRoot":"","sources":["../../src/tools/generateScreen.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE1D,MAAM,WAAW,GAAG;IAClB,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,QAAQ,CAAC,0DAA0D,CAAC;IACvE,IAAI,EAAE,CAAC;SACJ,IAAI,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;SAC/B,QAAQ,CAAC,iCAAiC,CAAC;IAC9C,YAAY,EAAE,CAAC;SACZ,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,OAAO,CAAC,EAAE,CAAC;SACX,QAAQ,CAAC,mFAAmF,CAAC;IAChG,YAAY,EAAE,CAAC;SACZ,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,0DAA0D,CAAC;CACxE,CAAC;AAEF,SAAS,qBAAqB,CAAC,IAAY,EAAE,IAAY;IACvD,MAAM,aAAa,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC;IAE9E,OAAO;;;0BAGiB,aAAa;;;yCAGE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;mCAClD,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;;;;;;;;;;;;;;;;;CAiB9E,CAAC;AACF,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAa,EAAE,IAAY;IACxD,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACnB,OAAO;;0BAEe,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;;;;;;;;;CASnD,CAAC;IACA,CAAC;IAED,OAAO;;0BAEiB,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;;;CAGnD,CAAC;AACF,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,MAAiB;IACxC,MAAM,CAAC,IAAI,CACT,uBAAuB,EACvB,8EAA8E,EAC9E,WAAW,EACX,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;YAChD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YACjC,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY;gBACjC,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC;gBACjC,CAAC,CAAC,MAAM,CAAC;YAEX,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAE1C,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,IAAI,MAAM,CAAC,CAAC;YACvD,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC3B,OAAO,aAAa,CAClB,IAAI,KAAK,CAAC,0BAA0B,UAAU,EAAE,CAAC,CAClD,CAAC;YACJ,CAAC;YAED,aAAa,CAAC,UAAU,EAAE,qBAAqB,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YAEvE,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;YAClD,IAAI,aAAa,GAAG,KAAK,CAAC;YAC1B,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;gBACjD,aAAa,CACX,UAAU,EACV,qBAAqB,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,IAAI,CAAC,CACpD,CAAC;gBACF,aAAa,GAAG,IAAI,CAAC;YACvB,CAAC;YAED,MAAM,MAAM,GAAG;gBACb,OAAO,EAAE,IAAI;gBACb,WAAW,EAAE,UAAU;gBACvB,WAAW,EAAE,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI;gBAC9C,cAAc,EAAE,aAAa;gBAC7B,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,QAAQ;gBAC3C,UAAU,EAAE;oBACV,aAAa;wBACX,CAAC,CAAC,uCAAuC,IAAI,CAAC,YAAY,EAAE;wBAC5D,CAAC,CAAC,IAAI;oBACR,QAAQ,UAAU,6BAA6B;oBAC/C,IAAI,CAAC,IAAI,KAAK,KAAK;wBACjB,CAAC,CAAC,2DAA2D;wBAC7D,CAAC,CAAC,IAAI;oBACR,IAAI,CAAC,IAAI,KAAK,OAAO;wBACnB,CAAC,CAAC,4EAA4E;wBAC9E,CAAC,CAAC,IAAI;iBACT,CAAC,MAAM,CAAC,OAAO,CAAC;aAClB,CAAC;YAEF,OAAO,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACvD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installDependency.d.ts","sourceRoot":"","sources":["../../src/tools/installDependency.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAyCzE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA8DhD"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { execSync } from "node:child_process";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { textResponse, errorResponse } from "../types.js";
|
|
6
|
+
const NATIVE_MODULE_INDICATORS = [
|
|
7
|
+
"react-native-reanimated",
|
|
8
|
+
"react-native-gesture-handler",
|
|
9
|
+
"react-native-screens",
|
|
10
|
+
"react-native-svg",
|
|
11
|
+
"react-native-maps",
|
|
12
|
+
"react-native-webview",
|
|
13
|
+
"expo-camera",
|
|
14
|
+
"expo-location",
|
|
15
|
+
"expo-notifications",
|
|
16
|
+
"expo-sensors",
|
|
17
|
+
"expo-media-library",
|
|
18
|
+
"expo-contacts",
|
|
19
|
+
"expo-calendar",
|
|
20
|
+
"expo-barcode-scanner",
|
|
21
|
+
"expo-print",
|
|
22
|
+
"expo-local-authentication",
|
|
23
|
+
"expo-audio",
|
|
24
|
+
"expo-video",
|
|
25
|
+
];
|
|
26
|
+
const inputSchema = {
|
|
27
|
+
package_name: z
|
|
28
|
+
.string()
|
|
29
|
+
.describe("Package to install (e.g. 'zustand' or '@tanstack/react-query'). Multiple packages separated by spaces."),
|
|
30
|
+
project_path: z
|
|
31
|
+
.string()
|
|
32
|
+
.optional()
|
|
33
|
+
.describe("Absolute path to the Expo project root. Defaults to cwd."),
|
|
34
|
+
dev: z
|
|
35
|
+
.boolean()
|
|
36
|
+
.optional()
|
|
37
|
+
.default(false)
|
|
38
|
+
.describe("Install as a dev dependency"),
|
|
39
|
+
};
|
|
40
|
+
export function register(server) {
|
|
41
|
+
server.tool("mobile_installDependency", "Install a package using npx expo install for Expo compatibility. Detects native modules and warns about Expo Go limitations.", inputSchema, async (args) => {
|
|
42
|
+
try {
|
|
43
|
+
const cwd = args.project_path || process.cwd();
|
|
44
|
+
const packages = args.package_name.split(/\s+/).filter(Boolean);
|
|
45
|
+
const appJsonPath = join(cwd, "app.json");
|
|
46
|
+
if (!existsSync(appJsonPath)) {
|
|
47
|
+
return errorResponse(new Error(`No app.json found at ${cwd}. Is this an Expo project root?`));
|
|
48
|
+
}
|
|
49
|
+
const nativePackages = packages.filter((p) => NATIVE_MODULE_INDICATORS.some((n) => p === n || p.startsWith(n + "@")));
|
|
50
|
+
const devFlag = args.dev ? "-- --save-dev" : "";
|
|
51
|
+
const command = `npx expo install ${packages.join(" ")} ${devFlag}`.trim();
|
|
52
|
+
const output = execSync(command, {
|
|
53
|
+
cwd,
|
|
54
|
+
encoding: "utf-8",
|
|
55
|
+
timeout: 120000,
|
|
56
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
57
|
+
});
|
|
58
|
+
const warnings = [];
|
|
59
|
+
if (nativePackages.length > 0) {
|
|
60
|
+
warnings.push(`Native modules detected: ${nativePackages.join(", ")}. These require a development build (npx expo run:ios / npx expo run:android) and will not work in Expo Go.`);
|
|
61
|
+
warnings.push("Run `npx expo prebuild` or use EAS Build to create a dev client.");
|
|
62
|
+
}
|
|
63
|
+
const result = {
|
|
64
|
+
success: true,
|
|
65
|
+
packages_installed: packages,
|
|
66
|
+
command_used: command,
|
|
67
|
+
output: output.trim(),
|
|
68
|
+
native_modules_detected: nativePackages,
|
|
69
|
+
warnings,
|
|
70
|
+
needs_dev_build: nativePackages.length > 0,
|
|
71
|
+
};
|
|
72
|
+
return textResponse(JSON.stringify(result, null, 2));
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
return errorResponse(err);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=installDependency.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installDependency.js","sourceRoot":"","sources":["../../src/tools/installDependency.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.
|
|
4
|
-
"description": "MCP server for mobile app development -
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "MCP server for mobile app development - 6 tools for environment checks, project scaffolding, device deployment, screen generation, component generation, and dependency installation.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|