Skip to content

feat(mcp): add start_recording and stop_recording tools#3135

Open
sinano1107 wants to merge 4 commits intomobile-dev-inc:mainfrom
sinano1107:feat/mcp-recording-tools-upstream
Open

feat(mcp): add start_recording and stop_recording tools#3135
sinano1107 wants to merge 4 commits intomobile-dev-inc:mainfrom
sinano1107:feat/mcp-recording-tools-upstream

Conversation

@sinano1107
Copy link
Copy Markdown

@sinano1107 sinano1107 commented Apr 4, 2026

Summary

  • Add start_recording and stop_recording MCP tools for controlling simulator screen recording
  • Internally delegates to LocalSimulatorUtils which wraps xcrun simctl io recordVideo with proper SIGINT handling via screenrecord.sh
  • RecordingManager tracks active recordings per device, preventing duplicate recordings and cleaning up on JVM shutdown
  • Optional output_path parameter allows specifying the destination file; defaults to a temp path

Motivation

The MCP server currently has no way to record the simulator screen. Users working with AI agents need to capture screen recordings for documentation, bug reports, or video production workflows. The existing maestro record command is self-contained and cannot run alongside MCP tool calls, so dedicated MCP tools are needed.

Implementation

  • RecordingManager: Manages recording lifecycle per device using ConcurrentHashMap with atomic putIfAbsent. Reuses LocalSimulatorUtils.startScreenRecording()/stopScreenRecording() — no new process management code.
  • StartRecordingTool: Returns recording_id and output_path on success.
  • StopRecordingTool: Accepts recording_id to prevent accidental stops. Returns video_path.
  • Registered in McpServer.kt alongside existing tools.

Test plan

  • Unit tests for RecordingManager (state management, error cases, output path copying)
  • Manual test: start_recording → interact with app → stop_recording → verify MP4 plays correctly

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com

Add MCP tools to start and stop simulator screen recording.
Internally delegates to LocalSimulatorUtils which wraps
xcrun simctl io recordVideo with proper SIGINT handling.

RecordingManager tracks active recordings per device and supports
an optional output_path parameter for specifying the destination.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@sinano1107
Copy link
Copy Markdown
Author

Code review

Found 1 issue:

  1. shutdown() bypasses LocalSimulatorUtils.stopScreenRecording() and reimplements process teardown inline. Per screenrecord.sh, closing stdin triggers kill -SIGINT to simctl, and the script then waits for simctl to finish writing the video. The current shutdown() uses destroyForcibly() after a 5-second timeout, which kills the wrapper bash process but may leave simctl mid-write, producing a corrupt/unplayable video file. The canonical stopScreenRecording() method handles this correctly with an unbounded waitFor(). shutdown() should delegate to stopScreenRecording() instead of reimplementing the sequence.

https://github.com/sinano1107/Maestro/blob/1e4c115a32b1ec6ab2511434373a53b5de36f117/maestro-cli/src/main/java/maestro/cli/mcp/tools/RecordingManager.kt#L66-L77

For reference, the established stop protocol in LocalSimulatorUtils:
https://github.com/sinano1107/Maestro/blob/1e4c115a32b1ec6ab2511434373a53b5de36f117/maestro-ios-driver/src/main/kotlin/util/LocalSimulatorUtils.kt#L719-L723

And the SIGINT contract in screenrecord.sh:
https://github.com/sinano1107/Maestro/blob/1e4c115a32b1ec6ab2511434373a53b5de36f117/maestro-ios-driver/src/main/resources/screenrecord.sh#L1-L8

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

sinano1107 and others added 3 commits April 4, 2026 16:23
Replace manual process teardown in shutdown() with a call to
LocalSimulatorUtils.stopScreenRecording(), which properly closes
stdin (triggering SIGINT via screenrecord.sh) and waits for simctl
to finish writing the video file. The previous approach used
destroyForcibly() after a 5-second timeout, which could kill the
wrapper process before simctl finalized the video.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- stopRecording: wrap in try/finally so activeRecordings.remove() is
  always called, preventing a device from being permanently locked if
  stopScreenRecording or file copy throws
- startRecording: add early containsKey check before launching the
  recording process to avoid wasteful process start/stop on duplicate
- Tool descriptions: explicitly state iOS Simulator only, since the
  implementation uses xcrun simctl which is not available on Android

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The early containsKey check is not atomic with putIfAbsent and gives
a false sense of safety. On a race, a second caller would pass the
check, start a recording process, then find it lost the putIfAbsent
race and have to stop the process it just started. putIfAbsent alone
is the correct guard.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant