Output format
Every successful call returns the same two things, on every tool surface:
structuredContent: the complete value Arcane returned, unchanged (arrays underitems, withpaginationalongside when Arcane pages the list). Clients and scripts that need every field read this. The one exception is the experimental output schemas (ARCANE_MCP_OUTPUT_SCHEMAS, off by default): with them on, a part of Arcane’s answer that does not match the tool’s declared schema is sent asnull, and the text then lists each such part with Arcane’s original value.- A text block: a Markdown rendering of that value, sized for a model to read. It is annotated with audience
assistant.
The text does not repeat the JSON. It says instead what it leaves out, so a model that reads only the text knows where to look.
Errors are different: a failed call returns isError with a plain-text message and a next step (for example, which list action finds valid IDs). It has no structured content and no Markdown rendering.
What the text contains
Section titled “What the text contains”| The result is | The text shows |
|---|---|
| a list | a heading, the item count and the page (3 item(s) — 12 total, page 1 of 4), and a table of the fields that identify and describe an item. An empty list says No items., or, for the main lists (containers, images, projects, volumes, networks, environments), what can leave it empty and how to widen it, such as No containers match. If search, label, standalone or updates was set, drop it to widen the list; … |
| one object | a heading and one - **field**: value line per field, the identifying and state fields first; multi-line text gets a fenced section of its own and nested objects and arrays one JSON block |
| a text download (logs, compose files, PEM) | the file name, its size and type, and the text itself in a fenced block |
| a binary download (tarballs, backups, profiles) | the metadata; the file itself is a separate embedded resource with audience user |
| the output of a log or exec handler | that output, as the handler wrote it |
Byte counts are also shown in human units (1500000000 (1.5 GB)), and long table cells are cut, except identifiers and names, which a later call needs whole.
What it leaves out, and says so
Section titled “What it leaves out, and says so”- Columns. A table shows the fields that identify and describe an item (the action’s own choice, or up to six picked by name). When items have more fields, a closing note lists them and names the action that returns one item in full, for example
container.get. - Rows. A table shows at most 50 rows. When a page has more, the text says how many were cut and how to read the rest (
startandlimit, or the list’s filters). - Null fields. They are omitted from an object’s lines.
structuredContent always has every row and every field.
A link to the object’s resource
Section titled “A link to the object’s resource”The results of environment.get, container.get and project.get end with one more content block, a resource_link to the resource that serves the same object: arcane://environment/{environmentId}, …/container/{containerId} or …/project/{projectId}, filled in with the call’s arguments (the default environment when environmentId was left out). It carries the resource’s name, title and application/json; a client can attach it to the conversation or read it again later with resources/read. The resource is the redacted view described on that page, so the link adds nothing the call did not already return. The text and structuredContent are unchanged, a failed call has no link, and it is the same on every surface.
Next steps
Section titled “Next steps”After a successful call the text may end with a short Next steps section, at most four lines:
- known follow-ups declared for the action (for example, after pulling an image, redeploying the project that uses it);
- the action’s related actions, with their titles. Destructive ones are marked as needing
confirm.
Actions the server does not serve are never suggested: read-only mode and ARCANE_MCP_EXCLUDE_TOOLS remove them from the list. In safe mode, actions that change state are still listed but marked “(safe mode: preview only)”: calling one returns the request it would send, not the change. The section is skipped where it adds nothing: downloads, log and exec output, and safe-mode previews (nothing ran). It lives only in the text and is never added to structuredContent.
Action names follow the surface
Section titled “Action names follow the surface”The action names the server writes (the next steps and the note about the full-item action) are spelled the way the calling surface takes them:
| Surface | Written as |
|---|---|
| dynamic | container.get (pass it to arcane_execute_action) |
| meta | arcane_container (action get) |
| individual | arcane_container_get |
Values that come from Arcane are never rewritten, even if they look like an action name.
Escaping values from Arcane
Section titled “Escaping values from Arcane”Arcane stores what Docker, compose files, image labels, registries and users put there, so the server treats every value as untrusted text. Models usually read the raw Markdown and copy values from it into their next call, so the escaping has two goals: nothing in a value may change the structure of the text, and an identifier must reach the model exactly as it is.
- Terminal sequences and invisible characters are removed. ANSI color and cursor sequences, OSC sequences (terminal titles, hyperlinks), C0 and C1 control bytes other than newline and tab, Unicode bidirectional controls and zero-width characters (ZWSP, ZWNJ, ZWJ, word joiner, a byte-order mark) are stripped. A carriage return is kept before a line feed, so CRLF text stays CRLF; a lone carriage return, which a terminal uses to overwrite a line, becomes a line feed, as do the Unicode line and paragraph separators. This applies to every value, and the finished text gets one more pass whatever wrote it.
- Plain values stay plain. A value Markdown would read as plain text is written as it is:
my_container,/var/lib/docker/volumes/x/_data,app_,*/5 * * * *,nginx@sha256:…,C:\data. - A single token that would form Markdown becomes a code span. For example a dangling image’s
<none>:<none>is written in backticks, where Markdown does not interpret it, and the model still reads it exactly. The fence is longer than any run of backticks in the value. - Text with spaces gets a backslash only where Markdown would form something: a
<that opens a tag, a comment or an autolink, a[followed later by](or][(a link or an image),*or_delimiters that have a matching closer (emphasis), and backtick runs that pair (a code span). Field names, bold keys and the values of a short list are escaped a little more strictly, because a partner of*,`,[or a word-edge_could be in the next value. - Table cells and headings also escape
|(cells) and a closing run of#(headings). A list item that would start a block of its own (a heading, a quote, a nested list, a rule, a fence, a task box) is kept literal. - Fenced blocks are verbatim. Multi-line text and JSON blocks keep their contents as they are (normalized as above), inside a fence longer than any run of backticks in them, so nothing in the content can close the block early.
None of this touches structuredContent: it carries the original value, byte for byte as Arcane returned it (except the parts adjusted to fit an output schema, when those are on).