@yawlabs/mcp-compliance 0.1.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.
@@ -0,0 +1,128 @@
1
+ import {
2
+ TEST_DEFINITIONS,
3
+ runComplianceSuite
4
+ } from "../chunk-4AQGMM2X.js";
5
+
6
+ // src/mcp/server.ts
7
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9
+ import { z } from "zod";
10
+ var server = new McpServer({
11
+ name: "mcp-compliance",
12
+ version: "0.1.0"
13
+ });
14
+ server.tool(
15
+ "mcp_compliance_test",
16
+ "Run the full MCP compliance test suite against a server URL. Returns grade (A-F), score, and detailed results for all 24 tests.",
17
+ {
18
+ url: z.string().url().describe("The MCP server URL to test (must be HTTP or HTTPS)")
19
+ },
20
+ async ({ url }) => {
21
+ try {
22
+ const report = await runComplianceSuite(url);
23
+ const summary = [
24
+ `Grade: ${report.grade} (${report.score}%)`,
25
+ `Overall: ${report.overall}`,
26
+ `Tests: ${report.summary.passed}/${report.summary.total} passed (${report.summary.requiredPassed}/${report.summary.required} required)`,
27
+ "",
28
+ ...report.tests.map(
29
+ (t) => `${t.passed ? "PASS" : "FAIL"} ${t.name}${t.required ? " (required)" : ""} \u2014 ${t.details}`
30
+ )
31
+ ];
32
+ if (report.serverInfo.name) {
33
+ summary.unshift(`Server: ${report.serverInfo.name} v${report.serverInfo.version || "?"}`);
34
+ }
35
+ return {
36
+ content: [
37
+ { type: "text", text: summary.join("\n") },
38
+ { type: "text", text: `
39
+
40
+ Full report:
41
+ ${JSON.stringify(report, null, 2)}` }
42
+ ]
43
+ };
44
+ } catch (err) {
45
+ return {
46
+ content: [{ type: "text", text: `Error running compliance test: ${err.message}` }],
47
+ isError: true
48
+ };
49
+ }
50
+ }
51
+ );
52
+ server.tool(
53
+ "mcp_compliance_badge",
54
+ "Get the badge markdown embed code for an MCP server. Runs the compliance test suite first to determine the grade.",
55
+ {
56
+ url: z.string().url().describe("The MCP server URL to test")
57
+ },
58
+ async ({ url }) => {
59
+ try {
60
+ const report = await runComplianceSuite(url);
61
+ const badge = report.badge;
62
+ return {
63
+ content: [{
64
+ type: "text",
65
+ text: [
66
+ `Grade: ${report.grade} (${report.score}%)`,
67
+ "",
68
+ "Markdown:",
69
+ badge.markdown,
70
+ "",
71
+ "HTML:",
72
+ badge.html
73
+ ].join("\n")
74
+ }]
75
+ };
76
+ } catch (err) {
77
+ return {
78
+ content: [{ type: "text", text: `Error: ${err.message}` }],
79
+ isError: true
80
+ };
81
+ }
82
+ }
83
+ );
84
+ server.tool(
85
+ "mcp_compliance_explain",
86
+ "Explain what a specific compliance test ID checks and why it matters.",
87
+ {
88
+ testId: z.string().describe('The test ID to explain (e.g., "transport-post", "lifecycle-init", "tools-schema")')
89
+ },
90
+ async ({ testId }) => {
91
+ const def = TEST_DEFINITIONS.find((t) => t.id === testId);
92
+ if (!def) {
93
+ const ids = TEST_DEFINITIONS.map((t) => t.id).join(", ");
94
+ return {
95
+ content: [{
96
+ type: "text",
97
+ text: `Unknown test ID: "${testId}"
98
+
99
+ Valid test IDs:
100
+ ${ids}`
101
+ }],
102
+ isError: true
103
+ };
104
+ }
105
+ return {
106
+ content: [{
107
+ type: "text",
108
+ text: [
109
+ `Test: ${def.id}`,
110
+ `Name: ${def.name}`,
111
+ `Category: ${def.category}`,
112
+ `Required: ${def.required ? "Yes" : "No"}`,
113
+ `Spec reference: https://modelcontextprotocol.io/specification/2025-11-25/${def.specRef}`,
114
+ "",
115
+ def.description
116
+ ].join("\n")
117
+ }]
118
+ };
119
+ }
120
+ );
121
+ async function main() {
122
+ const transport = new StdioServerTransport();
123
+ await server.connect(transport);
124
+ }
125
+ main().catch((err) => {
126
+ console.error("MCP server error:", err);
127
+ process.exit(1);
128
+ });
@@ -0,0 +1,99 @@
1
+ type TestCategory = 'transport' | 'lifecycle' | 'tools' | 'resources' | 'prompts' | 'errors' | 'schema';
2
+ interface TestResult {
3
+ id: string;
4
+ name: string;
5
+ category: TestCategory;
6
+ passed: boolean;
7
+ required: boolean;
8
+ details: string;
9
+ durationMs: number;
10
+ specRef?: string;
11
+ }
12
+ type Grade = 'A' | 'B' | 'C' | 'D' | 'F';
13
+ type Overall = 'pass' | 'partial' | 'fail';
14
+ interface ComplianceReport {
15
+ specVersion: string;
16
+ url: string;
17
+ timestamp: string;
18
+ score: number;
19
+ grade: Grade;
20
+ overall: Overall;
21
+ summary: {
22
+ total: number;
23
+ passed: number;
24
+ failed: number;
25
+ required: number;
26
+ requiredPassed: number;
27
+ };
28
+ categories: Record<string, {
29
+ passed: number;
30
+ total: number;
31
+ }>;
32
+ tests: TestResult[];
33
+ serverInfo: {
34
+ protocolVersion: string | null;
35
+ name: string | null;
36
+ version: string | null;
37
+ capabilities: Record<string, unknown>;
38
+ };
39
+ toolCount: number;
40
+ toolNames: string[];
41
+ resourceCount: number;
42
+ promptCount: number;
43
+ badge: {
44
+ imageUrl: string;
45
+ reportUrl: string;
46
+ markdown: string;
47
+ html: string;
48
+ };
49
+ }
50
+ interface TestDefinition {
51
+ id: string;
52
+ name: string;
53
+ category: TestCategory;
54
+ required: boolean;
55
+ specRef: string;
56
+ description: string;
57
+ }
58
+ /** All 24 test IDs with descriptions for the explain command */
59
+ declare const TEST_DEFINITIONS: TestDefinition[];
60
+
61
+ declare function computeGrade(score: number): Grade;
62
+ declare function computeScore(tests: TestResult[]): {
63
+ score: number;
64
+ grade: Grade;
65
+ overall: 'pass' | 'partial' | 'fail';
66
+ summary: {
67
+ total: number;
68
+ passed: number;
69
+ failed: number;
70
+ required: number;
71
+ requiredPassed: number;
72
+ };
73
+ categories: Record<string, {
74
+ passed: number;
75
+ total: number;
76
+ }>;
77
+ };
78
+
79
+ /**
80
+ * Generate badge URLs and markdown for a compliance report.
81
+ * Badge images are served by mcp.hosting.
82
+ */
83
+ declare function generateBadge(url: string): {
84
+ imageUrl: string;
85
+ reportUrl: string;
86
+ markdown: string;
87
+ html: string;
88
+ };
89
+
90
+ interface RunOptions {
91
+ /** Optional callback for progress updates */
92
+ onProgress?: (testId: string, passed: boolean, details: string) => void;
93
+ }
94
+ /**
95
+ * Run the full MCP compliance test suite against a URL.
96
+ */
97
+ declare function runComplianceSuite(url: string, options?: RunOptions): Promise<ComplianceReport>;
98
+
99
+ export { type ComplianceReport, type RunOptions, TEST_DEFINITIONS, type TestResult, computeGrade, computeScore, generateBadge, runComplianceSuite };
package/dist/runner.js ADDED
@@ -0,0 +1,14 @@
1
+ import {
2
+ TEST_DEFINITIONS,
3
+ computeGrade,
4
+ computeScore,
5
+ generateBadge,
6
+ runComplianceSuite
7
+ } from "./chunk-4AQGMM2X.js";
8
+ export {
9
+ TEST_DEFINITIONS,
10
+ computeGrade,
11
+ computeScore,
12
+ generateBadge,
13
+ runComplianceSuite
14
+ };
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@yawlabs/mcp-compliance",
3
+ "version": "0.1.0",
4
+ "description": "CLI tool and MCP server that tests MCP servers for spec compliance",
5
+ "license": "MIT",
6
+ "author": "Yaw Labs (https://yaw.sh)",
7
+ "type": "module",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/runner.js",
11
+ "types": "./dist/runner.d.ts"
12
+ }
13
+ },
14
+ "main": "./dist/runner.js",
15
+ "types": "./dist/runner.d.ts",
16
+ "bin": {
17
+ "mcp-compliance": "./dist/index.js"
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "README.md"
22
+ ],
23
+ "scripts": {
24
+ "build": "tsup",
25
+ "dev": "tsup --watch",
26
+ "test": "vitest run",
27
+ "lint": "tsc --noEmit",
28
+ "prepublishOnly": "npm run build"
29
+ },
30
+ "dependencies": {
31
+ "@modelcontextprotocol/sdk": "^1.12.1",
32
+ "chalk": "^5.4.1",
33
+ "commander": "^13.1.0",
34
+ "undici": "^7.8.0",
35
+ "zod": "^3.24.4"
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "^25.5.2",
39
+ "tsup": "^8.4.0",
40
+ "typescript": "^5.8.3",
41
+ "vitest": "^3.1.1"
42
+ },
43
+ "engines": {
44
+ "node": ">=18"
45
+ },
46
+ "keywords": [
47
+ "mcp",
48
+ "model-context-protocol",
49
+ "compliance",
50
+ "testing",
51
+ "cli"
52
+ ],
53
+ "repository": {
54
+ "type": "git",
55
+ "url": "https://github.com/yawlabs/mcp-compliance"
56
+ },
57
+ "homepage": "https://mcp.hosting"
58
+ }