Skip to main content

Middleware

The middleware system in @ag-ui/client provides a powerful way to transform, filter, and augment event streams flowing through agents. Middleware can intercept and modify events, add logging, implement authentication, filter tool calls, and more.
Examples below assume the relevant RxJS operators/utilities (map, tap, filter, finalize, catchError, switchMap, timer, of, etc.) are imported.

Types

MiddlewareFunction

A function that transforms the event stream.
Function middleware receives events exactly as emitted by the next middleware or agent.

Middleware

Abstract base class for creating middleware.
  • runNext() runs next.run(...) and normalizes chunk events into complete TEXT_MESSAGE_* / TOOL_CALL_* sequences.
  • runNextWithState() does the same and also provides accumulated messages and state after each event is applied.

Function-Based Middleware

The simplest way to create middleware is with a function. Function middleware is ideal for stateless transformations.

Basic Example

Transforming Events

Error Handling

Class-Based Middleware

For stateful operations or complex logic, extend the Middleware class.

Basic Implementation

Configuration-Based Middleware

Accumulator Helpers (Class Middleware)

Class middleware can use helper methods from Middleware to work with normalized events and accumulated state.

runNext()

runNext() forwards execution and normalizes chunk events into full TEXT_MESSAGE_* and TOOL_CALL_* events.

runNextWithState()

runNextWithState() returns { event, messages, state }, where messages and state are the accumulated values after each event has been applied.

Built-in Middleware

FilterToolCallsMiddleware

Filters tool calls based on allowed or disallowed lists. FilterToolCallsMiddleware filters emitted TOOL_CALL_* events (including args/results for blocked calls). It does not prevent tool execution in the upstream model/runtime.

Configuration

Allow Specific Tools

Block Specific Tools

You must specify exactly one of allowedToolCalls or disallowedToolCalls. The constructor throws if both or neither are provided.

Middleware Patterns

Timing Middleware

Rate Limiting

Other common patterns include retry logic and response caching.

Chaining Middleware

Multiple middleware can be combined to create sophisticated processing pipelines.

Advanced Usage

Conditional Middleware

Lifecycle Notes

Middleware added with agent.use(...) runs in runAgent() (and the legacy bridge path). connectAgent() currently calls connect() directly and does not run middleware.

Best Practices

  1. Single Responsibility: Each middleware should focus on one concern
  2. Error Handling: Always handle errors gracefully and consider recovery strategies
  3. Performance: Be mindful of processing overhead in high-throughput scenarios
  4. State Management: Use class-based middleware when state is required
  5. Testing: Write unit tests for each middleware independently
  6. Documentation: Document middleware behavior and side effects

TypeScript Support

The middleware system is fully typed for excellent IDE support: