Features
Custom JavaScript tools
Build a tool by writing a JavaScript file, validate it, and enable its ID in
agent.json. No API server edit or restart is needed when authoring tools.
Tools run when the model calls them; discovering a file does not enable or run
its execute function.
Write and enable
Put agent tools in data/tools/<name>.tool.js, beside the agent's other data.
Put reusable organization tools in shared/tools/<name>.tool.js. Both appear
in the agent configuration's Tools > Custom tools list, including errors.
Shared tools use each calling agent's own data and database files. If names
collide, the agent file overrides the shared file, and file tools override
built-in tools. The winning source and overridden sources appear in the catalog.
A broken override reports an error; it never falls back to a different implementation.
The filename supplies the ID: search-catalog.tool.js becomes
search_catalog, with no added prefix. Use a lowercase kebab filename; tool
names use lowercase letters, digits and underscores, up to 64 characters.
Enable it with the checkbox or add that ID to the existing tools array in
agent.json. Start a new session after changing
source, schemas, permissions or enabled tools. Existing sessions reject changed
source rather than gaining new permissions mid-session.
Config writes through Builder and MCP reject missing or invalid
enabled tools. New sessions check the files again, including changes made
outside the app. Broken tools remain visible in the catalog for repair.
Sessions inspect only enabled files, with at most 16 file tools per agent.
Catalog inspection validates 16 winning files per page, with four workers
sharing a three-second budget.
Budget failures are reported per file. Follow pagination.nextOffset by passing
offset to tool_catalog; the UI has a Load more button. Enabled tools appear
first. Neither startup nor catalog inspection evaluates shadowed files.
Copy docs/examples/tools/search-catalog.tool.js from the repository for a
SQLite search example, or docs/examples/tools/match-garment.tool.js for a
camera, database and LLM workflow.
For a minimal tool, write data/tools/search-catalog.tool.js:
export const schemaVersion = "worldagents.tool.v1";
export const metadata = { title: "Search catalog", description: "Find products by name." };
export const inputSchema = {
type: "object", properties: { query: { type: "string", minLength: 1 } },
required: ["query"], additionalProperties: false,
};
export const capabilities = { databases: { catalog: { path: "databases/catalog.sqlite", mode: "read" } } };
export async function execute(ctx, input) {
return ctx.db.query("catalog", "SELECT name FROM products WHERE name LIKE ? LIMIT 5", ["%" + input.query + "%"]);
}
The module exports schemaVersion = "worldagents.tool.v1",
metadata: {title, description}, an object inputSchema with
additionalProperties: false, and execute(ctx, input). Optional exports are
capabilities and timeoutMs (default 30000, maximum 120000). Use direct named
exports. Imports and require are unavailable.
execute returns a JSON value or a Promise. ctx.wait(ms) waits within the
same deadline. Arguments are checked against the input schema before execution.
Context capabilities
| Exported grant | Context API |
|---|---|
databases: {catalog: {path: "databases/catalog.sqlite", mode: "read"}} | await ctx.db.query("catalog", sql, parameters) returns {rows, truncated}. Read-only connection, one SELECT/WITH, at most 100 rows or 128 KiB, two-second deadline. |
Database mode: "write" | Also permits ctx.db.execute(name, sql, parameters) for a single INSERT, UPDATE, DELETE, REPLACE, CREATE TABLE or CREATE INDEX. Returns {changes,lastInsertRowid}. |
files: ["catalog"] | ctx.files.read("catalog/file.json") returns {path,text}. Also grants those files to LLM calls. Paths stay inside agent data. |
llm: {maxCalls: 3, models: ["default"]} | ctx.llm.generate({system,prompt,messages,data,files,schema,thinking,model,includeCurrentFrame}) returns {text,json}, using the trigger LLM helper. Up to six calls and eight files per call. |
camera: true | ctx.camera.available and capturedAt; permits includeCurrentFrame in LLM calls. Raw camera bytes are not handed to the script. |
ctx.now, ctx.agent and ctx.session expose time and nonsecret identity/mode
metadata. ctx.conversation.recentTurns contains up to twelve recent messages. Sequential
and parallel LLM calls share the tool's deadline and budget. Model calls have no
tools themselves and a 2048 output-token ceiling per call. All calls and their outcomes use the normal session tool log;
nested LLM calls also emit start/finish timing events.
The camera image is a snapshot at tool start; frames received over 15 seconds
ago are unavailable, so a tool cannot silently reuse an old camera session.
default uses the platform's Google text model. Operators may configure other
Google model aliases with WORLDAGENTS_TOOL_MODELS, a JSON object mapping alias
to model ID. Scripts receive aliases, never API keys. Other providers need a host
adapter; arbitrary outbound API access is unavailable.
Database files live only in the calling agent's data/databases folder. Always
bind SQL parameters. No attachments, arbitrary pragmas or extension loading.
Writable databases are shared across this agent's sessions and users: use them
for an intentionally shared or single-wearer store, not private per-user data.
Text files and tool inputs/results have a 256 KiB limit; each run permits at most
32 capability calls, eight concurrently. Database result truncation is explicit.
Each SQL operation runs in a separate process so a deadline can stop a query
even while SQLite is executing native code.
Builder and MCP
The per-draft Agent Builder's /agent mount points at data. It writes files
with apply_patch, calls tool_catalog to check schemas and file errors, then
edits the enabled tools array and calls validate_agent.
The web Builder uses the same tools as MCP, with its organization supplied
automatically. Through MCP, use help {topic: "tools"} for the full contract,
tool_catalog {organizationId,agentId,offset?} to inspect tools, file errors and
enabled IDs before enabling. Write source and
configuration through revision-checked write_file or edit_file: agent paths
are agents/<agentId>/data/tools/<name>.tool.js, and shared paths are
tools/<name>.tool.js. Read the config before editing its tools array.
Upload a prepared SQLite database through upload_file or file_transfer.
Runtime isolation
Custom tools reuse the trigger process sandbox and host capability broker, with separate execution processes per tool revision and session. Different tools do not share a process with different permissions or another session's context. Execution requires Bubblewrap by default, with no network, organization files, credentials or writable system mounts in the child. JavaScript VM contexts are not the security boundary. Source discovery also runs in that sandbox without capabilities. Custom tools are unavailable in public sessions.
WORLDAGENTS_TOOL_SANDBOX=process is an explicit trusted-development override,
including for local authoring on macOS. It is not a security boundary
for untrusted code. Child V8 heaps are limited to 128 MiB. Deadlines and session
cancellation abort outstanding host work. This does not provide per-organization
CPU or total native-memory quotas.
StyleEyes catalog
Build a new index from the downloaded snapshot:
node scripts/build-styleeyes-catalog.mjs /path/to/agent/data /tmp/catalog.sqlite
Upload the database to data/databases/catalog.sqlite, copy the two example
tools into data/tools, and enable search_catalog and
match_garment. The builder script refuses to overwrite an existing
database, publishes it only after a successful build, and removes temporary
files on failure so a corrected snapshot can be retried. It excludes photo
references whose retailer filename identifies a different style. The database
contains products, verified image references and
an FTS5 text index. Snapshot dates come from the source product notes; missing
dates stay unknown. It contains no live inventory or image embeddings.
The agent describes the visible garment, searches with hasPhotos: true, then
passes up to four returned styleIds to match_garment. The comparison
tool makes one LLM call with the current camera frame and those catalog photos;
catalog retrieval stays in the search tool. Ask to see the tag when an exact
style cannot be established visually.