npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

agent-express

v0.3.0

Published

Minimalist middleware framework for building AI agents in TypeScript

Readme

agent-express

Minimalist middleware framework for building AI agents in TypeScript.

npm version CI Coverage License TypeScript

Documentation · Getting Started · API Reference

Why agent-express

Three concepts: Agent, Session, and Middleware. That's the entire framework.

Every backend developer knows use(). Agent Express applies the Express.js middleware pattern to AI agents. One (ctx, next) interface replaces the 15-20 concepts you'll find in alternatives. If you've built an Express app, you already know the mental model.

Quick Start

npm install agent-express
import { Agent, tools } from "agent-express"
import { z } from "zod"

const agent = new Agent({
  model: "anthropic/claude-sonnet-4-6",
  instructions: "You are a helpful assistant.",
})

agent.use(tools.function({
  name: "greet",
  description: "Greet someone by name",
  schema: z.object({ name: z.string() }),
  execute: async ({ name }) => `Hello, ${name}!`,
}))

const { text } = await agent.run({ input: "Greet Alice" })
console.log(text)

Features

  • Middleware architecture -- 5 onion hooks (agent, session, turn, model, tool), one (ctx, next) pattern
  • Built-in guards -- budget caps, input/output validation, timeouts, iteration limits, HITL approval
  • Observability -- structured logging, OpenTelemetry metrics and traces, token tracking, tool recording
  • 12+ model providers -- any AI SDK provider via "provider/model" string (Anthropic, OpenAI, Google, Mistral, Groq, and more)
  • Model routing -- complexity-based model selection across providers
  • Memory management -- context window compaction with 5 strategies
  • Testing toolkit -- TestModel, FunctionModel, capture, record/replay, snapshots
  • MCP integration -- connect to MCP servers as tool sources
  • HTTP adapter -- SSE streaming out of the box
  • CLI -- agent-express dev with hot reload, agent-express test with CI output
  • Structured output -- Zod schema validation on model responses

Middleware Namespaces

Compose capabilities by stacking middleware:

import { Agent, guard, observe, model, memory, dev } from "agent-express"

const agent = new Agent({ model: "anthropic/claude-sonnet-4-6" })

agent
  .use(guard.budget({ limit: 1.00 }))
  .use(guard.approve({ approve: myApprovalFn }))
  .use(observe.usage())
  .use(model.retry())
  .use(memory.compaction({ maxTokens: 8192 }))
  .use(dev.console())

| Namespace | Middleware | Description | |---|---|---| | guard | budget, input, output, maxIterations, timeout, approve, piiRedact, rateLimit | Safety, cost, and compliance | | observe | usage, tools, duration, log, metrics, traces | Monitoring, metrics, and tracing | | search | file, web | Document search (RAG) and web search | | model | retry, router | LLM call management | | memory | compaction, store | Context window and session persistence | | tools | function, mcp | Tool registration | | dev | console | Development utilities |

Presets (separate packages):

| Package | Preset | Description | |---|---|---| | @agent-express/preset-support | supportBot() | Production support bot with RAG, PII, escalation, tone |

Writing Custom Middleware

A plain function passed to .use() becomes a turn hook:

agent.use(async (ctx, next) => {
  console.log(`Turn ${ctx.turnIndex}: ${ctx.input[0]?.content}`)
  await next()
  console.log(`Response: ${ctx.output}`)
})

For multiple hooks, return a Middleware object:

import type { Middleware } from "agent-express"

const analytics: Middleware = {
  name: "analytics",
  state: {
    "analytics:turns": { default: 0 },
    "analytics:cost": { default: 0, reducer: (prev, delta) => prev + delta },
  },
  turn: async (ctx, next) => {
    ctx.state["analytics:turns"] = ctx.turnIndex + 1
    await next()
  },
  model: async (ctx, next) => {
    const response = await next()
    ctx.state["analytics:cost"] = response.usage.inputTokens * 0.000003
    return response
  },
}

agent.use(analytics)

The 5 hooks form an onion — code before next() runs on the way in, code after runs on the way out:

agent → session → turn → model → [LLM call]
                       → tool  → [tool execution]

See built-in middleware for real-world examples: guard.budget, observe.usage, model.retry, memory.compaction

Sessions and Streaming

Multi-turn conversations with session state:

await agent.init()
const session = agent.session()

const r1 = await session.run({ input: "My name is Alice" })
const r2 = await session.run({ input: "What's my name?" })
// r2.text → "Your name is Alice"

await agent.dispose()

Streaming events as they happen:

for await (const event of agent.run({ input: "Hello" })) {
  if (event.type === "model:chunk") process.stdout.write(event.text)
  if (event.type === "tool:start") console.log(`Calling ${event.tool}...`)
}

Testing

Mock LLM calls in tests with agent-express/test:

import { TestModel, testAgent } from "agent-express/test"

const model = new TestModel([
  { text: "Hello! How can I help?" },
])

const result = await testAgent(agent, {
  model,
  input: "Hi",
})
expect(result.text).toContain("Hello")

Record and replay real API calls:

import { RecordModel, ReplayModel } from "agent-express/test"

// Record once (hits real API)
const record = new RecordModel("anthropic/claude-sonnet-4-6")
await agent.run({ input: "test", model: record })
record.save("tests/cassettes/greeting.json")

// Replay forever (no API calls)
const replay = ReplayModel.load("tests/cassettes/greeting.json")
await agent.run({ input: "test", model: replay })

Comparison

| 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 | | TypeScript | Strict, ESM only | TypeScript | TypeScript | TypeScript |

Package Entry Points

agent-express       -- Agent, Session, middleware namespaces, errors
agent-express/http  -- createHandler() SSE adapter
agent-express/test  -- TestModel, FunctionModel, testAgent()

CLI

npx create-agent-express                             # interactive wizard
npx create-agent-express --template support-bot      # template scaffold
npx agent-express dev [entry]                       # terminal chat + hot reload
npx agent-express test                              # agent test runner
npx agent-express test --ci                         # JUnit XML output for CI

Links