ezremote/Docs/Use the tools

Use the computer

Call devices first. Pass the selected UUID as deviceId on every hosted operation call. For MCP, discover current inputs with tools/list.

Read or change a file

Use files_read with path, files_write with path and content, or files_edit with path, oldText, and newText. These tools accept UTF-8 text up to 1 MB. Paths must be absolute or start with ~/.

files_write replaces the file. files_edit replaces text that must match exactly once. Read the file before editing and check the destination before overwriting it.

Run a command

Call shell_exec with command and, when needed, cwd and timeoutMs. The timeout is at most 55,000 milliseconds. It runs as the computer user who installed the helper. Shell access includes files outside any file-tool folder limits.

Check stdout, stderr, and code. A nonzero exit code means the command finished with an error.

If the result is outcome_unknown, the command may have run. Inspect operations or the resulting files. Never automatically repeat it. Reusing a request ID rejects duplicate execution; it does not replay the earlier result.

Transfer a file

For files too large for text tools, use a transfer. The agent host must save downloaded bytes itself; hosted MCP cannot write to its filesystem.

For large files:

  • Download: call bulk_export, poll bulk_status until ready, then fetch downloadUrl with your own download tool.
  • Upload: follow the part-upload steps below, then poll bulk_status until completed or failed.
  • Stop or clean up: call bulk_cancel. It deletes temporary cloud data, not a file already delivered to the computer.

Keep the same agent token for the whole job. Download links are private and short-lived. Jobs last up to one hour and support files up to 10 GiB. Temporary cloud storage is not a backup.

For small binary files, call transfer_start, send zero-based chunks with transfer_chunk using transferId, index, and base64, then call transfer_finish. Each chunk is at most 192 KB before base64 encoding. To download, call transfer_pull and set offset to the returned nextOffset until done is true.

Upload parts for a large file

  1. Call bulk_upload with deviceId and the exact byte size.
  2. Use the returned partSize. Add a one-based partNumber query parameter to uploadUrl, preserving its existing query parameters. PUT each binary part with Content-Length and X-Content-SHA256, the lowercase hex SHA-256 of that part. For an empty file, send one empty part.
  3. POST to completeUrl after all parts succeed.
  4. Call bulk_import with deviceId, transferId, and the destination path.

For REST bulk calls, supply a UUID requestId. Export and upload use it as the transfer ID, so inspect that ID with bulk.status if the start response is lost. Never automatically start another job for an unknown outcome.

Use the browser

The person must approve browser access separately. Approval covers every open tab in the selected Chrome or Helium profile, including signed-in pages.

Call browser_call with name: "tools/list", then use a returned tool name and its inputs. The helper starts Chrome DevTools MCP when needed. For a separate browser with copied logins, follow browser sessions. The person imports locally; call profiles/list and pass the chosen profile UUID on each browser call.

If browser_installing is returned, no browser action ran. Wait a few seconds and retry the call while the helper installs Chromium in the background. Do not automatically retry outcome_unknown. Live attach to daily Chrome/Helium is removed; both legacy browser values select the same managed profile.

Use local MCP servers

Call mcp.servers to list servers the person already configured for Claude, Claude Code, Codex, Cursor or Windsurf on that computer. It returns ids, names and sources, never commands or secrets. Call mcp.tools with { server } to see a server's tools, then mcp.call with { server, tool, arguments }. Stdio servers start for the call and stop afterwards. Calls run with the server's own permissions; the Terminal sandbox does not apply.

Read a Terminal result

Terminal commands run in a sandbox by default. Writes outside approved folders and the temp folder, reads of credentials or browser data, and connections to local network addresses fail. The result's sandbox.blocked is true and stderr contains a <sandbox_violations> block saying what was refused. Tell the person; do not try to work around it. update_helper_for_terminal_sandbox means the computer's helper is too old to sandbox: ask the person to rerun setup, or to allow full Terminal access. Temporary connections never include Terminal.

Handle an unavailable computer

  • Offline: Choose queue or discard based on the task. Use the queue flow below for authorized work that should run when the computer returns.
  • Expired or revoked: Request a new connection and local approval.

The operations tool returns recent operation metadata. The helper also keeps a bounded log at ~/.ezremote/activity.jsonl. Revocation and cancellation block new work but cannot guarantee stopping an operation already sent.

Queue work explicitly

A normal operation reports device_offline without scheduling work. Choose queue or discard based on the task, including authorized proactive work. Queueing is an agent decision; it does not need another human confirmation for already authorized work. Expired and revoked access still blocks work.

  1. Call queue_operation with { deviceId, operation, args, requestId }. Every operation name from tools/list can queue, using its dotted API name such as files.read or bulk.export. Generate and save a fresh UUID first. Do not reuse the ID of the earlier offline call.
  2. The default deadline is 30 minutes and the maximum is 12 hours. Set expiresInSeconds (60 to 43200), or waitUntil as a duration such as "3 hours", a clock time such as "3:30pm" with an IANA timeZone, or an ISO time. The job runs as soon as the computer is online and is discarded at the deadline. The reply's waitUntil states the interpreted deadline. Computer access and direct-token expiry can shorten it. Queueing never extends computer access.
  3. The immediate reply includes status: "queued", computerState, jobId and expiresAt. This confirms acceptance. Inform the person that the work is waiting.
  4. Call job_wait once with { jobId }. The service waits and returns the final result. Use reply.result on completion and inspect shell exit codes. Stop on completed, failed, cancelled, expired or outcome_unknown. Never repeatedly call job_status to keep a model busy while waiting.
  5. Use job_cancel to stop pending work. cancelled: true prevents dispatch. cancelled: false means it was dispatched or terminal and cannot be stopped by cancellation. Aborting a wait leaves the job queued.

