diff --git a/apps/playground/package.json b/apps/playground/package.json index 8866fbf..321e62e 100644 --- a/apps/playground/package.json +++ b/apps/playground/package.json @@ -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" diff --git a/package.json b/package.json index f1f9dc5..7682058 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/tools/mcp-json-diff/README.md b/tools/mcp-json-diff/README.md index cf92ff6..280e54a 100644 --- a/tools/mcp-json-diff/README.md +++ b/tools/mcp-json-diff/README.md @@ -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): @@ -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`) diff --git a/tools/mcp-json-diff/project.json b/tools/mcp-json-diff/project.json index 59e2e4b..e75cf49 100644 --- a/tools/mcp-json-diff/project.json +++ b/tools/mcp-json-diff/project.json @@ -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": { diff --git a/tools/mcp-json-diff/src/index.ts b/tools/mcp-json-diff/src/index.ts index b7749f9..9aac5b5 100644 --- a/tools/mcp-json-diff/src/index.ts +++ b/tools/mcp-json-diff/src/index.ts @@ -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) diff --git a/yarn.lock b/yarn.lock index 710bf6a..44ff699 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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" @@ -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 @@ -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" @@ -2098,7 +2189,7 @@ __metadata: "@emotion/react": ">=11" react: ">=18" react-dom: ">=18" - checksum: 10/0f795063be8a4d98d4fea4a952e8f6172e622b69c66456e888b14ca42b6e77b8fb1317130a6d5e176e8dc5483740b6e7bf3b312020021d259cc5e3b893504913 + checksum: 10/7d5e9a4d82d89bdd7b2fa270fae6c2273f13f0130ac392e65c4f119e71f6891c2bb7f8c62f653a83745294a3e75b82deff3f60f99370941c0425f87dce53759d languageName: node linkType: hard @@ -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"