mcp-server-diff 2.1.5 → 2.1.6

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
@@ -533,25 +533,26 @@ npx mcp-server-diff -b "..." -t "..." -o summary # One-line summary (default)
533
533
 
534
534
  ### HTTP Headers & Authentication
535
535
 
536
- For authenticated HTTP endpoints, pass headers with `-H`:
536
+ For authenticated HTTP endpoints, pass headers with `-H` (target) or `--base-header`:
537
537
 
538
538
  ```bash
539
- # Direct header value
539
+ # Direct header value for target
540
540
  npx mcp-server-diff -b "./server" -t "https://api.example.com/mcp" \
541
541
  -H "Authorization: Bearer your-token-here"
542
542
 
543
543
  # Read from environment variable (keeps secrets out of shell history)
544
544
  export MCP_TOKEN="your-secret-token"
545
545
  npx mcp-server-diff -b "./server" -t "https://api.example.com/mcp" \
546
- -H "Authorization: env:MCP_TOKEN"
546
+ -H "Authorization: Bearer env:MCP_TOKEN"
547
547
 
548
- # Prompt for secret interactively (hidden input)
548
+ # Prompt for secret interactively (hidden input, named "token")
549
549
  npx mcp-server-diff -b "./server" -t "https://api.example.com/mcp" \
550
- -H "Authorization: secret:"
550
+ -H "Authorization: Bearer secret:token"
551
551
 
552
- # Multiple headers
553
- npx mcp-server-diff -b "./server" -t "https://api.example.com/mcp" \
554
- -H "Authorization: env:TOKEN" -H "X-Custom-Header: value"
552
+ # Headers for both sides (e.g., comparing two authenticated servers)
553
+ npx mcp-server-diff \
554
+ -b "https://api.example.com/v1/mcp" --base-header "Authorization: Bearer secret:v1token" \
555
+ -t "https://api.example.com/v2/mcp" -H "Authorization: Bearer secret:v2token"
555
556
  ```
556
557
 
557
558
  ### Config File
@@ -593,7 +594,9 @@ npx mcp-server-diff -c servers.json -o diff
593
594
  |--------|-------------|
594
595
  | `-b, --base <cmd\|url>` | Base server command (stdio) or URL (http) |
595
596
  | `-t, --target <cmd\|url>` | Target server command (stdio) or URL (http) |
596
- | `-H, --header <header>` | HTTP header (repeatable). Use `env:VAR` or `secret:` for values |
597
+ | `-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`) |
597
600
  | `-c, --config <file>` | Config file with base and targets |
598
601
  | `-o, --output <format>` | Output: `diff`, `json`, `markdown`, `summary` (default) |
599
602
  | `-v, --verbose` | Verbose output |
@@ -601,6 +604,11 @@ npx mcp-server-diff -c servers.json -o diff
601
604
  | `-h, --help` | Show help |
602
605
  | `--version` | Show version |
603
606
 
607
+ **Header value patterns:**
608
+ - `Bearer your-token` — literal value
609
+ - `Bearer env:VAR_NAME` — read from environment variable
610
+ - `Bearer secret:name` — prompt once for "name", reuse if used multiple times
611
+
604
612
  ---
605
613
 
606
614
  ## License
package/dist/cli/index.js CHANGED
@@ -56870,6 +56870,8 @@ function parseCliArgs() {
56870
56870
  base: { type: "string", short: "b" },
56871
56871
  target: { type: "string", short: "t" },
56872
56872
  header: { type: "string", short: "H", multiple: true },
56873
+ "base-header": { type: "string", multiple: true },
56874
+ "target-header": { type: "string", multiple: true },
56873
56875
  config: { type: "string", short: "c" },
56874
56876
  output: { type: "string", short: "o", default: "summary" },
56875
56877
  verbose: { type: "boolean", short: "v", default: false },
@@ -56895,17 +56897,18 @@ USAGE:
56895
56897
  mcp-server-diff --config servers.json
56896
56898
 
56897
56899
  OPTIONS:
56898
- -b, --base <command> Base server command (stdio) or URL (http)
56899
- -t, --target <command> Target server command (stdio) or URL (http)
56900
- -H, --header <header> HTTP header (can be repeated, e.g. -H "Authorization: Bearer ...")
56901
- Use env:VAR_NAME to read value from environment variable
56902
- Use secret: to prompt for value securely (hidden input)
56903
- -c, --config <file> Config file with base and targets
56904
- -o, --output <format> Output format: diff, json, markdown, summary (default: summary)
56905
- -v, --verbose Verbose output
56906
- -q, --quiet Quiet mode (only output diffs)
56907
- -h, --help Show this help
56908
- --version Show version
56900
+ -b, --base <command> Base server command (stdio) or URL (http)
56901
+ -t, --target <command> Target server command (stdio) or URL (http)
56902
+ -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)
56905
+ Values support: env:VAR_NAME, secret:name, "Bearer secret:token"
56906
+ -c, --config <file> Config file with base and targets
56907
+ -o, --output <format> Output format: diff, json, markdown, summary (default: summary)
56908
+ -v, --verbose Verbose output
56909
+ -q, --quiet Quiet mode (only output diffs)
56910
+ -h, --help Show this help
56911
+ --version Show version
56909
56912
 
56910
56913
  CONFIG FILE FORMAT:
