Skip to content

Automation (REST & MCP)

Every Files operation except opening a connection is available over Probe's internal REST API and, through it, the MCP server. That lets a script — or an AI assistant in Claude or Cursor — list a bucket, download an object, tidy a directory, or push a build artifact as part of a larger task, against connections you've already opened in the app. These calls run beside your own work in the Files tab: a listing never moves the folder you're looking at, and a long transfer doesn't hold up other commands.

There is deliberately no REST or MCP way to open a connection. Credentials — passwords and S3 secret keys — stay entirely in the UI path: they cross into Probe's core once, at connect time, and are never persisted there, and the loopback REST surface is meant for local AI tooling that should not carry secrets. So the automation surface can only operate on connections you have already opened in the app. It lists them and drives file operations and transfers against their ids.

probe-mcp exposes nine remote-fs tools, all prefixed probe_. Each is a thin wrapper over the REST endpoints below.

List every open Files connection. No parameters. Returns each connection's id, protocol, host, server label, home path, and whether the transport is encrypted.

List a directory's entries, one page at a time.

ParameterTypeDescription
connection_idstringRequired. Id from probe_list_remote_fs_connections.
pathstringRequired. Absolute remote directory path, e.g. /var/www.
cursorstringnext_cursor from the previous page; omit it for the first page.
limitintegerEntries per page, 1–5000 (default 500).

Returns: each entry's name, path, isDir, size, and — when the backend reports them — modifiedMs, permissions, owner, symlinkTarget; total (entries in the directory) and next_cursor (null on the last page).

Create a directory.

ParameterTypeDescription
connection_idstringRequired.
pathstringRequired. Absolute path of the directory to create.

Rename or move an entry.

ParameterTypeDescription
connection_idstringRequired.
fromstringRequired. Current absolute path.
tostringRequired. New absolute path.

Delete one or more entries.

ParameterTypeDescription
connection_idstringRequired.
pathsarrayRequired. Entries to delete. Each item is { "path": string, "is_dir"?: boolean } (a probe_remote_fs_list_dir entry works as-is: its isDir is used and name, size, modifiedMs, permissions, owner, symlinkTarget are ignored; any other member is refused) — is_dir (default false) tells backends like S3 that distinguish an object from a prefix.

Directory deletes are recursive on S3 (every object under the prefix) and WebDAV (the server deletes the whole collection), and not recursive on SFTP and FTP/FTPS, where a non-empty directory fails. Every path is attempted even if an earlier one fails; the result lists deleted, and failed entries ({path, error: {code, message}}) when some couldn't be removed.

Enqueue a download to a local file. Returns a transferId immediately — poll probe_remote_fs_transfers for progress, or stop it with probe_remote_fs_cancel_transfer. The transfer also shows in the app's transfer queue, where you can pause or cancel it.

ParameterTypeDescription
connection_idstringRequired.
remote_pathstringRequired. Absolute remote source path.
local_pathstringRequired. Absolute local destination path inside ~/Downloads or Probe's transfers folder ~/.probe/transfers (Windows: %USERPROFILE%\Downloads, %USERPROFILE%\.probe\transfers). A relative path, a .., a symbolic link, a directory, a missing folder or a path outside those folders is refused.
overwritebooleanReplace local_path if it exists. Default false: an existing file is refused.

Enqueue an upload of a local file. Returns a transferId immediately — poll probe_remote_fs_transfers.

ParameterTypeDescription
connection_idstringRequired.
local_pathstringRequired. Absolute path of an existing regular file inside ~/Downloads or Probe's transfers folder ~/.probe/transfers (Windows: %USERPROFILE%\Downloads, %USERPROFILE%\.probe\transfers) — copy another file there first. Symbolic links are refused.
remote_pathstringRequired. Absolute remote destination path.

List the transfer queue: active transfers plus the 100 most recently finished. No parameters. Returns each transfer's transferId, connectionId, kind, state (queued / running / paused / done / cancelled / error), remote and local paths, byte offset, and — in the error state — error: { code, message }.

Cancel a queued, running or paused transfer, whether it started in the app or over MCP. It stays in the queue with state cancelled; a partly written destination file is left in place. Errors for an unknown id or a transfer that already finished.

ParameterTypeDescription
transfer_idstringRequired. transferId from a download/upload or probe_remote_fs_transfers.

All paths are under http://127.0.0.1:<port>/api/v1/ and require the Authorization: Bearer <token> header — see Discovery & auth. Connection ids are URL-encoded in the path.

Method & pathBodyReturns
GET /remote-fs/connections—{ "connections": [ … ] }
GET /remote-fs/transfers—{ "transfers": [ … ] }
POST /remote-fs/connections/{id}/list{ "path": "/var/www", "cursor"?, "limit"? }{ "path", "entries": [ … ], "total", "next_cursor" }
POST /remote-fs/connections/{id}/mkdir{ "path": "/var/www/new" }{ "ok": true }
POST /remote-fs/connections/{id}/rename{ "from": "…", "to": "…" }{ "ok": true }
POST /remote-fs/connections/{id}/delete{ "paths": [ { "path": "…", "is_dir": false } ] }{ "ok": true, "deleted": [ … ] } · 207 with failed on partial success
POST /remote-fs/connections/{id}/download{ "remote_path": "…", "local_path": "…", "overwrite": false }202 · { "transferId": "tr_…" }
POST /remote-fs/connections/{id}/upload{ "local_path": "…", "remote_path": "…" }202 · { "transferId": "tr_…" }
POST /remote-fs/transfers/{id}/cancel—{ "ok": true, "transferId": "tr_…" }

Transfers are asynchronous: the enqueue call returns 202 Accepted with a transferId, and you poll GET /remote-fs/transfers to watch it move through queued → running → done.

Failures return { "error": { "code", "message" } } with an HTTP status matched to the cause:

CauseHTTP
Auth / permission denied403
Not found404
Already exists / cancelled409
Host unreachable / host key rejected502
Unsupported / protocol error400
Invalid local_path (code invalidArgument), or cancelling a finished transfer400
I/O or transport failure500
The Files engine didn't answer in time (code timeout)504
Terminal window
PORT=$(jq -r .port ~/.probe/api-handshake.json)
TOKEN=$(jq -r .token ~/.probe/api-handshake.json)
BASE="http://127.0.0.1:$PORT/api/v1"
# 1. Find an open connection
CID=$(curl -s -H "Authorization: Bearer $TOKEN" \
"$BASE/remote-fs/connections" | jq -r '.connections[0].id')
# 2. List a directory
curl -s -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"path":"/var/www"}' \
"$BASE/remote-fs/connections/$CID/list" | jq
# 3. Enqueue a download, then poll the queue
curl -s -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"remote_path":"/var/www/config.yaml","local_path":"/Users/me/Downloads/config.yaml"}' \
"$BASE/remote-fs/connections/$CID/download"
# {"transferId":"tr_rest_..."}
curl -s -H "Authorization: Bearer $TOKEN" "$BASE/remote-fs/transfers" | jq