mcprigor 1.0.0-rc.2 → 1.0.0-rc.3
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 +9 -3
- package/dist/imports.d.ts +14 -2
- package/dist/imports.d.ts.map +1 -1
- package/dist/imports.js +37 -7
- package/dist/imports.js.map +1 -1
- package/docs/ENGINEER-SETUP.md +16 -0
- package/docs/GETTING-STARTED.md +7 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,17 +11,23 @@
|
|
|
11
11
|
|
|
12
12
|
MCP Rigor lets QA teams test MCP tools, resources, prompts, contracts, and transports without writing test code. Tests are deterministic: no AI model interprets the wording. Plain-language files use `.mcpr` to avoid collision with existing `.mcp` formats.
|
|
13
13
|
|
|
14
|
-
> Status: `1.0.0-rc.
|
|
14
|
+
> Status: `1.0.0-rc.3` release candidate. Node.js 20 or 22 is required.
|
|
15
15
|
|
|
16
16
|
## Install
|
|
17
17
|
|
|
18
|
-
In your test project:
|
|
18
|
+
MCP Rigor is published on npm as [`mcprigor`](https://www.npmjs.com/package/mcprigor). The package ships compiled code — there is nothing to build from source. In your test project:
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
21
|
npm install --save-dev mcprigor
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
Or try it instantly without installing:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx mcprigor@latest init tests/acceptance.mcpr
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Use `npx mcprigor` in commands below, or install globally (`npm install -g mcprigor`) to use `mcprigor` directly.
|
|
25
31
|
|
|
26
32
|
## Write your first test
|
|
27
33
|
|
package/dist/imports.d.ts
CHANGED
|
@@ -2,6 +2,18 @@ export interface ImportGraph {
|
|
|
2
2
|
source: string;
|
|
3
3
|
importedFiles: string[];
|
|
4
4
|
}
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
export interface ImportLimits {
|
|
6
|
+
rootDir?: string;
|
|
7
|
+
maxDepth?: number;
|
|
8
|
+
maxImports?: number;
|
|
9
|
+
maxFileBytes?: number;
|
|
10
|
+
}
|
|
11
|
+
/** Resolve flow-library imports and inline declarations without executing imported tests.
|
|
12
|
+
*
|
|
13
|
+
* Confinement: imports must stay inside the suite root (the entry file's directory by
|
|
14
|
+
* default). Absolute paths, `..` traversal escaping the root, and symlinks that
|
|
15
|
+
* canonicalize outside the root are rejected. Recursion depth, import count, and
|
|
16
|
+
* imported file size are limited to bound resource use on hostile inputs.
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveFlowImports(source: string, entryFile: string, stack?: string[], limits?: ImportLimits): Promise<ImportGraph>;
|
|
7
19
|
//# sourceMappingURL=imports.d.ts.map
|
package/dist/imports.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imports.d.ts","sourceRoot":"","sources":["../src/imports.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW;IAAG,MAAM,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,EAAE,CAAA;CAAE;
|
|
1
|
+
{"version":3,"file":"imports.d.ts","sourceRoot":"","sources":["../src/imports.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW;IAAG,MAAM,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,EAAE,CAAA;CAAE;AACxE,MAAM,WAAW,YAAY;IAAG,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE;AAMjH;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,GAAE,MAAM,EAAO,EAAE,MAAM,GAAE,YAAiB,GAAG,OAAO,CAAC,WAAW,CAAC,CA2BjJ"}
|
package/dist/imports.js
CHANGED
|
@@ -1,29 +1,59 @@
|
|
|
1
|
-
import { readFile, realpath } from "node:fs/promises";
|
|
2
|
-
import { dirname, resolve } from "node:path";
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { readFile, realpath, stat } from "node:fs/promises";
|
|
2
|
+
import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
|
|
3
|
+
const MAX_DEPTH = 8;
|
|
4
|
+
const MAX_IMPORTS = 64;
|
|
5
|
+
const MAX_FILE_BYTES = 1024 * 1024;
|
|
6
|
+
/** Resolve flow-library imports and inline declarations without executing imported tests.
|
|
7
|
+
*
|
|
8
|
+
* Confinement: imports must stay inside the suite root (the entry file's directory by
|
|
9
|
+
* default). Absolute paths, `..` traversal escaping the root, and symlinks that
|
|
10
|
+
* canonicalize outside the root are rejected. Recursion depth, import count, and
|
|
11
|
+
* imported file size are limited to bound resource use on hostile inputs.
|
|
12
|
+
*/
|
|
13
|
+
export async function resolveFlowImports(source, entryFile, stack = [], limits = {}) {
|
|
5
14
|
const canonical = await realpath(entryFile).catch(() => resolve(entryFile));
|
|
15
|
+
const rootDir = limits.rootDir ?? await realpath(dirname(canonical)).catch(() => resolve(dirname(canonical)));
|
|
16
|
+
const maxDepth = limits.maxDepth ?? MAX_DEPTH;
|
|
17
|
+
const maxImports = limits.maxImports ?? MAX_IMPORTS;
|
|
18
|
+
const maxFileBytes = limits.maxFileBytes ?? MAX_FILE_BYTES;
|
|
6
19
|
if (stack.includes(canonical))
|
|
7
20
|
throw new Error(`MCPLANG301 Import cycle: ${[...stack, canonical].join(" → ")}`);
|
|
21
|
+
if (stack.length >= maxDepth)
|
|
22
|
+
throw new Error(`MCPLANG303 Import depth exceeds ${maxDepth} in ${entryFile}`);
|
|
8
23
|
const expression = /^Import flows from\s+["']([^"']+)["']\s*$/gim;
|
|
9
24
|
const importedFiles = [];
|
|
10
25
|
const chunks = [];
|
|
11
26
|
let match;
|
|
12
27
|
while ((match = expression.exec(source))) {
|
|
13
|
-
const
|
|
28
|
+
const specifier = match[1];
|
|
29
|
+
if (isAbsolute(specifier))
|
|
30
|
+
throw new Error(`MCPLANG304 Import path must be relative to the test file, not absolute: ${specifier}`);
|
|
31
|
+
const imported = await confine(resolve(dirname(canonical), specifier), rootDir, specifier);
|
|
32
|
+
const size = await stat(imported).then((s) => s.size).catch(() => 0);
|
|
33
|
+
if (size > maxFileBytes)
|
|
34
|
+
throw new Error(`MCPLANG305 Imported flow library ${specifier} exceeds ${maxFileBytes} bytes`);
|
|
14
35
|
let importedSource;
|
|
15
36
|
try {
|
|
16
37
|
importedSource = await readFile(imported, "utf8");
|
|
17
38
|
}
|
|
18
39
|
catch {
|
|
19
|
-
throw new Error(`MCPLANG302 Cannot import flow library ${
|
|
40
|
+
throw new Error(`MCPLANG302 Cannot import flow library ${specifier} from ${entryFile}`);
|
|
20
41
|
}
|
|
21
|
-
const graph = await resolveFlowImports(importedSource, imported, [...stack, canonical]);
|
|
42
|
+
const graph = await resolveFlowImports(importedSource, imported, [...stack, canonical], { rootDir, maxDepth, maxImports, maxFileBytes });
|
|
22
43
|
importedFiles.push(imported, ...graph.importedFiles);
|
|
44
|
+
if (new Set(importedFiles).size > maxImports)
|
|
45
|
+
throw new Error(`MCPLANG306 More than ${maxImports} imported flow libraries`);
|
|
23
46
|
chunks.push(flowDeclarationsOnly(graph.source));
|
|
24
47
|
}
|
|
25
48
|
return { source: `${chunks.join("\n")}\n${source.replace(expression, "")}`, importedFiles: [...new Set(importedFiles)] };
|
|
26
49
|
}
|
|
50
|
+
async function confine(candidate, rootDir, specifier) {
|
|
51
|
+
const canonical = await realpath(candidate).catch(() => candidate);
|
|
52
|
+
const relation = relative(rootDir, canonical);
|
|
53
|
+
if (relation === "" || (!relation.startsWith(`..${sep}`) && relation !== ".." && !isAbsolute(relation)))
|
|
54
|
+
return canonical;
|
|
55
|
+
throw new Error(`MCPLANG307 Import ${specifier} resolves outside the test suite directory (${rootDir}). Keep shared flow libraries inside the suite folder.`);
|
|
56
|
+
}
|
|
27
57
|
function flowDeclarationsOnly(source) {
|
|
28
58
|
const lines = source.replaceAll("\r\n", "\n").split("\n");
|
|
29
59
|
const output = [];
|
package/dist/imports.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imports.js","sourceRoot":"","sources":["../src/imports.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"imports.js","sourceRoot":"","sources":["../src/imports.ts"],"names":[],"mappings":"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"}
|
package/docs/ENGINEER-SETUP.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
This guide covers the configuration engineers normally prepare once so QA authors can focus on scenarios.
|
|
4
4
|
|
|
5
|
+
## Install and pin
|
|
6
|
+
|
|
7
|
+
MCP Rigor is published on npm as [`mcprigor`](https://www.npmjs.com/package/mcprigor) and ships compiled code — teams and CI never build it from source.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install --save-dev mcprigor
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
For reproducible CI runs, pin an exact version in `package.json` and update it deliberately:
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{ "devDependencies": { "mcprigor": "1.0.0-rc.3" } }
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Release notes and tarball checksums for each version are on the [GitHub releases page](https://github.com/FusionOnePlatform/mcprigor/releases).
|
|
20
|
+
|
|
5
21
|
## Recommended repository layout
|
|
6
22
|
|
|
7
23
|
```text
|
package/docs/GETTING-STARTED.md
CHANGED
|
@@ -4,7 +4,7 @@ This guide takes you from installation to one passing MCP test.
|
|
|
4
4
|
|
|
5
5
|
## 1. Install MCP Rigor
|
|
6
6
|
|
|
7
|
-
You need Node.js 20 or 22.
|
|
7
|
+
You need Node.js 20 or 22. MCP Rigor is published on npm as [`mcprigor`](https://www.npmjs.com/package/mcprigor) — the package ships ready-to-run compiled code, so there is nothing to build from source.
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
mkdir mcp-acceptance-tests
|
|
@@ -13,6 +13,12 @@ npm init -y
|
|
|
13
13
|
npm install --save-dev mcprigor
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
+
To check the installation:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npx mcprigor --help
|
|
20
|
+
```
|
|
21
|
+
|
|
16
22
|
## 2. Create a test file
|
|
17
23
|
|
|
18
24
|
Create `calculator.mcpr`:
|