@mobilenext/mobile-mcp 0.0.28 → 0.0.30-beta
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 +10 -1
- package/lib/android.js +0 -116
- package/lib/ios.js +0 -36
- package/lib/iphone-simulator.js +0 -58
- package/lib/mobilecli.js +53 -0
- package/lib/server.js +34 -39
- package/package.json +7 -6
package/README.md
CHANGED
|
@@ -25,8 +25,11 @@ This server allows Agents and LLMs to interact with native iOS/Android applicati
|
|
|
25
25
|
</h4>
|
|
26
26
|
|
|
27
27
|
<h4 align="center">
|
|
28
|
+
<a href="https://github.com/mobile-next/mobile-mcp/wiki">
|
|
29
|
+
<img src="https://img.shields.io/badge/documentation-wiki-blue" alt="wiki" />
|
|
30
|
+
</a>
|
|
28
31
|
<a href="http://mobilenexthq.com/join-slack">
|
|
29
|
-
<img src="https://img.shields.io/badge/join-Slack-blueviolet?logo=slack&style=flat" alt="
|
|
32
|
+
<img src="https://img.shields.io/badge/join-Slack-blueviolet?logo=slack&style=flat" alt="join on Slack" />
|
|
30
33
|
</a>
|
|
31
34
|
</h4>
|
|
32
35
|
|
|
@@ -102,6 +105,12 @@ Setup our MCP with Cline, Cursor, Claude, VS Code, Github Copilot:
|
|
|
102
105
|
claude mcp add mobile -- npx -y @mobilenext/mobile-mcp@latest
|
|
103
106
|
```
|
|
104
107
|
|
|
108
|
+
[Gemini CLI:](https://cloud.google.com/gemini/docs/codeassist/gemini-cli)
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
gemini mcp add mobile npx -y @mobilenext/mobile-mcp@latest
|
|
112
|
+
```
|
|
113
|
+
|
|
105
114
|
[Read more in our wiki](https://github.com/mobile-next/mobile-mcp/wiki)! 🚀
|
|
106
115
|
|
|
107
116
|
|
package/lib/android.js
CHANGED
|
@@ -336,122 +336,6 @@ class AndroidRobot {
|
|
|
336
336
|
const rotation = this.adb("shell", "settings", "get", "system", "user_rotation").toString().trim();
|
|
337
337
|
return rotation === "0" ? "portrait" : "landscape";
|
|
338
338
|
}
|
|
339
|
-
async getDeviceLogs(options) {
|
|
340
|
-
const timeWindow = options?.timeWindow || "1m";
|
|
341
|
-
const filter = options?.filter;
|
|
342
|
-
const processFilter = options?.process;
|
|
343
|
-
let packageFilter = null;
|
|
344
|
-
let searchQuery = null;
|
|
345
|
-
let effectiveFilter = filter;
|
|
346
|
-
// For Android: if both process and filter are provided, combine them as "package:<process> <filter>"
|
|
347
|
-
if (processFilter && filter && !filter.includes("package:")) {
|
|
348
|
-
effectiveFilter = `package:${processFilter} ${filter}`;
|
|
349
|
-
}
|
|
350
|
-
else if (processFilter && !filter) {
|
|
351
|
-
effectiveFilter = `package:${processFilter}`;
|
|
352
|
-
}
|
|
353
|
-
// Handle Android package filtering syntax
|
|
354
|
-
if (effectiveFilter) {
|
|
355
|
-
if (effectiveFilter.startsWith("package:mine")) {
|
|
356
|
-
// Filter to user apps only
|
|
357
|
-
const query = effectiveFilter.replace("package:mine", "").trim();
|
|
358
|
-
searchQuery = query || null;
|
|
359
|
-
// Will filter user packages in post-processing
|
|
360
|
-
}
|
|
361
|
-
else if (effectiveFilter.includes("package:")) {
|
|
362
|
-
// Handle specific package filters like package:com.example.app search_term
|
|
363
|
-
const packageMatch = effectiveFilter.match(/package:([^\s]+)(?:\s+(.+))?/);
|
|
364
|
-
if (packageMatch) {
|
|
365
|
-
packageFilter = packageMatch[1];
|
|
366
|
-
searchQuery = packageMatch[2] || null;
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
else {
|
|
370
|
-
// Regular search filter
|
|
371
|
-
searchQuery = effectiveFilter;
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
const args = ["shell", "logcat"];
|
|
375
|
-
if (timeWindow) {
|
|
376
|
-
// Calculate timestamp for time-based filtering using -T
|
|
377
|
-
const timeInSeconds = this.parseTimeWindow(timeWindow);
|
|
378
|
-
const startTime = new Date(Date.now() - (timeInSeconds * 1000));
|
|
379
|
-
// Format as MM-dd HH:mm:ss.mmm
|
|
380
|
-
const month = String(startTime.getMonth() + 1).padStart(2, "0");
|
|
381
|
-
const day = String(startTime.getDate()).padStart(2, "0");
|
|
382
|
-
const hours = String(startTime.getHours()).padStart(2, "0");
|
|
383
|
-
const minutes = String(startTime.getMinutes()).padStart(2, "0");
|
|
384
|
-
const seconds = String(startTime.getSeconds()).padStart(2, "0");
|
|
385
|
-
const milliseconds = String(startTime.getMilliseconds()).padStart(3, "0");
|
|
386
|
-
const timeFormat = `${month}-${day} ${hours}:${minutes}:${seconds}.${milliseconds}`;
|
|
387
|
-
args.push("-T", timeFormat);
|
|
388
|
-
}
|
|
389
|
-
else {
|
|
390
|
-
args.push("-d");
|
|
391
|
-
}
|
|
392
|
-
// Add package filtering directly to logcat if we have a specific package
|
|
393
|
-
if (packageFilter && packageFilter !== "mine") {
|
|
394
|
-
// Use logcat's native package filtering with --pid
|
|
395
|
-
try {
|
|
396
|
-
// First get the PID(s) for this package
|
|
397
|
-
const pidOutput = this.adb("shell", "pidof", packageFilter).toString().trim();
|
|
398
|
-
if (pidOutput) {
|
|
399
|
-
const pids = pidOutput.split(/\s+/);
|
|
400
|
-
for (const pid of pids) {
|
|
401
|
-
args.push("--pid", pid);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
catch (error) {
|
|
406
|
-
// If pidof fails, fall back to post-processing
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
const output = this.adb(...args).toString();
|
|
410
|
-
// Post-process filtering
|
|
411
|
-
const lines = output.split("\n").filter(line => line.trim());
|
|
412
|
-
let filteredLines = lines;
|
|
413
|
-
// Filter by specific package if provided (fallback if --pid didn't work)
|
|
414
|
-
if (packageFilter && packageFilter !== "mine") {
|
|
415
|
-
filteredLines = filteredLines.filter(line => {
|
|
416
|
-
return line.includes(packageFilter);
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
// Filter for user packages if package:mine
|
|
420
|
-
if (filter && filter.startsWith("package:mine")) {
|
|
421
|
-
filteredLines = filteredLines.filter(line => {
|
|
422
|
-
// Look for user app indicators - avoid system/Android logs
|
|
423
|
-
return !line.includes("com.android.") &&
|
|
424
|
-
!line.includes("android.") &&
|
|
425
|
-
!line.includes("system_") &&
|
|
426
|
-
(line.includes("com.") || line.includes("io.") || line.includes("net.") || line.includes("app."));
|
|
427
|
-
});
|
|
428
|
-
}
|
|
429
|
-
// Apply text search if provided
|
|
430
|
-
if (searchQuery) {
|
|
431
|
-
filteredLines = filteredLines.filter(line => {
|
|
432
|
-
return line.toLowerCase().includes(searchQuery.toLowerCase());
|
|
433
|
-
});
|
|
434
|
-
}
|
|
435
|
-
return filteredLines.join("\n");
|
|
436
|
-
}
|
|
437
|
-
parseTimeWindow(timeWindow) {
|
|
438
|
-
const match = timeWindow.match(/^(\d+)([smh])$/);
|
|
439
|
-
if (!match) {
|
|
440
|
-
return 60;
|
|
441
|
-
}
|
|
442
|
-
const value = parseInt(match[1], 10);
|
|
443
|
-
const unit = match[2];
|
|
444
|
-
switch (unit) {
|
|
445
|
-
case "s":
|
|
446
|
-
return value;
|
|
447
|
-
case "m":
|
|
448
|
-
return value * 60;
|
|
449
|
-
case "h":
|
|
450
|
-
return value * 3600;
|
|
451
|
-
default:
|
|
452
|
-
return 60;
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
339
|
async getUiAutomatorDump() {
|
|
456
340
|
for (let tries = 0; tries < 10; tries++) {
|
|
457
341
|
const dump = this.adb("exec-out", "uiautomator", "dump", "/dev/tty").toString();
|
package/lib/ios.js
CHANGED
|
@@ -145,42 +145,6 @@ class IosRobot {
|
|
|
145
145
|
const wda = await this.wda();
|
|
146
146
|
return await wda.getOrientation();
|
|
147
147
|
}
|
|
148
|
-
async getDeviceLogs(options) {
|
|
149
|
-
await this.assertTunnelRunning();
|
|
150
|
-
const timeWindow = options?.timeWindow || "1m";
|
|
151
|
-
const filter = options?.filter;
|
|
152
|
-
const args = ["syslog"];
|
|
153
|
-
if (timeWindow) {
|
|
154
|
-
const timeInSeconds = this.parseTimeWindow(timeWindow);
|
|
155
|
-
args.push("--since");
|
|
156
|
-
args.push(`${timeInSeconds}s`);
|
|
157
|
-
}
|
|
158
|
-
let output = await this.ios(...args);
|
|
159
|
-
if (filter) {
|
|
160
|
-
const lines = output.split("\n");
|
|
161
|
-
const filteredLines = lines.filter(line => line.toLowerCase().includes(filter.toLowerCase()));
|
|
162
|
-
output = filteredLines.join("\n");
|
|
163
|
-
}
|
|
164
|
-
return output;
|
|
165
|
-
}
|
|
166
|
-
parseTimeWindow(timeWindow) {
|
|
167
|
-
const match = timeWindow.match(/^(\d+)([smh])$/);
|
|
168
|
-
if (!match) {
|
|
169
|
-
return 60;
|
|
170
|
-
}
|
|
171
|
-
const value = parseInt(match[1], 10);
|
|
172
|
-
const unit = match[2];
|
|
173
|
-
switch (unit) {
|
|
174
|
-
case "s":
|
|
175
|
-
return value;
|
|
176
|
-
case "m":
|
|
177
|
-
return value * 60;
|
|
178
|
-
case "h":
|
|
179
|
-
return value * 3600;
|
|
180
|
-
default:
|
|
181
|
-
return 60;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
148
|
}
|
|
185
149
|
exports.IosRobot = IosRobot;
|
|
186
150
|
class IosManager {
|
package/lib/iphone-simulator.js
CHANGED
|
@@ -124,64 +124,6 @@ class Simctl {
|
|
|
124
124
|
const wda = await this.wda();
|
|
125
125
|
return wda.getOrientation();
|
|
126
126
|
}
|
|
127
|
-
async getDeviceLogs(options) {
|
|
128
|
-
const timeWindow = options?.timeWindow || "1m";
|
|
129
|
-
const filter = options?.filter;
|
|
130
|
-
const processFilter = options?.process;
|
|
131
|
-
const deviceUuid = this.simulatorUuid;
|
|
132
|
-
let predicate = "";
|
|
133
|
-
let currentApp = null;
|
|
134
|
-
// If a specific process is provided, use that
|
|
135
|
-
if (processFilter) {
|
|
136
|
-
currentApp = processFilter;
|
|
137
|
-
predicate = `subsystem == "${processFilter}"`;
|
|
138
|
-
}
|
|
139
|
-
else {
|
|
140
|
-
// Try to detect currently running user apps from installed apps
|
|
141
|
-
try {
|
|
142
|
-
const runningApps = await this.listApps();
|
|
143
|
-
// Filter to non-Apple user apps
|
|
144
|
-
const userApps = runningApps
|
|
145
|
-
.map((app) => app.packageName)
|
|
146
|
-
.filter((appId) => !appId.startsWith("com.apple.") && appId.includes("."));
|
|
147
|
-
if (userApps.length > 0) {
|
|
148
|
-
// For now, just use the first user app found
|
|
149
|
-
// In the future, we could try to detect which is actually running
|
|
150
|
-
currentApp = userApps[0];
|
|
151
|
-
predicate = `subsystem == "${currentApp}"`;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
catch (error) {
|
|
155
|
-
// Failed to get apps, continue with fallback
|
|
156
|
-
}
|
|
157
|
-
// If no user app detected, use broader filter for non-Apple apps
|
|
158
|
-
if (!predicate) {
|
|
159
|
-
predicate = "subsystem CONTAINS \"com.\" AND NOT subsystem BEGINSWITH \"com.apple.\"";
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
if (filter) {
|
|
163
|
-
predicate += ` AND composedMessage CONTAINS[c] "${filter}"`;
|
|
164
|
-
}
|
|
165
|
-
const args = [
|
|
166
|
-
"spawn", deviceUuid, "log", "show",
|
|
167
|
-
"--last", timeWindow,
|
|
168
|
-
"--predicate", predicate,
|
|
169
|
-
"--info",
|
|
170
|
-
"--debug"
|
|
171
|
-
];
|
|
172
|
-
try {
|
|
173
|
-
const logs = this.simctl(...args).toString();
|
|
174
|
-
const appInfo = currentApp ? ` (focused on: ${currentApp})` : " (all non-Apple apps)";
|
|
175
|
-
const debugInfo = `DEBUG: Using predicate: ${predicate}${appInfo}\n\n`;
|
|
176
|
-
return `${debugInfo}${logs}`;
|
|
177
|
-
}
|
|
178
|
-
catch (error) {
|
|
179
|
-
if (error instanceof Error && error.message.includes("No logging subsystem")) {
|
|
180
|
-
return "No logs found for the current running applications.";
|
|
181
|
-
}
|
|
182
|
-
throw error;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
127
|
}
|
|
186
128
|
exports.Simctl = Simctl;
|
|
187
129
|
class SimctlManager {
|
package/lib/mobilecli.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getMobilecliPath = void 0;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
const getMobilecliPath = () => {
|
|
7
|
+
if (process.env.MOBILECLI_PATH) {
|
|
8
|
+
return process.env.MOBILECLI_PATH;
|
|
9
|
+
}
|
|
10
|
+
const platform = process.platform;
|
|
11
|
+
let binaryName = "mobilecli";
|
|
12
|
+
switch (platform) {
|
|
13
|
+
case "darwin":
|
|
14
|
+
binaryName += "-darwin";
|
|
15
|
+
break;
|
|
16
|
+
case "linux":
|
|
17
|
+
const arch = process.arch;
|
|
18
|
+
if (arch === "arm64") {
|
|
19
|
+
binaryName += "-linux-arm64";
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
binaryName += "-linux-amd64";
|
|
23
|
+
}
|
|
24
|
+
break;
|
|
25
|
+
case "win32":
|
|
26
|
+
binaryName += "-windows-amd64.exe";
|
|
27
|
+
break;
|
|
28
|
+
default:
|
|
29
|
+
throw new Error(`Unsupported platform: ${platform}`);
|
|
30
|
+
}
|
|
31
|
+
// Check if mobile-mcp is installed as a package
|
|
32
|
+
const currentPath = __filename;
|
|
33
|
+
const pathParts = currentPath.split(node_path_1.sep);
|
|
34
|
+
const lastNodeModulesIndex = pathParts.lastIndexOf("node_modules");
|
|
35
|
+
if (lastNodeModulesIndex !== -1) {
|
|
36
|
+
// We're inside node_modules, go to the last node_modules in the path
|
|
37
|
+
const nodeModulesParts = pathParts.slice(0, lastNodeModulesIndex + 1);
|
|
38
|
+
const lastNodeModulesPath = nodeModulesParts.join(node_path_1.sep);
|
|
39
|
+
const mobilecliPath = (0, node_path_1.join)(lastNodeModulesPath, "@mobilenext", "mobilecli", "bin", binaryName);
|
|
40
|
+
if ((0, node_fs_1.existsSync)(mobilecliPath)) {
|
|
41
|
+
return mobilecliPath;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Not in node_modules, look one directory up from current script
|
|
45
|
+
const scriptDir = (0, node_path_1.dirname)(__filename);
|
|
46
|
+
const parentDir = (0, node_path_1.dirname)(scriptDir);
|
|
47
|
+
const mobilecliPath = (0, node_path_1.join)(parentDir, "node_modules", "@mobilenext", "mobilecli", "bin", binaryName);
|
|
48
|
+
if ((0, node_fs_1.existsSync)(mobilecliPath)) {
|
|
49
|
+
return mobilecliPath;
|
|
50
|
+
}
|
|
51
|
+
throw new Error(`Could not find mobilecli binary for platform: ${platform}`);
|
|
52
|
+
};
|
|
53
|
+
exports.getMobilecliPath = getMobilecliPath;
|
package/lib/server.js
CHANGED
|
@@ -9,6 +9,7 @@ const zod_1 = require("zod");
|
|
|
9
9
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
10
10
|
const node_os_1 = __importDefault(require("node:os"));
|
|
11
11
|
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
12
|
+
const node_child_process_1 = require("node:child_process");
|
|
12
13
|
const logger_1 = require("./logger");
|
|
13
14
|
const android_1 = require("./android");
|
|
14
15
|
const robot_1 = require("./robot");
|
|
@@ -16,28 +17,12 @@ const iphone_simulator_1 = require("./iphone-simulator");
|
|
|
16
17
|
const ios_1 = require("./ios");
|
|
17
18
|
const png_1 = require("./png");
|
|
18
19
|
const image_utils_1 = require("./image-utils");
|
|
20
|
+
const mobilecli_1 = require("./mobilecli");
|
|
19
21
|
const getAgentVersion = () => {
|
|
20
22
|
const json = require("../package.json");
|
|
21
23
|
return json.version;
|
|
22
24
|
};
|
|
23
25
|
exports.getAgentVersion = getAgentVersion;
|
|
24
|
-
const getLatestAgentVersion = async () => {
|
|
25
|
-
const response = await fetch("https://api.github.com/repos/mobile-next/mobile-mcp/tags?per_page=1");
|
|
26
|
-
const json = await response.json();
|
|
27
|
-
return json[0].name;
|
|
28
|
-
};
|
|
29
|
-
const checkForLatestAgentVersion = async () => {
|
|
30
|
-
try {
|
|
31
|
-
const latestVersion = await getLatestAgentVersion();
|
|
32
|
-
const currentVersion = (0, exports.getAgentVersion)();
|
|
33
|
-
if (latestVersion !== currentVersion) {
|
|
34
|
-
(0, logger_1.trace)(`You are running an older version of the agent. Please update to the latest version: ${latestVersion}.`);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
catch (error) {
|
|
38
|
-
// ignore
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
26
|
const createMcpServer = () => {
|
|
42
27
|
const server = new mcp_js_1.McpServer({
|
|
43
28
|
name: "mobile-mcp",
|
|
@@ -49,6 +34,8 @@ const createMcpServer = () => {
|
|
|
49
34
|
});
|
|
50
35
|
// an empty object to satisfy windsurf
|
|
51
36
|
const noParams = zod_1.z.object({});
|
|
37
|
+
// will be replaced later by 'initialize' jsonrpc request
|
|
38
|
+
let clientName = "unknown";
|
|
52
39
|
const tool = (name, description, paramsSchema, cb) => {
|
|
53
40
|
const wrappedCb = async (args) => {
|
|
54
41
|
try {
|
|
@@ -89,6 +76,7 @@ const createMcpServer = () => {
|
|
|
89
76
|
Product: "mobile-mcp",
|
|
90
77
|
Version: (0, exports.getAgentVersion)(),
|
|
91
78
|
NodeVersion: process.version,
|
|
79
|
+
AgentName: clientName,
|
|
92
80
|
};
|
|
93
81
|
await fetch(url, {
|
|
94
82
|
method: "POST",
|
|
@@ -110,6 +98,19 @@ const createMcpServer = () => {
|
|
|
110
98
|
// ignore
|
|
111
99
|
}
|
|
112
100
|
};
|
|
101
|
+
const reportMobilecliVersion = async () => {
|
|
102
|
+
try {
|
|
103
|
+
const path = (0, mobilecli_1.getMobilecliPath)();
|
|
104
|
+
const output = (0, node_child_process_1.execFileSync)(path, ["--version"], { encoding: "utf8" }).toString().trim();
|
|
105
|
+
const version = output.startsWith("mobilecli version ")
|
|
106
|
+
? output.split(" ").pop()
|
|
107
|
+
: output;
|
|
108
|
+
await posthog("mobilecli_check", { MobilecliVersion: version || output });
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
await posthog("mobilecli_check", { MobilecliVersion: "failed" });
|
|
112
|
+
}
|
|
113
|
+
};
|
|
113
114
|
posthog("launch", {}).then();
|
|
114
115
|
const simulatorManager = new iphone_simulator_1.SimctlManager();
|
|
115
116
|
const getRobotFromDevice = (device) => {
|
|
@@ -253,7 +254,7 @@ const createMcpServer = () => {
|
|
|
253
254
|
await robot.openUrl(url);
|
|
254
255
|
return `Opened URL: ${url}`;
|
|
255
256
|
});
|
|
256
|
-
tool("
|
|
257
|
+
tool("mobile_swipe_on_screen", "Swipe on the screen", {
|
|
257
258
|
device: zod_1.z.string().describe("The device identifier to use. Use mobile_list_available_devices to find which devices are available to you."),
|
|
258
259
|
direction: zod_1.z.enum(["up", "down", "left", "right"]).describe("The direction to swipe"),
|
|
259
260
|
x: zod_1.z.number().optional().describe("The x coordinate to start the swipe from, in pixels. If not provided, uses center of screen"),
|
|
@@ -348,27 +349,21 @@ const createMcpServer = () => {
|
|
|
348
349
|
const orientation = await robot.getOrientation();
|
|
349
350
|
return `Current device orientation is ${orientation}`;
|
|
350
351
|
});
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
{
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
return `Device logs${timeText}${filterText}${processText}:\n${logs}`;
|
|
367
|
-
}
|
|
368
|
-
);
|
|
369
|
-
*/
|
|
370
|
-
// async check for latest agent version
|
|
371
|
-
checkForLatestAgentVersion().then();
|
|
352
|
+
// async report mobilecli version
|
|
353
|
+
reportMobilecliVersion().then();
|
|
354
|
+
const hook = server.connect;
|
|
355
|
+
server.connect = (transport) => {
|
|
356
|
+
transport.onmessage = (message) => {
|
|
357
|
+
if ("method" in message) {
|
|
358
|
+
const request = message;
|
|
359
|
+
if (request.method === "initialize") {
|
|
360
|
+
const initialize = request;
|
|
361
|
+
clientName = initialize.params.clientInfo.name || "unknown";
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
return hook.apply(server, [transport]);
|
|
366
|
+
};
|
|
372
367
|
return server;
|
|
373
368
|
};
|
|
374
369
|
exports.createMcpServer = createMcpServer;
|
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.
|
|
4
|
+
"version": "0.0.30-beta",
|
|
5
5
|
"description": "Mobile MCP",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -24,11 +24,12 @@
|
|
|
24
24
|
"lib"
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@modelcontextprotocol/sdk": "
|
|
28
|
-
"commander": "
|
|
29
|
-
"express": "
|
|
30
|
-
"fast-xml-parser": "
|
|
31
|
-
"zod-to-json-schema": "
|
|
27
|
+
"@modelcontextprotocol/sdk": "1.13.2",
|
|
28
|
+
"commander": "14.0.0",
|
|
29
|
+
"express": "5.1.0",
|
|
30
|
+
"fast-xml-parser": "5.2.5",
|
|
31
|
+
"zod-to-json-schema": "3.24.6",
|
|
32
|
+
"@mobilenext/mobilecli": "0.0.25"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
35
|
"@eslint/eslintrc": "^3.2.0",
|