Skip to main content
defineAgent is the single function you need to build an agent route. It returns a route handler that runs the model↔tool loop and streams the Vercel AI SDK data stream protocol back to the client. Export it as the default from an agent.ts file (or any post.ts) and Anvil mounts it automatically.

Quick start

The example below creates a fully working agent route at POST /chat that uses Anthropic’s Claude, answers questions, and can call a weather tool:

Configuration reference

defineAgent(config: DefineAgentConfig) accepts the following options:
LlmClient
The LlmClient instance to use for model calls. If omitted, the runtime falls back to resolveClient or the client attached by a withLlm() middleware.
(ctx: Context) => LlmClient | undefined
A per-request factory for the LlmClient. Useful when the client depends on request state (e.g. a per-tenant API key stored in ctx.state). Takes precedence over a middleware-attached client but not over an inline client.
string
Override the default model for this specific agent route. The value is passed through to the LlmClient and must be supported by one of its drivers.
string | (ctx: Context) => string | Promise<string>
The system prompt. Pass a plain string for a static prompt, or an async function to build the prompt from request context — for example, to inject the authenticated user’s name.
AgentTool[] | (ctx: Context) => AgentTool[] | Promise<AgentTool[]>
The tools available to the model during this run. Pass an array for a fixed set or a function to assemble tools dynamically from request context (e.g. scoped by user permissions).
number
Hard cap on the number of model↔tool turns. Defaults to 10. When the cap is reached the stream ends with a final event — the model’s last text (if any) is still returned. Use this to bound cost and latency on open-ended agent tasks.
Tracer
A Tracer instance for trace instrumentation. Each request opens a named trace; model calls and tool executions are recorded as child spans. Traces appear in the /_anvil dashboard.
string | (ctx: Context) => string
The name given to the trace opened for each request. Defaults to "agent <path>". Pass a string for a static name, or a function to derive it from the request context (e.g. to include the user ID).
BudgetConfig
Per-request cost cap. The runtime checks the accumulated cost before each model call and aborts the loop if the budget is exceeded.
Guardrail[]
An ordered array of guardrail policies applied to model output text and tool calls. Use the built-in helpers (contentFilter, toolPolicy, injectionGuard) or implement the Guardrail interface directly.
ContextStep[]
A context assembly pipeline that runs once before the loop starts. Steps can inject retrieved documents as system addenda (RAG), trim the message history to a token budget, or add static context fragments. Built-in steps: retrievalContext, tokenBudget, systemContext.
(ctx: Context) => ModelMessage[] | Promise<ModelMessage[]>
Custom message extractor. By default defineAgent reads body.messages — the standard AI SDK useChat request shape. Override this when your request body has a different structure.

Attaching a client with middleware

Use withLlm when you want to share one LlmClient instance across many routes without importing it into every file:
withLlm is a regular Anvil middleware. Place it in a _middleware.ts file to scope it to a subtree of routes, or at the root to share the client across your entire application.

Dynamic system prompt

Pass an async function to system to build the prompt from the incoming request:

Custom message extraction

Override getMessages when your frontend sends a request body that doesn’t match the AI SDK useChat shape:

Dynamic tools from context

Load tools based on the authenticated user’s permissions: