Development Choices

Versioning Tool Schemas for AI Agents

Author
Drew YoungwerthSoftware Engineer
Published
Section
AI Agents
Length
7 min read3 sources cited

Version tool schemas as model-facing contracts, not backend implementation details. Requiring a new argument breaks old calls; renaming a tool can alter both dispatch and model selection. Deploy incompatible schemas in parallel under distinct names or explicit versions, and retain per-version traces until the old contract is retired.

Tool schemas are agent-facing APIs

Matrix of compatible and breaking changes to agent tool schemas
Model-facing names and descriptions are contract surface too.

Versioning tool schemas used by AI agents means treating every tool definition as a released interface between the model, the tool-calling runtime, and the underlying function. The version boundary includes the tool name, description, accepted arguments, required fields, constraints, and any declared output structure.

The schema is not merely documentation for backend code. The OpenAI tools guide, checked 2026-08-26, shows function tools presented to a model with a name, description, parameter schema, and strictness setting; the model can decide whether to use a configured tool. Those fields therefore influence both the call the runtime can accept and the call the model attempts to produce.

The MCP tools specification dated 2025-11-25 makes the boundary explicit: a tool has a unique name and an inputSchema, clients discover those definitions through tools/list, and calls identify the tool by name and supply an arguments object. MCP tool schemas default to JSON Schema 2020-12 when $schema is absent.

That dialect identifier is separate from the release version of the tool. The JSON Schema 2020-12 core specification published 2022-06-16 defines $schema as a way to identify the meta-schema and vocabulary used to process a schema. It does not identify whether an application is exposing release 1 or release 2 of search_documents. Record the application-level version separately in the tool name, registry, deployment configuration, or trace metadata.

A new required argument is a breaking change

Changing a tool argument from absent or optional to required is a breaking change even when the underlying function remains backward compatible. Compatibility must be judged at the model-facing boundary: can a call that was valid under the released schema still pass the new schema and reach the function?

Suppose the released tool accepts archive_asset(asset_id). A new implementation may accept archive_asset(asset_id, reason) while continuing to supply a default reason when the second argument is missing. The function is backward compatible. If the new schema places reason in required, however, a call produced against the old definition no longer satisfies the new contract. A validator can reject it before the function has an opportunity to apply its default.

The same issue applies to previously recorded calls used for evaluation, retries, or reconstruction. Replaying an old arguments object against only the new schema can produce a validation failure that did not occur in the original run. Preserve the schema version with the run when implementing agent run replay and debugging, or the replay is testing a different contract.

Other schema edits should be classified by the same rule rather than by whether the implementation happens to tolerate them. Narrowing a property type, removing an accepted enum value, renaming a property, or moving it into a different object can invalidate arguments accepted by the earlier release. Adding an optional property does not invalidate an old arguments object by itself, but changing its description or default handling can still change what the model emits. Validation compatibility and model behavior are separate checks.

A compatibility adapter belongs behind the old schema. Keep the old definition valid, accept its old argument shape, and translate that shape for the new implementation. Do not advertise a required field and then rely on an undocumented runtime default: the advertised contract says the field must be present, while the implementation behaves as though it were optional.

The tool name is part of the behavioral contract

A tool name is part of the model-facing contract, so renaming it can change selection behavior as well as code compatibility. At the protocol level, the name identifies the target of the call. A runtime that routes get_customer will not automatically recognize find_customer unless an alias or adapter has been added.

At the model level, a rename is not a transparent alias. The model receives the tool definition and chooses tools from the supplied context. Replacing get_customer with find_customer_record changes that context even if both names dispatch to the same function with the same arguments. The size and direction of any selection change are unverified until measured against the prompts and model versions used in the application.

Treat a rename as both a tool release and a behavioral change. Evaluate whether the model still selects the tool for the same requests, whether it selects it when it should not, and whether existing instructions mention the old name. Coordinate that work with versioning prompts used by AI agents; a prompt pinned to the old name and a runtime exposing only the new name are incompatible releases.

Server-side aliasing can preserve code compatibility for calls that still contain the old name. It does not prove equivalent model behavior when the advertised definition changes. Conversely, advertising both names to the same model preserves discoverability but creates two visible choices for one capability. If both are exposed together, selection by name must be observed rather than assumed.

Parallel deployment needs distinct, observable versions

Parallel deployment is safer when old and new schemas have distinct names or explicit versions and both remain observable. The purpose is to prevent traffic, validation results, and tool outcomes from two contracts being merged under one label.

Distinct names can make the boundary concrete, such as archive_asset_v1 and archive_asset_v2. An explicit version can instead live in the tool registry, request metadata, and trace while different cohorts receive different definitions. In either case, every invocation must be attributable to the exact definition the model saw. A version field that exists only in source control is insufficient for a running system because it does not identify which definition produced a particular call.

Parallel deployment does not require exposing both definitions to every model invocation. A canary cohort can receive the new definition while the remaining traffic continues to receive the old one. The old schema and adapter stay runnable during observation and rollback. The release record should state which model, prompt, tool definition, and routing configuration formed each cohort; canary rollouts for agent changes covers that wider release boundary.

For each version, retain the advertised name, description, input schema, output schema when present, generated arguments, validation result, executor result, and final tool status. Separate counts for selection, argument-validation failure, execution failure, and successful completion. Without the version dimension, a reduction in calls to the old name can be mistaken for adoption of the new name even when the model is failing to select either tool.

MCP servers that declare the listChanged capability can notify clients that the available tool list changed. That supports rediscovery, but it is not schema-version negotiation and does not make an in-place breaking edit safe. A client learning that a list changed still needs an unambiguous contract to discover, invoke, trace, and roll back.

Parallel operation has a concrete cost: adapters, deployment configuration, evaluations, dashboards, and alerts must cover both contracts. Exposing two near-identical names in one context can also affect selection, so cohort separation is often easier to interpret than presenting both names together. Retire the old definition only after the chosen acceptance conditions have been met and old-name traffic has been accounted for. No universal traffic percentage or observation period is established by the supplied sources; set those thresholds from the application’s own baseline and risk.

Preserve the whole released definition

Store the complete definition rather than only a version string. Names and argument schemas are the obvious compatibility surfaces, but descriptions also guide the model, and declared output schemas guide clients parsing structured results. A trace labeled v2 is of limited use if the exact v2 definition cannot be recovered later.

Keep schema releases tied to their adapters and evaluations. Persisted tool arguments may also require agent state schema migrations when they are stored in checkpoints or resumable workflows. The next facts to check for any release are therefore the exact old and new definitions, which callers or stored runs still depend on the old contract, how each version is identified in telemetry, and what evidence will permit removal of the compatibility path.

Sources

  1. OpenAI tools guidedevelopers.openai.com
  2. MCP tools specification dated 2025-11-25modelcontextprotocol.io
  3. JSON Schema 2020-12 core specification published 2022-06-16json-schema.org

See also