@yawlabs/mcp-compliance 0.2.0 → 0.3.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.
@@ -1,132 +1,184 @@
1
1
  import {
2
2
  TEST_DEFINITIONS,
3
3
  runComplianceSuite
4
- } from "../chunk-SP24UFRC.js";
4
+ } from "../chunk-U66YZGE5.js";
5
5
 
6
6
  // src/mcp/server.ts
7
+ import { createRequire } from "module";
7
8
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
9
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
10
+
11
+ // src/mcp/tools.ts
9
12
  import { z } from "zod";
10
- import { createRequire } from "module";
11
- var require2 = createRequire(import.meta.url);
12
- var { version } = require2("../../package.json");
13
- var server = new McpServer({
14
- name: "mcp-compliance",
15
- version
16
- });
17
- server.tool(
18
- "mcp_compliance_test",
19
- "Run the full MCP compliance test suite against a server URL. Returns grade (A-F), score, and detailed results for all 43 tests covering transport, lifecycle, tools, resources, prompts, errors, and schema validation.",
20
- {
21
- url: z.string().url().describe("The MCP server URL to test (must be HTTP or HTTPS)")
22
- },
23
- async ({ url }) => {
24
- try {
25
- const report = await runComplianceSuite(url);
26
- const summary = [
27
- `Grade: ${report.grade} (${report.score}%)`,
28
- `Overall: ${report.overall}`,
29
- `Tests: ${report.summary.passed}/${report.summary.total} passed (${report.summary.requiredPassed}/${report.summary.required} required)`,
30
- "",
31
- ...report.tests.map(
32
- (t) => `${t.passed ? "PASS" : "FAIL"} ${t.name}${t.required ? " (required)" : ""} \u2014 ${t.details}`
33
- )
34
- ];
35
- if (report.serverInfo.name) {
36
- summary.unshift(`Server: ${report.serverInfo.name} v${report.serverInfo.version || "?"}`);
37
- }
38
- if (report.warnings.length > 0) {
39
- summary.push("", `Warnings (${report.warnings.length}):`);
40
- for (const w of report.warnings) {
41
- summary.push(` - ${w}`);
13
+ function registerTools(server2) {
14
+ server2.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 43 tests covering transport, lifecycle, tools, resources, prompts, errors, and schema validation.",
17
+ {
18
+ url: z.string().url().describe("The MCP server URL to test (must be HTTP or HTTPS)"),
19
+ auth: z.string().optional().describe('Authorization header value (e.g., "Bearer tok123")'),
20
+ headers: z.record(z.string()).optional().describe('Additional headers to include on all requests (e.g., {"X-Api-Key": "abc"})'),
21
+ timeout: z.number().optional().describe("Request timeout in milliseconds (default: 15000)"),
22
+ retries: z.number().optional().describe("Number of retries for failed tests (default: 0)"),
23
+ only: z.array(z.string()).optional().describe("Only run tests matching these categories or test IDs"),
24
+ skip: z.array(z.string()).optional().describe("Skip tests matching these categories or test IDs")
25
+ },
26
+ {
27
+ title: "Run MCP Compliance Tests",
28
+ readOnlyHint: true,
29
+ destructiveHint: false,
30
+ idempotentHint: true,
31
+ openWorldHint: true
32
+ },
33
+ async ({ url, auth, headers: extraHeaders, timeout, retries, only, skip }) => {
34
+ try {
35
+ const headers = { ...extraHeaders };
36
+ if (auth) headers.Authorization = auth;
37
+ const report = await runComplianceSuite(url, {
38
+ headers: Object.keys(headers).length > 0 ? headers : void 0,
39
+ timeout,
40
+ retries,
41
+ only,
42
+ skip
43
+ });
44
+ const summary = [
45
+ `Grade: ${report.grade} (${report.score}%)`,
46
+ `Overall: ${report.overall}`,
47
+ `Tests: ${report.summary.passed}/${report.summary.total} passed (${report.summary.requiredPassed}/${report.summary.required} required)`,
48
+ "",
49
+ ...report.tests.map(
50
+ (t) => `${t.passed ? "PASS" : "FAIL"} ${t.name}${t.required ? " (required)" : ""} \u2014 ${t.details}`
51
+ )
52
+ ];
53
+ if (report.serverInfo.name) {
54
+ summary.unshift(`Server: ${report.serverInfo.name} v${report.serverInfo.version || "?"}`);
42
55
  }
43
- }
44
- return {
45
- content: [
46
- { type: "text", text: summary.join("\n") },
47
- { type: "text", text: `
56
+ if (report.warnings.length > 0) {
57
+ summary.push("", `Warnings (${report.warnings.length}):`);
58
+ for (const w of report.warnings) {
59
+ summary.push(` - ${w}`);
60
+ }
61
+ }
62
+ return {
63
+ content: [
64
+ { type: "text", text: summary.join("\n") },
65
+ { type: "text", text: `
48
66
 
49
67
  Full report:
50
68
  ${JSON.stringify(report, null, 2)}` }
51
- ]
52
- };
53
- } catch (err) {
54
- return {
55
- content: [{ type: "text", text: `Error running compliance test: ${err.message}` }],
56
- isError: true
57
- };
69
+ ]
70
+ };
71
+ } catch (err) {
72
+ return {
73
+ content: [{ type: "text", text: `Error running compliance test: ${err.message}` }],
74
+ isError: true
75
+ };
76
+ }
58
77
  }
59
- }
60
- );
61
- server.tool(
62
- "mcp_compliance_badge",
63
- "Get the badge markdown embed code for an MCP server. Runs the compliance test suite first to determine the grade.",
64
- {
65
- url: z.string().url().describe("The MCP server URL to test")
66
- },
67
- async ({ url }) => {
68
- try {
69
- const report = await runComplianceSuite(url);
70
- const badge = report.badge;
71
- return {
72
- content: [{
73
- type: "text",
74
- text: [
75
- `Grade: ${report.grade} (${report.score}%)`,
76
- "",
77
- "Markdown:",
78
- badge.markdown,
79
- "",
80
- "HTML:",
81
- badge.html
82
- ].join("\n")
83
- }]
84
- };
85
- } catch (err) {
86
- return {
87
- content: [{ type: "text", text: `Error: ${err.message}` }],
88
- isError: true
89
- };
78
+ );
79
+ server2.tool(
80
+ "mcp_compliance_badge",
81
+ "Get the badge markdown embed code for an MCP server. Runs the compliance test suite first to determine the grade.",
82
+ {
83
+ url: z.string().url().describe("The MCP server URL to test"),
84
+ auth: z.string().optional().describe('Authorization header value (e.g., "Bearer tok123")'),
85
+ headers: z.record(z.string()).optional().describe("Additional headers to include on all requests"),
86
+ timeout: z.number().optional().describe("Request timeout in milliseconds (default: 15000)")
87
+ },
88
+ {
89
+ title: "Get Compliance Badge",
90
+ readOnlyHint: true,
91
+ destructiveHint: false,
92
+ idempotentHint: true,
93
+ openWorldHint: true
94
+ },
95
+ async ({ url, auth, headers: extraHeaders, timeout }) => {
96
+ try {
97
+ const headers = { ...extraHeaders };
98
+ if (auth) headers.Authorization = auth;
99
+ const report = await runComplianceSuite(url, {
100
+ headers: Object.keys(headers).length > 0 ? headers : void 0,
101
+ timeout
102
+ });
103
+ const badge = report.badge;
104
+ return {
105
+ content: [
106
+ {
107
+ type: "text",
108
+ text: [
109
+ `Grade: ${report.grade} (${report.score}%)`,
110
+ "",
111
+ "Markdown:",
112
+ badge.markdown,
113
+ "",
114
+ "HTML:",
115
+ badge.html
116
+ ].join("\n")
117
+ }
118
+ ]
119
+ };
120
+ } catch (err) {
121
+ return {
122
+ content: [{ type: "text", text: `Error: ${err.message}` }],
123
+ isError: true
124
+ };
125
+ }
90
126
  }
91
- }
92
- );
93
- server.tool(
94
- "mcp_compliance_explain",
95
- "Explain what a specific compliance test ID checks and why it matters.",
96
- {
97
- testId: z.string().describe('The test ID to explain (e.g., "transport-post", "lifecycle-init", "tools-schema")')
98
- },
99
- async ({ testId }) => {
100
- const def = TEST_DEFINITIONS.find((t) => t.id === testId);
101
- if (!def) {
102
- const ids = TEST_DEFINITIONS.map((t) => t.id).join(", ");
103
- return {
104
- content: [{
105
- type: "text",
106
- text: `Unknown test ID: "${testId}"
127
+ );
128
+ server2.tool(
129
+ "mcp_compliance_explain",
130
+ "Explain what a specific compliance test ID checks and why it matters.",
131
+ {
132
+ testId: z.string().describe('The test ID to explain (e.g., "transport-post", "lifecycle-init", "tools-schema")')
133
+ },
134
+ {
135
+ title: "Explain Compliance Test",
136
+ readOnlyHint: true,
137
+ destructiveHint: false,
138
+ idempotentHint: true,
139
+ openWorldHint: false
140
+ },
141
+ async ({ testId }) => {
142
+ const def = TEST_DEFINITIONS.find((t) => t.id === testId);
143
+ if (!def) {
144
+ return {
145
+ content: [
146
+ {
147
+ type: "text",
148
+ text: `Unknown test ID: "${testId}"
107
149
 
108
150
  Valid test IDs:
109
- ${ids}`
110
- }],
111
- isError: true
151
+ ${TEST_DEFINITIONS.map((t) => t.id).join(", ")}`
152
+ }
153
+ ],
154
+ isError: true
155
+ };
156
+ }
157
+ return {
158
+ content: [
159
+ {
160
+ type: "text",
161
+ text: [
162
+ `Test: ${def.id}`,
163
+ `Name: ${def.name}`,
164
+ `Category: ${def.category}`,
165
+ `Required: ${def.required ? "Yes" : "No"}`,
166
+ `Spec reference: https://modelcontextprotocol.io/specification/2025-11-25/${def.specRef}`,
167
+ "",
168
+ def.description
169
+ ].join("\n")
170
+ }
171
+ ]
112
172
  };
113
173
  }
114
- return {
115
- content: [{
116
- type: "text",
117
- text: [
118
- `Test: ${def.id}`,
119
- `Name: ${def.name}`,
120
- `Category: ${def.category}`,
121
- `Required: ${def.required ? "Yes" : "No"}`,
122
- `Spec reference: https://modelcontextprotocol.io/specification/2025-11-25/${def.specRef}`,
123
- "",
124
- def.description
125
- ].join("\n")
126
- }]
127
- };
128
- }
129
- );
174
+ );
175
+ }
176
+
177
+ // src/mcp/server.ts
178
+ var require2 = createRequire(import.meta.url);
179
+ var { version } = require2("../../package.json");
180
+ var server = new McpServer({ name: "mcp-compliance", version });
181
+ registerTools(server);
130
182
  async function main() {
131
183
  const transport = new StdioServerTransport();
132
184
  await server.connect(transport);
package/dist/runner.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- type TestCategory = 'transport' | 'lifecycle' | 'tools' | 'resources' | 'prompts' | 'errors' | 'schema';
1
+ type TestCategory = "transport" | "lifecycle" | "tools" | "resources" | "prompts" | "errors" | "schema";
2
2
  interface TestResult {
3
3
  id: string;
4
4
  name: string;
@@ -9,8 +9,8 @@ interface TestResult {
9
9
  durationMs: number;
10
10
  specRef?: string;
11
11
  }
12
- type Grade = 'A' | 'B' | 'C' | 'D' | 'F';
13
- type Overall = 'pass' | 'partial' | 'fail';
12
+ type Grade = "A" | "B" | "C" | "D" | "F";
13
+ type Overall = "pass" | "partial" | "fail";
14
14
  interface ComplianceReport {
15
15
  specVersion: string;
16
16
  toolVersion: string;
@@ -66,7 +66,7 @@ declare function computeGrade(score: number): Grade;
66
66
  declare function computeScore(tests: TestResult[]): {
67
67
  score: number;
68
68
  grade: Grade;
69
- overall: 'pass' | 'partial' | 'fail';
69
+ overall: "pass" | "partial" | "fail";
70
70
  summary: {
71
71
  total: number;
72
72
  passed: number;
package/dist/runner.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  computeScore,
5
5
  generateBadge,
6
6
  runComplianceSuite
7
- } from "./chunk-SP24UFRC.js";
7
+ } from "./chunk-U66YZGE5.js";
8
8
  export {
9
9
  TEST_DEFINITIONS,
10
10
  computeGrade,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yawlabs/mcp-compliance",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "CLI tool and MCP server that tests MCP servers for spec compliance",
5
5
  "license": "MIT",
6
6
  "author": "Yaw Labs (https://yaw.sh)",
@@ -24,7 +24,10 @@
24
24
  "build": "tsup",
25
25
  "dev": "tsup --watch",
26
26
  "test": "vitest run",
27
- "lint": "tsc --noEmit",
27
+ "lint": "biome check src/",
28
+ "lint:fix": "biome check --write src/",
29
+ "typecheck": "tsc --noEmit",
30
+ "test:ci": "npm run build && npm test",
28
31
  "prepublishOnly": "npm run build"
29
32
  },
30
33
  "dependencies": {
@@ -35,6 +38,7 @@
35
38
  "zod": "^3.24.4"
36
39
  },
37
40
  "devDependencies": {
41
+ "@biomejs/biome": "^1.9.4",
38
42
  "@types/node": "^25.5.2",
39
43
  "tsup": "^8.4.0",
40
44
  "typescript": "^5.8.3",
@@ -52,7 +56,10 @@
52
56
  ],
53
57
  "repository": {
54
58
  "type": "git",
55
- "url": "git+https://github.com/yawlabs/mcp-compliance.git"
59
+ "url": "git+https://github.com/YawLabs/mcp-compliance.git"
60
+ },
61
+ "bugs": {
62
+ "url": "https://github.com/YawLabs/mcp-compliance/issues"
56
63
  },
57
64
  "homepage": "https://mcp.hosting"
58
65
  }