Skip to main content
Agent routes are the agentic core of Anvil JS. Place an agent.ts file inside any folder under server/routes/ and Anvil automatically mounts it as a POST endpoint that streams the Vercel AI SDK data stream protocol. Because the wire format is compatible, you can point useChat directly at an agent route with no extra adapter code.

How an agent route works

An agent.ts file exports a handler built with defineAgent. When a request arrives, the runtime:
  1. Extracts the messages array from the JSON request body (AI SDK useChat shape by default).
  2. Runs the model↔tool loop — calls the model, executes any tools the model requests, feeds results back, and repeats.
  3. Streams each text token, tool call, and tool result back to the client as data stream parts.
  4. Emits a final summary event with token usage and cost once the model produces a response with no tool calls (or the iteration cap is reached).
The runtime enforces several safety properties automatically:
  • Iteration capmaxIterations (default 10) prevents the loop from running forever on a runaway tool-calling sequence.
  • AbortSignal propagation — when the HTTP client disconnects, the cancellation signal flows into every model call and tool execution. Partial work is stopped immediately.
  • Zod schema validation — each tool’s zodSchema is checked before execute runs. Invalid inputs return an error result to the model rather than crashing the handler.
  • Token tracking — input and output tokens are accumulated across every turn and reported in the final event.

File conventions

server/routes/
  chat/
    agent.ts          → POST /chat  (agent route)
  summarize/
    post.ts           → POST /summarize (also works with defineAgent)
Both agent.ts and post.ts work. Use agent.ts as the conventional name for routes whose primary purpose is running an agent loop.

Connecting a frontend

Because Anvil streams the Vercel AI SDK data stream protocol, useChat works out of the box:
import { useChat } from 'ai/react';

export function Chat() {
  const { messages, input, handleInputChange, handleSubmit } = useChat({
    api: '/chat',
    streamProtocol: 'data',
  });

  return (
    <form onSubmit={handleSubmit}>
      {messages.map((m) => (
        <p key={m.id}><strong>{m.role}:</strong> {m.content}</p>
      ))}
      <input value={input} onChange={handleInputChange} />
      <button type="submit">Send</button>
    </form>
  );
}
streamProtocol: 'data' is required. Anvil uses the prefix-coded data stream format, not raw text streaming.

What’s in this section

Define Agent

The defineAgent API — system prompts, tools, context pipelines, budgets, and the withLlm middleware.

LLM Client

LlmClient and its drivers for Anthropic, OpenAI, and Gemini — plus fallback chains and structured output.

Agent Tools

The AgentTool interface — Zod schemas, side-effect fencing, and the maxIterations safeguard.

Orchestration

AgentRegistry, agentAsTool, and withAgents — compose multi-agent systems without HTTP round-trips.