Custom Host Functions
Expose trusted host functions to agentOS coding agents as typed CLI commands, with scoped input, output, and execution behavior.
Expose your host JavaScript functions (defined with Zod input schemas) to agents as auto-generated CLI commands installed at /bin/agentos-{name} inside the VM, injected into the agent’s system prompt and callable inside scripts for code-mode token savings.
Getting started
Pass agentOS({ hostFunctions }) a record of collections. The keys name everything: the collection key becomes the CLI binary agentos-{name} and a guest global, and each function key becomes a subcommand. A function needs only an inputSchema and an execute handler. .describe() on the schema is what the agent reads.
import { agentOS, setup } from "@rivet-dev/agentos";
import { z } from "zod";
// Host functions are a record of collections. The keys name everything: the
// collection key becomes the CLI command /bin/agentos-{name} inside the VM, and
// each function key becomes one of its subcommands. A function needs only a Zod
// input schema and an `execute` handler that runs on the host; `.describe()` on
// the schema is what the agent reads.
const vm = agentOS({
hostFunctions: {
weather: {
forecast: {
inputSchema: z
.object({
city: z.string().describe("City name"),
days: z.number().optional().describe("Number of days"),
})
.describe("Get the weather forecast for a city"),
execute: async ({ city, days }) => {
const res = await fetch(
`https://api.weather.example/forecast?city=${city}&days=${days ?? 3}`,
);
return res.json();
},
examples: [
{
description: "3-day forecast for Paris",
input: { city: "Paris", days: 3 },
},
],
},
},
},
});
export const registry = setup({ use: { vm } });
registry.start();
import { createClient } from "@rivet-dev/agentos/client";
import type { registry } from "./server";
const client = createClient<typeof registry>({
endpoint: "http://localhost:6420",
});
const agent = client.vm.getOrCreate("my-agent");
// The agent invokes the host function itself as a shell command:
// agentos-weather forecast --city Paris --days 3
await agent.sessions.open({
agent: "claude",
env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! },
});
await agent.sessions.prompt({
content: [{ type: "text", text: "What's the weather in Paris?" }],
});
Each host function can override its timeout in milliseconds. The default is 30 seconds and can be changed with limits.hostFunctions.defaultTimeoutMs.
Zod to CLI mapping
Zod schema fields are converted to CLI flags automatically. Field names are converted from camelCase to kebab-case.
| Zod type | CLI syntax | Example |
|---|---|---|
z.string() | --name value | --path /tmp/out.png |
z.number() | --name 42 | --limit 5 |
z.boolean() | --flag / --no-flag | --full-page |
z.enum(["a","b"]) | --name a | --format json |
z.array(z.string()) | --name a --name b | --tags foo --tags bar |
Optional fields (via .optional()) become optional flags. Required fields are enforced at validation time. Use .describe() on Zod fields to generate useful --help output, and .describe() on the whole schema to describe the function itself.
What the agent sees
When host functions are registered, CLI shims are installed at /bin/agentos-{name} inside the VM and the function list is injected into the agent’s system prompt, so keep each schema’s .describe() concise to save tokens.
The agent interacts with host functions as shell commands:
# List all available host-function collections
agentos list-host-functions
# List the host functions in a specific collection
agentos list-host-functions weather
# Get help for a host function
agentos-weather forecast --help
# Call a host function with flags
agentos-weather forecast --city Paris --days 3
# Call a host function with inline JSON
agentos-weather forecast --json '{"city":"Paris","days":3}'
# Call a host function with JSON from a file
agentos-weather forecast --json-file /tmp/input.json
On success, the host function exits 0 and writes a JSON envelope to stdout:
{"ok":true,"result":{"temperature":22,"condition":"sunny"}}
On failure (validation or execution error), the host function exits non-zero and writes the error message to stderr:
Missing required flag: --city
Call host functions from JavaScript
Inline JavaScript and TypeScript get each collection as a frozen global, and each
host function as an async function, so generated code calls your tools like any
other API. Keys written in camelCase become kebab-case commands and camelCase guest
identifiers, so orderStore.listOrders is agentos-order-store list-orders on
the CLI and orderStore.listOrders(input) in guest JavaScript.
// Each collection is a global inside the VM, and each host function is an
// async function, so generated code calls your tools like any other API.
const llmGeneratedExpression = `(async () => {
const [sf, tokyo] = await Promise.all([
tools.weather({ city: "San Francisco" }),
tools.weather({ city: "Tokyo" }),
]);
return {
sanFrancisco: sf,
tokyo,
differenceF: Math.abs(sf.tempF - tokyo.tempF),
};
})()`;
The function resolves to the host function’s result and rejects on a schema
violation, a thrown error, or a timeout. A collection whose name is already a
global, such as process, is not defined, and a warning is written to stderr.
The hostFunction permission scope applies exactly as it does to the commands.
Host functions and MCP servers
agentOS supports two ways to give agents access to external functionality: host functions and session-scoped MCP servers. Both work, but they have different tradeoffs.
| Host functions | MCP servers | |
|---|---|---|
| How it works | Call JavaScript functions on the host directly | Connect to a standard MCP server |
| Authentication | None required. Calls go directly to the host process. | Requires custom auth configuration per server |
| Code mode | Built in. Host functions are exposed as CLI commands, so agents can call them inside scripts for up to 80% token reduction. | Requires extra work to make code mode work out of the box |
| Latency | Near-zero. Bound directly to the host process. | Extra network hop to reach the MCP server |
| Setup | Define host functions in your actor code with Zod schemas | Configure any standard MCP server |
Use host functions when you want to expose your own JavaScript functions to agents. Use MCP servers when you want to connect to existing third-party services. See Sessions for MCP server configuration.
Security
Host-function calls from the agent invoke your execute() functions on the host. Your functions run with full access to the host environment, so you can call databases, APIs, and services directly without proxying credentials into the VM. The agent never sees the credentials, only the function’s input/output contract.
Host functions run on the host with full access to the host environment, so do not expose functions that could compromise the host without appropriate safeguards.
Embedded API
Pass the same host-function collections to AgentOs.create(). The function contract and
generated VM commands are unchanged.
import { AgentOs } from "@rivet-dev/agentos-core";
import { z } from "zod";
// Host functions are defined exactly as they are for the actor. Pass them to
// AgentOs.create() and `execute` runs in this host process, with its input typed
// by its own schema.
const vm = await AgentOs.create({
hostFunctions: {
weather: {
forecast: {
inputSchema: z
.object({ city: z.string().describe("City name") })
.describe("Get the weather forecast for a city"),
execute: async ({ city }) => ({ city, temperature: 22 }),
},
},
},
});
// The agent calls it as `agentos-weather forecast --city Paris`.
const result = await vm.process.exec("agentos-weather forecast --city Paris");
console.log(result.stdout);
await vm.dispose();
Read more in the embedded API quickstart.