Skip to main content

LangChain agent middleware that validates LLM-generated tool-call arguments against each tool's schema before tool execution / HITL.

Project description

langchain-tool-args-validation-middleware

A LangChain agent middleware that validates LLM-generated tool-call arguments against each tool's schema before the tool runs (and before any human-in-the-loop approval step). When arguments are invalid it appends error ToolMessages and re-invokes the model so it can self-correct — all inside the model node, so only the final valid AIMessage ever enters the graph state.

pip install langchain-tool-args-validation-middleware            # Pydantic tools only
pip install "langchain-tool-args-validation-middleware[jsonschema]"  # + MCP / dict-schema tools

Why

LLMs frequently emit malformed tool calls: missing required fields, wrong types, hallucinated empty values, or extra keys. Without validation those reach the tool node and cause runtime errors or silent corruption — and in human-in-the-loop workflows, a human is asked to approve obviously-broken arguments. Catching this at the model boundary lets the agent fix itself in one extra model call instead of a full agent-loop iteration.

It complements, rather than replaces, ToolRetryMiddleware (retries on tool exceptions) and ModelRetryMiddleware (retries on model exceptions): this one retries on schema violations, before execution.

Trace showing the middleware catching an invalid tool call and prompting the model to self-correct

A trace of create_oos_alert: the model emitted arguments that violate the schema, the middleware rejected them with a precise error and a corrective hint, and the model retried — all inside the model node, before the tool ran.

Usage

from langchain.agents import create_agent
from langchain_tool_args_validation_middleware import ToolArgsValidationMiddleware

agent = create_agent(
    model,
    tools=tools,
    middleware=[ToolArgsValidationMiddleware()],  # resolves schemas from the agent's tools
)

Both validation paths are supported automatically:

  • Pydantic tools (@tool, or any tool with a BaseModel args_schema) → validated with BaseModel.model_validate.
  • MCP / dict-schema tools (args_schema is a raw JSON Schema dict) → validated with jsonschema (soft dependency, Draft7Validator by default).

Unknown tools (no resolvable schema) pass through unvalidated.

Configuration

Parameter Default Description
tools None Explicit tool list. If omitted, schemas are resolved lazily from request.tools and cached by tool-name set (handles dynamic toolsets).
max_retries 2 Validation-retry cycles per model invocation (up to max_retries + 1 model calls).
strip_empty_values True Recursively drop None / {} / [] before validation.
strip_placeholder_strings False Also drop placeholder strings like "null". Off by default — see below.
placeholder_strings conservative set Set used when string stripping is enabled.
json_schema_validator_class None Override the JSON Schema validator class. None → lazy Draft7Validator.
extra_validators None Extra (name, args) -> list[str] checks for domain rules.
on_failure "pass" After retries are exhausted: "pass" (fail open) or "raise".

Design decisions for the two thorniest cases

Batch (partial) failure

Providers (Anthropic, Gemini, OpenAI) require that every tool_call in an assistant message receive a matching ToolMessage before the next turn. So when a multi-call turn has any invalid call, the middleware emits:

  • an error ToolMessage for each invalid call, and
  • a "not executed" notice for each valid sibling call (it hasn't run yet — we're still inside the model node — so it can't have a real result), asking the model to re-issue the whole batch with corrected arguments.

The failed AIMessage is placed before these ToolMessages, and failed turns accumulate across retries so the model sees its repeated mistakes.

strip_empty_values and the write-back contract

LLMs (Gemini especially) emit explicit null/{}/[] for optional fields instead of omitting them, causing needless validation failures. When stripping is on, the cleaned arguments replace the originals on the tool call, so what we validate is exactly what executes — no soundness gap between validation and execution.

The trade-off: stripping a value that is meaningfully empty (e.g. tags: [] meaning "clear all tags", or null meaning "explicitly unset") changes behaviour. Container stripping (None/{}/[]) is on by default because it's usually safe. String-placeholder stripping is opt-in only — tokens like "NA" (Namibia's ISO code) are legitimate values and must never be dropped silently. Enable it deliberately with strip_placeholder_strings=True and a set you control.

Fail-open

After max_retries, the default on_failure="pass" returns the last response unchanged — the (still-invalid) args reach the tool node, where normal tool error handling takes over. This makes the middleware best-effort self-correction, not a hard guarantee. Use on_failure="raise" if you'd rather surface a ToolArgsValidationError.

Extra validators

Plug in domain rules without touching core behaviour. A bundled example flags LangChain internal message IDs (lc_<uuid>) that LLMs sometimes mistake for real data identifiers:

from langchain_tool_args_validation_middleware import detect_langchain_internal_ids

ToolArgsValidationMiddleware(extra_validators=[detect_langchain_internal_ids])

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

File details

Details for the file langchain_tool_args_validation_middleware-0.1.0.tar.gz.

File metadata

  • Download URL: langchain_tool_args_validation_middleware-0.1.0.tar.gz
  • Upload date:
  • Size: 453.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for langchain_tool_args_validation_middleware-0.1.0.tar.gz
Algorithm Hash digest
SHA256 c8df6a6597a30c840e5c09d15aa5d203bee9746c767a4bd59b29b9cbae292fa8
MD5 d74354107dc517427b97d18c38c8466a
BLAKE2b-256 bdef64c9ae52cbacf4dac17e92cf134ac02b342c9b2841cc7272354a92e90e29

See more details on using hashes here.

File details

Details for the file langchain_tool_args_validation_middleware-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: langchain_tool_args_validation_middleware-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 14.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for langchain_tool_args_validation_middleware-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d3b9be8fc49fed4e7fe2279c72346dca6544f8d86bb377321293dfe4c535952f
MD5 d694780f551bc7d1f2f36a829de652c0
BLAKE2b-256 14b88dff4394b8cbe48956fde3d2f69f902dd3609f179c8990e7e9b011b156de

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page