mcp-recon 0.2.2

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.
Files changed (95) hide show
  1. package/LICENSE +19 -0
  2. package/README.md +271 -0
  3. package/dist/bin/recon.d.ts +18 -0
  4. package/dist/bin/recon.d.ts.map +1 -0
  5. package/dist/bin/recon.js +361 -0
  6. package/dist/bin/recon.js.map +1 -0
  7. package/dist/caveats/index.d.ts +46 -0
  8. package/dist/caveats/index.d.ts.map +1 -0
  9. package/dist/caveats/index.js +186 -0
  10. package/dist/caveats/index.js.map +1 -0
  11. package/dist/caveats/render.d.ts +25 -0
  12. package/dist/caveats/render.d.ts.map +1 -0
  13. package/dist/caveats/render.js +100 -0
  14. package/dist/caveats/render.js.map +1 -0
  15. package/dist/caveats/types.d.ts +94 -0
  16. package/dist/caveats/types.d.ts.map +1 -0
  17. package/dist/caveats/types.js +17 -0
  18. package/dist/caveats/types.js.map +1 -0
  19. package/dist/classify/caveat.d.ts +29 -0
  20. package/dist/classify/caveat.d.ts.map +1 -0
  21. package/dist/classify/caveat.js +103 -0
  22. package/dist/classify/caveat.js.map +1 -0
  23. package/dist/classify/index.d.ts +21 -0
  24. package/dist/classify/index.d.ts.map +1 -0
  25. package/dist/classify/index.js +186 -0
  26. package/dist/classify/index.js.map +1 -0
  27. package/dist/classify/rules.d.ts +62 -0
  28. package/dist/classify/rules.d.ts.map +1 -0
  29. package/dist/classify/rules.js +219 -0
  30. package/dist/classify/rules.js.map +1 -0
  31. package/dist/classify/types.d.ts +45 -0
  32. package/dist/classify/types.d.ts.map +1 -0
  33. package/dist/classify/types.js +9 -0
  34. package/dist/classify/types.js.map +1 -0
  35. package/dist/enumerate.d.ts +79 -0
  36. package/dist/enumerate.d.ts.map +1 -0
  37. package/dist/enumerate.js +62 -0
  38. package/dist/enumerate.js.map +1 -0
  39. package/dist/fuzz/axes/boundary.d.ts +17 -0
  40. package/dist/fuzz/axes/boundary.d.ts.map +1 -0
  41. package/dist/fuzz/axes/boundary.js +143 -0
  42. package/dist/fuzz/axes/boundary.js.map +1 -0
  43. package/dist/fuzz/axes/encoding.d.ts +17 -0
  44. package/dist/fuzz/axes/encoding.d.ts.map +1 -0
  45. package/dist/fuzz/axes/encoding.js +59 -0
  46. package/dist/fuzz/axes/encoding.js.map +1 -0
  47. package/dist/fuzz/axes/path-traversal.d.ts +17 -0
  48. package/dist/fuzz/axes/path-traversal.d.ts.map +1 -0
  49. package/dist/fuzz/axes/path-traversal.js +56 -0
  50. package/dist/fuzz/axes/path-traversal.js.map +1 -0
  51. package/dist/fuzz/axes/schema-violation.d.ts +18 -0
  52. package/dist/fuzz/axes/schema-violation.d.ts.map +1 -0
  53. package/dist/fuzz/axes/schema-violation.js +74 -0
  54. package/dist/fuzz/axes/schema-violation.js.map +1 -0
  55. package/dist/fuzz/axes/type-confusion.d.ts +17 -0
  56. package/dist/fuzz/axes/type-confusion.d.ts.map +1 -0
  57. package/dist/fuzz/axes/type-confusion.js +67 -0
  58. package/dist/fuzz/axes/type-confusion.js.map +1 -0
  59. package/dist/fuzz/axes/url-hostility.d.ts +17 -0
  60. package/dist/fuzz/axes/url-hostility.d.ts.map +1 -0
  61. package/dist/fuzz/axes/url-hostility.js +61 -0
  62. package/dist/fuzz/axes/url-hostility.js.map +1 -0
  63. package/dist/fuzz/index.d.ts +41 -0
  64. package/dist/fuzz/index.d.ts.map +1 -0
  65. package/dist/fuzz/index.js +147 -0
  66. package/dist/fuzz/index.js.map +1 -0
  67. package/dist/fuzz/prng.d.ts +26 -0
  68. package/dist/fuzz/prng.d.ts.map +1 -0
  69. package/dist/fuzz/prng.js +52 -0
  70. package/dist/fuzz/prng.js.map +1 -0
  71. package/dist/fuzz/schema.d.ts +46 -0
  72. package/dist/fuzz/schema.d.ts.map +1 -0
  73. package/dist/fuzz/schema.js +84 -0
  74. package/dist/fuzz/schema.js.map +1 -0
  75. package/dist/fuzz/types.d.ts +53 -0
  76. package/dist/fuzz/types.d.ts.map +1 -0
  77. package/dist/fuzz/types.js +11 -0
  78. package/dist/fuzz/types.js.map +1 -0
  79. package/dist/index.d.ts +25 -0
  80. package/dist/index.d.ts.map +1 -0
  81. package/dist/index.js +25 -0
  82. package/dist/index.js.map +1 -0
  83. package/dist/report/index.d.ts +25 -0
  84. package/dist/report/index.d.ts.map +1 -0
  85. package/dist/report/index.js +133 -0
  86. package/dist/report/index.js.map +1 -0
  87. package/dist/scan/index.d.ts +52 -0
  88. package/dist/scan/index.d.ts.map +1 -0
  89. package/dist/scan/index.js +81 -0
  90. package/dist/scan/index.js.map +1 -0
  91. package/dist/transport.d.ts +43 -0
  92. package/dist/transport.d.ts.map +1 -0
  93. package/dist/transport.js +74 -0
  94. package/dist/transport.js.map +1 -0
  95. package/package.json +72 -0
