← Back to Blog

check_call: validation with execution removed from the universe

·4 min read

check_call exists so an agent can ask would this call be valid without risking what a valid call might do. Your procedures write rows, charge cards, send email. Validation must therefore be structurally incapable of invoking them, and structurally is the operative word: no flag, no best effort, no please-do-not.

Extraction, not duplication

Procedure.execute always had two phases before the call: bind arguments against the signature, then run each value through its pre-built pydantic TypeAdapter. Those phases are exactly what validation needs, so they became a method. validate_args binds and validates and returns; execute now calls validate_args and then invokes the function. One code path, two consumers, zero drift. If validation ever grows richer, both the hot RPC path and the MCP inherit it simultaneously.

Proving the negative

A claim like never executes deserves a test that would fail loudly if it were false. The suite registers a procedure whose body writes a sentinel file, calls check_call against it with valid arguments, asserts the answer is valid, and then asserts the sentinel does not exist. If anyone ever wires execution into validation, this test fails with a sentence that explains itself.

# check_call must never execute procedures
assert result.structured_content["valid"] is True
assert not sentinel.exists(), "check_call must never execute procedures"

Errors shaped for correction

When validation fails, the result is structured: valid false, plus per-parameter entries carrying the field and the pydantic message. An agent that passed a string where an integer belongs reads the exact parameter and constraint, corrects one line, and moves on. That loop, verify, fail specific, fix, re-verify, is the entire productivity argument for the tool, and it only works because errors name parameters instead of gesturing at payloads.