Files the model never has to read Loomcycle v1.125.0 shipped a File tool over HTTP and an inbox capture system that keeps files returned by MCP servers and web fetches in an ephemeral `inbox` volume instead of passing their bytes to the model, which now receives a one-line stub with path, media type, size, sha256 hash and originating tool. Capture is opt-in per MCP server via `binary_results: passthrough | capture`, where `capture` keeps base64 strings of at least 4,096 bytes only when they decode cleanly to a recognizable type, and files over `LOOMCYCLE_FILE_MAX_BYTES` (64 MiB) or a tree over `LOOMCYCLE_FILE_INBOX_MAX_BYTES` (1 GiB) are not kept. Every kept file emits a `file_captured` event on SSE, in the transcript, on gRPC and in both adapters, addressing the prior behavior where image, audio and resource blocks were dropped silently and binary HTTP bodies arrived as mangled strings. Files the model never has to read. Update, 2026-10-27: the File tool over HTTP described below as “on main” shipped in v1.125.0 . 1. Where the bytes used to go We are building a document-based reference system. It is fed by mail attachments and SharePoint files: board papers, scanned pages, spreadsheets, slide decks. An agent reaches them through MCP servers and plain HTTP, reads them, and turns them into documents a person can search. Until v1.122, the bytes of those files had nowhere good to go. An MCP tool result can carry image , audio and resource blocks. loomcycle passed text through and dropped the rest without a word, so an image-only result reached the model as an empty string. Some servers return a file as base64 inside a text block instead, and then the model received kilobytes or megabytes of characters it could neither read nor reproduce. It paid for every one of them in tokens. The HTTP tool and WebFetch returned a binary body as a mangled string. Nothing could download a URL into a volume, and nothing could upload a file unless the model re-typed its contents. That is a lot of effort spent on the one thing a model is worst at: carrying bytes it does not understand from one place to another. The fix was to stop asking it to. 2. The inbox and the one-line stub A file a tool returns is now kept in the run's inbox : an ephemeral volume of the run tree named inbox , made under the operator's dynamic root volume on the first capture and purged with the tree. The model sees one line instead of the bytes: file: volume:inbox:documents/report.pdf · application/pdf · 2.8 KiB · sha256:16d51716c3de… · from mcp:files/get attachment The line carries what the model needs to reason about the file: where it is, what type it is, how large it is, its hash, and which tool produced it. It does not carry the content. The model gets a reference to the file. The runtime keeps the file. A few details make the stub trustworthy: - Names keep their last segment, transliterated from non-latin and cut to A-Za-z0-9. - . A download tool often answers with bytes and no name; since 2161 the capture takes the name from the call's own fileName , filename , name or path argument before falling back to the tool name. A document imported from it is no longer titled "download-bytes". - The media type is the declared one, else sniffed. - Limits hold: a file over LOOMCYCLE FILE MAX BYTES 64 MiB or a tree over LOOMCYCLE FILE INBOX MAX BYTES 1 GiB gives file not kept: … . Either way, the bytes never reach the model. - Every kept file emits a file captured event on SSE, in the transcript, on gRPC and in both adapters. The Web UI shows it as one line in run cards and the terminal: ⇩ ref · type · size · from origin . A resource link becomes one line, resource text is kept, and an unknown block type is named rather than dropped silently. A text-only result is unchanged, byte for byte. 3. Base64 in text, by choice Base64 inside a text block is the awkward case. Sometimes a long base64 string is a file. Sometimes it is a cursor, a signature, or an opaque token the agent must pass back unchanged. A runtime that guessed wrong would break the second kind. So capture is opt-in, per server: binary results: passthrough | capture on mcp servers.