Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"dependencies": {
"react": "^19.2.5",
"lucide-react": "^1.8.0",
"@chakra-ui/react": "^3.34.0",
"@chakra-ui/react": "^3.35.0",
"json-difference": "1.16.1",
"react-dom": "^19.2.5",
"react-monaco-editor": "^0.59.0"
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
"graph": "yarn nx graph",
"graph:affected": "yarn nx graph --affected",
"release": "yarn nx run-many --target=version --parallel=1 ${0}",
"upgrade": "yarn npm-check-updates --interactive"
"upgrade": "yarn npm-check-updates --interactive",
"claude": "yarn nx run mcp-json-diff:build --no-tui && claude"
},
"devDependencies": {
"@chakra-ui/react": "^3.34.0",
"@anthropic-ai/claude-code": "^2.1.117",
"@chakra-ui/react": "^3.35.0",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@jscutlery/semver": "^6.1.2",
Expand Down
19 changes: 19 additions & 0 deletions tools/mcp-json-diff/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@ MCP server that exposes the `json-difference` library as tools for AI agents
All JSON inputs accept objects, arrays, or JSON-encoded strings.
Optional `isLodashLike: true` switches paths to bracket notation (`a[0].b`).

## Prompts

User-invocable templates (appear in Claude Desktop under `/` menu, in Claude Code as slash commands, etc.):

| Prompt | Arguments | Output |
|---|---|---|
| `summarize-breaking-changes` | `original`, `modified` | Breaking changes grouped by severity |
| `generate-changelog` | `original`, `modified`, `version?` | Keep-a-Changelog formatted entry |
| `explain-config-drift` | `baseline`, `actual` | Plain-language drift explanation + risk level |
| `migration-guide` | `v1`, `v2` | Field change table + numbered steps + transform snippet |

Each prompt instructs the agent to call the `get_diff` tool internally, so you get deterministic delta + LLM narrative in one shot.

## Running

Dev (via `tsx`, no build step):
Expand All @@ -30,6 +43,12 @@ yarn nx run mcp-json-diff:build
node tools/mcp-json-diff/bin/src/index.js
```

Browser inspector (opens MCP Inspector UI connected to this server):

```bash
yarn nx run mcp-json-diff:test-browser
```

## Client configuration

### Claude Code / Claude Desktop (`mcp.json` or `claude_desktop_config.json`)
Expand Down
6 changes: 6 additions & 0 deletions tools/mcp-json-diff/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@
"command": "yarn tsx {projectRoot}/src/index.ts"
}
},
"test-browser": {
"executor": "nx:run-commands",
"options": {
"command": "yarn dlx @modelcontextprotocol/inspector yarn tsx {projectRoot}/src/index.ts"
}
},
"type-check": {
"executor": "nx:run-commands",
"options": {
Expand Down
111 changes: 111 additions & 0 deletions tools/mcp-json-diff/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,117 @@ server.registerTool(
}
)

server.registerPrompt(
'summarize-breaking-changes',
{
title: 'Summarize breaking API changes',
description: 'Compare two API responses/schemas and list only the BREAKING changes, grouped by severity.',
argsSchema: {
original: z.string().describe('Original JSON (stringified)'),
modified: z.string().describe('Modified JSON (stringified)')
}
},
({ original, modified }) => ({
messages: [
{
role: 'user',
content: {
type: 'text',
text:
'Use the `get_diff` tool on the two JSON payloads below, then list ONLY the breaking changes. ' +
'Group them as: (1) Removed fields, (2) Type changes, (3) Renamed/moved fields. ' +
'For each item, give the path and a one-line impact note. Ignore additive changes.\n\n' +
`Original:\n\`\`\`json\n${original}\n\`\`\`\n\nModified:\n\`\`\`json\n${modified}\n\`\`\``
}
}
]
})
)

server.registerPrompt(
'generate-changelog',
{
title: 'Generate CHANGELOG entry from diff',
description: 'Produces a Keep-a-Changelog formatted entry (Added / Changed / Removed) from two JSON versions.',
argsSchema: {
original: z.string().describe('Previous version JSON (stringified)'),
modified: z.string().describe('New version JSON (stringified)'),
version: z.string().optional().describe('Version label for the entry header, e.g. "1.2.0"')
}
},
({ original, modified, version }) => ({
messages: [
{
role: 'user',
content: {
type: 'text',
text:
'Call `get_diff` on the two JSON payloads below and convert the delta into a CHANGELOG entry ' +
'following Keep-a-Changelog format with `### Added`, `### Changed`, `### Removed` sections. ' +
'Each bullet must reference the JSON path. Be terse.\n\n' +
`Version header: ${version ?? '(infer or use Unreleased)'}\n\n` +
`Previous:\n\`\`\`json\n${original}\n\`\`\`\n\nNew:\n\`\`\`json\n${modified}\n\`\`\``
}
}
]
})
)

