@yawlabs/mcp-compliance 0.10.1 → 0.11.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 +11 -3
- package/dist/{chunk-7KISK3FS.js → chunk-DGGPE3ZM.js} +74 -1
- package/dist/index.js +73 -1
- package/dist/mcp/server.js +1 -1
- package/dist/runner.d.ts +11 -1
- package/dist/runner.js +5 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://github.com/YawLabs/mcp-compliance/stargazers)
|
|
6
6
|
[](https://github.com/YawLabs/mcp-compliance/actions/workflows/ci.yml)
|
|
7
7
|
|
|
8
|
-
**Test any MCP server for spec compliance.**
|
|
8
|
+
**Test any MCP server for spec compliance.** 88-test suite covering transport, lifecycle, tools, resources, prompts, error handling, schema validation, and security against the [MCP specification](https://modelcontextprotocol.io/specification/2025-11-25). Works against **HTTP endpoints** (`https://my-server.com/mcp`) and **stdio servers** (`npx @modelcontextprotocol/server-filesystem /tmp`) alike. CLI, MCP server, and programmatic API.
|
|
9
9
|
|
|
10
10
|
Built and maintained by [Yaw Labs](https://yaw.sh).
|
|
11
11
|
|
|
@@ -479,7 +479,7 @@ Restart your MCP client and approve the server when prompted.
|
|
|
479
479
|
|
|
480
480
|
### Tools
|
|
481
481
|
|
|
482
|
-
- **mcp_compliance_test** — Run the full
|
|
482
|
+
- **mcp_compliance_test** — Run the full 88-test suite against a URL or stdio command. Supports auth, custom headers, env vars, timeout, retries, and category/test filtering. Returns grade, score, and detailed results.
|
|
483
483
|
- **mcp_compliance_badge** — Get the badge markdown/HTML for a server. Supports auth and custom headers.
|
|
484
484
|
- **mcp_compliance_explain** — Explain what a specific test ID checks and why it matters.
|
|
485
485
|
|
|
@@ -540,8 +540,16 @@ Consumer guidance:
|
|
|
540
540
|
|
|
541
541
|
The compliance testing methodology is published as an open specification:
|
|
542
542
|
|
|
543
|
-
- **[MCP Compliance Testing Specification](./MCP_COMPLIANCE_SPEC.md)** — test execution model, scoring algorithm, all
|
|
543
|
+
- **[MCP Compliance Testing Specification](./MCP_COMPLIANCE_SPEC.md)** — test execution model, scoring algorithm, all 88 test rules with pass/fail criteria (CC BY 4.0)
|
|
544
544
|
- **[Machine-readable rule catalog](./mcp-compliance-rules.json)** — JSON Schema-compliant catalog for programmatic consumption
|
|
545
|
+
- **[Why `mcp-compliance`](./docs/WHY.md)** — the problem, existing alternatives, what this tool does differently
|
|
546
|
+
- **[Fixing common failures](./docs/FIXES.md)** — recipes for the most frequent test failures with code snippets
|
|
547
|
+
- **[Spec version migration policy](./docs/SPEC_VERSION_MIGRATION.md)** — how this tool evolves with MCP spec releases
|
|
548
|
+
- **[mcp.hosting external API](./docs/EXT_API.md)** — public submit/retrieve/badge/delete endpoints used by `mcp-compliance badge` and any custom integrations
|
|
549
|
+
- **[Enterprise tier (draft)](./docs/ENTERPRISE.md)** — paid tier structure for organizations with scheduled/private/audit-track compliance needs
|
|
550
|
+
- **[Performance deep-dive](./docs/PERFORMANCE.md)** — why the suite is sequential and what parallel execution would cost
|
|
551
|
+
- **[Spec PR drafts](./docs/spec-prs/)** — our proposed MCP spec clarifications for ambiguous cases we've hit
|
|
552
|
+
- **[mcp.hosting integration spec](./docs/mcp-hosting-integration.md)** — the contract between this engine and the mcp.hosting platform: URL surfaces, data flow, storage model, badge API, leaderboard, router integration
|
|
545
553
|
|
|
546
554
|
These are complementary to (not competing with) the [official MCP specification](https://modelcontextprotocol.io/specification/2025-11-25). The MCP spec defines what servers must do; this spec defines how to verify compliance.
|
|
547
555
|
|
|
@@ -701,6 +701,33 @@ var TEST_DEFINITIONS = [
|
|
|
701
701
|
description: "Sends a tools/call request with _meta.progressToken and checks if the server sends progress notifications via SSE. Progress support is optional but recommended for long-running operations.",
|
|
702
702
|
recommendation: "When a request includes _meta.progressToken, send notifications/progress events via SSE to report progress. Include progressToken, progress (current), and optionally total fields."
|
|
703
703
|
},
|
|
704
|
+
{
|
|
705
|
+
id: "lifecycle-sampling-capability",
|
|
706
|
+
name: "Sampling capability shape",
|
|
707
|
+
category: "lifecycle",
|
|
708
|
+
required: false,
|
|
709
|
+
specRef: "client/sampling",
|
|
710
|
+
description: "If the server's initialize response or serverInfo implies it uses client-side sampling (sampling/createMessage), verify the capability declaration shape. Currently this is an advisory shape check \u2014 actually exercising the server\u2192client flow requires a client-side sampling handler and is out of scope.",
|
|
711
|
+
recommendation: "Sampling is a client capability (the client provides LLM access to the server). Servers don't declare sampling in their own capabilities; they just call sampling/createMessage against clients that advertise it. No server-side action required."
|
|
712
|
+
},
|
|
713
|
+
{
|
|
714
|
+
id: "lifecycle-roots-capability",
|
|
715
|
+
name: "Roots capability shape",
|
|
716
|
+
category: "lifecycle",
|
|
717
|
+
required: false,
|
|
718
|
+
specRef: "client/roots",
|
|
719
|
+
description: "Roots (filesystem root paths) is a client capability. This test verifies that if a server sends roots/list requests, it handles gracefully when the client doesn't declare the roots capability (i.e., doesn't crash).",
|
|
720
|
+
recommendation: "Before calling roots/list, check if the initialized client capabilities include 'roots'. If not, skip the call \u2014 the client can't respond. Never assume roots is available; it's opt-in on the client side."
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
id: "lifecycle-elicitation-capability",
|
|
724
|
+
name: "Elicitation capability shape",
|
|
725
|
+
category: "lifecycle",
|
|
726
|
+
required: false,
|
|
727
|
+
specRef: "client/elicitation",
|
|
728
|
+
description: "Elicitation (asking the user for structured input mid-operation) is a client capability added in 2025-11-25. This test verifies servers that use elicitation/create handle the case where clients don't support it.",
|
|
729
|
+
recommendation: "Before calling elicitation/create, check the initialized client capabilities. If elicitation is absent, fall back to a safer default (ask once up-front via tool parameters, or fail cleanly with a clear error)."
|
|
730
|
+
},
|
|
704
731
|
{
|
|
705
732
|
id: "lifecycle-meta-tolerance",
|
|
706
733
|
name: "Tolerates _meta field on requests",
|
|
@@ -1604,7 +1631,11 @@ async function runComplianceSuite(target, options = {}) {
|
|
|
1604
1631
|
try {
|
|
1605
1632
|
initRes = await rpc("initialize", {
|
|
1606
1633
|
protocolVersion: SPEC_VERSION,
|
|
1607
|
-
capabilities: {
|
|
1634
|
+
capabilities: {
|
|
1635
|
+
sampling: {},
|
|
1636
|
+
roots: { listChanged: true },
|
|
1637
|
+
elicitation: {}
|
|
1638
|
+
},
|
|
1608
1639
|
clientInfo: { name: "mcp-compliance", version: TOOL_VERSION }
|
|
1609
1640
|
});
|
|
1610
1641
|
const result = initRes?.body?.result;
|
|
@@ -2026,6 +2057,47 @@ async function runComplianceSuite(target, options = {}) {
|
|
|
2026
2057
|
}
|
|
2027
2058
|
}
|
|
2028
2059
|
);
|
|
2060
|
+
await test(
|
|
2061
|
+
"lifecycle-sampling-capability",
|
|
2062
|
+
"Sampling capability shape",
|
|
2063
|
+
"lifecycle",
|
|
2064
|
+
false,
|
|
2065
|
+
"client/sampling",
|
|
2066
|
+
async () => {
|
|
2067
|
+
if (!initRes || initRes.body?.error) {
|
|
2068
|
+
return { passed: false, details: "Server rejected initialize" };
|
|
2069
|
+
}
|
|
2070
|
+
return {
|
|
2071
|
+
passed: true,
|
|
2072
|
+
details: "Server accepted initialize with client sampling capability. Full server\u2192client sampling flow not exercised."
|
|
2073
|
+
};
|
|
2074
|
+
}
|
|
2075
|
+
);
|
|
2076
|
+
await test("lifecycle-roots-capability", "Roots capability shape", "lifecycle", false, "client/roots", async () => {
|
|
2077
|
+
if (!initRes || initRes.body?.error) {
|
|
2078
|
+
return { passed: false, details: "Server rejected initialize" };
|
|
2079
|
+
}
|
|
2080
|
+
return {
|
|
2081
|
+
passed: true,
|
|
2082
|
+
details: "Server accepted initialize. Full server\u2192client roots/list flow not exercised (requires a roots-aware client)."
|
|
2083
|
+
};
|
|
2084
|
+
});
|
|
2085
|
+
await test(
|
|
2086
|
+
"lifecycle-elicitation-capability",
|
|
2087
|
+
"Elicitation capability shape",
|
|
2088
|
+
"lifecycle",
|
|
2089
|
+
false,
|
|
2090
|
+
"client/elicitation",
|
|
2091
|
+
async () => {
|
|
2092
|
+
if (!initRes || initRes.body?.error) {
|
|
2093
|
+
return { passed: false, details: "Server rejected initialize" };
|
|
2094
|
+
}
|
|
2095
|
+
return {
|
|
2096
|
+
passed: true,
|
|
2097
|
+
details: "Server accepted initialize. Full server\u2192client elicitation/create flow not exercised."
|
|
2098
|
+
};
|
|
2099
|
+
}
|
|
2100
|
+
);
|
|
2029
2101
|
await test(
|
|
2030
2102
|
"lifecycle-meta-tolerance",
|
|
2031
2103
|
"Tolerates _meta field on requests",
|
|
@@ -3958,6 +4030,7 @@ async function runComplianceSuite(target, options = {}) {
|
|
|
3958
4030
|
}
|
|
3959
4031
|
|
|
3960
4032
|
export {
|
|
4033
|
+
urlHash,
|
|
3961
4034
|
generateBadge,
|
|
3962
4035
|
computeGrade,
|
|
3963
4036
|
computeScore,
|
package/dist/index.js
CHANGED
|
@@ -1047,6 +1047,33 @@ var TEST_DEFINITIONS = [
|
|
|
1047
1047
|
description: "Sends a tools/call request with _meta.progressToken and checks if the server sends progress notifications via SSE. Progress support is optional but recommended for long-running operations.",
|
|
1048
1048
|
recommendation: "When a request includes _meta.progressToken, send notifications/progress events via SSE to report progress. Include progressToken, progress (current), and optionally total fields."
|
|
1049
1049
|
},
|
|
1050
|
+
{
|
|
1051
|
+
id: "lifecycle-sampling-capability",
|
|
1052
|
+
name: "Sampling capability shape",
|
|
1053
|
+
category: "lifecycle",
|
|
1054
|
+
required: false,
|
|
1055
|
+
specRef: "client/sampling",
|
|
1056
|
+
description: "If the server's initialize response or serverInfo implies it uses client-side sampling (sampling/createMessage), verify the capability declaration shape. Currently this is an advisory shape check \u2014 actually exercising the server\u2192client flow requires a client-side sampling handler and is out of scope.",
|
|
1057
|
+
recommendation: "Sampling is a client capability (the client provides LLM access to the server). Servers don't declare sampling in their own capabilities; they just call sampling/createMessage against clients that advertise it. No server-side action required."
|
|
1058
|
+
},
|
|
1059
|
+
{
|
|
1060
|
+
id: "lifecycle-roots-capability",
|
|
1061
|
+
name: "Roots capability shape",
|
|
1062
|
+
category: "lifecycle",
|
|
1063
|
+
required: false,
|
|
1064
|
+
specRef: "client/roots",
|
|
1065
|
+
description: "Roots (filesystem root paths) is a client capability. This test verifies that if a server sends roots/list requests, it handles gracefully when the client doesn't declare the roots capability (i.e., doesn't crash).",
|
|
1066
|
+
recommendation: "Before calling roots/list, check if the initialized client capabilities include 'roots'. If not, skip the call \u2014 the client can't respond. Never assume roots is available; it's opt-in on the client side."
|
|
1067
|
+
},
|
|
1068
|
+
{
|
|
1069
|
+
id: "lifecycle-elicitation-capability",
|
|
1070
|
+
name: "Elicitation capability shape",
|
|
1071
|
+
category: "lifecycle",
|
|
1072
|
+
required: false,
|
|
1073
|
+
specRef: "client/elicitation",
|
|
1074
|
+
description: "Elicitation (asking the user for structured input mid-operation) is a client capability added in 2025-11-25. This test verifies servers that use elicitation/create handle the case where clients don't support it.",
|
|
1075
|
+
recommendation: "Before calling elicitation/create, check the initialized client capabilities. If elicitation is absent, fall back to a safer default (ask once up-front via tool parameters, or fail cleanly with a clear error)."
|
|
1076
|
+
},
|
|
1050
1077
|
{
|
|
1051
1078
|
id: "lifecycle-meta-tolerance",
|
|
1052
1079
|
name: "Tolerates _meta field on requests",
|
|
@@ -1950,7 +1977,11 @@ async function runComplianceSuite(target, options = {}) {
|
|
|
1950
1977
|
try {
|
|
1951
1978
|
initRes = await rpc("initialize", {
|
|
1952
1979
|
protocolVersion: SPEC_VERSION,
|
|
1953
|
-
capabilities: {
|
|
1980
|
+
capabilities: {
|
|
1981
|
+
sampling: {},
|
|
1982
|
+
roots: { listChanged: true },
|
|
1983
|
+
elicitation: {}
|
|
1984
|
+
},
|
|
1954
1985
|
clientInfo: { name: "mcp-compliance", version: TOOL_VERSION }
|
|
1955
1986
|
});
|
|
1956
1987
|
const result = initRes?.body?.result;
|
|
@@ -2372,6 +2403,47 @@ async function runComplianceSuite(target, options = {}) {
|
|
|
2372
2403
|
}
|
|
2373
2404
|
}
|
|
2374
2405
|
);
|
|
2406
|
+
await test(
|
|
2407
|
+
"lifecycle-sampling-capability",
|
|
2408
|
+
"Sampling capability shape",
|
|
2409
|
+
"lifecycle",
|
|
2410
|
+
false,
|
|
2411
|
+
"client/sampling",
|
|
2412
|
+
async () => {
|
|
2413
|
+
if (!initRes || initRes.body?.error) {
|
|
2414
|
+
return { passed: false, details: "Server rejected initialize" };
|
|
2415
|
+
}
|
|
2416
|
+
return {
|
|
2417
|
+
passed: true,
|
|
2418
|
+
details: "Server accepted initialize with client sampling capability. Full server\u2192client sampling flow not exercised."
|
|
2419
|
+
};
|
|
2420
|
+
}
|
|
2421
|
+
);
|
|
2422
|
+
await test("lifecycle-roots-capability", "Roots capability shape", "lifecycle", false, "client/roots", async () => {
|
|
2423
|
+
if (!initRes || initRes.body?.error) {
|
|
2424
|
+
return { passed: false, details: "Server rejected initialize" };
|
|
2425
|
+
}
|
|
2426
|
+
return {
|
|
2427
|
+
passed: true,
|
|
2428
|
+
details: "Server accepted initialize. Full server\u2192client roots/list flow not exercised (requires a roots-aware client)."
|
|
2429
|
+
};
|
|
2430
|
+
});
|
|
2431
|
+
await test(
|
|
2432
|
+
"lifecycle-elicitation-capability",
|
|
2433
|
+
"Elicitation capability shape",
|
|
2434
|
+
"lifecycle",
|
|
2435
|
+
false,
|
|
2436
|
+
"client/elicitation",
|
|
2437
|
+
async () => {
|
|
2438
|
+
if (!initRes || initRes.body?.error) {
|
|
2439
|
+
return { passed: false, details: "Server rejected initialize" };
|
|
2440
|
+
}
|
|
2441
|
+
return {
|
|
2442
|
+
passed: true,
|
|
2443
|
+
details: "Server accepted initialize. Full server\u2192client elicitation/create flow not exercised."
|
|
2444
|
+
};
|
|
2445
|
+
}
|
|
2446
|
+
);
|
|
2375
2447
|
await test(
|
|
2376
2448
|
"lifecycle-meta-tolerance",
|
|
2377
2449
|
"Tolerates _meta field on requests",
|
package/dist/mcp/server.js
CHANGED
package/dist/runner.d.ts
CHANGED
|
@@ -98,6 +98,16 @@ declare function computeScore(tests: TestResult[]): {
|
|
|
98
98
|
}>;
|
|
99
99
|
};
|
|
100
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Generate a short, deterministic hash of a URL for badge paths.
|
|
103
|
+
* SHA-256 truncated to 24 hex chars (96 bits of entropy) — matches the
|
|
104
|
+
* server-side hash width used by mcp.hosting for `/compliance/ext/<hash>`.
|
|
105
|
+
*
|
|
106
|
+
* Exported so mcp.hosting (and other consumers) can compute matching
|
|
107
|
+
* hashes when looking up reports/badges by URL. The hash is the canonical
|
|
108
|
+
* key for `/compliance/ext/<hash>` and `/api/compliance/ext/<hash>/badge`.
|
|
109
|
+
*/
|
|
110
|
+
declare function urlHash(url: string): string;
|
|
101
111
|
/**
|
|
102
112
|
* Generate badge URLs and markdown for a compliance report.
|
|
103
113
|
* Badge images are served by mcp.hosting.
|
|
@@ -165,4 +175,4 @@ interface RunOptions {
|
|
|
165
175
|
*/
|
|
166
176
|
declare function runComplianceSuite(target: string | TransportTarget, options?: RunOptions): Promise<ComplianceReport>;
|
|
167
177
|
|
|
168
|
-
export { type ComplianceReport, type PreviewOptions, type RunOptions, SPEC_BASE, SPEC_VERSION, TEST_DEFINITIONS, type TestResult, computeGrade, computeScore, generateBadge, parseSSEResponse, previewTests, runComplianceSuite };
|
|
178
|
+
export { type ComplianceReport, type PreviewOptions, type RunOptions, SPEC_BASE, SPEC_VERSION, TEST_DEFINITIONS, type TestResult, computeGrade, computeScore, generateBadge, parseSSEResponse, previewTests, runComplianceSuite, urlHash };
|
package/dist/runner.js
CHANGED
|
@@ -7,8 +7,9 @@ import {
|
|
|
7
7
|
generateBadge,
|
|
8
8
|
parseSSEResponse,
|
|
9
9
|
previewTests,
|
|
10
|
-
runComplianceSuite
|
|
11
|
-
|
|
10
|
+
runComplianceSuite,
|
|
11
|
+
urlHash
|
|
12
|
+
} from "./chunk-DGGPE3ZM.js";
|
|
12
13
|
export {
|
|
13
14
|
SPEC_BASE,
|
|
14
15
|
SPEC_VERSION,
|
|
@@ -18,5 +19,6 @@ export {
|
|
|
18
19
|
generateBadge,
|
|
19
20
|
parseSSEResponse,
|
|
20
21
|
previewTests,
|
|
21
|
-
runComplianceSuite
|
|
22
|
+
runComplianceSuite,
|
|
23
|
+
urlHash
|
|
22
24
|
};
|
package/package.json
CHANGED