mcp-server-diff 2.1.6 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -223,6 +223,46 @@ When using `configurations`, each object supports:
223
223
  | `headers` | HTTP headers for this configuration | No |
224
224
  | `env_vars` | Additional environment variables | No |
225
225
  | `custom_messages` | Config-specific custom messages | No |
226
+ | `base_start_command` | Command for baseline comparison (skips git checkout for this config) | No |
227
+ | `base_server_url` | URL for baseline HTTP server (used with `base_start_command`) | No |
228
+
229
+ ### Comparing Against External Servers
230
+
231
+ When comparing against external servers (e.g., Docker images, remote services), use `base_start_command` to specify a different command for the baseline. This skips git checkout for that configuration and probes the specified server directly:
232
+
233
+ ```yaml
234
+ configurations: |
235
+ [
236
+ {
237
+ "name": "compare-versions",
238
+ "transport": "stdio",
239
+ "start_command": "docker run -i ghcr.io/example/mcp-server:v2.0.0",
240
+ "base_start_command": "docker run -i ghcr.io/example/mcp-server:v1.0.0"
241
+ }
242
+ ]
243
+ ```
244
+
245
+ This is useful for:
246
+ - **Version comparison**: Compare a new release against the previous version
247
+ - **Golden reference testing**: Compare your local code against a known-good reference
248
+ - **Cross-implementation testing**: Compare different implementations of the same server
249
+ - **Self-testing CI**: Verify the action detects diffs by comparing two known-different servers
250
+
251
+ For HTTP transport, use `base_server_url` alongside `base_start_command`:
252
+
253
+ ```yaml
254
+ configurations: |
255
+ [
256
+ {
257
+ "name": "http-comparison",
258
+ "transport": "streamable-http",
259
+ "start_command": "docker run -p 3000:3000 myserver:latest",
260
+ "server_url": "http://localhost:3000/mcp",
261
+ "base_start_command": "docker run -p 3001:3000 myserver:v1.0.0",
262
+ "base_server_url": "http://localhost:3001/mcp"
263
+ }
264
+ ]
265
+ ```
226
266
 
227
267
  ## How It Works
228
268
 
@@ -595,8 +635,8 @@ npx mcp-server-diff -c servers.json -o diff
595
635
  | `-b, --base <cmd\|url>` | Base server command (stdio) or URL (http) |
596
636
  | `-t, --target <cmd\|url>` | Target server command (stdio) or URL (http) |
597
637
  | `-H, --header <header>` | HTTP header for target (repeatable) |
598
- | `--base-header <header>` | HTTP header for base server (repeatable) |
599
- | `--target-header <header>` | HTTP header for target (same as `-H`) |
638
+ | `-B, --base-header <header>` | HTTP header for base server (repeatable) |
639
+ | `-T, --target-header <header>` | HTTP header for target (same as `-H`) |
600
640
  | `-c, --config <file>` | Config file with base and targets |
601
641
  | `-o, --output <format>` | Output: `diff`, `json`, `markdown`, `summary` (default) |
602
642
  | `-v, --verbose` | Verbose output |
