@yawlabs/mcp-compliance 0.2.0 → 0.2.1
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 +119 -79
- package/dist/mcp/server.js +108 -105
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1095,7 +1095,125 @@ function formatJson(report) {
|
|
|
1095
1095
|
// src/index.ts
|
|
1096
1096
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
1097
1097
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
1098
|
+
|
|
1099
|
+
// src/mcp/tools.ts
|
|
1098
1100
|
import { z } from "zod";
|
|
1101
|
+
function registerTools(server) {
|
|
1102
|
+
server.tool(
|
|
1103
|
+
"mcp_compliance_test",
|
|
1104
|
+
"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.",
|
|
1105
|
+
{
|
|
1106
|
+
url: z.string().url().describe("The MCP server URL to test (must be HTTP or HTTPS)")
|
|
1107
|
+
},
|
|
1108
|
+
async ({ url }) => {
|
|
1109
|
+
try {
|
|
1110
|
+
const report = await runComplianceSuite(url);
|
|
1111
|
+
const summary = [
|
|
1112
|
+
`Grade: ${report.grade} (${report.score}%)`,
|
|
1113
|
+
`Overall: ${report.overall}`,
|
|
1114
|
+
`Tests: ${report.summary.passed}/${report.summary.total} passed (${report.summary.requiredPassed}/${report.summary.required} required)`,
|
|
1115
|
+
"",
|
|
1116
|
+
...report.tests.map(
|
|
1117
|
+
(t) => `${t.passed ? "PASS" : "FAIL"} ${t.name}${t.required ? " (required)" : ""} \u2014 ${t.details}`
|
|
1118
|
+
)
|
|
1119
|
+
];
|
|
1120
|
+
if (report.serverInfo.name) {
|
|
1121
|
+
summary.unshift(`Server: ${report.serverInfo.name} v${report.serverInfo.version || "?"}`);
|
|
1122
|
+
}
|
|
1123
|
+
if (report.warnings.length > 0) {
|
|
1124
|
+
summary.push("", `Warnings (${report.warnings.length}):`);
|
|
1125
|
+
for (const w of report.warnings) {
|
|
1126
|
+
summary.push(` - ${w}`);
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
return {
|
|
1130
|
+
content: [
|
|
1131
|
+
{ type: "text", text: summary.join("\n") },
|
|
1132
|
+
{ type: "text", text: `
|
|
1133
|
+
|
|
1134
|
+
Full report:
|
|
1135
|
+
${JSON.stringify(report, null, 2)}` }
|
|
1136
|
+
]
|
|
1137
|
+
};
|
|
1138
|
+
} catch (err) {
|
|
1139
|
+
return {
|
|
1140
|
+
content: [{ type: "text", text: `Error running compliance test: ${err.message}` }],
|
|
1141
|
+
isError: true
|
|
1142
|
+
};
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
);
|
|
1146
|
+
server.tool(
|
|
1147
|
+
"mcp_compliance_badge",
|
|
1148
|
+
"Get the badge markdown embed code for an MCP server. Runs the compliance test suite first to determine the grade.",
|
|
1149
|
+
{
|
|
1150
|
+
url: z.string().url().describe("The MCP server URL to test")
|
|
1151
|
+
},
|
|
1152
|
+
async ({ url }) => {
|
|
1153
|
+
try {
|
|
1154
|
+
const report = await runComplianceSuite(url);
|
|
1155
|
+
const badge = report.badge;
|
|
1156
|
+
return {
|
|
1157
|
+
content: [{
|
|
1158
|
+
type: "text",
|
|
1159
|
+
text: [
|
|
1160
|
+
`Grade: ${report.grade} (${report.score}%)`,
|
|
1161
|
+
"",
|
|
1162
|
+
"Markdown:",
|
|
1163
|
+
badge.markdown,
|
|
1164
|
+
"",
|
|
1165
|
+
"HTML:",
|
|
1166
|
+
badge.html
|
|
1167
|
+
].join("\n")
|
|
1168
|
+
}]
|
|
1169
|
+
};
|
|
1170
|
+
} catch (err) {
|
|
1171
|
+
return {
|
|
1172
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
1173
|
+
isError: true
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
);
|
|
1178
|
+
server.tool(
|
|
1179
|
+
"mcp_compliance_explain",
|
|
1180
|
+
"Explain what a specific compliance test ID checks and why it matters.",
|
|
1181
|
+
{
|
|
1182
|
+
testId: z.string().describe('The test ID to explain (e.g., "transport-post", "lifecycle-init", "tools-schema")')
|
|
1183
|
+
},
|
|
1184
|
+
async ({ testId }) => {
|
|
1185
|
+
const def = TEST_DEFINITIONS.find((t) => t.id === testId);
|
|
1186
|
+
if (!def) {
|
|
1187
|
+
return {
|
|
1188
|
+
content: [{
|
|
1189
|
+
type: "text",
|
|
1190
|
+
text: `Unknown test ID: "${testId}"
|
|
1191
|
+
|
|
1192
|
+
Valid test IDs:
|
|
1193
|
+
${TEST_DEFINITIONS.map((t) => t.id).join(", ")}`
|
|
1194
|
+
}],
|
|
1195
|
+
isError: true
|
|
1196
|
+
};
|
|
1197
|
+
}
|
|
1198
|
+
return {
|
|
1199
|
+
content: [{
|
|
1200
|
+
type: "text",
|
|
1201
|
+
text: [
|
|
1202
|
+
`Test: ${def.id}`,
|
|
1203
|
+
`Name: ${def.name}`,
|
|
1204
|
+
`Category: ${def.category}`,
|
|
1205
|
+
`Required: ${def.required ? "Yes" : "No"}`,
|
|
1206
|
+
`Spec reference: https://modelcontextprotocol.io/specification/2025-11-25/${def.specRef}`,
|
|
1207
|
+
"",
|
|
1208
|
+
def.description
|
|
1209
|
+
].join("\n")
|
|
1210
|
+
}]
|
|
1211
|
+
};
|
|
1212
|
+
}
|
|
1213
|
+
);
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
// src/index.ts
|
|
1099
1217
|
var require2 = createRequire2(import.meta.url);
|
|
1100
1218
|
var { version } = require2("../package.json");
|
|
1101
1219
|
function parseHeaderArg(value, prev) {
|
|
@@ -1179,85 +1297,7 @@ Error: ${err.message}
|
|
|
1179
1297
|
});
|
|
1180
1298
|
program.command("mcp").description("Start the MCP compliance server (stdio transport)").action(async () => {
|
|
1181
1299
|
const server = new McpServer({ name: "mcp-compliance", version });
|
|
1182
|
-
server
|
|
1183
|
-
"mcp_compliance_test",
|
|
1184
|
-
"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.",
|
|
1185
|
-
{ url: z.string().url().describe("The MCP server URL to test (must be HTTP or HTTPS)") },
|
|
1186
|
-
async ({ url }) => {
|
|
1187
|
-
try {
|
|
1188
|
-
const report = await runComplianceSuite(url);
|
|
1189
|
-
const summary = [
|
|
1190
|
-
`Grade: ${report.grade} (${report.score}%)`,
|
|
1191
|
-
`Overall: ${report.overall}`,
|
|
1192
|
-
`Tests: ${report.summary.passed}/${report.summary.total} passed (${report.summary.requiredPassed}/${report.summary.required} required)`,
|
|
1193
|
-
"",
|
|
1194
|
-
...report.tests.map(
|
|
1195
|
-
(t) => `${t.passed ? "PASS" : "FAIL"} ${t.name}${t.required ? " (required)" : ""} \u2014 ${t.details}`
|
|
1196
|
-
)
|
|
1197
|
-
];
|
|
1198
|
-
if (report.serverInfo.name) {
|
|
1199
|
-
summary.unshift(`Server: ${report.serverInfo.name} v${report.serverInfo.version || "?"}`);
|
|
1200
|
-
}
|
|
1201
|
-
if (report.warnings.length > 0) {
|
|
1202
|
-
summary.push("", `Warnings (${report.warnings.length}):`);
|
|
1203
|
-
for (const w of report.warnings) summary.push(` - ${w}`);
|
|
1204
|
-
}
|
|
1205
|
-
return {
|
|
1206
|
-
content: [
|
|
1207
|
-
{ type: "text", text: summary.join("\n") },
|
|
1208
|
-
{ type: "text", text: `
|
|
1209
|
-
|
|
1210
|
-
Full report:
|
|
1211
|
-
${JSON.stringify(report, null, 2)}` }
|
|
1212
|
-
]
|
|
1213
|
-
};
|
|
1214
|
-
} catch (err) {
|
|
1215
|
-
return { content: [{ type: "text", text: `Error running compliance test: ${err.message}` }], isError: true };
|
|
1216
|
-
}
|
|
1217
|
-
}
|
|
1218
|
-
);
|
|
1219
|
-
server.tool(
|
|
1220
|
-
"mcp_compliance_badge",
|
|
1221
|
-
"Get the badge markdown embed code for an MCP server. Runs the compliance test suite first to determine the grade.",
|
|
1222
|
-
{ url: z.string().url().describe("The MCP server URL to test") },
|
|
1223
|
-
async ({ url }) => {
|
|
1224
|
-
try {
|
|
1225
|
-
const report = await runComplianceSuite(url);
|
|
1226
|
-
const badge = report.badge;
|
|
1227
|
-
return {
|
|
1228
|
-
content: [{
|
|
1229
|
-
type: "text",
|
|
1230
|
-
text: [`Grade: ${report.grade} (${report.score}%)`, "", "Markdown:", badge.markdown, "", "HTML:", badge.html].join("\n")
|
|
1231
|
-
}]
|
|
1232
|
-
};
|
|
1233
|
-
} catch (err) {
|
|
1234
|
-
return { content: [{ type: "text", text: `Error: ${err.message}` }], isError: true };
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1237
|
-
);
|
|
1238
|
-
server.tool(
|
|
1239
|
-
"mcp_compliance_explain",
|
|
1240
|
-
"Explain what a specific compliance test ID checks and why it matters.",
|
|
1241
|
-
{ testId: z.string().describe('The test ID to explain (e.g., "transport-post", "lifecycle-init", "tools-schema")') },
|
|
1242
|
-
async ({ testId }) => {
|
|
1243
|
-
const def = TEST_DEFINITIONS.find((t) => t.id === testId);
|
|
1244
|
-
if (!def) {
|
|
1245
|
-
return {
|
|
1246
|
-
content: [{ type: "text", text: `Unknown test ID: "${testId}"
|
|
1247
|
-
|
|
1248
|
-
Valid test IDs:
|
|
1249
|
-
${TEST_DEFINITIONS.map((t) => t.id).join(", ")}` }],
|
|
1250
|
-
isError: true
|
|
1251
|
-
};
|
|
1252
|
-
}
|
|
1253
|
-
return {
|
|
1254
|
-
content: [{
|
|
1255
|
-
type: "text",
|
|
1256
|
-
text: [`Test: ${def.id}`, `Name: ${def.name}`, `Category: ${def.category}`, `Required: ${def.required ? "Yes" : "No"}`, `Spec reference: https://modelcontextprotocol.io/specification/2025-11-25/${def.specRef}`, "", def.description].join("\n")
|
|
1257
|
-
}]
|
|
1258
|
-
};
|
|
1259
|
-
}
|
|
1260
|
-
);
|
|
1300
|
+
registerTools(server);
|
|
1261
1301
|
const transport = new StdioServerTransport();
|
|
1262
1302
|
await server.connect(transport);
|
|
1263
1303
|
});
|
package/dist/mcp/server.js
CHANGED
|
@@ -6,127 +6,130 @@ import {
|
|
|
6
6
|
// src/mcp/server.ts
|
|
7
7
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
8
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
9
|
-
import { z } from "zod";
|
|
10
9
|
import { createRequire } from "module";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
server.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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}`);
|
|
10
|
+
|
|
11
|
+
// src/mcp/tools.ts
|
|
12
|
+
import { z } from "zod";
|
|
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
|
+
},
|
|
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 || "?"}`);
|
|
42
34
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
35
|
+
if (report.warnings.length > 0) {
|
|
36
|
+
summary.push("", `Warnings (${report.warnings.length}):`);
|
|
37
|
+
for (const w of report.warnings) {
|
|
38
|
+
summary.push(` - ${w}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
content: [
|
|
43
|
+
{ type: "text", text: summary.join("\n") },
|
|
44
|
+
{ type: "text", text: `
|
|
48
45
|
|
|
49
46
|
Full report:
|
|
50
47
|
${JSON.stringify(report, null, 2)}` }
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
48
|
+
]
|
|
49
|
+
};
|
|
50
|
+
} catch (err) {
|
|
51
|
+
return {
|
|
52
|
+
content: [{ type: "text", text: `Error running compliance test: ${err.message}` }],
|
|
53
|
+
isError: true
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
server2.tool(
|
|
59
|
+
"mcp_compliance_badge",
|
|
60
|
+
"Get the badge markdown embed code for an MCP server. Runs the compliance test suite first to determine the grade.",
|
|
61
|
+
{
|
|
62
|
+
url: z.string().url().describe("The MCP server URL to test")
|
|
63
|
+
},
|
|
64
|
+
async ({ url }) => {
|
|
65
|
+
try {
|
|
66
|
+
const report = await runComplianceSuite(url);
|
|
67
|
+
const badge = report.badge;
|
|
68
|
+
return {
|
|
69
|
+
content: [{
|
|
70
|
+
type: "text",
|
|
71
|
+
text: [
|
|
72
|
+
`Grade: ${report.grade} (${report.score}%)`,
|
|
73
|
+
"",
|
|
74
|
+
"Markdown:",
|
|
75
|
+
badge.markdown,
|
|
76
|
+
"",
|
|
77
|
+
"HTML:",
|
|
78
|
+
badge.html
|
|
79
|
+
].join("\n")
|
|
80
|
+
}]
|
|
81
|
+
};
|
|
82
|
+
} catch (err) {
|
|
83
|
+
return {
|
|
84
|
+
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
85
|
+
isError: true
|
|
86
|
+
};
|
|
87
|
+
}
|
|
58
88
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
89
|
+
);
|
|
90
|
+
server2.tool(
|
|
91
|
+
"mcp_compliance_explain",
|
|
92
|
+
"Explain what a specific compliance test ID checks and why it matters.",
|
|
93
|
+
{
|
|
94
|
+
testId: z.string().describe('The test ID to explain (e.g., "transport-post", "lifecycle-init", "tools-schema")')
|
|
95
|
+
},
|
|
96
|
+
async ({ testId }) => {
|
|
97
|
+
const def = TEST_DEFINITIONS.find((t) => t.id === testId);
|
|
98
|
+
if (!def) {
|
|
99
|
+
return {
|
|
100
|
+
content: [{
|
|
101
|
+
type: "text",
|
|
102
|
+
text: `Unknown test ID: "${testId}"
|
|
103
|
+
|
|
104
|
+
Valid test IDs:
|
|
105
|
+
${TEST_DEFINITIONS.map((t) => t.id).join(", ")}`
|
|
106
|
+
}],
|
|
107
|
+
isError: true
|
|
108
|
+
};
|
|
109
|
+
}
|
|
71
110
|
return {
|
|
72
111
|
content: [{
|
|
73
112
|
type: "text",
|
|
74
113
|
text: [
|
|
75
|
-
`
|
|
114
|
+
`Test: ${def.id}`,
|
|
115
|
+
`Name: ${def.name}`,
|
|
116
|
+
`Category: ${def.category}`,
|
|
117
|
+
`Required: ${def.required ? "Yes" : "No"}`,
|
|
118
|
+
`Spec reference: https://modelcontextprotocol.io/specification/2025-11-25/${def.specRef}`,
|
|
76
119
|
"",
|
|
77
|
-
|
|
78
|
-
badge.markdown,
|
|
79
|
-
"",
|
|
80
|
-
"HTML:",
|
|
81
|
-
badge.html
|
|
120
|
+
def.description
|
|
82
121
|
].join("\n")
|
|
83
122
|
}]
|
|
84
123
|
};
|
|
85
|
-
} catch (err) {
|
|
86
|
-
return {
|
|
87
|
-
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
88
|
-
isError: true
|
|
89
|
-
};
|
|
90
124
|
}
|
|
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}"
|
|
125
|
+
);
|
|
126
|
+
}
|
|
107
127
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
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
|
-
);
|
|
128
|
+
// src/mcp/server.ts
|
|
129
|
+
var require2 = createRequire(import.meta.url);
|
|
130
|
+
var { version } = require2("../../package.json");
|
|
131
|
+
var server = new McpServer({ name: "mcp-compliance", version });
|
|
132
|
+
registerTools(server);
|
|
130
133
|
async function main() {
|
|
131
134
|
const transport = new StdioServerTransport();
|
|
132
135
|
await server.connect(transport);
|