mobile-debug-mcp 0.12.4 → 0.12.5

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.
@@ -291,15 +291,26 @@ export class iOSManage {
291
291
  }
292
292
  async startApp(bundleId, deviceId = "booted") {
293
293
  validateBundleId(bundleId);
294
+ // Prepare instrumentation object upfront so it can be returned to callers
295
+ const instrumentation = { ts: new Date().toISOString(), action: 'startApp', cmd: 'xcrun', args: ['simctl', 'launch', deviceId, bundleId], cwd: process.cwd(), env: { PATH: process.env.PATH, XCRUN_PATH: process.env.XCRUN_PATH } };
294
296
  try {
297
+ // Instrumentation: persist and emit to stderr for server logs
298
+ try {
299
+ await fs.appendFile('/tmp/mcp_startapp_instrument.log', JSON.stringify(instrumentation) + '\n');
300
+ }
301
+ catch (e) { }
302
+ try {
303
+ console.error('MCP-STARTAPP-EXEC', JSON.stringify(instrumentation));
304
+ }
305
+ catch (e) { }
295
306
  const result = await execCommand(['simctl', 'launch', deviceId, bundleId], deviceId);
296
307
  const device = await getIOSDeviceMetadata(deviceId);
297
- return { device, appStarted: !!result.output, launchTimeMs: 1000 };
308
+ return { device, appStarted: !!result.output, launchTimeMs: 1000, instrumentation };
298
309
  }
299
310
  catch (e) {
300
311
  const diag = execCommandWithDiagnostics(['simctl', 'launch', deviceId, bundleId], deviceId);
301
312
  const device = await getIOSDeviceMetadata(deviceId);
302
- return { device, appStarted: false, launchTimeMs: 0, error: e instanceof Error ? e.message : String(e), diagnostics: diag };
313
+ return { device, appStarted: false, launchTimeMs: 0, error: e instanceof Error ? e.message : String(e), diagnostics: diag, instrumentation };
303
314
  }
304
315
  }
305
316
  async terminateApp(bundleId, deviceId = "booted") {
package/dist/ios/utils.js CHANGED
@@ -96,7 +96,40 @@ export function validateBundleId(bundleId) {
96
96
  }
97
97
  export function execCommand(args, deviceId = "booted") {
98
98
  return new Promise((resolve, reject) => {
99
+ // Instrumentation: append a JSON line with timestamp, command, args, cwd and selected env vars
100
+ try {
101
+ const mcpEnv = {};
102
+ for (const k of Object.keys(process.env || {})) {
103
+ if (k.startsWith('MCP_'))
104
+ mcpEnv[k] = process.env[k];
105
+ }
106
+ const instrument = {
107
+ timestamp: new Date().toISOString(),
108
+ command: getXcrunCmd(),
109
+ args,
110
+ cwd: process.cwd(),
111
+ env: {
112
+ PATH: process.env.PATH,
113
+ XCRUN_PATH: process.env.XCRUN_PATH,
114
+ ...mcpEnv
115
+ }
116
+ };
117
+ try {
118
+ require('fs').appendFileSync('/tmp/mcp_exec_instrument.log', JSON.stringify(instrument) + '\n');
119
+ }
120
+ catch (e) { }
121
+ }
122
+ catch (e) {
123
+ // swallow instrumentation errors to avoid changing behavior
124
+ }
99
125
  // Use spawn for better stream control and consistency with Android implementation
126
+ // Instrument: emit a JSON line to stderr so the MCP server stderr/stdout capture can record the exact command and env
127
+ try {
128
+ const instLine = JSON.stringify({ ts: new Date().toISOString(), cmd: getXcrunCmd(), args, cwd: process.cwd(), PATH: process.env.PATH });
129
+ // Use stderr so it appears in server logs reliably
130
+ console.error('MCP-INSTRUMENT-EXEC', instLine);
131
+ }
132
+ catch (e) { }
100
133
  const child = spawn(getXcrunCmd(), args);
101
134
  let stdout = '';
102
135
  let stderr = '';
@@ -110,6 +143,17 @@ export function execCommand(args, deviceId = "booted") {
110
143
  stderr += data.toString();
111
144
  });
112
145
  }
146
+ // Additional instrumentation: write pid and env snapshot when child starts
147
+ try {
148
+ const pidInfo = { ts: new Date().toISOString(), childPid: (child.pid || null), invoked: getXcrunCmd(), args };
149
+ try {
150
+ require('fs').appendFileSync('/tmp/mcp_exec_instrument.log', JSON.stringify(pidInfo) + '\n');
151
+ }
152
+ catch (e) { }
153
+ }
154
+ catch (e) {
155
+ // ignore
156
+ }
113
157
  const DEFAULT_XCRUN_LOG_TIMEOUT = parseInt(process.env.MCP_XCRUN_LOG_TIMEOUT || '', 10) || 30000; // env (ms) or default 30s
114
158
  const DEFAULT_XCRUN_CMD_TIMEOUT = parseInt(process.env.MCP_XCRUN_TIMEOUT || '', 10) || 60000; // env (ms) or default 60s
115
159
  const timeoutMs = args.includes('log') ? DEFAULT_XCRUN_LOG_TIMEOUT : DEFAULT_XCRUN_CMD_TIMEOUT; // choose appropriate timeout
@@ -133,6 +177,11 @@ export function execCommand(args, deviceId = "booted") {
133
177
  });
134
178
  }
