A connection you can build on.
DeviceMirror connects a local device bridge to a secure cloud workspace. Watch a live WebRTC stream, send an action from your browser, call the HTTP API, or give a compatible MCP client access to the same devices.
The local bridge is responsible for the screen source and action execution. A simulated adapter is included for development and demonstrations. It renders changing frames, encodes real video, and executes requests against its state. The console always labels these devices as simulated.
Your first connected device
- Sign in to the console with Google. DeviceMirror creates a workspace owned by your authenticated account.
- Select Add device, give it a name, and choose Simulated device.
- Copy the connector token shown once. Store it securely on the machine that runs the device bridge.
- Run the simulated bridge as described below. Keep the process running while watching or controlling the device.
- Open the device in the console and select Start live video. Tap or swipe on the stream, send text, or take a screenshot.
Register each device separately. Each connector token belongs to exactly one device. Deleting a device revokes its connection.
Run without a phone
The simulator runs on your computer and connects outward to the cloud. It uses the same device connector boundary that a physical device adapter implements. Your cloud account must own the device and have available capacity.
cd apps/mock-device
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
cp config.example.json config.json
chmod 600 config.json
# Set the endpoint and each device's ID and token in config.json.
.venv/bin/python connector.py --config config.json
The repository's apps/mock-device/README.md contains
current runtime prerequisites and multi-device options. Keep your
connector token out of shell history and shared logs.
To test multiple devices, register another device and start another adapter with its own device ID and connector token. Disconnecting a local process makes that device unavailable; it must reconnect before screenshots and controls can complete.
HTTP API
API requests use HTTPS and a Bearer credential created in Console → API credentials. A browser session cannot be replaced by a connector token; connector tokens are only for the local bridge.
export DM_URL='https://device-mirror.jiulong.hu'
export DM_API_KEY='your-scoped-api-credential'
curl "$DM_URL/v1/devices" \
-H "Authorization: Bearer $DM_API_KEY"
| Method | Path | Purpose |
|---|---|---|
| GET | /v1/devices |
List devices in your credential's scope |
| GET | /v1/devices/{id} |
Read device details and status |
| GET | /v1/devices/{id}/screenshot |
Capture a fresh image |
| POST | /v1/devices/{id}/actions |
Tap, swipe, type, or send a key |
| POST | /v1/devices/{id}/video-sessions |
Create a bounded live viewing session |
| GET | /v1/me |
Read workspace entitlement and usage |
Download OpenAPI specification ↗
Take a screenshot
curl "$DM_URL/v1/devices/$DM_DEVICE_ID/screenshot" \
-H "Authorization: Bearer $DM_API_KEY"
# Response includes mimeType, base64 data, width, height.
# This is an on-demand capture, not the live video transport.
Send an action
curl -X POST "$DM_URL/v1/devices/$DM_DEVICE_ID/actions" \
-H "Authorization: Bearer $DM_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"type":"tap","x":195,"y":422}'
# Swipe: pixel coordinates in the device's reported dimensions
# {"type":"swipe","x":195,"y":650,"endX":195,"endY":250,"durationMs":350}
# Type: {"type":"type","text":"Hello from DeviceMirror"}
# Key: {"type":"key","key":"HOME"}
Use width and height returned by the device.
The included simulated phone defaults to 390 × 844. Invalid
coordinates and unsupported actions return an error. A request is only
successful when the connected adapter confirms it.
Errors
Failures return a structured error.code and
error.message. Keep the request ID when contacting your
operator. Handle 401 by checking credentials, 403 by checking
permissions, 404 by checking device ownership, 409/503 by checking the
bridge, and 429 by backing off or checking quota. Do not immediately
retry an action if its execution status is unknown.
Connect an MCP client
DeviceMirror exposes an HTTPS Streamable HTTP MCP endpoint. Create an API credential with the scopes and device access your client needs, then configure your MCP client with the endpoint and Bearer header.
{
"mcpServers": {
"device-mirror": {
"type": "http",
"url": "https://device-mirror.jiulong.hu/mcp",
"headers": {
"Authorization": "Bearer YOUR_SCOPED_API_CREDENTIAL"
}
}
}
}
Configuration keys vary by client. Choose HTTP or Streamable HTTP transport; a local stdio command is not required. The standard tool flow is list devices → capture screenshot → inspect the image → execute an action → capture again.
Keep credentials in your client's secret store or environment substitution mechanism. Never commit the example with a real credential. Revoking a credential in the console removes its ability to make new requests.
Live video
Video uses WebRTC with Cloudflare Realtime SFU. Control requests travel through authenticated API requests and the device's outbound connection. Screenshots are individual captures and are not used as a video substitute.
-
Send
POST /v1/devices/{id}/video-sessionswith{}. Save the returned lease ID, ICE servers and expiry;negotiation:"server-offer"identifies the receiving flow. -
Create an
RTCPeerConnectionwith those ICE servers and install itstrackhandler. Do not create an initial local offer or pre-add a transceiver for this fresh receiving session. -
Send
POST /v1/devices/{id}/video-sessions/{leaseId}/trackswith the empty JSON object{}. The service selects the authorized publication and returns an SFU-generated SDP offer. -
Apply the returned offer with
setRemoteDescription, then callcreateAnswerandsetLocalDescription. Wait for ICE gathering to complete. -
Send
PUT /v1/devices/{id}/video-sessions/{leaseId}/renegotiatewith{sessionDescription:{type:"answer",sdp}}. Continue only after the service accepts that answer. Attach the received track to a video element and verify that frames are playing. - Delete the lease when finished. Each lease permits one track negotiation; start a new lease after an uncertain negotiation failure.
This public pilot is additionally capped at 20 registered simulated devices across the deployment, 2,000 reserved video minutes per UTC month, and 20 model requests per UTC day. These deployment limits apply alongside each account's allowance. Capacity exhaustion returns HTTP 429. Viewing sessions are time limited. The console shows their status and asks you to reconnect after expiry. Starting a session reserves its bounded duration against the monthly video allowance. A device must have a connected publisher before media can flow.
Built-in device agent
The console's agent sends a fresh device screenshot to the operator's configured vision model. A model can request a device action, inspect the result, and respond with its findings. Tool execution uses the same ownership checks and operation limits as the HTTP API.
Model credentials remain on the server. If the operator has not configured a model provider, the console reports that setup is required. It does not substitute a generated demo reply. You remain responsible for the tasks you authorize on your devices.
Access and credentials
Google sign-in establishes your account. Device ownership is enforced by the server. API credentials are stored as hashes and shown only at creation. Choose a name, the minimum required scopes, and optionally individual devices.
| Scope | Allows |
|---|---|
devices:read |
Discover devices, read status, capture screenshots |
devices:control |
Execute tap, swipe, text, and key actions |
devices:video |
Create and negotiate live video sessions |
A connector token authenticates a specific local bridge, not a user or an MCP client. Keep it separate from API credentials. Use a dedicated API credential for each integration so that it can be revoked independently.
Subscriptions and resource limits
A free workspace includes 2 registered devices, 3,000 operations, and 60 video minutes each month. The Pro test plan includes 10 registered devices, 100,000 operations, and 1,000 video minutes per month.
Checkout and the customer portal are hosted by Stripe. Workspace access changes after the service verifies and synchronizes the subscription. Visiting a success URL alone does not grant Pro access. Refresh the billing page after a change; webhook delivery can take a moment.
Requests also have rate and concurrency limits. Public registration and simulated devices do not bypass these protections. Model provider costs are separate from workspace video and operation allowances.
When a connection needs attention
The device is offline
Check that the local bridge is running, that its URL is correct, and that it uses the connector token for that exact device. Wait for its reconnect attempt or restart the bridge. Deleting a device permanently revokes that registration.
Video does not start
Confirm the device is online and that Realtime configuration is ready. Check your remaining video allowance. Browser or network policies can block WebRTC or autoplay; use the video play control when needed. DeviceMirror reports negotiation failures instead of replacing the video with an animation.
Sign-in returns to an error
Use the same hostname throughout sign-in. The deployment's Google
callback is
https://device-mirror.jiulong.hu/api/auth/callback/google. An operator should check the OAuth client's approved origins and
callback URL, server secrets, and authentication logs.
An integration receives access denied
Check that the credential is active, has the correct scope, and includes the requested device. Devices belonging to another account are not accessible. Create a new scoped credential rather than sharing a connector token.
Pro is not reflected after checkout
Refresh billing status and check Stripe webhook delivery. Only an active, unexpired subscription to the configured price grants Pro. Operators should inspect webhook signature configuration and event processing before changing account data.
Privacy and operational access
DeviceMirror processes account details, connection metadata, usage, and operational logs to provide the service. Screenshots and control inputs pass through the service when requested. A model task sends its screenshot and instructions to the operator's configured model provider.
Only connect devices you own or have permission to operate. Grant integrations the minimum access they need, revoke unused credentials, and avoid entering sensitive personal data into a simulated demonstration. Service administrators can inspect basic user, device, subscription, usage, and runtime records through a server-authorized admin interface.
For repository setup, environment variables, deployment resources, and
physical adapter integration, consult the checked-in
docs/cloud/ documentation. This page describes the cloud
interfaces; physical-device compatibility must be verified with the
relevant hardware and local bridge.