Agents Middleware API Reference
Purpose and Scope
This page is a reference for the agent middleware type surface used by developers who customize LangChain agent behavior. In the agent model described by the docs, an agent is a model calling tools in a loop until the task is complete, and the surrounding harness is responsible for supplying context, prompts, tools, and behavior controls at the right time. Middleware is the typed extension point for changing that harness without rewriting the agent loop itself. It is where application code can adapt model requests, intercept tool calls, add safety checks, enrich state, or observe lifecycle transitions in a structured way.
Sources: libs/langchain_v1/langchain/agents/middleware/types.py
The reference source for this page is the middleware types module in the LangChain v1 package. That module is important because middleware implementations are not just informal callbacks; they participate in a public contract with named request and response objects, handler callables, state schemas, runtime context, and synchronous plus asynchronous hook variants. When you write a custom middleware class, the practical question is not only which method to override, but also what each method is allowed to receive, return, and pass forward. The types module answers that by defining the vocabulary shared by built-in middleware, custom middleware, and the agent runtime.
Sources: libs/langchain_v1/langchain/agents/middleware/types.py
Relevant Source Files
- libs/langchain_v1/langchain/agents/middleware/types.py — Defines the middleware API contract for LangChain v1 agents, including the middleware base type, request and response objects used around model and tool execution, hook signatures, and type aliases that describe handler behavior.
Core Middleware Concepts
Middleware should be read as harness customization rather than as a replacement for models, tools, or prompts. The official agent docs frame the harness as everything around the model-and-tool loop: the model selection, prompt, available tools, and middleware that shapes behavior. That framing matters because middleware operates at the boundaries between those pieces. A middleware can prepare inputs before a model call, inspect or transform the model result after the call, decide how a tool call is handled, or contribute lifecycle behavior around the broader agent run. It can also be composed with other middleware, so implementations should be narrow, predictable, and explicit about which phase they affect.
Sources: libs/langchain_v1/langchain/agents/middleware/types.py
The central design idea is that a middleware method receives typed context rather than a loose bag of arguments. Model-call middleware works with a model request and returns a model response, usually by invoking a handler that continues the call chain. Tool-call middleware works with a tool-call request and returns the result expected by the agent runtime. Lifecycle hooks work with the agent state and runtime context, allowing middleware to read the conversation and application context or return state updates. This separation keeps model behavior, tool behavior, and lifecycle behavior distinct, which makes middleware easier to combine and test.
Sources: libs/langchain_v1/langchain/agents/middleware/types.py
The API also reflects the fact that production agents run in different execution styles. A local prototype might use synchronous invocation, while a web service, background worker, or streaming application may need asynchronous behavior. The middleware type surface therefore includes sync and async forms for the same conceptual phases. Implementers should prefer matching the execution mode used by the agent application and should avoid blocking operations inside asynchronous hooks. When middleware calls the next handler, it is participating in a chain-of-responsibility pattern: each middleware can act before the handler, after it, or replace the downstream behavior when that is intentional.
Sources: libs/langchain_v1/langchain/agents/middleware/types.py
API Components
The main public component is the middleware base class used to define custom behavior for an agent. It is the stable place to look for overridable methods and their signatures. A custom implementation typically subclasses that base, overrides one or more hooks, and is passed to the agent creation API alongside the model, tools, and prompt configuration. The type module also defines request and response shapes for model calls and tool calls. Those request objects carry the information a middleware needs to make decisions, while the response objects describe what must be returned to keep the agent loop consistent.
Sources: libs/langchain_v1/langchain/agents/middleware/types.py
A useful way to divide the API is by runtime phase. Before-agent and after-agent style hooks are concerned with the whole run and are useful for initializing or finalizing state. Before-model and after-model hooks are concerned with the state that will be sent to a language model and the state update produced after the model responds. Wrap-model-call hooks are more powerful because they receive a request plus a handler; they can choose a different model, adjust parameters, record timing, apply retry logic, enforce policy, or call the handler and then inspect the result. Tool-call wrapping follows the same idea at the tool boundary.
Sources: libs/langchain_v1/langchain/agents/middleware/types.py
The handler types are a key part of the contract. In wrapper hooks, the handler represents the remaining middleware chain and the default runtime operation. Calling it delegates to the next layer; not calling it means the middleware is taking responsibility for producing a valid response. This makes wrappers suitable for advanced controls such as guardrails, human approval, caching, or short-circuiting unsafe actions. It also means wrapper implementations must be careful to preserve the expected return type. Returning an arbitrary object can break the agent loop, while returning the defined response shape allows downstream state handling, observability, and retries to continue working.
Sources: libs/langchain_v1/langchain/agents/middleware/types.py
Hook Reference
| Extension point | Typical use | Contract shape |
|---|---|---|
| Agent lifecycle hooks | Initialize, validate, or finalize agent state for a run | Receive state and runtime context; may return state updates or no update |
| Model preparation hooks | Add context, alter messages, enforce request policy before the model runs | Operate before the model invocation phase using typed agent state and runtime information |
| Model result hooks | Inspect or transform model output after the model returns | Operate after model response handling and may contribute state updates |
| Model wrapper hooks | Surround the model call itself | Receive a model request and a handler, then return a model response |
| Tool wrapper hooks | Surround tool execution | Receive a tool-call request and a handler, then return the tool-call result expected by the agent |
| Async hook variants | Support asynchronous agents and I/O-heavy middleware | Mirror the sync phase while returning awaitable results |
The table is intentionally phase-oriented because developers usually know the behavior they need before they know the exact method name. If the middleware only needs to add information to state, a lifecycle or before-model hook is usually sufficient. If it must measure, retry, route, deny, or replace a model call, a wrapper is the better fit. If it must intervene when the model requests external action, tool-call wrapping is the appropriate boundary. Choosing the narrowest hook helps avoid hidden coupling between unrelated concerns and makes it easier for other middleware in the same agent to remain predictable.
Sources: libs/langchain_v1/langchain/agents/middleware/types.py
Implementation Guidance
When implementing middleware, start by identifying whether the behavior belongs to the model boundary, the tool boundary, or the run lifecycle. A dynamic system instruction, for example, is usually model-bound because it shapes the next model request. A policy that blocks a dangerous external operation is tool-bound because it should evaluate the specific tool call the model requested. A run-level audit stamp or request identifier is lifecycle-bound because it applies to the entire agent execution. This classification keeps middleware cohesive and prevents a single class from becoming an unstructured collection of unrelated cross-cutting logic.
Sources: libs/langchain_v1/langchain/agents/middleware/types.py
State updates should be treated as part of the agent’s public data flow rather than as hidden side effects. Hooks that return updates should return only the fields they intentionally change, leaving the runtime to merge those updates according to the agent state contract. Middleware that mutates state in place can be harder to reason about, especially when multiple middleware instances run in sequence. Similarly, wrapper hooks should preserve the request and response semantics expected by the handler chain. If a middleware modifies a request before passing it onward, that modification should be deliberate and compatible with later middleware and the underlying model or tool implementation.
Sources: libs/langchain_v1/langchain/agents/middleware/types.py
Async implementations should mirror the synchronous behavior as closely as possible. If an implementation provides both forms, the two paths should enforce the same policy, attach the same metadata, and produce equivalent state updates. Divergence between sync and async hooks can create subtle production bugs when an application changes invocation mode. Middleware that performs network calls, external lookups, approvals, or persistence is usually a strong candidate for asynchronous implementation. Middleware that performs simple in-memory transformations can often remain synchronous, but it still needs to respect the typed request and response contract defined by the middleware module.
Sources: libs/langchain_v1/langchain/agents/middleware/types.py
Example Patterns
A dynamic prompt middleware can read runtime context and inject task-specific instructions before the model call. This is useful when the base agent definition is stable but user role, tenant configuration, or session context changes from run to run. The important design constraint is that prompt shaping should happen at the model boundary and should not silently alter tool behavior unless that is part of the middleware’s documented purpose. In practice, this kind of middleware should be small: gather the contextual facts, produce the instruction or message change, and return the resulting state update through the supported hook contract.
Sources: libs/langchain_v1/langchain/agents/middleware/types.py
A safety middleware can wrap tool calls and require approval, validation, or denial before the requested tool executes. This aligns with the official docs description of middleware as a place for runtime safety controls. The wrapper receives the typed tool request, evaluates it, and either delegates to the handler or returns a valid result that represents the intervention. The benefit of placing this logic at the tool boundary is precision: the middleware can inspect the exact tool name, arguments, and runtime context instead of trying to infer intent only from the model’s natural-language output.
Sources: libs/langchain_v1/langchain/agents/middleware/types.py
An observability middleware can wrap model calls, record timing and metadata, call the handler, and then attach or emit information about the response. This pattern should avoid changing semantic behavior unless observability is explicitly coupled with policy enforcement. Because wrappers compose, observability middleware is often most useful when it faithfully delegates and records what happened around the call. The typed model request and response objects make this possible without depending on provider-specific internals. That separation is especially useful in LangChain, where the same agent harness may be used with different chat model providers over time.
Sources: libs/langchain_v1/langchain/agents/middleware/types.py
Relationship to Agents, Integrations, and Deployment
Middleware sits beside the other core agent configuration inputs. The docs describe direct configuration through the model, tools, and system prompt, then point to middleware for advanced capabilities. That distinction is important for maintainability. Use direct configuration when the behavior is static and declarative. Use middleware when the behavior depends on runtime context, needs to intercept execution, or must coordinate cross-cutting concerns such as context engineering, policy, caching, human review, or telemetry. In deployed agent systems, this separation also helps teams review which code changes affect model selection, tool access, or safety boundaries.
Sources: libs/langchain_v1/langchain/agents/middleware/types.py
The middleware integrations docs also frame middleware as an ecosystem surface. Built-in and community middleware can be shared because they depend on a common contract rather than on a single application’s private call graph. That is why the type definitions matter: they are the compatibility layer between an agent runtime and reusable behavior packages. When evaluating a middleware integration, check which hooks it implements, what state it expects, whether it supports asynchronous execution, and how it behaves when combined with other middleware. For deployment, also consider whether it performs external I/O, depends on environment configuration, or changes tool authorization behavior.
Sources: libs/langchain_v1/langchain/agents/middleware/types.py
Practical Checklist
Before adding custom middleware, define the exact runtime phase it should affect, the state fields it reads, the state fields it writes, and whether it must support synchronous, asynchronous, or both execution modes. Prefer wrappers only when you need to surround or replace a model or tool operation; otherwise use narrower lifecycle or before-and-after hooks. Keep return values aligned with the typed contract, call the handler when delegation is intended, and document any short-circuit behavior. After implementation, test the middleware in combination with at least one other middleware so ordering and composition assumptions are visible before production use.
Sources: libs/langchain_v1/langchain/agents/middleware/types.py
Next, read the broader middleware overview for conceptual guidance, then inspect the agent configuration page to see where middleware is supplied during agent construction. If your goal is safety, continue to guardrails and human-in-the-loop workflows. If your goal is runtime visibility, pair this API reference with callbacks and observability. If your goal is reusable packaging, compare your implementation against the shared type contract in the source module and keep provider-specific assumptions outside the generic middleware class whenever possible.