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.
map, tap, filter, finalize, catchError, switchMap, timer, of, etc.) are imported.
Types
MiddlewareFunction
A function that transforms the event stream.Middleware
Abstract base class for creating middleware.runNext()runsnext.run(...)and normalizes chunk events into completeTEXT_MESSAGE_*/TOOL_CALL_*sequences.runNextWithState()does the same and also provides accumulatedmessagesandstateafter 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 theMiddleware class.
Basic Implementation
Configuration-Based Middleware
Accumulator Helpers (Class Middleware)
Class middleware can use helper methods fromMiddleware 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
disallowedToolCalls instead of allowedToolCalls.
Middleware Patterns
Timing Middleware
Rate Limiting
Chaining Middleware
Multiple middleware can be combined to create sophisticated processing pipelines.Advanced Usage
Conditional Middleware
Lifecycle Notes
Middleware added withagent.use(...) runs in runAgent() (and the legacy bridge path). connectAgent() currently calls connect() directly and does not run middleware.
Best Practices
- Single Responsibility: Each middleware should focus on one concern
- Error Handling: Always handle errors gracefully and consider recovery strategies
- Performance: Be mindful of processing overhead in high-throughput scenarios
- State Management: Use class-based middleware when state is required
- Testing: Write unit tests for each middleware independently
- Documentation: Document middleware behavior and side effects