@yawlabs/mcp-compliance 0.13.2 → 0.13.4
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/{chunk-X5CVUDPW.js → chunk-6PF56RRO.js} +24 -9
- package/dist/index.js +53 -12
- package/dist/mcp/server.js +1 -1
- package/dist/runner.d.ts +12 -0
- package/dist/runner.js +1 -1
- package/package.json +1 -1
|
@@ -1447,6 +1447,7 @@ async function runComplianceSuite(target, options = {}) {
|
|
|
1447
1447
|
}
|
|
1448
1448
|
const nextId = createIdCounter(1e3);
|
|
1449
1449
|
const timeout = options.timeout || 15e3;
|
|
1450
|
+
const startupTimeout = options.startupTimeout ?? Math.max(timeout, 6e4);
|
|
1450
1451
|
const retries = options.retries || 0;
|
|
1451
1452
|
let sessionId = null;
|
|
1452
1453
|
let negotiatedProtocolVersion = null;
|
|
@@ -1702,16 +1703,24 @@ async function runComplianceSuite(target, options = {}) {
|
|
|
1702
1703
|
}
|
|
1703
1704
|
);
|
|
1704
1705
|
let initRes = null;
|
|
1706
|
+
const initStart = Date.now();
|
|
1705
1707
|
try {
|
|
1706
|
-
initRes = await
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1708
|
+
initRes = await mcpRequest(
|
|
1709
|
+
backendUrl,
|
|
1710
|
+
"initialize",
|
|
1711
|
+
{
|
|
1712
|
+
protocolVersion: SPEC_VERSION,
|
|
1713
|
+
capabilities: {
|
|
1714
|
+
sampling: {},
|
|
1715
|
+
roots: { listChanged: true },
|
|
1716
|
+
elicitation: {}
|
|
1717
|
+
},
|
|
1718
|
+
clientInfo: { name: "mcp-compliance", version: TOOL_VERSION }
|
|
1712
1719
|
},
|
|
1713
|
-
|
|
1714
|
-
|
|
1720
|
+
nextId,
|
|
1721
|
+
buildHeaders2(),
|
|
1722
|
+
startupTimeout
|
|
1723
|
+
);
|
|
1715
1724
|
const result = initRes?.body?.result;
|
|
1716
1725
|
if (result) {
|
|
1717
1726
|
serverInfo.protocolVersion = result.protocolVersion || null;
|
|
@@ -1730,8 +1739,14 @@ async function runComplianceSuite(target, options = {}) {
|
|
|
1730
1739
|
}
|
|
1731
1740
|
} catch {
|
|
1732
1741
|
}
|
|
1742
|
+
const initElapsed = Date.now() - initStart;
|
|
1743
|
+
if (initRes && initElapsed > timeout) {
|
|
1744
|
+
warnings.push(
|
|
1745
|
+
`Initialize handshake took ${initElapsed}ms \u2014 longer than --timeout ${timeout}ms. Per-test requests use --timeout, so slow servers may flake. Consider raising --timeout.`
|
|
1746
|
+
);
|
|
1747
|
+
}
|
|
1733
1748
|
try {
|
|
1734
|
-
await mcpNotification(backendUrl, "notifications/initialized", void 0, buildHeaders2(),
|
|
1749
|
+
await mcpNotification(backendUrl, "notifications/initialized", void 0, buildHeaders2(), startupTimeout);
|
|
1735
1750
|
} catch {
|
|
1736
1751
|
}
|
|
1737
1752
|
const hasTools = !!serverInfo.capabilities.tools;
|
package/dist/index.js
CHANGED
|
@@ -559,6 +559,7 @@ function validate(raw, source) {
|
|
|
559
559
|
const allowed = /* @__PURE__ */ new Set([
|
|
560
560
|
"target",
|
|
561
561
|
"timeout",
|
|
562
|
+
"startupTimeout",
|
|
562
563
|
"preflightTimeout",
|
|
563
564
|
"retries",
|
|
564
565
|
"only",
|
|
@@ -1803,6 +1804,7 @@ async function runComplianceSuite(target, options = {}) {
|
|
|
1803
1804
|
}
|
|
1804
1805
|
const nextId = createIdCounter(1e3);
|
|
1805
1806
|
const timeout = options.timeout || 15e3;
|
|
1807
|
+
const startupTimeout = options.startupTimeout ?? Math.max(timeout, 6e4);
|
|
1806
1808
|
const retries = options.retries || 0;
|
|
1807
1809
|
let sessionId = null;
|
|
1808
1810
|
let negotiatedProtocolVersion = null;
|
|
@@ -2058,16 +2060,24 @@ async function runComplianceSuite(target, options = {}) {
|
|
|
2058
2060
|
}
|
|
2059
2061
|
);
|
|
2060
2062
|
let initRes = null;
|
|
2063
|
+
const initStart = Date.now();
|
|
2061
2064
|
try {
|
|
2062
|
-
initRes = await
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2065
|
+
initRes = await mcpRequest(
|
|
2066
|
+
backendUrl,
|
|
2067
|
+
"initialize",
|
|
2068
|
+
{
|
|
2069
|
+
protocolVersion: SPEC_VERSION,
|
|
2070
|
+
capabilities: {
|
|
2071
|
+
sampling: {},
|
|
2072
|
+
roots: { listChanged: true },
|
|
2073
|
+
elicitation: {}
|
|
2074
|
+
},
|
|
2075
|
+
clientInfo: { name: "mcp-compliance", version: TOOL_VERSION }
|
|
2068
2076
|
},
|
|
2069
|
-
|
|
2070
|
-
|
|
2077
|
+
nextId,
|
|
2078
|
+
buildHeaders2(),
|
|
2079
|
+
startupTimeout
|
|
2080
|
+
);
|
|
2071
2081
|
const result = initRes?.body?.result;
|
|
2072
2082
|
if (result) {
|
|
2073
2083
|
serverInfo.protocolVersion = result.protocolVersion || null;
|
|
@@ -2086,8 +2096,14 @@ async function runComplianceSuite(target, options = {}) {
|
|
|
2086
2096
|
}
|
|
2087
2097
|
} catch {
|
|
2088
2098
|
}
|
|
2099
|
+
const initElapsed = Date.now() - initStart;
|
|
2100
|
+
if (initRes && initElapsed > timeout) {
|
|
2101
|
+
warnings.push(
|
|
2102
|
+
`Initialize handshake took ${initElapsed}ms \u2014 longer than --timeout ${timeout}ms. Per-test requests use --timeout, so slow servers may flake. Consider raising --timeout.`
|
|
2103
|
+
);
|
|
2104
|
+
}
|
|
2089
2105
|
try {
|
|
2090
|
-
await mcpNotification(backendUrl, "notifications/initialized", void 0, buildHeaders2(),
|
|
2106
|
+
await mcpNotification(backendUrl, "notifications/initialized", void 0, buildHeaders2(), startupTimeout);
|
|
2091
2107
|
} catch {
|
|
2092
2108
|
}
|
|
2093
2109
|
const hasTools = !!serverInfo.capabilities.tools;
|
|
@@ -5161,6 +5177,20 @@ function formatHtml(report) {
|
|
|
5161
5177
|
</html>`;
|
|
5162
5178
|
}
|
|
5163
5179
|
|
|
5180
|
+
// src/stdio-split.ts
|
|
5181
|
+
function splitStdioTarget(s) {
|
|
5182
|
+
if (/["'`]/.test(s)) {
|
|
5183
|
+
throw new Error(
|
|
5184
|
+
"stdio command contains quote characters \u2014 pass the command and its args as separate tokens (e.g. `mcp-compliance test node dist/index.js serve`) instead of wrapping them in one quoted string."
|
|
5185
|
+
);
|
|
5186
|
+
}
|
|
5187
|
+
const tokens = s.trim().split(/\s+/).filter(Boolean);
|
|
5188
|
+
if (tokens.length === 0) {
|
|
5189
|
+
throw new Error("stdio command is empty");
|
|
5190
|
+
}
|
|
5191
|
+
return { command: tokens[0], args: tokens.slice(1) };
|
|
5192
|
+
}
|
|
5193
|
+
|
|
5164
5194
|
// src/token-store.ts
|
|
5165
5195
|
import { createHash as createHash2 } from "crypto";
|
|
5166
5196
|
import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
@@ -5290,10 +5320,17 @@ function buildTarget(positional, extraArgs, opts) {
|
|
|
5290
5320
|
...opts.envFile ? readEnvFile(opts.envFile) : {},
|
|
5291
5321
|
...opts.env ?? {}
|
|
5292
5322
|
};
|
|
5323
|
+
let command = positional;
|
|
5324
|
+
let args = extraArgs;
|
|
5325
|
+
if (extraArgs.length === 0 && /\s/.test(positional)) {
|
|
5326
|
+
const split = splitStdioTarget(positional);
|
|
5327
|
+
command = split.command;
|
|
5328
|
+
args = split.args;
|
|
5329
|
+
}
|
|
5293
5330
|
return {
|
|
5294
5331
|
type: "stdio",
|
|
5295
|
-
command
|
|
5296
|
-
args
|
|
5332
|
+
command,
|
|
5333
|
+
args,
|
|
5297
5334
|
env: Object.keys(env).length ? env : void 0,
|
|
5298
5335
|
cwd: opts.cwd,
|
|
5299
5336
|
verbose: opts.verbose
|
|
@@ -5383,8 +5420,11 @@ program.command("test").description("Run the full compliance test suite against
|
|
|
5383
5420
|
{}
|
|
5384
5421
|
).option("--auth <token>", 'Shorthand for -H "Authorization: <token>" (HTTP only)').option("-E, --env <var>", 'Set env var for stdio command ("KEY=VALUE", repeatable)', parseEnvVar, {}).option("--env-file <path>", "Load env vars from file (KEY=VALUE per line, stdio only)").option("--cwd <dir>", "Working directory for stdio command").option(
|
|
5385
5422
|
"--timeout <ms>",
|
|
5386
|
-
"
|
|
5423
|
+
"Per-request timeout in milliseconds (applies to every test request after the initial handshake)",
|
|
5387
5424
|
"15000"
|
|
5425
|
+
).option(
|
|
5426
|
+
"--startup-timeout <ms>",
|
|
5427
|
+
"Deadline for the initial initialize handshake (default: max(--timeout, 60000); covers cold `npx` cache fetches before a stdio server starts)"
|
|
5388
5428
|
).option("--no-color", "Disable colored output (also honors NO_COLOR env var)").option("--watch", "Re-run tests when files in the cwd change (stdio targets only)").option(
|
|
5389
5429
|
"--concurrency <n>",
|
|
5390
5430
|
"Max parallel-safe tests in flight (default 1; see docs/PERFORMANCE.md before raising)",
|
|
@@ -5446,6 +5486,7 @@ Testing ${describeTarget(transportTarget)}...
|
|
|
5446
5486
|
}
|
|
5447
5487
|
const report2 = await runComplianceSuite(transportTarget, {
|
|
5448
5488
|
timeout: parsePositiveInt(opts.timeout, "--timeout", 1),
|
|
5489
|
+
startupTimeout: opts.startupTimeout ? parsePositiveInt(opts.startupTimeout, "--startup-timeout", 1) : config?.startupTimeout,
|
|
5449
5490
|
preflightTimeout: opts.preflightTimeout ? parsePositiveInt(opts.preflightTimeout, "--preflight-timeout", 1) : config?.preflightTimeout,
|
|
5450
5491
|
retries: parsePositiveInt(opts.retries, "--retries"),
|
|
5451
5492
|
concurrency: parsePositiveInt(opts.concurrency, "--concurrency", 1),
|
package/dist/mcp/server.js
CHANGED
package/dist/runner.d.ts
CHANGED
|
@@ -180,6 +180,18 @@ interface RunOptions {
|
|
|
180
180
|
headers?: Record<string, string>;
|
|
181
181
|
/** Request timeout in milliseconds (default: 15000) */
|
|
182
182
|
timeout?: number;
|
|
183
|
+
/**
|
|
184
|
+
* Deadline for the initial `initialize` handshake + `initialized`
|
|
185
|
+
* notification, in milliseconds. Kept separate from `timeout` because
|
|
186
|
+
* cold-started stdio servers — especially `npx @pkg ...` targets where
|
|
187
|
+
* npm has to resolve and fetch the package before the MCP server
|
|
188
|
+
* starts — can take tens of seconds to produce their first response,
|
|
189
|
+
* while steady-state requests complete in milliseconds. Default is
|
|
190
|
+
* `max(timeout, 60000)` so users who bump `--timeout` keep that value
|
|
191
|
+
* and users on defaults get 60s for startup. Does not apply to any
|
|
192
|
+
* per-test requests past the handshake.
|
|
193
|
+
*/
|
|
194
|
+
startupTimeout?: number;
|
|
183
195
|
/** Number of retries for failed tests (default: 0) */
|
|
184
196
|
retries?: number;
|
|
185
197
|
/** Only run tests matching these category names or test IDs */
|
package/dist/runner.js
CHANGED
package/package.json
CHANGED