server.registerPrompt(
'explain-config-drift',
{
title: 'Explain config drift in plain language',
description: 'Compare two configs (baseline vs actual) and explain drift in prose aimed at non-engineers.',
argsSchema: {
baseline: z.string().describe('Baseline/expected config JSON (stringified)'),
actual: z.string().describe('Current/observed config JSON (stringified)')
}
},
({ baseline, actual }) => ({
messages: [
{
role: 'user',
content: {
type: 'text',
text:
'Use `get_diff` on the two configs below. Then write a short paragraph (non-technical audience) ' +
'explaining how the actual config drifted from the baseline. Mention concrete settings, not JSON paths. ' +
'End with a one-line risk assessment: low / medium / high and why.\n\n' +
`Baseline:\n\`\`\`json\n${baseline}\n\`\`\`\n\nActual:\n\`\`\`json\n${actual}\n\`\`\``
}
}
]
})
)

server.registerPrompt(
'migration-guide',
{
title: 'Generate schema migration guide',
description: 'Produces a step-by-step migration guide for consumers moving from schema v1 to v2.',
argsSchema: {
v1: z.string().describe('Old schema example JSON (stringified)'),
v2: z.string().describe('New schema example JSON (stringified)')
}
},
({ v1, v2 }) => ({
messages: [
{
role: 'user',
content: {
type: 'text',
text:
'Use `get_diff` to compare the two schema examples below. Then produce a migration guide with: ' +
'(1) a summary table of field-level changes, ' +
'(2) numbered migration steps a consumer must perform, ' +
'(3) a sample `jq` or code snippet to transform a v1 payload into v2 where feasible.\n\n' +
`v1 example:\n\`\`\`json\n${v1}\n\`\`\`\n\nv2 example:\n\`\`\`json\n${v2}\n\`\`\``
}
}
]
})
)

