Validate Tool Results Before Model Context
Validate every tool result against a declared success-or-failure schema before it reaches model context. Reject malformed and oversized values as typed failures, never success-like prose. Apply truncation only after the original result passes validation, then shape the validated data to the context budget and record the boundary decision.
Prerequisites
You need control of the boundary between the tool transport and the code that assembles model context. At that boundary, keep the raw result outside the conversation until it has been classified, parsed, and validated. Define separate budgets for what the client will receive and what the model will see; they solve different problems.
You also need a declared result contract for each tool. If a tool can return structured data, use a schema rather than treating its response as arbitrary text. The contract must distinguish successful data from failure data and set limits appropriate to that tool. There is no supplied universal byte, item, or character limit, so choose those values from the tool’s legitimate output and the downstream context budget rather than copying an arbitrary threshold.
Steps
-
Define the success contract and the failure contract
Start with the complete value the tool is allowed to return. Specify required fields, value types, allowed variants, unknown-property behavior, and bounds on strings, arrays, and objects. The JSON Schema Draft 2020-12 validation specification, published 16 June 2022 defines structural assertions including
type,required,maxLength,maxItems, andmaxProperties.Do not describe only the successful payload. Wrap the result in a discriminated outcome or keep an equivalent distinction in the transport protocol:
type ToolOutcome<T> = | { ok: true; data: T } | { ok: false error: { code: "timeout" | "result_too_large" | "invalid_json" | "schema_mismatch" retryable: boolean } }The codes are an example, not a universal taxonomy. Use codes that callers can act on. The important condition is that success and failure cannot validate as the same variant. This is the same design decision covered when choosing structured output instead of free text, but it applies at the tool boundary rather than to the agent’s final answer.
JSON Schema validates structure, not every business meaning. If a field must name an existing resource, fall within an authorization boundary, or agree with another system, add an application-level check. The specification notes that structural validation can be insufficient for semantic use and that
formatis annotation-only unless assertion behavior is enabled. -
Place the gate between the transport and context assembly
Receive the tool response into a temporary result object that the prompt builder cannot read directly. The pipeline should be one-way:
tool transport → size gate → parse → schema validation → typed outcome → context projectionThe distinction between the two validation directions matters. Input validation protects the tool from malformed or unacceptable arguments. Output validation protects the agent from malformed or unexpectedly large results returned by the tool or an upstream dependency.
The Model Context Protocol tools specification dated 25 November 2025 makes both sides explicit: servers must validate tool inputs, clients should validate tool results before passing them to a model, and tools may publish an
outputSchemafor structured results. When an MCP server supplies that schema, compile and enforce it in the client. When it does not, define a client-owned schema or keep the result out of model context. Accepting unstructured text without a contract is appropriate only when the model is deliberately meant to read untrusted prose and the client still enforces a receive limit. -
Reject transport and execution failures as typed failures
Classify the transport result before inspecting it as successful data. A timeout, non-success response, decoding failure, or tool execution error must select the failure branch. A tool error should be represented as a typed failure rather than prose that the model can mistake for successful data.
For an MCP server,
isError: trueis the protocol-level discriminator for a tool execution error. Preserve that distinction when converting the result into the client’s internal type. If the server includes a human-readable explanation, keep it as detail inside the failure value; do not make that sentence the only indication that the call failed.This costs some adapter code because every tool transport must map its failure modes into the shared outcome type. The alternative leaves each prompt template to infer whether text such as “no records found,” “permission denied,” or a vendor error page is data or failure. A typed
codeandretryablefield let the runtime decide whether to stop, ask for different arguments, or apply a fallback policy for a failed agent run without asking the model to classify opaque prose. -
Validate the complete original value before truncating it
Parse the complete received value, then validate that value against the success schema. Validation must happen before truncation so a truncated invalid value is not accidentally made to fit the schema.
The order changes the result. Suppose a tool returns more array items than its contract permits. Slicing the array first could turn a schema violation into an apparently valid success. Cutting a string before validation could remove the characters that made the original value unacceptable. In both cases, the agent would receive a value marked as valid even though the tool never produced that valid value.
Treat parse failure, schema mismatch, and a breached receive limit as failures. Do not repair missing fields, coerce types, discard unknown properties, or shorten values merely to make validation pass. Normalization is acceptable only when it is part of the declared contract and occurs consistently before the validator evaluates the intended canonical value. Otherwise, return a typed failure and retain the validation path for diagnosis.
-
Enforce size at both boundaries without silently clipping
Apply a hard receive ceiling before allocating or parsing an unbounded response. If the transport crosses that ceiling, stop reading or reject the result as
result_too_large; do not truncate it into a candidate success. Then use schema limits such asmaxItems,maxLength, andmaxPropertiesto enforce the tool-specific contract on the complete parsed value.These controls address different conditions. The receive ceiling protects the client while handling bytes from the tool. Schema limits reject a structurally valid but unexpectedly large result. The later model-context budget controls how much of an already valid result is useful for the next model call.
A low limit can reject legitimate exports or searches, while a high limit can consume memory and leave little usable model context. Set the receive ceiling above the largest legitimate result the tool contract allows, then test it with representative responses. If the legitimate result is too large for one model call, change the tool to paginate or return a narrower selection. Context clipping is not a substitute for a bounded tool contract.
-
Create a model-facing projection only from validated success data
After the original success value passes validation, select the fields and records needed for the next step. This is where truncation, summarization, pagination, or omission may occur. Keep that projection separate from the validated source result so logs and retries do not confuse the reduced context view with the tool’s actual response.
If the projection is incomplete, represent that condition in metadata the runtime and model can see, such as
complete: falseplus a continuation token supplied by the tool. Do not end a string mid-value or present a partial collection as complete. The right projection depends on the next task: a lookup may need one exact record, while analysis may need a page plus continuation state. The broader context-window management policy should decide what enters the prompt after this boundary has established what is valid.The cost is an extra model-facing type or serializer. That separation is the wrong answer only when the validated result is already small, entirely relevant, and accepted directly by the model interface; even then, keep the validation gate rather than bypassing it.
-
Test the boundary and record its decision
Add cases for a valid success, a typed tool failure, invalid JSON, a missing required field, a wrong type, an unknown variant, a schema-limit breach, and a transport-limit breach. Add one case proving that an oversized or otherwise invalid value fails before any context projection can shorten it. Assert that raw invalid data never appears in the constructed model messages.
Record the tool name, schema version, outcome code, validation location, and result size with the run’s correlation identifier. This makes the boundary decision reconstructable without requiring failure prose to survive prompt assembly.
The archived 2023 OWASP LLM Top 10 v1.1 warns that neglected output validation can enable downstream exploitation and that overreliance can compromise decisions. That page discusses model output rather than tool output; applying the same untrusted-output boundary before tool data enters model context is an engineering inference. The test is concrete: malformed tool data is rejected by code, not left for the model to notice.
Expected result
Done means every tool call produces exactly one validated outcome: a complete success value conforming to its declared schema, or a typed failure. Invalid and over-limit raw results never enter model context. Any reduction happens only to validated success data, and an incomplete projection is marked as incomplete rather than presented as the original result.
Sources
- JSON Schema Draft 2020-12 validation specification, published 16 June 2022json-schema.org
- Model Context Protocol tools specification dated 25 November 2025modelcontextprotocol.io
- archived 2023 OWASP LLM Top 10 v1.1owasp.org
See also
What a vendor skill pack is, how Cloudinary's installs and lets a team select skills, and why vendor-side versioning changes assistant behaviour without review.
Build reproducible prompt artifacts, log prompt and model versions, evaluate changes, canary releases, and roll back without redeploying.
Authenticate, deduplicate, persist, and queue webhook events before an agent runs, keeping sender responses fast and retries under your control.
A five-step procedure for handling a secret an agent was just issued: local env file, verified ignore rule, no client bundle, no reliance on redaction.