Contract-test the agent-tool boundary before it drifts

Your agent and your tools agree on a shape only by convention. Put that agreement in a contract a test can check, and a breaking API change fails in CI instead of in production.

B

Balagei G Nagarajan

5 MIN READ


Tested as agent tools, real REST APIs fail across a whole taxonomy of error modes.A framework that generated over 2,400 test cases found schema mismatches, input misinterpretation, and output-handling failures are recurring, not rare (A Framework for Testing and Adapting REST APIs as LLM Tools, arXiv:2504.15546, 2025). A tool call that returns 200 isn't proof the contract held.

Two precisely interlocking glowing connectors, one from an agent and one from a tool, with a verification seal at the joint that lights green when the shapes match and red when they diverge
Runtime validation guards the call; contract tests guard the release.
— from “Contract-test the agent-tool boundary before it drifts”

Key facts.

  • Real REST APIs exposed as agent tools fail in patterned ways: a framework that generated over 2,400 test cases across domains produced a taxonomy of errors including input misinterpretation, output failures, and schema mismatches (A Framework for Testing and Adapting REST APIs as LLM Tools, arXiv:2504.15546, 2025).
  • Consumer-driven contract testing, the agent publishes the shape it depends on and the provider verifies it in CI, catches a breaking change in the provider's build instead of in your agent (consumer-driven contract testing, e.g. Pact, established practice).
  • Breaking-change detection at the boundary, an OpenAPI or schema diff run on every pull request, turns a silent provider change into a failed check before release (API change-management practice).

Why isn't a passing call proof the contract holds?

Because a call can succeed at the transport level while violating the shape the agent expects. The model emits arguments it believes are right, the tool returns a 200, and the agent proceeds, but a required field may be missing, a type may have changed, or an error may be hiding in a success body. Nothing checked the actual structure against an agreed contract. The taxonomy from testing real APIs as tools names this directly: input misinterpretation, output failures, and schema mismatches are recurring modes, not rare ones (arXiv:2504.15546). A passing call tells you the wire worked. It doesn't tell you the data matched what the agent was built to handle.

What does contract testing add over a schema check?

It catches the breaking change before deploy, not after. A runtime JSON Schema check validates a single call as it happens, which is essential, but it only fires once production traffic hits the drift. Consumer-driven contract testing flips that: the agent, as the consumer, publishes the exact shape it depends on, and the tool provider verifies that contract in its own CI, so a field rename or a removed property fails the provider's build before it reaches you. Standardizing the boundary as enforceable schemas makes the contract a real artifact both sides hold rather than an assumption. Runtime validation guards the call; contract tests guard the release.

# Validate every tool response against the agreed contract, at the boundary
from jsonschema import validate
INVOICE = {"type":"object",
 "required":["id","status","currency"], # status + currency are NOT optional
 "properties":{"status":{"enum":["paid","open","declined"]}}}
validate(tool_response, INVOICE) # raises on drift -> caught here, not three steps later

How do you put contracts at every boundary?

Make the shape explicit and check it on both clocks. Define each tool's inputs and outputs as a JSON Schema, validate every call and response against it at the wrapper before the model sees a result or the tool runs, and reject drift loudly instead of letting it through. Publish consumer-driven contracts, with a framework like Pact or bidirectional contract testing, so providers verify the agent's expectations in CI and a breaking change fails their pipeline. Keep a schema registry, version your tools, and run breaking-change detection, an OpenAPI diff plus contract tests, on every pull request. Adopt an enforceable schema standard so the contract is enforced, not documented. The agent then runs against a contract that something actually checks.

A CI pipeline where a tool provider's change runs against a published consumer contract; a matching change passes green, while a removed required field trips a red contract-test failure before the deploy stage

Where the boundary breaks and how a contract holds it

Boundary failureWithout a contractWith contract testing
Provider renames a fieldAgent misparses silentlyContract test fails in provider CI
Required field removedDownstream null/garbageSchema validation rejects at the boundary
Type changedCoercion error laterRuntime validation catches the call
Error in a 200 bodyTreated as successError shape is part of the schema
New required inputCalls start failingBreaking-change detection in PR

The pattern is that an agent and its tools agree on a shape only by convention, so a quiet provider change becomes a silent agent failure. Make the agreement an explicit schema, validate it at every call, and verify consumer contracts in CI, and drift fails a build instead of a customer. Treating the agent-tool boundary as a checked contract rather than a shared assumption is reliability at the pattern level, which is what VibeModel builds as the Pattern Intelligence Layer.

Frequently asked questions

Is runtime validation enough on its own?
it's necessary but reactive, it catches drift only once production traffic hits it. Pair it with consumer-driven contract tests so the breaking change fails in the provider's CI before it ever deploys.

Does adopting MCP give me this for free?
It standardizes the boundary as enforceable schemas, which is most of the value, but you still want consumer contracts and breaking-change detection in CI to catch provider changes ahead of release.

what's the cheapest first step?
A JSON Schema validator on every tool response at the wrapper. It converts silent shape drift into a loud, catchable error at the exact boundary where it happens.


Share this post

Join the discussion

Have a take, a war story, or a question? Sign in with GitHub to comment and react. Comments are powered by GitHub Discussions, ad-free and yours to moderate.

Continue Reading

Find where your agent breaks, before you build it

Faultmap maps where your agent will fail from the goal and your data, then hands you the first test suite it has to pass.