job_wait supports MCP task augmentation. A task-capable host can receive a task ID immediately, manage the wait outside model turns, and retrieve the result through tasks/result. Task status and cancellation are authorized by the same job ownership checks. Without task support, the tool waits in its current HTTP call. Host timeouts can close that call; resume waiting with the same job ID rather than submitting again. Waking a closed agent thread requires integration in the host app. A hosted MCP server alone cannot start a new model turn in another app.

If a submission reply is lost, inspect the saved request ID with job_status. Duplicate IDs reject execution without replaying a result. For outcome_unknown, inspect activity or resulting state before deciding what to do.

The helper reconnect wakes the service's queue. Dispatch rechecks permission, folder policy, revocation and the original computer-access deadline. OAuth jobs follow the saved MCP connection across credential renewal; hourly credential expiry does not discard accepted work. Direct-token jobs require that individual token to remain valid. A reconnect never extends a job's deadline. Known undelivered calls can wait again; uncertain sends never retry automatically.

Queued transfer calls still depend on their transfer's existing state and expiry. Queueing a chunk, import or status call cannot recreate expired transfer state. Bulk work uses the public origin saved at submission and the ordinary bulk authorization path, rather than forwarding public bulk arguments directly to the helper.

Arguments and results are capped at 1.5 MB of encoded JSON each. Oversized results report job_result_too_large after the operation may have run. Results remain until one hour after the job deadline. Queued and dispatched jobs count toward 32 jobs per computer and 1,000 globally. Retained completed, cancelled, failed and expired jobs do not consume active queue capacity. A separate storage budget of 64 MiB per computer and 256 MiB globally includes reserved result space; job_storage_full means retained results must expire before more work can be accepted. D1 stores these arguments and results.

REST uses POST https://tunnel.masti.lol/api/jobs with the queue body and an agent bearer token. /api/jobs/wait waits up to 55 seconds for a result; a client library can repeat that wait without involving a model. /api/jobs/status inspects once, and /api/jobs/cancel cancels, each accepting { "jobId": "..." }.

The SDK exposes remote.queue(deviceId, operation, args, { requestId, expiresInSeconds }), remote.waitJob(jobId, { signal, onUpdate }), remote.job and remote.cancelJob. waitJob uses the server wait endpoint. Pi exposes ezremote_queue with the same optional deadline and ezremote_job, which waits by default. Set wait: false to inspect once. Native Pi tools still run on the Pi host when device is omitted. Configure a scoped agent token through isolated Pi settings; store only the activated agent token.

Keep a Terminal session

Use shell.start for commands that may exceed 30 seconds, servers, prompts and REPLs. Save its sessionId. Call shell.read with that ID, a byte cursor starting at 0 and optional waitMs up to 20000. Save the returned cursor for the next read unchanged. bytesDropped > 0 means output was lost from the buffer. maxBytes defaults to 65536 and accepts 4–524288 bytes. The cursor advances only over returned bytes; more: true means output remains. Reads preserve UTF-8 character boundaries. running and code say whether the process has exited. Send a line with shell.write, including its newline. Use shell.status, shell.list and shell.kill to inspect or stop work.

Each connection has eight sessions, each with a 1 MB output buffer. Sessions end at connection expiry or after two hours without an agent read, write or status call. Set idleMinutes on shell.start to 1–1440 minutes; the default is 120. Connection expiry always wins. Exited output is retained for five minutes. Background jobs (cmd &) end when the session's shell exits; keep the shell in the foreground, for example with wait, to keep them running. Sessions run through the same Terminal sandbox as shell.exec. Pipes support interactive line input, but programs requiring a TTY are unsupported. The person can inspect and kill sessions from the helper tray or ezremote sessions list and ezremote sessions kill ID.

Read, list and search with coverage

files.read without limit returns the whole file up to 1 MB. Before writing a file back, check that its read status is complete. For large files, page with a zero-based line offset and a limit up to 1000, or tail: true. Pass the opaque cursor to continue. files.list accepts depth up to 20 and limit up to 1000. files.stat returns size, type and modification time. files.search accepts a literal query, mode: "names" or "content", depth, limit, timeoutMs up to 3000 and maxBytes up to 16000000. Content search returns the first matching line per file. Continue with cursor and the same query options.

All these results include status, skipped and scannedBytes. complete covers the requested range; partial has another page or skipped files; truncated hit a size or entry budget; timed_out hit the time budget. Follow a cursor when one is returned and retain skipped-file reports across pages. Empty results establish absence only when status is complete. Binary files, files over the search byte budget, inaccessible paths and symlink directories are reported in skipped. Search scans at most 1 MB per file. Raise the budget or use paged reads to inspect an oversized file. Filesystem edits during pagination can change results.

Resolve an uncertain outcome

Never retry outcome_unknown blindly. Save the request ID and call MCP resolve_operation with deviceId and requestId, or POST those fields to https://tunnel.masti.lol/api/operations/resolve. The SDK provides remote.resolve(deviceId, requestId). Resolution never runs the operation again.

running describes a mutating request the current helper has admitted. Read-only calls are not journaled and resolve to unknown; they are safe to retry. refused_expired means it did not start because its deadline passed. finished includes the saved reply. A resultTruncated reply retains the success or failure but omits an oversized result. unknown means the helper is offline, has no retained record, or restarted while the outcome was uncertain. Inspect activity and resulting state before deciding what to do next. A fresh ID is new work, not a safe retry. Results remain available for at most 24 hours, 1,024 requests or 16 MB, whichever limit is reached first, and each reply is capped at 64 KB.

Synchronous calls accept deadlineInMs from 1000 to 55000, default 30000. This is an admission deadline, not cancellation of a running command. Use sessions for long commands and explicit offline jobs when work should wait for a computer.