135
179
  export function execCommandWithDiagnostics(args, deviceId = "booted") {
180
+ try {
181
+ const syncInst = { ts: new Date().toISOString(), cmd: getXcrunCmd(), args, cwd: process.cwd() };
182
+ require('fs').appendFileSync('/tmp/mcp_exec_instrument_sync.log', JSON.stringify(syncInst) + '\n');
183
+ }
184
+ catch (e) { }
136
185
  // Run synchronously to capture stdout/stderr and exitCode reliably for diagnostics
137
186
  const DEFAULT_XCRUN_LOG_TIMEOUT = parseInt(process.env.MCP_XCRUN_LOG_TIMEOUT || '', 10) || 30000;
138
187
  const DEFAULT_XCRUN_CMD_TIMEOUT = parseInt(process.env.MCP_XCRUN_TIMEOUT || '', 10) || 60000;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mobile-debug-mcp",
3
- "version": "0.12.4",
3
+ "version": "0.12.5",
4
4
  "description": "MCP server for mobile app debugging (Android + iOS), with focus on security and reliability",
5
5
  "type": "module",
6
6
  "bin": {
package/src/ios/manage.ts CHANGED
@@ -298,14 +298,20 @@ export class iOSManage {
298
298
 
299
299
  async startApp(bundleId: string, deviceId: string = "booted"): Promise<StartAppResponse> {
300
300
  validateBundleId(bundleId)
301
+ // Prepare instrumentation object upfront so it can be returned to callers
302
+ const instrumentation = { ts: new Date().toISOString(), action: 'startApp', cmd: 'xcrun', args: ['simctl','launch', deviceId, bundleId], cwd: process.cwd(), env: { PATH: process.env.PATH, XCRUN_PATH: process.env.XCRUN_PATH } }
301
303
  try {
304
+ // Instrumentation: persist and emit to stderr for server logs
305
+ try { await fs.appendFile('/tmp/mcp_startapp_instrument.log', JSON.stringify(instrumentation) + '\n') } catch (e) {}
306
+ try { console.error('MCP-STARTAPP-EXEC', JSON.stringify(instrumentation)) } catch (e) {}
307
+
302
308
  const result = await execCommand(['simctl', 'launch', deviceId, bundleId], deviceId)
303
309
  const device = await getIOSDeviceMetadata(deviceId)
304
- return { device, appStarted: !!result.output, launchTimeMs: 1000 }
310
+ return { device, appStarted: !!result.output, launchTimeMs: 1000, instrumentation }
305
311
  } catch (e:any) {
306
312
  const diag = execCommandWithDiagnostics(['simctl', 'launch', deviceId, bundleId], deviceId)
307
313
  const device = await getIOSDeviceMetadata(deviceId)
308
- return { device, appStarted: false, launchTimeMs: 0, error: e instanceof Error ? e.message : String(e), diagnostics: diag } as any
314
+ return { device, appStarted: false, launchTimeMs: 0, error: e instanceof Error ? e.message : String(e), diagnostics: diag, instrumentation } as any
309
315
  }
310
316
  }
311
317
 
package/src/ios/utils.ts CHANGED
@@ -88,7 +88,41 @@ export function validateBundleId(bundleId: string) {
88
88
 
89
89
  export function execCommand(args: string[], deviceId: string = "booted"): Promise<IOSResult> {
90
90
  return new Promise((resolve, reject) => {
91
+ // Instrumentation: append a JSON line with timestamp, command, args, cwd and selected env vars
92
+ try {
93
+ const mcpEnv: Record<string,string|undefined> = {}
94
+ for (const k of Object.keys(process.env || {})) {
95
+ if (k.startsWith('MCP_')) mcpEnv[k] = process.env[k]
96
+ }
97
+
98
+ const instrument = {
99
+ timestamp: new Date().toISOString(),
100
+ command: getXcrunCmd(),
101
+ args,
102
+ cwd: process.cwd(),
103
+ env: {
104
+ PATH: process.env.PATH,
105
+ XCRUN_PATH: process.env.XCRUN_PATH,
106
+ ...mcpEnv
107
+ }
108
+ }
109
+
110
+ try {
111
+ require('fs').appendFileSync('/tmp/mcp_exec_instrument.log', JSON.stringify(instrument) + '\n')
112
+ } catch (e) {}
113
+
114
+ } catch (e) {
115
+ // swallow instrumentation errors to avoid changing behavior
116
+ }
117
+
91
118
  // Use spawn for better stream control and consistency with Android implementation
119
+ // Instrument: emit a JSON line to stderr so the MCP server stderr/stdout capture can record the exact command and env
120
+ try {
121
+ const instLine = JSON.stringify({ ts: new Date().toISOString(), cmd: getXcrunCmd(), args, cwd: process.cwd(), PATH: process.env.PATH })
122
+ // Use stderr so it appears in server logs reliably
123
+ console.error('MCP-INSTRUMENT-EXEC', instLine)
124
+ } catch (e) {}
125
+
92
126
  const child = spawn(getXcrunCmd(), args)
93
127
 
94
128
  let stdout = ''
@@ -106,6 +140,14 @@ export function execCommand(args: string[], deviceId: string = "booted"): Promis
106
140
  })
107
141
  }
108
142
 
143
+ // Additional instrumentation: write pid and env snapshot when child starts
144
+ try {
145
+ const pidInfo = { ts: new Date().toISOString(), childPid: (child.pid || null), invoked: getXcrunCmd(), args }
146
+ try { require('fs').appendFileSync('/tmp/mcp_exec_instrument.log', JSON.stringify(pidInfo) + '\n') } catch (e) {}
147
+ } catch (e) {
148
+ // ignore
149
+ }
150
+
109
151
  const DEFAULT_XCRUN_LOG_TIMEOUT = parseInt(process.env.MCP_XCRUN_LOG_TIMEOUT || '', 10) || 30000 // env (ms) or default 30s
110
152
  const DEFAULT_XCRUN_CMD_TIMEOUT = parseInt(process.env.MCP_XCRUN_TIMEOUT || '', 10) || 60000 // env (ms) or default 60s
111
153
  const timeoutMs = args.includes('log') ? DEFAULT_XCRUN_LOG_TIMEOUT : DEFAULT_XCRUN_CMD_TIMEOUT // choose appropriate timeout
@@ -131,6 +173,11 @@ export function execCommand(args: string[], deviceId: string = "booted"): Promis
131
173
  }
132
174
 
133
175
  export function execCommandWithDiagnostics(args: string[], deviceId: string = "booted") {
176
+ try {
177
+ const syncInst = { ts: new Date().toISOString(), cmd: getXcrunCmd(), args, cwd: process.cwd() }
178
+ require('fs').appendFileSync('/tmp/mcp_exec_instrument_sync.log', JSON.stringify(syncInst) + '\n')
179
+ } catch (e) {}
180
+
134
181
  // Run synchronously to capture stdout/stderr and exitCode reliably for diagnostics
135
182
  const DEFAULT_XCRUN_LOG_TIMEOUT = parseInt(process.env.MCP_XCRUN_LOG_TIMEOUT || '', 10) || 30000
136
183
  const DEFAULT_XCRUN_CMD_TIMEOUT = parseInt(process.env.MCP_XCRUN_TIMEOUT || '', 10) || 60000
package/src/types.ts CHANGED
@@ -12,6 +12,7 @@ export interface StartAppResponse {
12
12
  launchTimeMs: number;
13
13
  error?: string;
14
14
  diagnostics?: any;
15
+ instrumentation?: any;
15
16
  }
16
17
 
17
18
  export interface TerminateAppResponse {