package/dist/cli/index.js CHANGED
@@ -56368,6 +56368,7 @@ function isMethodNotFound(error) {
56368
56368
  async function probeServer(options) {
56369
56369
  const result = {
56370
56370
  initialize: null,
56371
+ instructions: null,
56371
56372
  tools: null,
56372
56373
  prompts: null,
56373
56374
  resources: null,
@@ -56426,6 +56427,12 @@ async function probeServer(options) {
56426
56427
  serverInfo,
56427
56428
  capabilities: serverCapabilities,
56428
56429
  };
56430
+ // Get server instructions
56431
+ const instructions = client.getInstructions();
56432
+ if (instructions) {
56433
+ result.instructions = instructions;
56434
+ log.info(` Got server instructions (${instructions.length} chars)`);
56435
+ }
56429
56436
  // Probe tools if supported
56430
56437
  if (serverCapabilities?.tools) {
56431
56438
  try {
@@ -56623,6 +56630,9 @@ function probeResultToFiles(result) {
56623
56630
  if (result.initialize) {
56624
56631
  files.set("initialize", JSON.stringify(normalizeProbeResult(result.initialize), null, 2));
56625
56632
  }
56633
+ if (result.instructions) {
56634
+ files.set("instructions", result.instructions);
56635
+ }
56626
56636
  if (result.tools) {
56627
56637
  files.set("tools", JSON.stringify(normalizeProbeResult(result.tools), null, 2));
56628
56638
  }
@@ -56870,8 +56880,8 @@ function parseCliArgs() {
56870
56880
  base: { type: "string", short: "b" },
56871
56881
  target: { type: "string", short: "t" },
56872
56882
  header: { type: "string", short: "H", multiple: true },
56873
- "base-header": { type: "string", multiple: true },
56874
- "target-header": { type: "string", multiple: true },
56883
+ "base-header": { type: "string", short: "B", multiple: true },
56884
+ "target-header": { type: "string", short: "T", multiple: true },
56875
56885
  config: { type: "string", short: "c" },
56876
56886
  output: { type: "string", short: "o", default: "summary" },
56877
56887
  verbose: { type: "boolean", short: "v", default: false },
@@ -56900,8 +56910,8 @@ OPTIONS:
56900
56910
  -b, --base <command> Base server command (stdio) or URL (http)
56901
56911
  -t, --target <command> Target server command (stdio) or URL (http)
56902
56912
  -H, --header <header> HTTP header for target (repeatable)
56903
- --base-header <header> HTTP header for base server (repeatable)
56904
- --target-header <hdr> HTTP header for target server (repeatable, same as -H)
56913
+ -B, --base-header <header> HTTP header for base server (repeatable)
56914
+ -T, --target-header <hdr> HTTP header for target server (repeatable, same as -H)
56905
56915
  Values support: env:VAR_NAME, secret:name, "Bearer secret:token"
56906
56916
  -c, --config <file> Config file with base and targets
56907
56917
  -o, --output <format> Output format: diff, json, markdown, summary (default: summary)
@@ -57060,44 +57070,46 @@ function findSecrets(headerStrings) {
57060
57070
  */
57061
57071
  async function promptSecret(prompt) {
57062
57072
  return new Promise((resolve) => {
57063
- const rl = external_readline_namespaceObject.createInterface({
57064
- input: process.stdin,
57065
- output: process.stdout,
57066
- });
57073
+ process.stdout.write(`${prompt}: `);
57067
57074
  // Hide input by using raw mode if available
57068
- if (process.stdin.isTTY) {
57069
- process.stdout.write(`${prompt}: `);
57075
+ if (process.stdin.isTTY && process.stdin.setRawMode) {
57070
57076
  process.stdin.setRawMode(true);
57071
57077
  process.stdin.resume();
57078
+ process.stdin.setEncoding("utf8");
57072
57079
  let value = "";
57073
57080
  const onData = (char) => {
57074
- const c = char.toString();
57075
- if (c === "\n" || c === "\r") {
57081
+ if (char === "\n" || char === "\r" || char === "\u0004") {
57076
57082
  process.stdin.setRawMode(false);
57083
+ process.stdin.pause();
57077
57084
  process.stdin.removeListener("data", onData);
57078
- rl.close();
57079
57085
  process.stdout.write("\n");
57080
57086
  resolve(value);
57081
57087
  }
57082
- else if (c === "\u0003") {
57088
+ else if (char === "\u0003") {
57083
57089
  // Ctrl+C
57090
+ process.stdin.setRawMode(false);
57091
+ process.stdout.write("\n");
57084
57092
  process.exit(1);
57085
57093
  }
57086
- else if (c === "\u007F" || c === "\b") {
57094
+ else if (char === "\u007F" || char === "\b") {
57087
57095
  // Backspace
57088
57096
  if (value.length > 0) {
57089
57097
  value = value.slice(0, -1);
57090
57098
  }
57091
57099
  }
57092
57100
  else {
57093
- value += c;
57101
+ value += char;
57094
57102
  }
57095
57103
  };
57096
57104
  process.stdin.on("data", onData);
57097
57105
  }
57098
57106
  else {
57099
- // Non-TTY: just read the line (won't be hidden)
57100
- rl.question(`${prompt}: `, (answer) => {
57107
+ // Non-TTY: use readline (won't be hidden)
57108
+ const rl = external_readline_namespaceObject.createInterface({
57109
+ input: process.stdin,
57110
+ output: process.stdout,
57111
+ });
57112
+ rl.question("", (answer) => {
57101
57113
  rl.close();
57102
57114
  resolve(answer);
57103
57115
  });
@@ -18,6 +18,14 @@ export interface TestConfiguration {
18
18
  startup_wait_ms?: number;
19
19
  /** Command to run after stopping the MCP server for this config (cleanup) */
20
20
  post_test_command?: string;
21
+ /**
22
+ * Command to use for the base/comparison server instead of checking out a git ref.
23
+ * When set, skips git operations and uses this command directly for comparison.
24
+ * Useful for comparing against external servers (e.g., docker images of previous releases).
25
+ */
26
+ base_start_command?: string;
27
+ /** Server URL for base comparison (for HTTP transport with base_start_command) */
28
+ base_server_url?: string;
21
29
  }
22
30
  export interface CustomMessage {
23
31
  id: number;
@@ -53,6 +61,7 @@ export interface ActionInputs {
53
61
  }
54
62
  export interface ProbeResult {
55
63
  initialize: InitializeInfo | null;
64
+ instructions: string | null;
56
65
  tools: ToolsResult | null;
57
66
  prompts: PromptsResult | null;
58
67
  resources: ResourcesResult | null;
package/dist/index.js CHANGED
@@ -56618,6 +56618,7 @@ function isMethodNotFound(error) {
56618
56618
  async function probeServer(options) {
56619
56619
  const result = {
56620
56620
  initialize: null,
56621
+ instructions: null,
56621
56622
  tools: null,
56622
56623
  prompts: null,
56623
56624
  resources: null,
@@ -56676,6 +56677,12 @@ async function probeServer(options) {
56676
56677
  serverInfo,
56677
56678
  capabilities: serverCapabilities,
56678
56679
  };
56680
+ // Get server instructions
56681
+ const instructions = client.getInstructions();
56682
+ if (instructions) {
56683
+ result.instructions = instructions;
56684
+ log.info(` Got server instructions (${instructions.length} chars)`);
56685
+ }
56679
56686
  // Probe tools if supported
56680
56687
  if (serverCapabilities?.tools) {
56681
56688
  try {
@@ -56873,6 +56880,9 @@ function probeResultToFiles(result) {
56873
56880
  if (result.initialize) {
56874
56881
  files.set("initialize", JSON.stringify(normalizeProbeResult(result.initialize), null, 2));
56875
56882
  }
56883
+ if (result.instructions) {
56884
+ files.set("instructions", result.instructions);
56885
+ }
56876
56886
  if (result.tools) {
56877
56887
  files.set("tools", JSON.stringify(normalizeProbeResult(result.tools), null, 2));
56878
56888
  }
@@ -57133,8 +57143,10 @@ async function runPostTestCommand(config, workDir) {
57133
57143
  /**
57134
57144
  * Probe a server with a specific configuration
57135
57145
  * @param useSharedServer - If true, skip starting per-config HTTP server (shared server is already running)
57146
+ * @param overrideCommand - If provided, use this command instead of config.start_command (for base_start_command)
57147
+ * @param overrideUrl - If provided, use this URL instead of config.server_url (for base_server_url)
57136
57148
  */
57137
- async function probeWithConfig(config, workDir, globalEnvVars, globalHeaders, globalCustomMessages, useSharedServer = false) {
57149
+ async function probeWithConfig(config, workDir, globalEnvVars, globalHeaders, globalCustomMessages, useSharedServer = false, overrideCommand, overrideUrl) {
57138
57150
  const configEnvVars = parseEnvVars(config.env_vars);
57139
57151
  const envVars = { ...globalEnvVars, ...configEnvVars };
57140
57152
  const headers = { ...globalHeaders, ...config.headers };
@@ -57142,12 +57154,12 @@ async function probeWithConfig(config, workDir, globalEnvVars, globalHeaders, gl
57142
57154
  // Run pre-test command before probing
57143
57155
  await runPreTestCommand(config, workDir);
57144
57156
  if (config.transport === "stdio") {
57145
- const command = config.start_command || "";
57157
+ const command = overrideCommand || config.start_command || "";
57146
57158
  const parts = command.split(/\s+/);
57147
57159
  const cmd = parts[0];
57148
57160
  const args = parts.slice(1);
57149
- // Also parse additional args if provided
57150
- if (config.args) {
57161
+ // Also parse additional args if provided (only when not using override)
57162
+ if (!overrideCommand && config.args) {
57151
57163
  args.push(...config.args.split(/\s+/));
57152
57164
  }
57153
57165
  return await probeServer({
@@ -57163,13 +57175,17 @@ async function probeWithConfig(config, workDir, globalEnvVars, globalHeaders, gl
57163
57175
  // For HTTP transport, optionally start the server if start_command is provided
57164
57176
  // Skip if using shared server
57165
57177
  let serverProcess = null;
57178
+ const serverUrl = overrideUrl || config.server_url;
57166
57179
  try {
57167
- if (config.start_command && !useSharedServer) {
57168
- serverProcess = await startHttpServer(config, workDir, envVars);
57180
+ const startCmd = overrideCommand || config.start_command;
57181
+ if (startCmd && !useSharedServer) {
57182
+ // Create a temporary config for the override
57183
+ const tempConfig = { ...config, start_command: startCmd };
57184
+ serverProcess = await startHttpServer(tempConfig, workDir, envVars);
57169
57185
  }
57170
57186
  return await probeServer({
57171
57187
  transport: "streamable-http",
57172
- url: config.server_url,
57188
+ url: serverUrl,
57173
57189
  headers,
57174
57190
  envVars,
57175
57191
  customMessages,
@@ -57442,13 +57458,18 @@ async function startSharedHttpServer(command, workDir, waitMs, envVars) {
57442
57458
  }
57443
57459
  /**
57444
57460
  * Probe a single configuration (without comparison)
57445
- */
57446
- async function probeConfig(config, workDir, envVars, headers, customMessages, useSharedServer) {
57447
- lib_core.info(` šŸ“‹ Probing: ${config.name} (${config.transport})`);
57461
+ * @param overrideCommand - If provided, use this command instead of config.start_command
57462
+ * @param overrideUrl - If provided, use this URL instead of config.server_url
57463
+ */
57464
+ async function probeConfig(config, workDir, envVars, headers, customMessages, useSharedServer, overrideCommand, overrideUrl) {
57465
+ const displayName = overrideCommand
57466
+ ? `${config.name} (base: ${overrideCommand.slice(0, 50)}${overrideCommand.length > 50 ? "..." : ""})`
57467
+ : config.name;
57468
+ lib_core.info(` šŸ“‹ Probing: ${displayName} (${config.transport})`);
57448
57469
  const start = Date.now();
57449
57470
  let result;
57450
57471
  try {
57451
- result = await probeWithConfig(config, workDir, envVars, headers, customMessages, useSharedServer);
57472
+ result = await probeWithConfig(config, workDir, envVars, headers, customMessages, useSharedServer, overrideCommand, overrideUrl);
57452
57473
  }
57453
57474
  finally {
57454
57475
  await runPostTestCommand(config, workDir);
@@ -57491,6 +57512,7 @@ async function runAllTests(ctx) {
57491
57512
  branchResults.set(config.name, {
57492
57513
  result: {
57493
57514
  initialize: null,
57515
+ instructions: null,
57494
57516
  tools: null,
57495
57517
  prompts: null,
57496
57518
  resources: null,
@@ -57511,65 +57533,102 @@ async function runAllTests(ctx) {
57511
57533
  }
57512
57534
  }
57513
57535
  // ========================================
57514
- // PHASE 2: Probe all configs on base ref
57536
+ // PHASE 2: Probe all configs for base comparison
57515
57537
  // ========================================
57516
- lib_core.info(`\nšŸ”„ Phase 2: Testing comparison ref: ${ctx.compareRef}...`);
57517
- const worktreePath = external_path_.join(ctx.workDir, ".mcp-diff-base");
57518
- let useWorktree = false;
57519
- try {
57520
- // Set up comparison ref
57521
- useWorktree = await createWorktree(ctx.compareRef, worktreePath);
57522
- if (!useWorktree) {
57523
- lib_core.info(" Worktree not available, using checkout");
57524
- await checkout(ctx.compareRef);
57525
- }
57526
- const baseWorkDir = useWorktree ? worktreePath : ctx.workDir;
57527
- // Build on base
57528
- lib_core.info("šŸ”Ø Building on comparison ref...");
57529
- await runBuild(baseWorkDir, ctx.inputs);
57530
- let baseServerProcess = null;
57531
- try {
57532
- // Start HTTP server for base ref if needed
57533
- if (useSharedServer) {
57534
- baseServerProcess = await startSharedHttpServer(httpStartCommand, baseWorkDir, httpStartupWaitMs, globalEnvVars);
57538
+ // Split configs: those with base_start_command use direct probing,
57539
+ // those without need git checkout/worktree
57540
+ const configsWithBaseCommand = ctx.inputs.configurations.filter((c) => c.base_start_command);
57541
+ const configsNeedingGit = ctx.inputs.configurations.filter((c) => !c.base_start_command);
57542
+ // PHASE 2a: Probe configs with base_start_command directly (no git operations)
57543
+ if (configsWithBaseCommand.length > 0) {
57544
+ lib_core.info(`\nšŸ”„ Phase 2a: Testing ${configsWithBaseCommand.length} config(s) with explicit base commands...`);
57545
+ for (const config of configsWithBaseCommand) {
57546
+ try {
57547
+ // Use base_start_command/base_server_url for comparison
57548
+ const probeData = await probeConfig(config, ctx.workDir, // Use current workdir since we're not checking out
57549
+ globalEnvVars, globalHeaders, globalCustomMessages, false, // Don't use shared server for base commands
57550
+ config.base_start_command, config.base_server_url);
57551
+ baseResults.set(config.name, probeData);
57535
57552
  }
57536
- for (const config of ctx.inputs.configurations) {
57537
- const configUsesSharedServer = useSharedServer && config.transport === "streamable-http";
57538
- try {
57539
- const probeData = await probeConfig(config, baseWorkDir, globalEnvVars, globalHeaders, globalCustomMessages, configUsesSharedServer);
57540
- baseResults.set(config.name, probeData);
57553
+ catch (error) {
57554
+ lib_core.error(`Failed to probe ${config.name} with base command: ${error}`);
57555
+ baseResults.set(config.name, {
57556
+ result: {
57557
+ initialize: null,
57558
+ instructions: null,
57559
+ tools: null,
57560
+ prompts: null,
57561
+ resources: null,
57562
+ resourceTemplates: null,
57563
+ customResponses: new Map(),
57564
+ error: String(error),
57565
+ },
57566
+ time: 0,
57567
+ });
57568
+ }
57569
+ }
57570
+ }
57571
+ // PHASE 2b: Probe configs needing git checkout
57572
+ if (configsNeedingGit.length > 0) {
57573
+ lib_core.info(`\nšŸ”„ Phase 2b: Testing comparison ref: ${ctx.compareRef}...`);
57574
+ const worktreePath = external_path_.join(ctx.workDir, ".mcp-diff-base");
57575
+ let useWorktree = false;
57576
+ try {
57577
+ // Set up comparison ref
57578
+ useWorktree = await createWorktree(ctx.compareRef, worktreePath);
57579
+ if (!useWorktree) {
57580
+ lib_core.info(" Worktree not available, using checkout");
57581
+ await checkout(ctx.compareRef);
57582
+ }
57583
+ const baseWorkDir = useWorktree ? worktreePath : ctx.workDir;
57584
+ // Build on base
57585
+ lib_core.info("šŸ”Ø Building on comparison ref...");
57586
+ await runBuild(baseWorkDir, ctx.inputs);
57587
+ let baseServerProcess = null;
57588
+ try {
57589
+ // Start HTTP server for base ref if needed
57590
+ if (useSharedServer) {
57591
+ baseServerProcess = await startSharedHttpServer(httpStartCommand, baseWorkDir, httpStartupWaitMs, globalEnvVars);
57541
57592
  }
57542
- catch (error) {
57543
- lib_core.error(`Failed to probe ${config.name} on base ref: ${error}`);
57544
- baseResults.set(config.name, {
57545
- result: {
57546
- initialize: null,
57547
- tools: null,
57548
- prompts: null,
57549
- resources: null,
57550
- resourceTemplates: null,
57551
- customResponses: new Map(),
57552
- error: String(error),
57553
- },
57554
- time: 0,
57555
- });
57593
+ for (const config of configsNeedingGit) {
57594
+ const configUsesSharedServer = useSharedServer && config.transport === "streamable-http";
57595
+ try {
57596
+ const probeData = await probeConfig(config, baseWorkDir, globalEnvVars, globalHeaders, globalCustomMessages, configUsesSharedServer);
57597
+ baseResults.set(config.name, probeData);
57598
+ }
57599
+ catch (error) {
57600
+ lib_core.error(`Failed to probe ${config.name} on base ref: ${error}`);
57601
+ baseResults.set(config.name, {
57602
+ result: {
57603
+ initialize: null,
57604
+ instructions: null,
57605
+ tools: null,
57606
+ prompts: null,
57607
+ resources: null,
57608
+ resourceTemplates: null,
57609
+ customResponses: new Map(),
57610
+ error: String(error),
57611
+ },
57612
+ time: 0,
57613
+ });
57614
+ }
57615
+ }
57616
+ }
57617
+ finally {
57618
+ if (baseServerProcess) {
57619
+ lib_core.info("šŸ›‘ Stopping base ref HTTP server...");
57620
+ stopHttpServer(baseServerProcess);
57556
57621
  }
57557
57622
  }
57558
57623
  }
57559
57624
  finally {
57560
- if (baseServerProcess) {
57561
- lib_core.info("šŸ›‘ Stopping base ref HTTP server...");
57562
- stopHttpServer(baseServerProcess);
57625
+ // Clean up
57626
+ if (useWorktree) {
57627
+ await removeWorktree(worktreePath);
57628
+ }
57629
+ else {
57630
+ await checkoutPrevious();
57563
57631
  }
57564
- }
57565
- }
57566
- finally {
57567
- // Clean up
57568
- if (useWorktree) {
57569
- await removeWorktree(worktreePath);
57570
- }
57571
- else {
57572
- await checkoutPrevious();
57573
57632
  }
57574
57633
  }
57575
57634
  // ========================================
@@ -57773,6 +57832,7 @@ function saveReport(report, markdown, outputDir) {
57773
57832
  external_fs_.appendFileSync(githubOutput, `has_differences=${report.diffCount > 0}\n`);
57774
57833
  external_fs_.appendFileSync(githubOutput, `passed_count=${report.passedCount}\n`);
57775
57834
  external_fs_.appendFileSync(githubOutput, `diff_count=${report.diffCount}\n`);
57835
+ external_fs_.appendFileSync(githubOutput, `total_configs=${report.results.length}\n`);
57776
57836
  }
57777
57837
  // Also set via core for compatibility
57778
57838
  lib_core.setOutput("report_path", mdPath);
@@ -57780,6 +57840,7 @@ function saveReport(report, markdown, outputDir) {
57780
57840
  lib_core.setOutput("has_differences", report.diffCount > 0);
57781
57841
  lib_core.setOutput("passed_count", report.passedCount);
57782
57842
  lib_core.setOutput("diff_count", report.diffCount);
57843
+ lib_core.setOutput("total_configs", report.results.length);
57783
57844
  }
57784
57845
  /**
57785
57846
  * Write a simple summary for PR comments
package/dist/types.d.ts CHANGED
@@ -18,6 +18,14 @@ export interface TestConfiguration {
18
18
  startup_wait_ms?: number;
19
19
  /** Command to run after stopping the MCP server for this config (cleanup) */
20
20
  post_test_command?: string;
21
+ /**
22
+ * Command to use for the base/comparison server instead of checking out a git ref.
23
+ * When set, skips git operations and uses this command directly for comparison.
24
+ * Useful for comparing against external servers (e.g., docker images of previous releases).
25
+ */
26
+ base_start_command?: string;
27
+ /** Server URL for base comparison (for HTTP transport with base_start_command) */
28
+ base_server_url?: string;
21
29
  }
22
30
  export interface CustomMessage {
23
31
  id: number;
@@ -53,6 +61,7 @@ export interface ActionInputs {
53
61
  }
54
62
  export interface ProbeResult {
55
63
  initialize: InitializeInfo | null;
64
+ instructions: string | null;
56
65
  tools: ToolsResult | null;
57
66
  prompts: PromptsResult | null;
58
67
  resources: ResourcesResult | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-server-diff",
3
- "version": "2.1.6",
3
+ "version": "2.2.0",
4
4
  "description": "Diff MCP server public interfaces - CLI tool and GitHub Action",
5
5
  "main": "dist/index.js",
6
6
  "bin": {