Tool Diff

What changed in a tool’s contract between two server versions, with breaking changes classified rather than just listed. Adding an optional parameter and making one required are both edits to `properties`; only one breaks every existing call.

src/components/ui/tool-diff.tsx

About

From the component's own source, where it was written.

What changed in a tool's contract between two versions of a server.

MCP servers update underneath you. A parameter gets renamed, a previously optional field becomes required, an enum loses a value — and the agent carries on calling the tool the old way until something fails in a way that looks like a model problem.

**Breaking changes are classified, not just listed.** Adding an optional parameter and making an existing one required are both "a change to properties", and only one of them will break every existing call. The rule this encodes: removals and newly-required fields break; additions and relaxed requirements do not.

A type change is always breaking, even integer to number, because the caller is a model that was shown the old schema and will keep producing the old shape.

Install

npx astralyx-ui add tool-diff

Usage

import { ToolDiff } from '@/components/ui/tool-diff'

<ToolDiff tool="read_file" before={old.inputSchema} after={next.inputSchema} />

Composer

Change the props and the source below follows.

read_file0.6.2 → 0.7.03 breaking
  • pathdescriptionFile to read.Absolute path, inside the granted roots.
  • encodingvalues changedutf-8, base64, latin1utf-8, base64
  • encodingnow required
  • max_bytesremovedinteger
  • limitaddedinteger
Props

No props to configure.

tsx
<ToolDiff
  tool="read_file"
  before={old.inputSchema}
  after={next.inputSchema}
  beforeLabel="0.6.2"
  afterLabel="0.7.0"
/>

Examples

A server upgrade that breaks three things

API

Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.

ToolDiff

Extends Omit<ComponentProps<'div'>, 'children'>.

PropTypeDefaultDescription
toolstringrequired—
beforeJsonSchemarequiredThe two input schemas. Compared one level deep, which is where tool parameters live.
afterJsonSchemarequiredThe two input schemas. Compared one level deep, which is where tool parameters live.
beforeLabelReactNode'before'—
afterLabelReactNode'after'—
breakingLabelstring'breaking'—
identicalLabelstring'This tool’s contract is unchanged.'—
changesToolChange[]—Supply your own comparison instead of the built-in one.
rulesencoded, not guessed—Removals, newly-required fields, type changes and removed enum values break. Additions, relaxed requirements and description edits do not.
typealways breaking—Even integer → number. The caller is a model that was shown the old schema and will keep producing the old shape.

Functions

FunctionSignatureDescription
toolChangeBreakingRecord<'type' | 'added' | 'removed' | 'required' | 'enum' | 'optional' | 'description', boolean>—
diffToolSchemas(before: JsonSchema, after: JsonSchema) => ToolChange[]Exported, so CI can fail a server upgrade that breaks a contract.

Types

ToolChange

What changed in a tool's contract between two versions of a server.

FieldTypeDescription
pathstring—
kind'added' | 'removed' | 'type' | 'required' | 'optional' | 'enum' | 'description'—
before?string—
after?string—
breakingbooleanRemovals, newly-required fields, type changes and removed enum values break. Additions, relaxed requirements and description edits do not.