@yawlabs/mcp-compliance 0.13.4 → 0.14.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/dist/index.js +144 -89
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4772,18 +4772,25 @@ var CATEGORY_LABELS = {
|
|
|
4772
4772
|
security: "Security"
|
|
4773
4773
|
};
|
|
4774
4774
|
var CATEGORY_ORDER = ["transport", "lifecycle", "tools", "resources", "prompts", "errors", "schema", "security"];
|
|
4775
|
+
var GRADE_ART = {
|
|
4776
|
+
A: [" \u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551", "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551", "\u2588\u2588\u2551 \u2588\u2588\u2551", "\u255A\u2550\u255D \u255A\u2550\u255D"],
|
|
4777
|
+
B: ["\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557", "\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D", "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557", "\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D", "\u255A\u2550\u2550\u2550\u2550\u2550\u255D "],
|
|
4778
|
+
C: [" \u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D", "\u2588\u2588\u2551 ", "\u2588\u2588\u2551 ", "\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557", " \u255A\u2550\u2550\u2550\u2550\u2550\u255D"],
|
|
4779
|
+
D: ["\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557", "\u2588\u2588\u2551 \u2588\u2588\u2551", "\u2588\u2588\u2551 \u2588\u2588\u2551", "\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D", "\u255A\u2550\u2550\u2550\u2550\u2550\u255D "],
|
|
4780
|
+
F: ["\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557", "\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D", "\u2588\u2588\u2588\u2588\u2588\u2557 ", "\u2588\u2588\u2554\u2550\u2550\u255D ", "\u2588\u2588\u2551 ", "\u255A\u2550\u255D "]
|
|
4781
|
+
};
|
|
4775
4782
|
function gradeColor(grade) {
|
|
4776
4783
|
switch (grade) {
|
|
4777
4784
|
case "A":
|
|
4778
|
-
return chalk.green.bold(
|
|
4785
|
+
return (s) => chalk.green.bold(s);
|
|
4779
4786
|
case "B":
|
|
4780
|
-
return chalk.greenBright.bold(
|
|
4787
|
+
return (s) => chalk.greenBright.bold(s);
|
|
4781
4788
|
case "C":
|
|
4782
|
-
return chalk.yellow.bold(
|
|
4789
|
+
return (s) => chalk.yellow.bold(s);
|
|
4783
4790
|
case "D":
|
|
4784
|
-
return chalk.rgb(255, 165, 0).bold(
|
|
4791
|
+
return (s) => chalk.rgb(255, 165, 0).bold(s);
|
|
4785
4792
|
case "F":
|
|
4786
|
-
return chalk.red.bold(
|
|
4793
|
+
return (s) => chalk.red.bold(s);
|
|
4787
4794
|
}
|
|
4788
4795
|
}
|
|
4789
4796
|
function overallColor(overall) {
|
|
@@ -4798,104 +4805,136 @@ function overallColor(overall) {
|
|
|
4798
4805
|
return overall;
|
|
4799
4806
|
}
|
|
4800
4807
|
}
|
|
4801
|
-
function
|
|
4802
|
-
|
|
4803
|
-
const
|
|
4804
|
-
|
|
4805
|
-
let line = `${icon} ${t.name}${req}${dur}
|
|
4806
|
-
${chalk.dim(` ${t.details}`)}`;
|
|
4807
|
-
if (!t.passed) {
|
|
4808
|
-
const def = TEST_DEFINITIONS.find((d) => d.id === t.id);
|
|
4809
|
-
if (def?.recommendation) {
|
|
4810
|
-
line += `
|
|
4811
|
-
${chalk.cyan(` Fix: ${def.recommendation}`)}`;
|
|
4812
|
-
}
|
|
4813
|
-
}
|
|
4814
|
-
return line;
|
|
4808
|
+
function makeBar(passed, total, width = 24) {
|
|
4809
|
+
if (total === 0) return { filled: "", rest: "\u2500".repeat(width) };
|
|
4810
|
+
const n = Math.max(0, Math.min(width, Math.round(passed / total * width)));
|
|
4811
|
+
return { filled: "\u2588".repeat(n), rest: "\u2591".repeat(width - n) };
|
|
4815
4812
|
}
|
|
4813
|
+
function barColor(passed, total) {
|
|
4814
|
+
if (total === 0) return (s) => chalk.dim(s);
|
|
4815
|
+
const pct2 = passed / total;
|
|
4816
|
+
if (pct2 >= 1) return (s) => chalk.green(s);
|
|
4817
|
+
if (pct2 >= 0.85) return (s) => chalk.greenBright(s);
|
|
4818
|
+
if (pct2 >= 0.6) return (s) => chalk.yellow(s);
|
|
4819
|
+
return (s) => chalk.red(s);
|
|
4820
|
+
}
|
|
4821
|
+
function padRight(s, n) {
|
|
4822
|
+
return s.length >= n ? s : s + " ".repeat(n - s.length);
|
|
4823
|
+
}
|
|
4824
|
+
function padLeft(s, n) {
|
|
4825
|
+
return s.length >= n ? s : " ".repeat(n - s.length) + s;
|
|
4826
|
+
}
|
|
4827
|
+
var RULE = "\u2500".repeat(62);
|
|
4828
|
+
var HEAVY_RULE = "\u2550".repeat(62);
|
|
4816
4829
|
function formatTerminal(report) {
|
|
4817
|
-
const
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4830
|
+
const out = [];
|
|
4831
|
+
const color = gradeColor(report.grade);
|
|
4832
|
+
const art = GRADE_ART[report.grade];
|
|
4833
|
+
out.push("");
|
|
4834
|
+
out.push(chalk.bold(" MCP COMPLIANCE REPORT"));
|
|
4835
|
+
out.push(chalk.dim(` ${HEAVY_RULE}`));
|
|
4822
4836
|
if (report.serverInfo.name) {
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
)
|
|
4827
|
-
);
|
|
4837
|
+
const v = report.serverInfo.version ? ` v${report.serverInfo.version}` : "";
|
|
4838
|
+
const proto = report.serverInfo.protocolVersion ? ` (protocol ${report.serverInfo.protocolVersion})` : "";
|
|
4839
|
+
out.push(chalk.dim(` Server: ${report.serverInfo.name}${v}${proto}`));
|
|
4828
4840
|
}
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
+
out.push(chalk.dim(` Target: ${report.url}`));
|
|
4842
|
+
out.push(chalk.dim(` Spec: ${report.specVersion} \xB7 Tool v${report.toolVersion} \xB7 ${report.timestamp}`));
|
|
4843
|
+
out.push("");
|
|
4844
|
+
const reqOk = report.summary.requiredPassed === report.summary.required;
|
|
4845
|
+
const infoRows = [
|
|
4846
|
+
"",
|
|
4847
|
+
"",
|
|
4848
|
+
`${chalk.bold("GRADE")} ${color(report.grade)} ${chalk.bold(`${report.score}%`)}`,
|
|
4849
|
+
`${chalk.dim("Overall ")}${overallColor(report.overall)}`,
|
|
4850
|
+
`${chalk.dim("Tests ")}${chalk.green(String(report.summary.passed))}${chalk.dim("/")}${report.summary.total}${report.summary.failed > 0 ? chalk.dim(" (") + chalk.red(`${report.summary.failed} failed`) + chalk.dim(")") : ""}`,
|
|
4851
|
+
`${chalk.dim("Required ")}${reqOk ? chalk.green(`${report.summary.requiredPassed}/${report.summary.required} \u2713`) : chalk.red(`${report.summary.requiredPassed}/${report.summary.required}`)}`
|
|
4852
|
+
];
|
|
4853
|
+
for (let i = 0; i < 6; i++) {
|
|
4854
|
+
out.push(` ${color(art[i])} ${infoRows[i] || ""}`);
|
|
4841
4855
|
}
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4856
|
+
out.push("");
|
|
4857
|
+
out.push(chalk.bold(" CATEGORY BREAKDOWN"));
|
|
4858
|
+
out.push(chalk.dim(` ${RULE}`));
|
|
4859
|
+
const cats = CATEGORY_ORDER.filter((c) => report.categories[c] && report.categories[c].total > 0);
|
|
4860
|
+
const maxLabel = Math.max(...cats.map((c) => (CATEGORY_LABELS[c] || c).length));
|
|
4861
|
+
for (const cat of cats) {
|
|
4862
|
+
const stats = report.categories[cat];
|
|
4846
4863
|
const label = CATEGORY_LABELS[cat] || cat;
|
|
4847
|
-
const
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
}
|
|
4854
|
-
const caps = report.serverInfo.capabilities;
|
|
4855
|
-
const declared = Object.keys(caps).filter((k) => caps[k] !== void 0);
|
|
4856
|
-
if (declared.length > 0) {
|
|
4857
|
-
lines.push("");
|
|
4858
|
-
lines.push(chalk.dim(` Capabilities: ${declared.join(", ")}`));
|
|
4859
|
-
}
|
|
4860
|
-
if (report.toolCount > 0) {
|
|
4861
|
-
lines.push(
|
|
4862
|
-
chalk.dim(
|
|
4863
|
-
` Tools (${report.toolCount}): ${report.toolNames.slice(0, 10).join(", ")}${report.toolCount > 10 ? "..." : ""}`
|
|
4864
|
-
)
|
|
4865
|
-
);
|
|
4866
|
-
}
|
|
4867
|
-
if (report.resourceCount > 0) {
|
|
4868
|
-
lines.push(
|
|
4869
|
-
chalk.dim(
|
|
4870
|
-
` Resources (${report.resourceCount}): ${report.resourceNames.slice(0, 10).join(", ")}${report.resourceCount > 10 ? "..." : ""}`
|
|
4871
|
-
)
|
|
4864
|
+
const { filled, rest } = makeBar(stats.passed, stats.total, 24);
|
|
4865
|
+
const colorFn = barColor(stats.passed, stats.total);
|
|
4866
|
+
const pct2 = stats.total === 0 ? 0 : Math.round(stats.passed / stats.total * 100);
|
|
4867
|
+
const ratio = `${stats.passed}/${stats.total}`;
|
|
4868
|
+
out.push(
|
|
4869
|
+
` ${padRight(label, maxLabel)} ${colorFn(filled)}${chalk.dim(rest)} ${padLeft(ratio, 7)} ${padLeft(`${pct2}%`, 4)}`
|
|
4872
4870
|
);
|
|
4873
4871
|
}
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
)
|
|
4872
|
+
out.push("");
|
|
4873
|
+
const failed = report.tests.filter((t) => !t.passed);
|
|
4874
|
+
if (failed.length > 0) {
|
|
4875
|
+
out.push(chalk.bold.red(` FAILED TESTS (${failed.length})`));
|
|
4876
|
+
out.push(chalk.dim(` ${RULE}`));
|
|
4877
|
+
for (const t of failed) {
|
|
4878
|
+
const req = t.required ? chalk.red("required") : chalk.dim("optional");
|
|
4879
|
+
out.push(
|
|
4880
|
+
` ${chalk.red("\u2717")} ${chalk.bold(t.name)} ${chalk.dim(`[${t.id}]`)} ${req} ${chalk.dim(`${t.durationMs}ms`)}`
|
|
4881
|
+
);
|
|
4882
|
+
out.push(` ${t.details}`);
|
|
4883
|
+
const def = TEST_DEFINITIONS.find((d) => d.id === t.id);
|
|
4884
|
+
if (def?.recommendation) {
|
|
4885
|
+
out.push(` ${chalk.cyan(`\u2192 ${def.recommendation}`)}`);
|
|
4886
|
+
}
|
|
4887
|
+
if (t.specRef) {
|
|
4888
|
+
out.push(chalk.dim(` spec: ${t.specRef}`));
|
|
4889
|
+
}
|
|
4890
|
+
out.push("");
|
|
4891
|
+
}
|
|
4892
|
+
} else {
|
|
4893
|
+
out.push(` ${chalk.green.bold("\u2713 All tests passed")}`);
|
|
4894
|
+
out.push("");
|
|
4880
4895
|
}
|
|
4881
4896
|
if (report.warnings.length > 0) {
|
|
4882
|
-
|
|
4883
|
-
|
|
4897
|
+
out.push(chalk.bold.yellow(` WARNINGS (${report.warnings.length})`));
|
|
4898
|
+
out.push(chalk.dim(` ${RULE}`));
|
|
4884
4899
|
for (const w of report.warnings) {
|
|
4885
|
-
|
|
4900
|
+
out.push(` ${chalk.yellow("!")} ${w}`);
|
|
4886
4901
|
}
|
|
4902
|
+
out.push("");
|
|
4903
|
+
}
|
|
4904
|
+
const caps = report.serverInfo.capabilities;
|
|
4905
|
+
const declared = Object.keys(caps).filter((k) => caps[k] !== void 0);
|
|
4906
|
+
const hasContext = declared.length > 0 || report.toolCount > 0 || report.resourceCount > 0 || report.promptCount > 0;
|
|
4907
|
+
if (hasContext) {
|
|
4908
|
+
out.push(chalk.bold(" SERVER CONTEXT"));
|
|
4909
|
+
out.push(chalk.dim(` ${RULE}`));
|
|
4910
|
+
if (declared.length > 0) {
|
|
4911
|
+
out.push(chalk.dim(` Capabilities: ${declared.join(", ")}`));
|
|
4912
|
+
}
|
|
4913
|
+
if (report.toolCount > 0) {
|
|
4914
|
+
const more = report.toolCount > 10 ? ", ..." : "";
|
|
4915
|
+
out.push(chalk.dim(` Tools (${report.toolCount}): ${report.toolNames.slice(0, 10).join(", ")}${more}`));
|
|
4916
|
+
}
|
|
4917
|
+
if (report.resourceCount > 0) {
|
|
4918
|
+
const more = report.resourceCount > 10 ? ", ..." : "";
|
|
4919
|
+
out.push(
|
|
4920
|
+
chalk.dim(` Resources (${report.resourceCount}): ${report.resourceNames.slice(0, 10).join(", ")}${more}`)
|
|
4921
|
+
);
|
|
4922
|
+
}
|
|
4923
|
+
if (report.promptCount > 0) {
|
|
4924
|
+
const more = report.promptCount > 10 ? ", ..." : "";
|
|
4925
|
+
out.push(chalk.dim(` Prompts (${report.promptCount}): ${report.promptNames.slice(0, 10).join(", ")}${more}`));
|
|
4926
|
+
}
|
|
4927
|
+
out.push("");
|
|
4887
4928
|
}
|
|
4888
|
-
lines.push("");
|
|
4889
4929
|
if (report.url.startsWith("stdio:")) {
|
|
4890
|
-
|
|
4891
|
-
chalk.dim(" Badge:
|
|
4930
|
+
out.push(
|
|
4931
|
+
chalk.dim(" Badge: stdio targets aren't published. Run with --output badge.svg for a local badge image.")
|
|
4892
4932
|
);
|
|
4893
4933
|
} else {
|
|
4894
|
-
|
|
4895
|
-
lines.push(` ${report.badge.markdown}`);
|
|
4934
|
+
out.push(chalk.dim(` Badge: ${report.badge.markdown}`));
|
|
4896
4935
|
}
|
|
4897
|
-
|
|
4898
|
-
return
|
|
4936
|
+
out.push("");
|
|
4937
|
+
return out.join("\n");
|
|
4899
4938
|
}
|
|
4900
4939
|
function formatJson(report) {
|
|
4901
4940
|
return JSON.stringify(report, null, 2);
|
|
@@ -5271,6 +5310,16 @@ function parsePositiveInt(value, name, min = 0) {
|
|
|
5271
5310
|
function parseList(value) {
|
|
5272
5311
|
return value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
5273
5312
|
}
|
|
5313
|
+
function ttyHint(format) {
|
|
5314
|
+
if (!process.stdout.isTTY) return;
|
|
5315
|
+
process.stderr.write(
|
|
5316
|
+
chalk2.dim(
|
|
5317
|
+
` [tip] --format=${format} is machine-readable. Redirect with \`> report.${format === "sarif" ? "sarif" : format}\` or drop --format for the human report.
|
|
5318
|
+
|
|
5319
|
+
`
|
|
5320
|
+
)
|
|
5321
|
+
);
|
|
5322
|
+
}
|
|
5274
5323
|
function parseEnvVar(value, prev) {
|
|
5275
5324
|
const idx = value.indexOf("=");
|
|
5276
5325
|
if (idx === -1) throw new Error(`Invalid env var: "${value}" (expected "KEY=VALUE")`);
|
|
@@ -5503,14 +5552,17 @@ Testing ${describeTarget(transportTarget)}...
|
|
|
5503
5552
|
console.log("");
|
|
5504
5553
|
}
|
|
5505
5554
|
if (opts.format === "json") {
|
|
5555
|
+
ttyHint("json");
|
|
5506
5556
|
console.log(formatJson(report2));
|
|
5507
5557
|
} else if (opts.format === "sarif") {
|
|
5558
|
+
ttyHint("sarif");
|
|
5508
5559
|
console.log(formatSarif(report2));
|
|
5509
5560
|
} else if (opts.format === "github") {
|
|
5510
5561
|
console.log(formatGithub(report2));
|
|
5511
5562
|
} else if (opts.format === "markdown") {
|
|
5512
5563
|
console.log(formatMarkdown(report2));
|
|
5513
5564
|
} else if (opts.format === "html") {
|
|
5565
|
+
ttyHint("html");
|
|
5514
5566
|
console.log(formatHtml(report2));
|
|
5515
5567
|
} else {
|
|
5516
5568
|
console.log(formatTerminal(report2));
|
|
@@ -5870,7 +5922,10 @@ program.command("mcp").description("Start the MCP compliance server (stdio trans
|
|
|
5870
5922
|
await startServer();
|
|
5871
5923
|
});
|
|
5872
5924
|
if (process.argv.length <= 2) {
|
|
5873
|
-
|
|
5874
|
-
|
|
5925
|
+
startServer().catch((err) => {
|
|
5926
|
+
console.error(err);
|
|
5927
|
+
process.exit(1);
|
|
5928
|
+
});
|
|
5929
|
+
} else {
|
|
5930
|
+
program.parse();
|
|
5875
5931
|
}
|
|
5876
|
-
program.parse();
|
package/package.json
CHANGED