File Tools
Let agents keep files between runs. Save reports and downloads into folders, find them again by metadata, and pass them to the browser or to other agents.
On this page
Quick start
Add the file tools to an agent. Agents choose the path when they save a file, and folders appear as soon as a file lands in them. See Files for the data model and limits.
version: "1.0"
name: invoice-collector
model: connic/gpt-5.6-luna
description: "Downloads invoices from supplier portals and files them"
system_prompt: |
Download this month's invoice from the supplier portal and save it
under invoices/<supplier>/. Mark new invoices with status "new".
tools:
- web_browser_open
- web_browser_observe
- web_browser_act
- web_browser_download
- file_list
- file_update
files:
folders: [invoices]
prevent_delete: trueAccess control
The files block limits what an agent can see and change. Without it, the agent can use the whole file tree.
folderscovers each folder and everything below it:invoicesincludesinvoices/2026/acme.pdf. With folders set, files outside them are invisible to the agent.prevent_writeblocks file_write and file_update. Moving a file needs write access to both folders.prevent_deleteblocks file_delete.session_onlyshows the agent only files written in its current session, which keeps one end user's files away from another's. It requires session.- A folder setting overrides the top-level setting, and the most specific folder wins.
session:
key: context.customer_id
files:
session_only: true # each customer sees only their own uploads
prevent_delete: true
folders:
uploads: {}
templates:
session_only: false # shared by every session
prevent_write: trueA file an agent cannot see is never replaced. If its path is taken, the new file is saved under a numbered name instead.
Use in custom tools
Custom tools import the same functions from connic.tools. The agent's access control applies to these calls too.
from connic.tools import file_list, file_read, file_update
async def next_pending_invoice() -> dict:
"""Return the oldest invoice that still needs processing."""
result = await file_list(
"invoices",
recursive=True,
filter={"status": "new", "_mime_type": "application/pdf"},
sort={"_created_at": 1},
limit=1,
)
if not result["files"]:
return {"done": True}
invoice = result["files"][0]
await file_update(invoice["_path"], metadata={"status": "processing"})
return invoiceBrowser downloads and uploads
The browser tools move files to and from project files. A save_to ending in / is a folder that keeps the downloaded name.
# Save a download straight into files (up to 100 MiB)
await web_browser_download("e12", include_content=False, save_to="invoices/acme/")
# Put a stored file into an upload field
await web_browser_upload("e7", path="contracts/signed/acme.pdf")Tool reference
file_list
List the files and subfolders in a folder, or every file below it. Filters use the database filter operators on custom metadata and on the system fields _name, _path, _folder, _mime_type, _size, _source, _agent, _run_id, _session_id, _created_at, and _updated_at.
result = await file_list("invoices/2026")
# {"folder": "invoices/2026", "folders": ["acme"], "files": [...], "count": 3, "total": 3}
# Everything this session wrote, anywhere in the tree
result = await file_list(recursive=True, scope="session")| Parameter | Type | Default | Description |
|---|---|---|---|
| folder | str | "" | Folder to list. Empty for the root. |
| recursive | bool | False | Return every file below the folder instead of listing subfolders. |
| filter | dict | None | Metadata filter. |
| scope | str | "all" | "session" or "run" keeps only files written in the current session or run. |
| sort | dict | {"_name": 1} | Sort order. 1 ascending, -1 descending. |
| limit | int | 100 | Maximum files to return, up to 1000. |
| skip | int | 0 | Files to skip for pagination. |
file_read
Read a file. The model gets the full text of text files, and PDFs and images as an attachment it can read, for files up to 20 MiB. Other formats and larger files return the metadata and an error; pass their path to the browser or a custom tool instead. In custom tools, file_read returns the raw bytes of any file as content.
# In a custom tool
result = await file_read("reports/summary.md")
# {"file": {"_path": "reports/summary.md", ...}, "content": b"# Summary ..."}
text = result["content"].decode("utf-8")| Parameter | Type | Default | Description |
|---|---|---|---|
| path | str | required | File path. |
file_write
Write a text file. Missing folders are created. Returns the stored file, including its final _path. In custom tools, content can also be bytes, for binary files such as PDFs or images.
result = await file_write(
"reports/2026-10/summary.md",
"# October summary\n...",
metadata={"status": "draft", "customer": "acme"},
)
# result["_path"] is the stored path, e.g. "reports/2026-10/summary (2).md" if the name was taken| Parameter | Type | Default | Description |
|---|---|---|---|
| path | str | required | File path. |
| content | str | bytes | required | Text content. Bytes only in custom tools. |
| mime_type | str | None | Content type. Guessed from the file name if omitted. |
| metadata | dict | None | Custom fields. Keys must not start with an underscore. |
| overwrite | bool | False | Replace an existing file at the path. Otherwise the new file gets a numbered name. |
file_update
Move or rename a file or a folder, or merge metadata into a file. A None value removes a metadata field. Moving onto a path that is taken fails.
# Rename a file and mark it processed
await file_update("inbox/acme.pdf", new_path="done/acme.pdf", metadata={"status": "processed"})
# Move a whole folder
await file_update("inbox/2026-09", new_path="archive/2026-09")| Parameter | Type | Default | Description |
|---|---|---|---|
| path | str | required | Path of the file or folder. |
| new_path | str | None | New path. Moving a folder moves everything in it. |
| metadata | dict | None | Fields to merge into a file's metadata. |
file_delete
Delete a file, or a folder and everything in it. Returns the number of deleted files.
await file_delete("tmp/scratch.txt")
await file_delete("tmp", recursive=True)| Parameter | Type | Default | Description |
|---|---|---|---|
| path | str | required | Path of the file or folder. |
| recursive | bool | False | Required to delete a folder. |