@@ -0,0 +1,84 @@
1
+ /**
2
+ * JSON Schema walker — extracts what the fuzzer needs to know about a
3
+ * tool's argument shape.
4
+ *
5
+ * v0.1 supports JSON Schema Draft 7 (the dialect the official MCP
6
+ * filesystem server uses). We don't validate the schema — we extract
7
+ * structural facts: which keys exist, what types they declare, which
8
+ * are required, which look like paths or URLs.
9
+ *
10
+ * Heuristic shape-detection: arg names containing `path` are flagged
11
+ * path-shaped; arg names containing `url`, `origin`, `endpoint` are
12
+ * URL-shaped. The classifier (week 3) will refine these heuristics.
13
+ */
14
+ /** Default seed for the deterministic fuzz PRNG (matches docs/SPEC.md). */
15
+ export const DEFAULT_FUZZ_SEED = 0xc0_ffee;
16
+ /** Default per-tool fuzz budget (matches docs/SPEC.md). */
17
+ export const DEFAULT_FUZZ_BUDGET = 200;
18
+ /** Walk one tool's `inputSchema`. Returns structural facts. */
19
+ export function extractToolFacts(name, inputSchema) {
20
+ if (inputSchema === null || typeof inputSchema !== "object") {
21
+ return { name, args: [], isArgless: true };
22
+ }
23
+ const schema = inputSchema;
24
+ const properties = (schema["properties"] ?? {});
25
+ const requiredList = Array.isArray(schema["required"]) ? schema["required"] : [];
26
+ const requiredSet = new Set(requiredList);
27
+ const args = [];
28
+ for (const [argName, argSchema] of Object.entries(properties)) {
29
+ if (argSchema === null || typeof argSchema !== "object")
30
+ continue;
31
+ const a = argSchema;
32
+ const declaredType = pickType(a["type"]);
33
+ const enumRaw = a["enum"];
34
+ const enumValues = Array.isArray(enumRaw) ? enumRaw : undefined;
35
+ args.push({
36
+ path: [argName],
37
+ declaredType,
38
+ required: requiredSet.has(argName),
39
+ isPathShaped: looksPathy(argName),
40
+ isUrlShaped: looksUrly(argName),
41
+ isCommandShaped: looksCommandy(argName),
42
+ ...(enumValues ? { enumValues } : {}),
43
+ });
44
+ }
45
+ return { name, args, isArgless: args.length === 0 };
46
+ }
47
+ function pickType(declared) {
48
+ if (typeof declared === "string") {
49
+ if (declared === "string" ||
50
+ declared === "number" ||
51
+ declared === "integer" ||
52
+ declared === "boolean" ||
53
+ declared === "array" ||
54
+ declared === "object" ||
55
+ declared === "null") {
56
+ return declared;
57
+ }
58
+ }
59
+ if (Array.isArray(declared) && declared.length > 0) {
60
+ // Multi-type arg (`type: ["string", "null"]`) — pick the first non-null.
61
+ const first = declared.find((t) => t !== "null") ?? declared[0];
62
+ if (typeof first === "string")
63
+ return pickType(first);
64
+ }
65
+ return "unknown";
66
+ }
67
+ function looksPathy(argName) {
68
+ // Suppress when the arg name carries a non-path semantic that the
69
+ // path-name list (`source`, `target`, `destination`, ...) would otherwise
70
+ // false-positive on. Canonical case: `source_timezone` / `target_timezone`
71
+ // on `mcp-server-time` (F006). Add additional stop words here as new
72
+ // false-positive shapes surface.
73
+ if (/(^|_)(timezone|tz|zone|region|locale|currency|country|language)($|_|s$)/i.test(argName)) {
74
+ return false;
75
+ }
76
+ return /(^|_)(path|file|dir|directory|src|source|destination|dest|target)($|_|s$)/i.test(argName);
77
+ }
78
+ function looksUrly(argName) {
79
+ return /(^|_)(url|uri|origin|endpoint|host|hostname)($|_|s$)/i.test(argName);
80
+ }
81
+ function looksCommandy(argName) {
82
+ return /(^|_)(cmd|command|argv|exec|args)($|_|s$)/i.test(argName);
83
+ }
84
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/fuzz/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Wire-format types for the v0.1 fuzz document. Mirrors the Rust core's
3
+ * `mcp_recon_core::fuzzer` types so the two stay in lock-step.
4
+ *
5
+ * Schema tag: `mcp-recon/v0.1/fuzz`. Downstream consumers (classifier,
6
+ * reporter, capnagent's caveat-suggestion bridge) parse based on this
7
+ * string.
8
+ */
9
+ /** Schema-version tag for fuzz documents. */
10
+ export declare const FUZZ_SCHEMA: "mcp-recon/v0.1/fuzz";
11
+ /** Six categorical axes (per docs/SPEC.md §"Fuzzing strategy"). */
12
+ export type FuzzAxis = "boundary_values" | "type_confusion" | "encoding_tricks" | "path_traversal" | "url_hostility" | "schema_violation";
13
+ /** What the underlying tool / transport did with the fuzz input. */
14
+ export type FuzzOutcome = {
15
+ kind: "ok";
16
+ snippet: string;
17
+ } | {
18
+ kind: "protocol_error";
19
+ message: string;
20
+ } | {
21
+ kind: "runtime_error";
22
+ message: string;
23
+ };
24
+ /** One recorded fuzz interaction. */
25
+ export interface FuzzCall {
26
+ tool: string;
27
+ axis: FuzzAxis;
28
+ rationale: string;
29
+ arguments: Record<string, unknown>;
30
+ outcome: FuzzOutcome;
31
+ }
32
+ /** Per-tool count summary. */
33
+ export interface FuzzToolSummary {
34
+ tool: string;
35
+ total: number;
36
+ ok: number;
37
+ protocol_error: number;
38
+ runtime_error: number;
39
+ }
40
+ /** Top-level fuzz document. */
41
+ export interface FuzzResults {
42
+ schema: typeof FUZZ_SCHEMA;
43
+ scanned_at: string;
44
+ server: {
45
+ name?: string;
46
+ version?: string;
47
+ };
48
+ seed: number;
49
+ budget: number;
50
+ summary: FuzzToolSummary[];
51
+ calls: FuzzCall[];
52
+ }
53
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/fuzz/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,6CAA6C;AAC7C,eAAO,MAAM,WAAW,EAAG,qBAA8B,CAAC;AAE1D,mEAAmE;AACnE,MAAM,MAAM,QAAQ,GAChB,iBAAiB,GACjB,gBAAgB,GAChB,iBAAiB,GACjB,gBAAgB,GAChB,eAAe,GACf,kBAAkB,CAAC;AAEvB,oEAAoE;AACpE,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/C,qCAAqC;AACrC,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,8BAA8B;AAC9B,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,+BAA+B;AAC/B,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,WAAW,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Wire-format types for the v0.1 fuzz document. Mirrors the Rust core's
3
+ * `mcp_recon_core::fuzzer` types so the two stay in lock-step.
4
+ *
5
+ * Schema tag: `mcp-recon/v0.1/fuzz`. Downstream consumers (classifier,
6
+ * reporter, capnagent's caveat-suggestion bridge) parse based on this
7
+ * string.
8
+ */
9
+ /** Schema-version tag for fuzz documents. */
10
+ export const FUZZ_SCHEMA = "mcp-recon/v0.1/fuzz";
11
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/fuzz/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,6CAA6C;AAC7C,MAAM,CAAC,MAAM,WAAW,GAAG,qBAA8B,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * `@mcp-recon/cli` — public library surface.
3
+ *
4
+ * The CLI binary in `bin/recon.ts` is the daily-driver. This module
5
+ * re-exports the same primitives so other tools can compose them
6
+ * without spawning the CLI as a subprocess.
7
+ *
8
+ * v0.1 surface (per docs/SPEC.md):
9
+ *
10
+ * - parseServerSpec(spec) — string → ServerSpec
11
+ * - openClient(spec) — ServerSpec → connected SDK Client
12
+ * - enumerate(client) → ToolInventory (one tool inventory document)
13
+ * - fuzz(client, inventory) → FuzzResults (one fuzz document)
14
+ *
15
+ * v0.1 weeks 3+ will add `classify`, `report`, `scan` exports here.
16
+ */
17
+ export { type ServerSpec, parseServerSpec, openClient, closeClient, } from "./transport.js";
18
+ export { type ToolInventory, type EnumeratedTool, type EnumerateOptions, enumerate, INVENTORY_SCHEMA, DEFAULT_ENUMERATE_TIMEOUT_MS, DEFAULT_MAX_DESCRIPTION_CHARS, TRUNCATION_MARKER, } from "./enumerate.js";
19
+ export { type FuzzAxis, type FuzzCall, type FuzzOutcome, type FuzzResults, type FuzzToolSummary, type FuzzOptions, fuzz, FUZZ_SCHEMA, } from "./fuzz/index.js";
20
+ export { type AuthorityLevel, type Classification, type ClassificationResults, type DataClass, classify, noisyOr, synthesizeCaveat, CLASSIFICATION_SCHEMA, } from "./classify/index.js";
21
+ export { type RenderInput, renderMarkdown, } from "./report/index.js";
22
+ export { type CaveatBindings, type CaveatPlan, type CaveatsResults, type FlagReason, planCaveats, CAVEATS_SCHEMA, } from "./caveats/index.js";
23
+ export { renderCaveatsMarkdown } from "./caveats/render.js";
24
+ export { type ScanResult, type ScanOptions, scan, } from "./scan/index.js";
25
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EACL,KAAK,UAAU,EACf,eAAe,EACf,UAAU,EACV,WAAW,GACZ,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,SAAS,EACT,gBAAgB,EAChB,4BAA4B,EAC5B,6BAA6B,EAC7B,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,IAAI,EACJ,WAAW,GACZ,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,SAAS,EACd,QAAQ,EACR,OAAO,EACP,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,KAAK,WAAW,EAChB,cAAc,GACf,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,WAAW,EACX,cAAc,GACf,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,OAAO,EACL,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,IAAI,GACL,MAAM,iBAAiB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * `@mcp-recon/cli` — public library surface.
3
+ *
4
+ * The CLI binary in `bin/recon.ts` is the daily-driver. This module
5
+ * re-exports the same primitives so other tools can compose them
6
+ * without spawning the CLI as a subprocess.
7
+ *
8
+ * v0.1 surface (per docs/SPEC.md):
9
+ *
10
+ * - parseServerSpec(spec) — string → ServerSpec
11
+ * - openClient(spec) — ServerSpec → connected SDK Client
12
+ * - enumerate(client) → ToolInventory (one tool inventory document)
13
+ * - fuzz(client, inventory) → FuzzResults (one fuzz document)
14
+ *
15
+ * v0.1 weeks 3+ will add `classify`, `report`, `scan` exports here.
16
+ */
17
+ export { parseServerSpec, openClient, closeClient, } from "./transport.js";
18
+ export { enumerate, INVENTORY_SCHEMA, DEFAULT_ENUMERATE_TIMEOUT_MS, DEFAULT_MAX_DESCRIPTION_CHARS, TRUNCATION_MARKER, } from "./enumerate.js";
19
+ export { fuzz, FUZZ_SCHEMA, } from "./fuzz/index.js";
20
+ export { classify, noisyOr, synthesizeCaveat, CLASSIFICATION_SCHEMA, } from "./classify/index.js";
21
+ export { renderMarkdown, } from "./report/index.js";
22
+ export { planCaveats, CAVEATS_SCHEMA, } from "./caveats/index.js";
23
+ export { renderCaveatsMarkdown } from "./caveats/render.js";
24
+ export { scan, } from "./scan/index.js";
25
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAEL,eAAe,EACf,UAAU,EACV,WAAW,GACZ,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAIL,SAAS,EACT,gBAAgB,EAChB,4BAA4B,EAC5B,6BAA6B,EAC7B,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAOL,IAAI,EACJ,WAAW,GACZ,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAKL,QAAQ,EACR,OAAO,EACP,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAEL,cAAc,GACf,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAKL,WAAW,EACX,cAAc,GACf,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,OAAO,EAGL,IAAI,GACL,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Markdown reporter — render an inventory + classification (+ optional
3
+ * fuzz) into a single Markdown threat profile a security reviewer
4
+ * actually reads.
5
+ *
6
+ * The output structure:
7
+ *
8
+ * 1. Heading + generated-at + server identity
9
+ * 2. Summary stats (counts, distributions, headline finding)
10
+ * 3. Per-tool sections, ordered by authority (privileged →
11
+ * destructive → write → read), with the recommended caveat in
12
+ * a copy-pasteable code block
13
+ * 4. Footer linking back to capnagent / mcp-recon docs
14
+ */
15
+ import type { ToolInventory } from "../enumerate.js";
16
+ import type { FuzzResults } from "../fuzz/types.js";
17
+ import type { ClassificationResults } from "../classify/types.js";
18
+ export interface RenderInput {
19
+ inventory: ToolInventory;
20
+ classification: ClassificationResults;
21
+ fuzz?: FuzzResults;
22
+ }
23
+ /** Render a Markdown threat profile. Pure function — no I/O. */
24
+ export declare function renderMarkdown(input: RenderInput): string;
25
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/report/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAGV,qBAAqB,EAEtB,MAAM,sBAAsB,CAAC;AAS9B,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,aAAa,CAAC;IACzB,cAAc,EAAE,qBAAqB,CAAC;IACtC,IAAI,CAAC,EAAE,WAAW,CAAC;CACpB;AAED,gEAAgE;AAChE,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CA6EzD"}
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Markdown reporter — render an inventory + classification (+ optional
3
+ * fuzz) into a single Markdown threat profile a security reviewer
4
+ * actually reads.
5
+ *
6
+ * The output structure:
7
+ *
8
+ * 1. Heading + generated-at + server identity
9
+ * 2. Summary stats (counts, distributions, headline finding)
10
+ * 3. Per-tool sections, ordered by authority (privileged →
11
+ * destructive → write → read), with the recommended caveat in
12
+ * a copy-pasteable code block
13
+ * 4. Footer linking back to capnagent / mcp-recon docs
14
+ */
15
+ const AUTHORITY_ORDER = {
16
+ privileged: 0,
17
+ destructive: 1,
18
+ write: 2,
19
+ read: 3,
20
+ };
21
+ /** Render a Markdown threat profile. Pure function — no I/O. */
22
+ export function renderMarkdown(input) {
23
+ const { inventory, classification, fuzz } = input;
24
+ const out = [];
25
+ const serverLabel = `${inventory.server.name ?? "(unnamed server)"} v${inventory.server.version ?? "?"}`;
26
+ out.push(`# Threat profile: ${serverLabel}`);
27
+ out.push("");
28
+ out.push(`> Generated by [mcp-recon](https://github.com/euanmcrosson-dotcom/mcp-recon) at ${classification.scanned_at}.`);
29
+ out.push("> Companion to [capnagent](https://github.com/euanmcrosson-dotcom/capnagent) — every recommended caveat below is a copy-pasteable capnagent predicate.");
30
+ out.push("");
31
+ // ─── Summary ─────────────────────────────────────────────────
32
+ out.push("## Summary");
33
+ out.push("");
34
+ const byClass = countBy(classification.classifications, (c) => c.data_class);
35
+ const byAuthority = countBy(classification.classifications, (c) => c.authority_level);
36
+ const cdpCount = classification.classifications.filter((c) => c.confused_deputy_candidate).length;
37
+ const total = classification.classifications.length;
38
+ out.push(`- **Tools:** ${total}`);
39
+ out.push(`- **Data-class distribution:** ${formatDistribution(byClass)}`);
40
+ out.push(`- **Authority-level distribution:** ${formatDistribution(byAuthority)}`);
41
+ out.push(`- **Confused-deputy candidates:** ${cdpCount} / ${total}${cdpCount === 0 ? " ✅" : ""}`);
42
+ if (fuzz) {
43
+ const fOk = fuzz.summary.reduce((acc, s) => acc + s.ok, 0);
44
+ const fProto = fuzz.summary.reduce((acc, s) => acc + s.protocol_error, 0);
45
+ const fRuntime = fuzz.summary.reduce((acc, s) => acc + s.runtime_error, 0);
46
+ const fTotal = fOk + fProto + fRuntime;
47
+ out.push(`- **Fuzz:** ${fTotal} calls — ok=${fOk}, protocol_error=${fProto}, runtime_error=${fRuntime}${fRuntime > 0 ? " ⚠️ at least one input escaped protocol-error handling" : ""}`);
48
+ }
49
+ out.push("");
50
+ // ─── Per-tool sections ───────────────────────────────────────
51
+ out.push("## Tools");
52
+ out.push("");
53
+ const fuzzByTool = new Map();
54
+ if (fuzz) {
55
+ for (const s of fuzz.summary) {
56
+ fuzzByTool.set(s.tool, toolFuzzSummary(s));
57
+ }
58
+ }
59
+ // Sort by authority (privileged first), then by data-class, then by name.
60
+ const sortedClassifications = [...classification.classifications].sort((a, b) => {
61
+ const ord = AUTHORITY_ORDER[a.authority_level] - AUTHORITY_ORDER[b.authority_level];
62
+ if (ord !== 0)
63
+ return ord;
64
+ if (a.data_class !== b.data_class)
65
+ return a.data_class.localeCompare(b.data_class);
66
+ return a.tool.localeCompare(b.tool);
67
+ });
68
+ for (const c of sortedClassifications) {
69
+ const tool = inventory.tools.find((t) => t.name === c.tool);
70
+ out.push(...renderTool(c, tool?.description ?? "", fuzzByTool.get(c.tool)));
71
+ out.push("");
72
+ }
73
+ // ─── Footer ──────────────────────────────────────────────────
74
+ out.push("---");
75
+ out.push("");
76
+ out.push("## How to read this report");
77
+ out.push("");
78
+ out.push("Every classification has a `confidence` score (noisy-OR over fired rules — see [docs/METHODOLOGY.md](https://github.com/euanmcrosson-dotcom/mcp-recon/blob/master/docs/METHODOLOGY.md)) and a free-form `rationale` listing every rule that fired. If you disagree with a classification:");
79
+ out.push("");
80
+ out.push("1. Read the `rationale` — which rules fired, and which you'd reweight.");
81
+ out.push("2. Edit `crates/mcp-recon-core/src/rules/v0_1.rs` (rule weights) or `packages/mcp-recon-cli/src/classify/rules.ts` (TS mirror) and re-run.");
82
+ out.push("3. Open an issue with your reasoning so future runs benefit.");
83
+ out.push("");
84
+ out.push("**Recommended caveats are starting points, not finish lines.** Tighten the placeholders to match your deployment, and consider attaching additional caveats (caller binding, time-of-day windows, rate limits enforced upstream).");
85
+ out.push("");
86
+ return out.join("\n");
87
+ }
88
+ function renderTool(c, description, fuzzStats) {
89
+ const out = [];
90
+ const cdp = c.confused_deputy_candidate ? " ⚠️ confused-deputy candidate" : "";
91
+ out.push(`### ${c.tool}`);
92
+ out.push("");
93
+ out.push(`**Class:** \`${c.data_class}\` · **Authority:** \`${c.authority_level}\`${cdp} · **Confidence:** ${(c.confidence * 100).toFixed(1)}%`);
94
+ if (description) {
95
+ const trimmed = description.length > 320 ? `${description.slice(0, 320)}…` : description;
96
+ out.push("");
97
+ out.push(`**Description:** ${trimmed.replace(/\s+/g, " ")}`);
98
+ }
99
+ out.push("");
100
+ out.push(`**Rationale:** ${c.rationale}`);
101
+ if (fuzzStats) {
102
+ out.push("");
103
+ out.push(`**Fuzz:** ${fuzzStats.total} calls — ok=${fuzzStats.ok}, protocol_error=${fuzzStats.protocol_error}, runtime_error=${fuzzStats.runtime_error}${fuzzStats.runtime_error > 0 ? " ⚠️" : ""}`);
104
+ }
105
+ out.push("");
106
+ out.push(`**Recommended capnagent caveat:**`);
107
+ out.push("");
108
+ out.push("```");
109
+ out.push(c.recommended_caveat);
110
+ out.push("```");
111
+ return out;
112
+ }
113
+ function countBy(items, key) {
114
+ const out = {};
115
+ for (const item of items) {
116
+ const k = key(item);
117
+ out[k] = (out[k] ?? 0) + 1;
118
+ }
119
+ return out;
120
+ }
121
+ function formatDistribution(d) {
122
+ const entries = Object.entries(d).sort((a, b) => b[1] - a[1]);
123
+ return entries.map(([k, v]) => `${k} (${v})`).join(", ") || "(none)";
124
+ }
125
+ function toolFuzzSummary(s) {
126
+ return {
127
+ total: s.total,
128
+ ok: s.ok,
129
+ protocol_error: s.protocol_error,
130
+ runtime_error: s.runtime_error,
131
+ };
132
+ }
133
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/report/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * `scan` — one-shot orchestrator: enumerate → fuzz → classify → report.
3
+ *
4
+ * The daily-driver command. Composes the four primitives defined in
5
+ * `enumerate.ts`, `fuzz/index.ts`, `classify/index.ts`, `report/index.ts`
6
+ * and writes all four artefacts to a single output directory.
7
+ *
8
+ * When operator bindings are supplied (caller / sandbox-prefix / expiry /
9
+ * per-tool overrides), `scan` also emits a 5th artefact, `caveats.json`,
10
+ * by running the classification through the caveats planner. This closes
11
+ * the bridge to capnagent in a single command. Without bindings, `scan`
12
+ * keeps its original 4-artefact behaviour intact.
13
+ *
14
+ * Returns the bag of intermediate values so a caller (CLI or library)
15
+ * can post-process beyond the on-disk artefacts.
16
+ */
17
+ import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
18
+ import type { CaveatBindings, CaveatsResults } from "../caveats/types.js";
19
+ import type { ClassificationResults } from "../classify/types.js";
20
+ import type { ToolInventory } from "../enumerate.js";
21
+ import { type FuzzOptions } from "../fuzz/index.js";
22
+ import type { FuzzResults } from "../fuzz/types.js";
23
+ export interface ScanResult {
24
+ inventory: ToolInventory;
25
+ fuzz: FuzzResults;
26
+ classification: ClassificationResults;
27
+ reportMarkdown: string;
28
+ /** Present when bindings triggered caveats emission. */
29
+ caveats?: CaveatsResults;
30
+ }
31
+ export interface ScanOptions extends FuzzOptions {
32
+ /** If set, write the artefacts to this directory. */
33
+ outDir?: string;
34
+ /**
35
+ * If set AND has at least one of caller / sandbox_prefix / expiry /
36
+ * per_tool_overrides populated, scan additionally produces a caveats
37
+ * document (and writes `caveats.json` when `outDir` is set). Empty
38
+ * objects, or objects with only undefined values, do not trigger
39
+ * caveats emission.
40
+ */
41
+ bindings?: CaveatBindings;
42
+ }
43
+ /**
44
+ * Run the full pipeline. Caller owns the client (open + close).
45
+ *
46
+ * Side effects (only when `outDir` is provided):
47
+ * - mkdir -p outDir
48
+ * - write inventory.json / fuzz.json / classification.json / report.md
49
+ * - write caveats.json IFF bindings provided with at least one field set
50
+ */
51
+ export declare function scan(client: Client, options?: ScanOptions): Promise<ScanResult>;
52
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/scan/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAGxE,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAElE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAQ,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,aAAa,CAAC;IACzB,IAAI,EAAE,WAAW,CAAC;IAClB,cAAc,EAAE,qBAAqB,CAAC;IACtC,cAAc,EAAE,MAAM,CAAC;IACvB,wDAAwD;IACxD,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B;AAED,MAAM,WAAW,WAAY,SAAQ,WAAW;IAC9C,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAmBD;;;;;;;GAOG;AACH,wBAAsB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,CAmCzF"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * `scan` — one-shot orchestrator: enumerate → fuzz → classify → report.
3
+ *
4
+ * The daily-driver command. Composes the four primitives defined in
5
+ * `enumerate.ts`, `fuzz/index.ts`, `classify/index.ts`, `report/index.ts`
6
+ * and writes all four artefacts to a single output directory.
7
+ *
8
+ * When operator bindings are supplied (caller / sandbox-prefix / expiry /
9
+ * per-tool overrides), `scan` also emits a 5th artefact, `caveats.json`,
10
+ * by running the classification through the caveats planner. This closes
11
+ * the bridge to capnagent in a single command. Without bindings, `scan`
12
+ * keeps its original 4-artefact behaviour intact.
13
+ *
14
+ * Returns the bag of intermediate values so a caller (CLI or library)
15
+ * can post-process beyond the on-disk artefacts.
16
+ */
17
+ import * as fs from "node:fs";
18
+ import * as path from "node:path";
19
+ import { planCaveats } from "../caveats/index.js";
20
+ import { classify } from "../classify/index.js";
21
+ import { enumerate } from "../enumerate.js";
22
+ import { fuzz } from "../fuzz/index.js";
23
+ import { renderMarkdown } from "../report/index.js";
24
+ /**
25
+ * Returns true if the bindings object has at least one populated field
26
+ * relevant to caveats emission. We treat empty strings as "set" so the
27
+ * operator's intent is honoured (the bridge handles substitution
28
+ * downstream and will flag empty values via `unsubstituted_placeholder`
29
+ * if appropriate).
30
+ */
31
+ function hasAnyBinding(bindings) {
32
+ if (bindings.caller !== undefined)
33
+ return true;
34
+ if (bindings.sandbox_prefix !== undefined)
35
+ return true;
36
+ if (bindings.expiry !== undefined)
37
+ return true;
38
+ if (bindings.per_tool_overrides !== undefined) {
39
+ if (Object.keys(bindings.per_tool_overrides).length > 0)
40
+ return true;
41
+ }
42
+ return false;
43
+ }
44
+ /**
45
+ * Run the full pipeline. Caller owns the client (open + close).
46
+ *
47
+ * Side effects (only when `outDir` is provided):
48
+ * - mkdir -p outDir
49
+ * - write inventory.json / fuzz.json / classification.json / report.md
50
+ * - write caveats.json IFF bindings provided with at least one field set
51
+ */
52
+ export async function scan(client, options = {}) {
53
+ const { outDir, bindings, ...fuzzOptions } = options;
54
+ const inventory = await enumerate(client);
55
+ const fuzzResults = await fuzz(client, inventory, fuzzOptions);
56
+ const classification = classify(inventory, fuzzResults);
57
+ const reportMarkdown = renderMarkdown({
58
+ inventory,
59
+ classification,
60
+ fuzz: fuzzResults,
61
+ });
62
+ const caveats = bindings !== undefined && hasAnyBinding(bindings)
63
+ ? planCaveats(classification, bindings)
64
+ : undefined;
65
+ if (outDir !== undefined) {
66
+ fs.mkdirSync(outDir, { recursive: true });
67
+ fs.writeFileSync(path.join(outDir, "inventory.json"), `${JSON.stringify(inventory, null, 2)}\n`);
68
+ fs.writeFileSync(path.join(outDir, "fuzz.json"), `${JSON.stringify(fuzzResults, null, 2)}\n`);
69
+ fs.writeFileSync(path.join(outDir, "classification.json"), `${JSON.stringify(classification, null, 2)}\n`);
70
+ const md = reportMarkdown.endsWith("\n") ? reportMarkdown : `${reportMarkdown}\n`;
71
+ fs.writeFileSync(path.join(outDir, "report.md"), md);
72
+ if (caveats !== undefined) {
73
+ fs.writeFileSync(path.join(outDir, "caveats.json"), `${JSON.stringify(caveats, null, 2)}\n`);
74
+ }
75
+ }
76
+ const result = { inventory, fuzz: fuzzResults, classification, reportMarkdown };
77
+ if (caveats !== undefined)
78
+ result.caveats = caveats;
79
+ return result;
80
+ }
81
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/scan/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Server-spec parsing + transport construction.
3
+ *
4
+ * Per docs/SPEC.md, v0.1 supports three forms:
5
+ *
6
+ * stdio:<command> [args...] — spawn process; stdio transport
7
+ * stdio:./path/to/binary --arg — relative path also accepted
8
+ * http://localhost:3000 — HTTP transport
9
+ *
10
+ * The HTTP transport stub is here for v0.1; the implementation
11
+ * lands in week 2 once we have a real HTTP-transport MCP server to
12
+ * test against. For week 1 the focus is stdio + the official
13
+ * @modelcontextprotocol/server-filesystem.
14
+ */
15
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
16
+ /** A parsed server-spec — the union of supported transport variants. */
17
+ export type ServerSpec = {
18
+ kind: "stdio";
19
+ command: string;
20
+ args: string[];
21
+ } | {
22
+ kind: "http";
23
+ url: string;
24
+ };
25
+ /**
26
+ * Parse a server-spec string into structured form.
27
+ *
28
+ * Throws if the spec doesn't match a supported shape — we fail loud
29
+ * here rather than silently degrading to a default, because a
30
+ * mistyped spec produces a confusing connect-failure later.
31
+ */
32
+ export declare function parseServerSpec(spec: string): ServerSpec;
33
+ /**
34
+ * Open a connected MCP `Client` for the given spec.
35
+ *
36
+ * Caller owns shutdown — pass the result to `closeClient` when
37
+ * finished. We do not auto-close on process exit because some
38
+ * commands (`scan`) hold the client across multiple steps.
39
+ */
40
+ export declare function openClient(spec: ServerSpec): Promise<Client>;
41
+ /** Close a client opened by `openClient`. Safe to call multiple times. */
42
+ export declare function closeClient(client: Client): Promise<void>;
43
+ //# sourceMappingURL=transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAGnE,wEAAwE;AACxE,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,GAClD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAElC;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAwBxD;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAuBlE;AAED,0EAA0E;AAC1E,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/D"}