Streams and files
An MCP tool call is a request and a response. Much of Arcane is not: logs and stats arrive over WebSockets, the live feed is Server-Sent Events, image pulls stream progress, and backups are binary files. This page explains how each of those becomes something a tool call can return.
Live streams become bounded actions
Section titled “Live streams become bounded actions”Live streams are actions that connect, listen for a bounded window, and return what arrived. Every window is capped at 120 seconds, and collected output at ARCANE_MCP_MAX_DOWNLOAD_BYTES.
container.logs, project.logs and swarm_service.logs read the matching WebSocket log stream.
| Parameter | Default | Meaning |
|---|---|---|
containerId / projectId / serviceId | — | What to read (required) |
tail | 100 | Lines from the end of the log, or all |
since | — | RFC 3339 timestamp, Unix timestamp or relative duration (10m) |
timestamps | false | Prefix each line with its timestamp |
follow | false | Keep listening for new lines for seconds after the existing ones |
seconds | 5 | Longest time to listen |
maxLines | 1000 | Stop after this many lines |
By default the action returns the current tail and stops. With follow: true it also waits for new lines — useful for “watch the logs while I restart it”. For the complete log as a file, use container.logs_download instead.
diagnostics.server_logs returns the Arcane server’s own recent log entries, listening for new ones for seconds (default 2) up to maxLines (default 500).
Stats and diagnostics samples
Section titled “Stats and diagnostics samples”container.stats, container.stats_all, system.stats, diagnostics.snapshot and diagnostics.actors_stream collect samples messages (default 1, at most 60), waiting at most seconds (default 10). One sample answers “how much memory is it using?”; several show a trend.
The live feed
Section titled “The live feed”stream.snapshot subscribes to Arcane’s multiplexed SSE feed (GET /api/stream) and returns the events received.
| Parameter | Default | Meaning |
|---|---|---|
channels | all five | Comma-separated: environments, dashboard, activities, events, version |
limit | — | Maximum activities in each activities-channel snapshot |
seconds | 5 | How long to listen |
maxEvents | 100 | Stop after this many events |
For history rather than what is happening now, use event.list or activity.list.
Running a command in a container
Section titled “Running a command in a container”container.exec runs one command through Arcane’s container terminal and returns its output and exit code:
{ "action": "container.exec", "params": { "containerId": "db", "command": "df -h /var/lib/postgresql" }}| Parameter | Default | Meaning |
|---|---|---|
containerId | — | Container ID or name (required) |
command | — | Shell command line (required) |
shell | /bin/sh | Shell that runs it |
seconds | 30 | Timeout, at most 120 |
The terminal is an interactive TTY, so the server first silences it (empty prompts, no line editing, stty -echo), then runs the command in a subshell framed by markers that an echo of the typed line cannot contain. The result is {output, exitCode, completed}; on timeout completed is false and a note says the output may be partial. Commands that wait for input are stopped at the timeout. container.exec is a write action, so read-only mode removes it and safe mode previews it.
Diagnostics commands
Section titled “Diagnostics commands”diagnostics.run_command sends one command over the diagnostics WebSocket: leakScan (goroutine leak scan), dump (a named runtime profile as text) or profile (capture a named profile for seconds and return it base64-encoded). The pprof routes are separate actions (diagnostics.pprof_*).
Progress streams are followed to the end
Section titled “Progress streams are followed to the end”Some Arcane routes answer with a stream of JSON progress lines rather than one document: image.pull, image.build, project.up, project.pull, project.build and project.redeploy. The action reads the stream until Arcane closes it and returns a summary: the final status, the activity ID, any errors reported along the way, and the last progress lines. The model gets “pulled nginx:1.27, done” rather than thousands of layer updates.
These requests are not cut off by ARCANE_MCP_TIMEOUT, because a large pull can legitimately take minutes.
Progress notifications
Section titled “Progress notifications”When a client sends a tool call with a progress token (MCP _meta.progressToken), the long operations above also report progress while they run, as notifications/progress: the progress streams per line (“abc123 Downloading: 45%” when Docker gives a layer’s size), the log, stats and live-feed windows per message received, and container.exec per chunk of output. Notifications are rate-limited to at most two a second, and a final one closes every call (“completed (121 event(s))”, “collected 12 log line(s): stream closed by Arcane”). It works the same on all three tool surfaces. A call without a token gets no notifications, and the result is the same either way.
Downloads
Section titled “Downloads”Routes that return a file — log downloads, Compose and template files, mTLS bundles and certificates, backup archives, pprof profiles — are returned according to their content:
| Content | Returned as |
|---|---|
Text (text/*, JSON, YAML, XML, PEM, or an untyped body without NUL bytes) | The text in content, with filename, contentType, bytes and truncated |
| Binary, within the cap | The same metadata plus an MCP embedded resource (arcane://download/<filename>) carrying the bytes, base64-encoded |
| Binary, over the cap | Metadata only, with a note to raise ARCANE_MCP_MAX_DOWNLOAD_BYTES |
Text over the cap is truncated at ARCANE_MCP_MAX_DOWNLOAD_BYTES (4 MiB by default) and says so. Raise the limit for large backups, keeping in mind that everything returned lands in the model’s context.
Uploads
Section titled “Uploads”Multipart file fields
Section titled “Multipart file fields”Where an Arcane route takes multipart/form-data, its file fields become file objects in the action’s schema:
{ "filename": "compose.yaml", "content": "services:\n web:\n image: nginx:1.27\n"}Pass the content as UTF-8 text in content, or as base64 in contentBase64 — exactly one of the two. Array file fields (such as files of project.create) take a list of such objects. Other form fields are ordinary parameters. Control characters and invalid UTF-8 in filename are sent percent-encoded (a backspace becomes %08), since a multipart header cannot carry them; Arcane then sees the encoded name.
Raw bodies
Section titled “Raw bodies”Routes whose body is application/octet-stream take the same pair at the top level: content for text, contentBase64 for binary.
Chunked upload sessions
Section titled “Chunked upload sessions”Arcane moves large files (image archives, volume backups) through chunked upload sessions, and the catalog exposes them as the upload domain:
upload.session_createwith thekind,filenameandsize→ anuploadId;upload.chunk_putfor each chunk (index, pluscontentorcontentBase64) — re-sending a chunk is idempotent;- the consuming action with the
uploadId, for exampleimage.upload(load adocker savearchive) orvolume_backup.upload(restore a volume).
upload.session_get reports progress and upload.session_delete abandons a session. Through an assistant this suits files of modest size; every chunk travels through the model’s context as base64.