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 +17 -9
- package/dist/cli/index.js +58 -45
- package/package.json +1 -1
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
|
-
#
|
|
553
|
-
npx mcp-server-diff
|
|
554
|
-
-
|
|
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)
|
|
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>
|
|
56899
|
-
-t, --target <command>
|
|
56900
|
-
-H, --header <header>
|
|
56901
|
-
|
|
56902
|
-
|
|
56903
|
-
|
|
56904
|
-
-
|
|
56905
|
-
-
|
|
56906
|
-
-
|
|
56907
|
-
-
|
|
56908
|
-
|
|
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
|
-
*
|
|
56994
|
-
*
|
|
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.
|
|
57017
|
-
//
|
|
57018
|
-
const
|
|
57019
|
-
if (
|
|
57020
|
-
|
|
57021
|
-
|
|
57022
|
-
|
|
57023
|
-
|
|
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
|
|
57040
|
+
* Find secrets that need prompts, returns array of {name, label} objects
|
|
57033
57041
|
*/
|
|
57034
|
-
function
|
|
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
|
|
57040
|
-
|
|
57041
|
-
|
|
57042
|
-
|
|
57043
|
-
|
|
57044
|
-
|
|
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(
|
|
57105
|
-
const
|
|
57106
|
-
if (
|
|
57107
|
-
return
|
|
57108
|
-
for (const name of
|
|
57109
|
-
|
|
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
|
|
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
|
-
|
|
57340
|
-
|
|
57341
|
-
const
|
|
57342
|
-
|
|
57343
|
-
|
|
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",
|
|
57358
|
+
base: commandToConfig(values.base, "base", baseHeaders),
|
|
57359
|
+
targets: [commandToConfig(values.target, "target", targetHeaders)],
|
|
57347
57360
|
};
|
|
57348
57361
|
}
|
|
57349
57362
|
else {
|