
Langchain Middleware
Wire human approval gates and custom hooks into LangChain/LangGraph agents before risky tools run in production.
Overview
Langchain-middleware is an agent skill most often used in Build (also Ship) that implements HumanInTheLoopMiddleware, custom LangChain middleware hooks, and structured-output patterns for production LangChain agents.
Install
npx skills add https://github.com/langchain-ai/langchain-skills --skill langchain-middlewareWhat is this skill?
- HumanInTheLoopMiddleware with interrupt_on and allowed_decisions (approve, edit, reject)
- Requires MemorySaver (or checkpointer) plus thread_id for every HITL workflow
- Custom middleware hooks for logging, retries, and error handling on tool calls
- Command resume patterns to continue the graph after human decisions
- Structured output guidance with Pydantic (Python) and Zod (JS) on agent pipelines
- HumanInTheLoopMiddleware supports allowed_decisions including approve, edit, and reject on gated tools
- Checkpointer plus thread_id configuration is required for all documented HITL workflows
Adoption & trust: 7.5k installs on skills.sh; 782 GitHub stars; 3/3 security scanners passed (skills.sh audits); trending (+100% hot-view momentum).
What problem does it solve?
Your LangChain agent can call risky tools automatically with no pause, audit trail, or safe resume path after a human says no.
Who is it for?
Indie builders running LangChain agents with real side-effect tools (email, payments, deletes) who need explicit human gates without rewriting the whole graph.
Skip if: Teams not using LangChain/LangGraph, one-shot LLM calls with no tools, or flows where every tool is read-only and policy already allows full autonomy.
When should I use this skill?
Invoke when you need human-in-the-loop approval, custom middleware, or structured output for LangChain agents.
What do I get? / Deliverables
You get interrupt-gated tool execution, checkpointer-backed threads, and Command-based resume so approved work continues and rejected calls never silently retry.
- Agent setup with HumanInTheLoopMiddleware and interrupt_on map
- Custom middleware module for hooks and resume handling
- Structured output schema wired via Pydantic or Zod
Recommended Skills
Journey fit
Spans multiple journey phases - primary shelf plus alternate fits below.
Middleware is where production agent behavior is defined—tool interception belongs in the build phase when assembling LangChain agents. Agent-tooling is the canonical shelf because the skill centers on LangChain `create_agent`, middleware stacks, checkpointers, and HITL—not generic app CRUD.
Where it fits
Wrap send_email and similar tools with HumanInTheLoopMiddleware before exposing the agent to beta users.
Define interrupt_on policies and allowed_decisions so destructive tools cannot run without explicit approval.
Add custom middleware for retries and logging after incidents on flaky external tool APIs.
How it compares
Use for LangChain-native middleware and HITL—not as a substitute for generic API rate limiting or non-agent Express middleware.
Common Questions / FAQ
Who is langchain-middleware for?
Solo and small teams building LangChain or LangGraph agents who need human-in-the-loop approval, custom interceptors, or structured outputs before shipping agent automation.
When should I use langchain-middleware?
During Build when wiring agent tooling; during Ship when hardening security around dangerous tools; whenever you need HITL, custom middleware hooks, or Pydantic/Zod structured output on LangChain agents.
Is langchain-middleware safe to install?
Review the Security Audits panel on this Prism page and inspect the skill package before granting your agent network or shell access in production HITL flows.
SKILL.md
READMESKILL.md - Langchain Middleware
<overview> Middleware patterns for production LangChain agents: - **HumanInTheLoopMiddleware** / **humanInTheLoopMiddleware**: Pause before dangerous tool calls for human approval - **Custom middleware**: Intercept tool calls for error handling, logging, retry logic - **Command resume**: Continue execution after human decisions (approve, edit, reject) **Requirements:** Checkpointer + thread_id config for all HITL workflows. </overview> --- ## Human-in-the-Loop <ex-basic-hitl-setup> <python> Set up an agent with HITL middleware that pauses before sending emails for approval. ```python from langchain.agents import create_agent from langchain.agents.middleware import HumanInTheLoopMiddleware from langgraph.checkpoint.memory import MemorySaver from langchain.tools import tool @tool def send_email(to: str, subject: str, body: str) -> str: """Send an email.""" return f"Email sent to {to}" agent = create_agent( model="gpt-4.1", tools=[send_email], checkpointer=MemorySaver(), # Required for HITL middleware=[ HumanInTheLoopMiddleware( interrupt_on={ "send_email": {"allowed_decisions": ["approve", "edit", "reject"]}, } ) ], ) ``` </python> <typescript> Set up an agent with HITL that pauses before sending emails for human approval. ```typescript import { createAgent, humanInTheLoopMiddleware } from "langchain"; import { MemorySaver } from "@langchain/langgraph"; import { tool } from "@langchain/core/tools"; import { z } from "zod"; const sendEmail = tool( async ({ to, subject, body }) => `Email sent to ${to}`, { name: "send_email", description: "Send an email", schema: z.object({ to: z.string(), subject: z.string(), body: z.string() }), } ); const agent = createAgent({ model: "anthropic:claude-sonnet-4-5", tools: [sendEmail], checkpointer: new MemorySaver(), middleware: [ humanInTheLoopMiddleware({ interruptOn: { send_email: { allowedDecisions: ["approve", "edit", "reject"] } }, }), ], }); ``` </typescript> </ex-basic-hitl-setup> <ex-running-with-interrupts> <python> Run the agent, detect an interrupt, then resume execution after human approval. ```python from langgraph.types import Command config = {"configurable": {"thread_id": "session-1"}} # Step 1: Agent runs until it needs to call tool result1 = agent.invoke({ "messages": [{"role": "user", "content": "Send email to john@example.com"}] }, config=config) # Check for interrupt if "__interrupt__" in result1: print(f"Waiting for approval: {result1['__interrupt__']}") # Step 2: Human approves result2 = agent.invoke( Command(resume={"decisions": [{"type": "approve"}]}), config=config ) ``` </python> <typescript> Run the agent, detect an interrupt, then resume execution after human approval. ```typescript import { Command } from "@langchain/langgraph"; const config = { configurable: { thread_id: "session-1" } }; // Step 1: Agent runs until it needs to call tool const result1 = await agent.invoke({ messages: [{ role: "user", content: "Send email to john@example.com" }] }, config); // Check for interrupt if (result1.__interrupt__) { console.log(`Waiting for approval: ${result1.__interrupt__}`); } // Step 2: Human approves const result2 = await agent.invoke( new Command({ resume: { decisions: [{ type: "approve" }] } }), config ); ``` </typescript> </ex-running-with-interrupts> <ex-editing-tool-arguments> <python> Edit the tool arguments before approving when the original values need correction. ```python # Human edits the arguments — edited_action must include name + args result2 = agent.invoke