@mobilenext/mobile-mcp 0.0.50 → 0.0.52

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 CHANGED
@@ -350,6 +350,31 @@ Or add the standard config under `mcpServers` in your settings as shown above.
350
350
 
351
351
  [Read more in our wiki](https://github.com/mobile-next/mobile-mcp/wiki)! 🚀
352
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`.
353
378
 
354
379
  ### 🛠️ How to Use 📝
355
380
 
@@ -435,6 +460,30 @@ When launched, Mobile MCP can connect to:
435
460
 
436
461
  Make sure you have your mobile platform SDKs (Xcode, Android SDK) installed and configured properly before running Mobile Next Mobile MCP.
437
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
+
438
487
  ### Running in "headless" mode on Simulators/Emulators
439
488
 
440
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,34 @@ 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
+ (0, logger_1.error)("WARNING: MOBILEMCP_AUTH is not set. The SSE server will accept unauthenticated connections. Set MOBILEMCP_AUTH to require Bearer token authentication.");
19
+ }
20
+ if (authToken) {
21
+ app.use((req, res, next) => {
22
+ if (req.headers.authorization !== `Bearer ${authToken}`) {
23
+ res.status(401).json({ error: "Unauthorized" });
24
+ return;
25
+ }
26
+ next();
27
+ });
28
+ }
29
+ // Block cross-origin requests — MCP clients are not browsers
30
+ app.use((req, res, next) => {
31
+ if (req.headers.origin) {
32
+ res.status(403).json({ error: "Cross-origin requests are not allowed" });
33
+ return;
34
+ }
35
+ if (req.method === "OPTIONS") {
36
+ res.status(403).end();
37
+ return;
38
+ }
39
+ next();
40
+ });
16
41
  let transport = null;
17
42
  app.post("/mcp", (req, res) => {
18
43
  if (transport) {
@@ -21,13 +46,17 @@ const startSseServer = async (port) => {
21
46
  });
22
47
  app.get("/mcp", (req, res) => {
23
48
  if (transport) {
24
- transport.close();
49
+ res.status(409).json({ error: "Another client is already connected. Disconnect the existing client first." });
50
+ return;
25
51
  }
26
52
  transport = new sse_js_1.SSEServerTransport("/mcp", res);
53
+ transport.onclose = () => {
54
+ transport = null;
55
+ };
27
56
  server.connect(transport);
28
57
  });
29
- app.listen(port, () => {
30
- (0, logger_1.error)(`mobile-mcp ${(0, server_1.getAgentVersion)()} sse server listening on http://localhost:${port}/mcp`);
58
+ app.listen(port, host, () => {
59
+ (0, logger_1.error)(`mobile-mcp ${(0, server_1.getAgentVersion)()} sse server listening on http://${host}:${port}/mcp`);
31
60
  });
32
61
  };
33
62
  const startStdioServer = async () => {
@@ -46,12 +75,28 @@ const startStdioServer = async () => {
46
75
  const main = async () => {
47
76
  commander_1.program
48
77
  .version((0, server_1.getAgentVersion)())
49
- .option("--port <port>", "Start SSE server on this port")
78
+ .option("--listen <listen>", "Start SSE server on [host:]port")
50
79
  .option("--stdio", "Start stdio server (default)")
51
80
  .parse(process.argv);
52
81
  const options = commander_1.program.opts();
53
- if (options.port) {
54
- await startSseServer(+options.port);
82
+ if (options.listen) {
83
+ const listen = options.listen.trim();
84
+ const lastColon = listen.lastIndexOf(":");
85
+ let host = "localhost";
86
+ let rawPort;
87
+ if (lastColon > 0) {
88
+ host = listen.substring(0, lastColon);
89
+ rawPort = listen.substring(lastColon + 1);
90
+ }
91
+ else {
92
+ rawPort = listen;
93
+ }
94
+ const port = Number.parseInt(rawPort, 10);
95
+ if (!host || !rawPort || !Number.isInteger(port) || port < 1 || port > 65535) {
96
+ (0, logger_1.error)(`Invalid --listen value "${listen}". Expected [host:]port with port 1-65535.`);
97
+ process.exit(1);
98
+ }
99
+ await startSseServer(host, port);
55
100
  }
56
101
  else {
57
102
  await startStdioServer();
package/lib/mobilecli.js CHANGED
@@ -51,7 +51,7 @@ class Mobilecli {
51
51
  // We're inside node_modules, go to the last node_modules in the path
52
52
  const nodeModulesParts = pathParts.slice(0, lastNodeModulesIndex + 1);
53
53
  const lastNodeModulesPath = nodeModulesParts.join(node_path_1.sep);
54
- const mobilecliPath = (0, node_path_1.join)(lastNodeModulesPath, "@mobilenext", "mobilecli", "bin", binaryName);
54
+ const mobilecliPath = (0, node_path_1.join)(lastNodeModulesPath, "mobilecli", "bin", binaryName);
55
55
  if ((0, node_fs_1.existsSync)(mobilecliPath)) {
56
56
  return mobilecliPath;
57
57
  }
@@ -59,7 +59,7 @@ class Mobilecli {
59
59
  // Not in node_modules, look one directory up from current script
60
60
  const scriptDir = (0, node_path_1.dirname)(__filename);
61
61
  const parentDir = (0, node_path_1.dirname)(scriptDir);
62
- const mobilecliPath = (0, node_path_1.join)(parentDir, "node_modules", "@mobilenext", "mobilecli", "bin", binaryName);
62
+ const mobilecliPath = (0, node_path_1.join)(parentDir, "node_modules", "mobilecli", "bin", binaryName);
63
63
  if ((0, node_fs_1.existsSync)(mobilecliPath)) {
64
64
  return mobilecliPath;
65
65
  }
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";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mobilenext/mobile-mcp",
3
3
  "mcpName": "io.github.mobile-next/mobile-mcp",
4
- "version": "0.0.50",
4
+ "version": "0.0.52",
5
5
  "description": "Mobile MCP",
6
6
  "repository": {
7
7
  "type": "git",
@@ -34,7 +34,7 @@
34
34
  "zod-to-json-schema": "3.25.0"
35
35
  },
36
36
  "optionalDependencies": {
37
- "@mobilenext/mobilecli": "0.1.60"
37
+ "mobilecli": "0.2.0"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@eslint/eslintrc": "^3.2.0",