@mobilenext/mobile-mcp 0.0.49 → 0.0.51
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/README.md +49 -9
- package/lib/index.js +32 -6
- package/lib/server.js +7 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,24 +7,15 @@ This server allows Agents and LLMs to interact with native iOS/Android applicati
|
|
|
7
7
|
<a href="https://github.com/mobile-next/mobile-mcp">
|
|
8
8
|
<img src="https://img.shields.io/github/stars/mobile-next/mobile-mcp" alt="Mobile Next Stars" />
|
|
9
9
|
</a>
|
|
10
|
-
<a href="https://github.com/mobile-next/mobile-mcp">
|
|
11
|
-
<img src="https://img.shields.io/github/contributors/mobile-next/mobile-mcp?color=green" alt="Mobile Next Downloads" />
|
|
12
|
-
</a>
|
|
13
10
|
<a href="https://www.npmjs.com/package/@mobilenext/mobile-mcp">
|
|
14
11
|
<img src="https://img.shields.io/npm/dm/@mobilenext/mobile-mcp?logo=npm&style=flat&color=red" alt="npm" />
|
|
15
12
|
</a>
|
|
16
13
|
<a href="https://github.com/mobile-next/mobile-mcp/releases">
|
|
17
14
|
<img src="https://img.shields.io/github/release/mobile-next/mobile-mcp" />
|
|
18
15
|
</a>
|
|
19
|
-
<a href="https://github.com/mobile-next/mobile-mcp/blob/main/LICENSE">
|
|
20
|
-
<img src="https://img.shields.io/badge/license-Apache 2.0-blue.svg" alt="Mobile MCP is released under the Apache-2.0 License" />
|
|
21
|
-
</a>
|
|
22
16
|
<a href="https://insiders.vscode.dev/redirect?url=vscode%3Amcp%2Finstall%3F%7B%22name%22%3A%22mobile-mcp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40mobilenext%2Fmobile-mcp%40latest%22%5D%7D">
|
|
23
17
|
<img src="https://img.shields.io/badge/VS_Code-VS_Code?style=flat-square&label=Install%20Server&color=0098FF" alt="Install in VS Code" />
|
|
24
18
|
</a>
|
|
25
|
-
</h4>
|
|
26
|
-
|
|
27
|
-
<h4 align="center">
|
|
28
19
|
<a href="https://github.com/mobile-next/mobile-mcp/wiki">
|
|
29
20
|
<img src="https://img.shields.io/badge/documentation-wiki-blue" alt="wiki" />
|
|
30
21
|
</a>
|
|
@@ -359,6 +350,31 @@ Or add the standard config under `mcpServers` in your settings as shown above.
|
|
|
359
350
|
|
|
360
351
|
[Read more in our wiki](https://github.com/mobile-next/mobile-mcp/wiki)! 🚀
|
|
361
352
|
|
|
353
|
+
### SSE Server Mode
|
|
354
|
+
|
|
355
|
+
By default, Mobile MCP runs over stdio. To start an SSE server instead, use the `--listen` flag:
|
|
356
|
+
|
|
357
|
+
```bash
|
|
358
|
+
npx @mobilenext/mobile-mcp@latest --listen 3000
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
This binds to `localhost:3000`. To bind to a specific interface:
|
|
362
|
+
|
|
363
|
+
```bash
|
|
364
|
+
npx @mobilenext/mobile-mcp@latest --listen 0.0.0.0:3000
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Then configure your MCP client to connect to `http://<host>:3000/mcp`.
|
|
368
|
+
|
|
369
|
+
#### Authorization
|
|
370
|
+
|
|
371
|
+
To require Bearer token authorization on the SSE server, set the `MOBILEMCP_AUTH` environment variable:
|
|
372
|
+
|
|
373
|
+
```bash
|
|
374
|
+
MOBILEMCP_AUTH=my-secret-token npx @mobilenext/mobile-mcp@latest --listen 3000
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
When set, all requests must include the header `Authorization: Bearer my-secret-token`.
|
|
362
378
|
|
|
363
379
|
### 🛠️ How to Use 📝
|
|
364
380
|
|
|
@@ -444,6 +460,30 @@ When launched, Mobile MCP can connect to:
|
|
|
444
460
|
|
|
445
461
|
Make sure you have your mobile platform SDKs (Xcode, Android SDK) installed and configured properly before running Mobile Next Mobile MCP.
|
|
446
462
|
|
|
463
|
+
### Telemetry
|
|
464
|
+
|
|
465
|
+
Mobile MCP collects anonymous usage telemetry via PostHog. To disable it, set the `MOBILEMCP_DISABLE_TELEMETRY` environment variable:
|
|
466
|
+
|
|
467
|
+
```bash
|
|
468
|
+
MOBILEMCP_DISABLE_TELEMETRY=1 npx @mobilenext/mobile-mcp@latest
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
For json configurations:
|
|
472
|
+
|
|
473
|
+
```json
|
|
474
|
+
{
|
|
475
|
+
"mcpServers": {
|
|
476
|
+
"mobile-mcp": {
|
|
477
|
+
"command": "npx",
|
|
478
|
+
"args": ["-y", "@mobilenext/mobile-mcp@latest"],
|
|
479
|
+
"env": {
|
|
480
|
+
"MOBILEMCP_DISABLE_TELEMETRY": "1"
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
```
|
|
486
|
+
|
|
447
487
|
### Running in "headless" mode on Simulators/Emulators
|
|
448
488
|
|
|
449
489
|
When you do not have a real device connected to your machine, you can run Mobile MCP with an emulator or simulator in the background.
|
package/lib/index.js
CHANGED
|
@@ -10,9 +10,19 @@ const server_1 = require("./server");
|
|
|
10
10
|
const logger_1 = require("./logger");
|
|
11
11
|
const express_1 = __importDefault(require("express"));
|
|
12
12
|
const commander_1 = require("commander");
|
|
13
|
-
const startSseServer = async (port) => {
|
|
13
|
+
const startSseServer = async (host, port) => {
|
|
14
14
|
const app = (0, express_1.default)();
|
|
15
15
|
const server = (0, server_1.createMcpServer)();
|
|
16
|
+
const authToken = process.env.MOBILEMCP_AUTH;
|
|
17
|
+
if (authToken) {
|
|
18
|
+
app.use((req, res, next) => {
|
|
19
|
+
if (req.headers.authorization !== `Bearer ${authToken}`) {
|
|
20
|
+
res.status(401).json({ error: "Unauthorized" });
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
next();
|
|
24
|
+
});
|
|
25
|
+
}
|
|
16
26
|
let transport = null;
|
|
17
27
|
app.post("/mcp", (req, res) => {
|
|
18
28
|
if (transport) {
|
|
@@ -26,8 +36,8 @@ const startSseServer = async (port) => {
|
|
|
26
36
|
transport = new sse_js_1.SSEServerTransport("/mcp", res);
|
|
27
37
|
server.connect(transport);
|
|
28
38
|
});
|
|
29
|
-
app.listen(port, () => {
|
|
30
|
-
(0, logger_1.error)(`mobile-mcp ${(0, server_1.getAgentVersion)()} sse server listening on http
|
|
39
|
+
app.listen(port, host, () => {
|
|
40
|
+
(0, logger_1.error)(`mobile-mcp ${(0, server_1.getAgentVersion)()} sse server listening on http://${host}:${port}/mcp`);
|
|
31
41
|
});
|
|
32
42
|
};
|
|
33
43
|
const startStdioServer = async () => {
|
|
@@ -46,12 +56,28 @@ const startStdioServer = async () => {
|
|
|
46
56
|
const main = async () => {
|
|
47
57
|
commander_1.program
|
|
48
58
|
.version((0, server_1.getAgentVersion)())
|
|
49
|
-
.option("--
|
|
59
|
+
.option("--listen <listen>", "Start SSE server on [host:]port")
|
|
50
60
|
.option("--stdio", "Start stdio server (default)")
|
|
51
61
|
.parse(process.argv);
|
|
52
62
|
const options = commander_1.program.opts();
|
|
53
|
-
if (options.
|
|
54
|
-
|
|
63
|
+
if (options.listen) {
|
|
64
|
+
const listen = options.listen.trim();
|
|
65
|
+
const lastColon = listen.lastIndexOf(":");
|
|
66
|
+
let host = "localhost";
|
|
67
|
+
let rawPort;
|
|
68
|
+
if (lastColon > 0) {
|
|
69
|
+
host = listen.substring(0, lastColon);
|
|
70
|
+
rawPort = listen.substring(lastColon + 1);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
rawPort = listen;
|
|
74
|
+
}
|
|
75
|
+
const port = Number.parseInt(rawPort, 10);
|
|
76
|
+
if (!host || !rawPort || !Number.isInteger(port) || port < 1 || port > 65535) {
|
|
77
|
+
(0, logger_1.error)(`Invalid --listen value "${listen}". Expected [host:]port with port 1-65535.`);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
await startSseServer(host, port);
|
|
55
81
|
}
|
|
56
82
|
else {
|
|
57
83
|
await startStdioServer();
|
package/lib/server.js
CHANGED
|
@@ -78,6 +78,9 @@ const createMcpServer = () => {
|
|
|
78
78
|
}));
|
|
79
79
|
};
|
|
80
80
|
const posthog = async (event, properties) => {
|
|
81
|
+
if (process.env.MOBILEMCP_DISABLE_TELEMETRY) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
81
84
|
try {
|
|
82
85
|
const url = "https://us.i.posthog.com/i/v0/e/";
|
|
83
86
|
const api_key = "phc_KHRTZmkDsU7A8EbydEK8s4lJpPoTDyyBhSlwer694cS";
|
|
@@ -351,6 +354,10 @@ const createMcpServer = () => {
|
|
|
351
354
|
device: zod_1.z.string().describe("The device identifier to use. Use mobile_list_available_devices to find which devices are available to you."),
|
|
352
355
|
url: zod_1.z.string().describe("The URL to open"),
|
|
353
356
|
}, { destructiveHint: true }, async ({ device, url }) => {
|
|
357
|
+
const allowUnsafeUrls = process.env.MOBILEMCP_ALLOW_UNSAFE_URLS === "1";
|
|
358
|
+
if (!allowUnsafeUrls && !url.startsWith("http://") && !url.startsWith("https://")) {
|
|
359
|
+
throw new robot_1.ActionableError("Only http:// and https:// URLs are allowed. Set MOBILEMCP_ALLOW_UNSAFE_URLS=1 to allow other URL schemes.");
|
|
360
|
+
}
|
|
354
361
|
const robot = getRobotFromDevice(device);
|
|
355
362
|
await robot.openUrl(url);
|
|
356
363
|
return `Opened URL: ${url}`;
|