AI agents
Give an agent file tools in one line with s3delivery/ai.
s3delivery/ai turns an S3Delivery client into a set of
tools an AI agent can call — uploadFile, listFiles, getFileUrl,
deleteFile, and getUsage. Spread them into your agent's tools: and it can
store, find, link, and clean up files on its own.
The tools are shaped to drop straight into the Vercel AI SDK
(tool()-compatible: { description, inputSchema, execute }) and work with any
framework that accepts that shape. ai is an optional peer dependency — this
module never imports it, so the core SDK stays dependency-free apart from zod.
import { S3Delivery } from "s3delivery";
import { createFileTools } from "s3delivery/ai";
import { generateText } from "ai";
const tools = createFileTools(new S3Delivery());
await generateText({
model,
tools, // uploadFile, listFiles, getFileUrl, deleteFile, getUsage
prompt: "Save these notes as notes.txt and give me a shareable link.",
});Server-side only
createFileTools wraps a client that holds your secret API key. Run it on the server (your API
route, agent backend, or job runner) — never in the browser.
The tools
Each tool carries an LLM-friendly description and a zod-validated inputSchema,
and returns a compact, JSON-serializable result.
| Tool | Input | Result |
|---|---|---|
uploadFile | name, text or base64, contentType?, visibility? | The stored file (id, name, url, …) |
listFiles | limit?, offset? | { files: [...] } with ids and urls |
getFileUrl | fileId, download? | { url, file } — a fresh shareable URL |
deleteFile | fileId | { id, deleted: true } |
getUsage | (none) | Bytes used, file count, plan, and limits |
uploadFile takes the bytes as text (plain UTF-8, for text files) or base64
(for binary). The result objects are summarized — id, name, content type, size,
status, visibility, and url — dropping timestamps the model rarely needs.
Safety knobs
Two built-in controls let you decide how much an agent can do.
readOnly
Expose only the read tools (listFiles, getFileUrl, getUsage). The mutating
tools (uploadFile, deleteFile) are omitted entirely. A safe default for
untrusted agents.
const safe = createFileTools(s3, { readOnly: true });requireApproval + onApproval
Gate mutating tools behind human approval. Pass true to gate all mutating tools,
or a per-tool map to gate specific ones.
const tools = createFileTools(s3, {
requireApproval: { deleteFile: true },
onApproval: async ({ tool, input }) =>
confirm(`Allow agent to ${tool} ${JSON.stringify(input)}?`),
});When a gated tool is invoked, onApproval runs first:
- Return
true→ the action runs and its real result is returned. - Return
false(or throw) → the tool returns{ status: "denied", tool, message }and does not act.
If you do not supply onApproval, a gated tool returns
{ status: "approval_required", tool, message, input } instead of acting. The
model surfaces that to your app, which can prompt the human out-of-band and re-run
the action on approval — useful when approval is asynchronous (a Slack message, a
UI confirmation) rather than a synchronous confirm.
| Option | Type | Notes |
|---|---|---|
readOnly | boolean | Omit the mutating tools entirely. |
requireApproval | boolean | { uploadFile?: boolean; deleteFile?: boolean } | Gate mutating tools behind approval. |
onApproval | ({ tool, input }) => boolean | Promise<boolean> | Resolve a gated call: true runs it, false/throw denies. |
Wiring into the Vercel AI SDK
createFileTools returns a record of tool name → tool, so spread it straight into
tools:. It composes with your own tools.
import { S3Delivery } from "s3delivery";
import { createFileTools } from "s3delivery/ai";
import { generateText, tool } from "ai";
import { z } from "zod";
const s3 = new S3Delivery();
const result = await generateText({
model,
tools: {
...createFileTools(s3, { requireApproval: { deleteFile: true } }),
// your own tools alongside the file tools
getWeather: tool({
description: "Get the weather for a city.",
inputSchema: z.object({ city: z.string() }),
execute: async ({ city }) => fetchWeather(city),
}),
},
prompt: "Generate a CSV of last week's sales and give me a download link.",
});The tools match the object the AI SDK's tool() helper returns, so you do not
need to wrap them. They also work with any other framework that accepts the same
{ description, inputSchema, execute } shape.
Use the tools directly
The tools are plain objects, so you can call execute yourself — handy for tests
or non-LLM automation.
const { listFiles, getFileUrl } = createFileTools(s3, { readOnly: true });
const { files } = await listFiles.execute({ limit: 5 });
const { url } = await getFileUrl.execute({ fileId: files[0].id });A read-only retrieval agent
A common, safe pattern: let an agent find and link files, but never write.
const tools = createFileTools(s3, { readOnly: true });
await generateText({
model,
tools, // listFiles, getFileUrl, getUsage
prompt: "Find the latest invoice PDF and give me a shareable link.",
});The agent can discover ids with listFiles, then mint a fresh URL with
getFileUrl — with no way to upload or delete.