agent-express (ctx, next) pattern for AI agents
Fast and minimalist agentic AI framework in TypeScript.
Only three concepts: Agent, Session, Middleware.
The middleware pattern from Express, Koa, and Hono — applied to building AI agents.
Everything beyond the core agent loop — retries, cost tracking, memory, tools, guardrails and other — is middleware.
const { text } = await new Agent({
model: "anthropic/claude-sonnet-4-6",
instructions: "You are a helpful assistant.",
})
.use(observe.usage())
.run("What is the meaning of life?").result const agent = new Agent({
model: "anthropic/claude-sonnet-4-6",
instructions: "You are a weather assistant.",
})
agent.use(tools.function({
name: "get_weather",
description: "Get weather for a city",
schema: z.object({ city: z.string() }),
execute: async ({ city }) => `Weather in ${city}: 72°F`,
}))
const { text } = await agent.run("Weather in Tokyo?").result const { text, state } = await new Agent({
model: "anthropic/claude-sonnet-4-6",
instructions: "You are a helpful assistant.",
})
.use(observe.usage()) // token tracking
.use(guard.budget({ limit: 1 })) // $1 cost cap
.use(model.retry({ maxRetries: 3 })) // auto-retry
.run("What is the meaning of life?").result
console.log(state["observe:usage"]) // { inputTokens, outputTokens } Every feature above is a (ctx, next) middleware. Same pattern, 6 namespaces.
Or build your own — same interface, infinite possibilities.
agent.use(guard.budget({ limit: 5 })) // $5 USD cost cap
agent.use(guard.approve({ tool: "email" })) // require approval
agent.use(guard.timeout({ turn: 30_000 })) // 30s timeout agent.use(observe.usage()) // token tracking → state['observe:usage']
agent.use(observe.tools()) // tool call recording
agent.use(observe.duration()) // turn timing
agent.use(observe.log()) // structured JSON logging agent.use(model.retry({ maxRetries: 3 })) // exponential backoff
agent.use(model.router({ // complexity routing
tiers: [{ model: "anthropic/claude-haiku-4-5-20251001", maxTokens: 100 }]
})) agent.use(memory.compaction({
strategy: "sliding-window",
maxTokens: 8000,
})) agent.use(tools.function({
name: "get_weather",
description: "Get weather for a city",
parameters: z.object({ city: z.string() }),
execute: async ({ city }) => `Weather in ${city}: 72°F`,
})) agent.use(dev.console()) // full lifecycle terminal trace Fewer concepts, more built-in capabilities
| Feature | agent-express | Mastra | Vercel AI SDK | LangChain.js |
|---|---|---|---|---|
| Core concepts | 3 | 15-20 | 5-8 | 30+ |
| Extension model | Middleware (ctx, next) | Processors, Tools, Workflows | Hooks, Providers | Chains, Agents, Tools, Memory |
| Built-in testing | Yes | No | No | No |
| Cost control | guard.budget() | Manual | Manual | Manual |
| Human-in-the-loop | guard.approve() | Manual | No | Manual |
| Sessions | First-class Session | Manual state | No | Memory modules |
| Memory management | memory.compaction() | Plugin-based | No | Memory modules |
| Observability | observe.* namespace | External tools | Manual | Callbacks |
| Streaming | SSE via createHandler() | Built-in | First-class (streamText) | Callbacks |
| TypeScript | Strict, ESM only | TypeScript | TypeScript | TypeScript |
Minimal agent starter with one tool
Coding assistant with file tools and approval gates
Research agent with model routing and output guards
Production support bot with budget, approval, and memory
"provider/model" string format, e.g., "anthropic/claude-sonnet-4-6" or "openai/gpt-4o".createHandler(agent) from agent-express/http returns a Web-standard Request/Response handler. Mount it on any route. See the HTTP & Framework Integration guide.agent-express/test. Tests run without real API calls, zero cost, zero latency. See the Testing guide.(ctx, next) from Express, Koa, and Hono. Middleware composition replaces dedicated APIs for memory, guards, tools, tracing. Graph-based topology (LangGraph) is more powerful for complex state machines, but most agents are linear loops with cross-cutting concerns. Middleware is the simpler abstraction for the common case.