const main = async () => {
const transport = new StdioServerTransport()
await server.connect(transport)
Expand Down
112 changes: 102 additions & 10 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,100 @@ __metadata:
languageName: node
linkType: hard

"@ark-ui/react@npm:^5.34.1":
version: 5.36.1
resolution: "@ark-ui/react@npm:5.36.1"
"@anthropic-ai/claude-code-darwin-arm64@npm:2.1.117":
version: 2.1.117
resolution: "@anthropic-ai/claude-code-darwin-arm64@npm:2.1.117"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard

"@anthropic-ai/claude-code-darwin-x64@npm:2.1.117":
version: 2.1.117
resolution: "@anthropic-ai/claude-code-darwin-x64@npm:2.1.117"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard

"@anthropic-ai/claude-code-linux-arm64-musl@npm:2.1.117":
version: 2.1.117
resolution: "@anthropic-ai/claude-code-linux-arm64-musl@npm:2.1.117"
conditions: os=linux & cpu=arm64 & libc=musl
languageName: node
linkType: hard

"@anthropic-ai/claude-code-linux-arm64@npm:2.1.117":
version: 2.1.117
resolution: "@anthropic-ai/claude-code-linux-arm64@npm:2.1.117"
conditions: os=linux & cpu=arm64 & libc=glibc
languageName: node
linkType: hard

"@anthropic-ai/claude-code-linux-x64-musl@npm:2.1.117":
version: 2.1.117
resolution: "@anthropic-ai/claude-code-linux-x64-musl@npm:2.1.117"
conditions: os=linux & cpu=x64 & libc=musl
languageName: node
linkType: hard

"@anthropic-ai/claude-code-linux-x64@npm:2.1.117":
version: 2.1.117
resolution: "@anthropic-ai/claude-code-linux-x64@npm:2.1.117"
conditions: os=linux & cpu=x64 & libc=glibc
languageName: node
linkType: hard

"@anthropic-ai/claude-code-win32-arm64@npm:2.1.117":
version: 2.1.117
resolution: "@anthropic-ai/claude-code-win32-arm64@npm:2.1.117"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard

"@anthropic-ai/claude-code-win32-x64@npm:2.1.117":
version: 2.1.117
resolution: "@anthropic-ai/claude-code-win32-x64@npm:2.1.117"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard

"@anthropic-ai/claude-code@npm:^2.1.117":
version: 2.1.117
resolution: "@anthropic-ai/claude-code@npm:2.1.117"
dependencies:
"@anthropic-ai/claude-code-darwin-arm64": "npm:2.1.117"
"@anthropic-ai/claude-code-darwin-x64": "npm:2.1.117"
"@anthropic-ai/claude-code-linux-arm64": "npm:2.1.117"
"@anthropic-ai/claude-code-linux-arm64-musl": "npm:2.1.117"
"@anthropic-ai/claude-code-linux-x64": "npm:2.1.117"
"@anthropic-ai/claude-code-linux-x64-musl": "npm:2.1.117"
"@anthropic-ai/claude-code-win32-arm64": "npm:2.1.117"
"@anthropic-ai/claude-code-win32-x64": "npm:2.1.117"
dependenciesMeta:
"@anthropic-ai/claude-code-darwin-arm64":
optional: true
"@anthropic-ai/claude-code-darwin-x64":
optional: true
"@anthropic-ai/claude-code-linux-arm64":
optional: true
"@anthropic-ai/claude-code-linux-arm64-musl":
optional: true
"@anthropic-ai/claude-code-linux-x64":
optional: true
"@anthropic-ai/claude-code-linux-x64-musl":
optional: true
"@anthropic-ai/claude-code-win32-arm64":
optional: true
"@anthropic-ai/claude-code-win32-x64":
optional: true
bin:
claude: bin/claude.exe
checksum: 10/348b448a7ff026dd24676248a144dffccb9f5cab46edebf1348abdd5c7a69f822fecda69690cb65790d687bbd17cb059f5415b2517fd197e8dbcf9c73ca7416c
languageName: node
linkType: hard

"@ark-ui/react@npm:5.36.2":
version: 5.36.2
resolution: "@ark-ui/react@npm:5.36.2"
dependencies:
"@internationalized/date": "npm:3.12.0"
"@zag-js/accordion": "npm:1.40.0"
Expand Down Expand Up @@ -132,7 +223,7 @@ __metadata:
peerDependencies:
react: ">=18.0.0"
react-dom: ">=18.0.0"
checksum: 10/710a6c8c6ea30fbe71b726cd0e44af7ed85d37c750c88402c4cdafaf1a1a80f972853fe0796a2eaa107edc0a0f684105a542e7b72580f2c83e0ea670c58a5f3d
checksum: 10/042ac4040f3d331c40adc2077544f39e270c7b7ee1289d7b4d2e92fb2259fa030105910ff6640a8f6c62d436fe109b3efc4bb77d0b4bf603f466028ebd35acda
languageName: node
linkType: hard

Expand Down Expand Up @@ -2083,11 +2174,11 @@ __metadata:
languageName: node
linkType: hard

"@chakra-ui/react@npm:^3.34.0":
version: 3.34.0
resolution: "@chakra-ui/react@npm:3.34.0"
"@chakra-ui/react@npm:^3.35.0":
version: 3.35.0
resolution: "@chakra-ui/react@npm:3.35.0"
dependencies:
"@ark-ui/react": "npm:^5.34.1"
"@ark-ui/react": "npm:5.36.2"
"@emotion/is-prop-valid": "npm:^1.4.0"
"@emotion/serialize": "npm:^1.3.3"
"@emotion/use-insertion-effect-with-fallbacks": "npm:^1.2.0"
Expand All @@ -2098,7 +2189,7 @@ __metadata:
"@emotion/react": ">=11"
react: ">=18"
react-dom: ">=18"
checksum: 10/0f795063be8a4d98d4fea4a952e8f6172e622b69c66456e888b14ca42b6e77b8fb1317130a6d5e176e8dc5483740b6e7bf3b312020021d259cc5e3b893504913
checksum: 10/7d5e9a4d82d89bdd7b2fa270fae6c2273f13f0130ac392e65c4f119e71f6891c2bb7f8c62f653a83745294a3e75b82deff3f60f99370941c0425f87dce53759d
languageName: node
linkType: hard

Expand Down Expand Up @@ -15449,7 +15540,8 @@ __metadata:
version: 0.0.0-use.local
resolution: "root@workspace:."
dependencies:
"@chakra-ui/react": "npm:^3.34.0"
"@anthropic-ai/claude-code": "npm:^2.1.117"
"@chakra-ui/react": "npm:^3.35.0"
"@emotion/react": "npm:^11.14.0"
"@emotion/styled": "npm:^11.14.1"
"@jscutlery/semver": "npm:^6.1.2"
Expand Down
Loading