56911
56914
  {
@@ -56990,8 +56993,10 @@ function commandToConfig(command, name, headers) {
56990
56993
  /**
56991
56994
  * Parse header strings into a record
56992
56995
  * Accepts formats: "Header: value" or "Header=value"
56993
- * Values starting with "env:" read from environment variables
56994
- * Values starting with "secret:" will be prompted (collected separately)
56996
+ * Special value patterns:
56997
+ * env:VAR_NAME - reads from environment variable
56998
+ * secret:name - prompts for secret (name is the prompt label)
56999
+ * "Bearer secret:token" - prefix + secret (prompts for "token", prepends "Bearer ")
56995
57000
  */
56996
57001
  function parseHeaders(headerStrings, secretValues) {
56997
57002
  const headers = {};
@@ -57013,14 +57018,17 @@ function parseHeaders(headerStrings, secretValues) {
57013
57018
  }
57014
57019
  value = envValue;
57015
57020
  }
57016
- else if (value.startsWith("secret:")) {
57017
- // Use prompted secret value
57018
- const secretKey = `${key}`;
57019
- if (secretValues?.has(secretKey)) {
57020
- value = secretValues.get(secretKey);
57021
- }
57022
- else {
57023
- throw new Error(`Secret value for header ${key} not collected`);
57021
+ else if (value.includes("secret:")) {
57022
+ // Replace secret:name with the prompted value
57023
+ const secretMatch = value.match(/secret:(\w+)/);
57024
+ if (secretMatch) {
57025
+ const secretName = secretMatch[1];
57026
+ if (secretValues?.has(secretName)) {
57027
+ value = value.replace(`secret:${secretName}`, secretValues.get(secretName));
57028
+ }
57029
+ else {
57030
+ throw new Error(`Secret value for "${secretName}" not collected`);
57031
+ }
57024
57032
  }
57025
57033
  }
57026
57034
  headers[key] = value;
@@ -57029,21 +57037,19 @@ function parseHeaders(headerStrings, secretValues) {
57029
57037
  return headers;
57030
57038
  }
57031
57039
  /**
57032
- * Find headers that need secret prompts
57040
+ * Find secrets that need prompts, returns array of {name, label} objects
57033
57041
  */
57034
- function findSecretHeaders(headerStrings) {
57042
+ function findSecrets(headerStrings) {
57035
57043
  const secrets = [];
57036
57044
  if (!headerStrings)
57037
57045
  return secrets;
57038
57046
  for (const h of headerStrings) {
57039
- const colonIdx = h.indexOf(":");
57040
- const eqIdx = h.indexOf("=");
57041
- const sepIdx = colonIdx > 0 ? colonIdx : eqIdx;
57042
- if (sepIdx > 0) {
57043
- const key = h.substring(0, sepIdx).trim();
57044
- const value = h.substring(sepIdx + 1).trim();
57045
- if (value.startsWith("secret:")) {
57046
- secrets.push(key);
57047
+ const secretMatch = h.match(/secret:(\w+)/);
57048
+ if (secretMatch) {
57049
+ const name = secretMatch[1];
57050
+ // Don't add duplicates
57051
+ if (!secrets.find((s) => s.name === name)) {
57052
+ secrets.push({ name, label: name });
57047
57053
  }
57048
57054
  }
57049
57055
  }
@@ -57101,14 +57107,14 @@ async function promptSecret(prompt) {
57101
57107
  /**
57102
57108
  * Prompt for secret values with hidden input
57103
57109
  */
57104
- async function promptSecrets(headerNames) {
57105
- const secrets = new Map();
57106
- if (headerNames.length === 0)
57107
- return secrets;
57108
- for (const name of headerNames) {
57109
- secrets.set(name, await promptSecret(`Enter value for header "${name}"`));
57110
+ async function promptSecrets(secrets) {
57111
+ const values = new Map();
57112
+ if (secrets.length === 0)
57113
+ return values;
57114
+ for (const { name, label } of secrets) {
57115
+ values.set(name, await promptSecret(`Enter ${label}`));
57110
57116
  }
57111
- return secrets;
57117
+ return values;
57112
57118
  }
57113
57119
  /**
57114
57120
  * Probe a server and return results
@@ -57336,14 +57342,21 @@ async function main() {
57336
57342
  config = loadConfig(values.config);
57337
57343
  }
57338
57344
  else if (values.base && values.target) {
57339
- const headerStrings = values.header;
57340
- // Prompt for any secret: values before parsing
57341
- const secretHeaderNames = findSecretHeaders(headerStrings);
57342
- const secretValues = await promptSecrets(secretHeaderNames);
57343
- const headers = parseHeaders(headerStrings, secretValues);
57345
+ // Combine -H and --target-header for target, use --base-header for base
57346
+ const baseHeaderStrings = values["base-header"];
57347
+ const targetHeaderStrings = [
57348
+ ...(values.header || []),
57349
+ ...(values["target-header"] || []),
57350
+ ];
57351
+ // Find all secrets needed from both header sets
57352
+ const allHeaderStrings = [...(baseHeaderStrings || []), ...targetHeaderStrings];
57353
+ const secrets = findSecrets(allHeaderStrings);
57354
+ const secretValues = await promptSecrets(secrets);
57355
+ const baseHeaders = parseHeaders(baseHeaderStrings, secretValues);
57356
+ const targetHeaders = parseHeaders(targetHeaderStrings.length > 0 ? targetHeaderStrings : undefined, secretValues);
57344
57357
  config = {
57345
- base: commandToConfig(values.base, "base"),
57346
- targets: [commandToConfig(values.target, "target", headers)],
57358
+ base: commandToConfig(values.base, "base", baseHeaders),
57359
+ targets: [commandToConfig(values.target, "target", targetHeaders)],
57347
57360
  };
57348
57361
  }
57349
57362
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-server-diff",
3
- "version": "2.1.5",
3
+ "version": "2.1.6",
4
4
  "description": "Diff MCP server public interfaces - CLI tool and GitHub Action",
5
5
  "main": "dist/index.js",
6
6
  "bin": {