diff
Compare text or data in various formats (JSON, YAML, XML, etc.) and generate readable diffs in text, JSON, or JSON patch formats for clear analysis.
Instructions
compare text or data and get a readable diff
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| state | Yes |
Implementation Reference
- packages/diff-mcp/src/server.ts:23-110 (registration)Registers the 'diff' MCP tool, including inline input schema and handler function.server.tool( "diff", "compare text or data and get a readable diff", { state: z.object({ left: inputDataSchema.describe("The left side of the diff."), leftFormat: formatSchema .optional() .describe("format of left side of the diff"), right: inputDataSchema.describe( "The right side of the diff (to compare with the left side).", ), rightFormat: formatSchema .optional() .describe("format of right side of the diff"), outputFormat: z .enum(["text", "json", "jsonpatch"]) .default("text") .describe( "The output format. " + "text: (default) human readable text diff, " + "json: a compact json diff (jsondiffpatch delta format), " + "jsonpatch: json patch diff (RFC 6902)", ) .optional(), }), }, ({ state }) => { try { const jsondiffpatch = create({ textDiff: { ...(state.outputFormat === "jsonpatch" ? { // jsonpatch doesn't support text diffs minLength: Number.MAX_VALUE, } : {}), }, }); const left = parseData(state.left, state.leftFormat); const right = parseData(state.right, state.rightFormat); const delta = jsondiffpatch.diff(left, right); const output = state.outputFormat === "json" ? delta : state.outputFormat === "jsonpatch" ? jsonpatchFormatter.format(delta) : consoleFormatter.format(delta, left); const legend = state.outputFormat === "text" ? `\n\nlegend: - lines starting with "+" indicate new property or item array - lines starting with "-" indicate removed property or item array - "value => newvalue" indicate property value changed - "x: ~> y indicate array item moved from index x to y - text diffs are lines that start "line,char" numbers, and have a line below with "+" under added chars, and "-" under removed chars. - you can use this exact representations when showing differences to the user \n` : ""; return { content: [ { type: "text", text: (typeof output === "string" ? output : JSON.stringify(output, null, 2)) + legend, }, ], }; } catch (error) { const message = error instanceof Error ? error.message : String(error); return { isError: true, content: [ { type: "text", text: `error creating diff: ${message}`, }, ], }; } }, );
- packages/diff-mcp/src/server.ts:50-109 (handler)Handler function that parses inputs, computes diff using jsondiffpatch.create().diff(), and formats output as text, JSON delta, or JSONPatch.({ state }) => { try { const jsondiffpatch = create({ textDiff: { ...(state.outputFormat === "jsonpatch" ? { // jsonpatch doesn't support text diffs minLength: Number.MAX_VALUE, } : {}), }, }); const left = parseData(state.left, state.leftFormat); const right = parseData(state.right, state.rightFormat); const delta = jsondiffpatch.diff(left, right); const output = state.outputFormat === "json" ? delta : state.outputFormat === "jsonpatch" ? jsonpatchFormatter.format(delta) : consoleFormatter.format(delta, left); const legend = state.outputFormat === "text" ? `\n\nlegend: - lines starting with "+" indicate new property or item array - lines starting with "-" indicate removed property or item array - "value => newvalue" indicate property value changed - "x: ~> y indicate array item moved from index x to y - text diffs are lines that start "line,char" numbers, and have a line below with "+" under added chars, and "-" under removed chars. - you can use this exact representations when showing differences to the user \n` : ""; return { content: [ { type: "text", text: (typeof output === "string" ? output : JSON.stringify(output, null, 2)) + legend, }, ], }; } catch (error) { const message = error instanceof Error ? error.message : String(error); return { isError: true, content: [ { type: "text", text: `error creating diff: ${message}`, }, ], }; } },
- Input schema (Zod) for the 'diff' tool state, supporting various input formats and output options.state: z.object({ left: inputDataSchema.describe("The left side of the diff."), leftFormat: formatSchema .optional() .describe("format of left side of the diff"), right: inputDataSchema.describe( "The right side of the diff (to compare with the left side).", ), rightFormat: formatSchema .optional() .describe("format of right side of the diff"), outputFormat: z .enum(["text", "json", "jsonpatch"]) .default("text") .describe( "The output format. " + "text: (default) human readable text diff, " + "json: a compact json diff (jsondiffpatch delta format), " + "jsonpatch: json patch diff (RFC 6902)", ) .optional(), }),
- Helper function to parse string inputs in formats like JSON/5, YAML, TOML, XML/HTML into JavaScript objects for diffing.const parseData = ( data: z.infer<typeof inputDataSchema>, format: z.infer<typeof formatSchema> | undefined, ) => { if (typeof data !== "string") { // already parsed return data; } if (!format || format === "text") { return data; } if (format === "json") { try { return JSON.parse(data); } catch { // if json is invalid, try json5 return json5.parse(data); } } if (format === "json5") { return json5.parse(data); } if (format === "yaml") { return yaml.load(data); } if (format === "xml") { const parser = new XMLParser({ ignoreAttributes: false, preserveOrder: true, }); return parser.parse(data); } if (format === "html") { const parser = new XMLParser({ ignoreAttributes: false, preserveOrder: true, unpairedTags: ["hr", "br", "link", "meta"], stopNodes: ["*.pre", "*.script"], processEntities: true, htmlEntities: true, }); return parser.parse(data); } if (format === "toml") { return tomlParse(data); } format satisfies never; throw new Error(`unsupported format: ${format}`); };
- Zod schema for input data: string, object, or array.const inputDataSchema = z .string() .or(z.record(z.string(), z.unknown())) .or(z.array(z.unknown()));
- Zod schema for input format options.const formatSchema = z .enum(["text", "json", "json5", "yaml", "toml", "xml", "html"]) .default("json5");