@vitest-agent/mcp 2.0.14 → 2.1.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.
- package/index.js +1 -1
- package/package.json +5 -5
- package/tools/acceptance-metrics.js +12 -12
- package/tools/cache-health.js +1 -1
- package/tools/commit-changes.js +1 -1
- package/tools/errors.js +2 -2
- package/tools/failure-signature-get.js +1 -1
- package/tools/history.js +3 -3
- package/tools/hypothesis.js +1 -1
- package/tools/note.js +2 -2
- package/tools/tdd-artifact.js +1 -1
- package/tools/tdd-phase-transition-request.js +1 -1
- package/tools/turn-search.js +4 -4
- package/utils/effect-to-zod.js +63 -1
package/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import { parseSessionEnvExports, recoverSessionContextFromSessionEnv } from "./s
|
|
|
12
12
|
*
|
|
13
13
|
* @public
|
|
14
14
|
*/
|
|
15
|
-
const CURRENT_MCP_VERSION = "2.0
|
|
15
|
+
const CURRENT_MCP_VERSION = "2.1.0";
|
|
16
16
|
|
|
17
17
|
//#endregion
|
|
18
18
|
export { CURRENT_MCP_VERSION, McpLive, appRouter, buildMcpServer, createCallerFactory, createCurrentSessionIdRef, createSessionContextRef, parseSessionEnvExports, recoverSessionContextFromSessionEnv, startMcpServer };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitest-agent/mcp",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Model Context Protocol server for vitest-agent. Exposes 53 tools for agent access to test data, TDD lifecycle, and session management.",
|
|
6
6
|
"keywords": [
|
|
@@ -38,12 +38,12 @@
|
|
|
38
38
|
"vitest-agent-mcp": "bin/vitest-agent-mcp.js"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@effect/platform-node": "4.0.0-beta.
|
|
42
|
-
"@effect/sql-sqlite-node": "4.0.0-beta.
|
|
41
|
+
"@effect/platform-node": "4.0.0-beta.107",
|
|
42
|
+
"@effect/sql-sqlite-node": "4.0.0-beta.107",
|
|
43
43
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
44
44
|
"@trpc/server": "^11.18.0",
|
|
45
|
-
"@vitest-agent/sdk": "2.0.
|
|
46
|
-
"effect": "4.0.0-beta.
|
|
45
|
+
"@vitest-agent/sdk": "2.0.15",
|
|
46
|
+
"effect": "4.0.0-beta.107",
|
|
47
47
|
"zod": "^4.4.3"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
@@ -17,33 +17,33 @@ const totalAnnotation = { description: "Sample size — number of observations t
|
|
|
17
17
|
const ratioAnnotation = { description: "Compliance ratio in [0, 1]. Multiply by 100 for the percentage form rendered in the markdown view." };
|
|
18
18
|
const AcceptanceMetricsResult = Schema.Struct({
|
|
19
19
|
phaseEvidenceIntegrity: Schema.Struct({
|
|
20
|
-
total: Schema.
|
|
21
|
-
compliant: Schema.
|
|
22
|
-
ratio: Schema.
|
|
20
|
+
total: Schema.Finite.annotate(totalAnnotation),
|
|
21
|
+
compliant: Schema.Finite.annotate({ description: "Phase transitions that cited a valid artifact and passed binding-rule validation." }),
|
|
22
|
+
ratio: Schema.Finite.annotate(ratioAnnotation)
|
|
23
23
|
}).annotate({
|
|
24
24
|
title: "Phase-evidence integrity",
|
|
25
25
|
description: "Fraction of accepted TDD phase transitions whose cited artifact satisfied the D2 binding rules. Spec target ≥80%."
|
|
26
26
|
}),
|
|
27
27
|
complianceHookResponsiveness: Schema.Struct({
|
|
28
|
-
total: Schema.
|
|
29
|
-
withFollowup: Schema.
|
|
30
|
-
ratio: Schema.
|
|
28
|
+
total: Schema.Finite.annotate(totalAnnotation),
|
|
29
|
+
withFollowup: Schema.Finite.annotate({ description: "PreToolUse denials / `additionalContext` reminders the orchestrator acknowledged in the next turn." }),
|
|
30
|
+
ratio: Schema.Finite.annotate(ratioAnnotation)
|
|
31
31
|
}).annotate({
|
|
32
32
|
title: "Compliance-hook responsiveness",
|
|
33
33
|
description: "Fraction of compliance signals from PreToolUse hooks the orchestrator acted on. Spec target ≥40%."
|
|
34
34
|
}),
|
|
35
35
|
orientationUsefulness: Schema.Struct({
|
|
36
|
-
total: Schema.
|
|
37
|
-
referencedCount: Schema.
|
|
38
|
-
ratio: Schema.
|
|
36
|
+
total: Schema.Finite.annotate(totalAnnotation),
|
|
37
|
+
referencedCount: Schema.Finite.annotate({ description: "Sessions where `triage_brief` / `wrapup_prompt` content was referenced in subsequent decisions." }),
|
|
38
|
+
ratio: Schema.Finite.annotate(ratioAnnotation)
|
|
39
39
|
}).annotate({
|
|
40
40
|
title: "Orientation usefulness",
|
|
41
41
|
description: "Fraction of sessions where orientation prompts measurably steered orchestrator behaviour. Spec target ≥50%."
|
|
42
42
|
}),
|
|
43
43
|
antiPatternDetectionRate: Schema.Struct({
|
|
44
|
-
total: Schema.
|
|
45
|
-
cleanSessions: Schema.
|
|
46
|
-
ratio: Schema.
|
|
44
|
+
total: Schema.Finite.annotate(totalAnnotation),
|
|
45
|
+
cleanSessions: Schema.Finite.annotate({ description: "Sessions that produced no `tdd_artifacts(kind='test_weakened')` rows or DATABASE_BYPASS notes." }),
|
|
46
|
+
ratio: Schema.Finite.annotate(ratioAnnotation)
|
|
47
47
|
}).annotate({
|
|
48
48
|
title: "Anti-pattern detection rate",
|
|
49
49
|
description: "Fraction of sessions free of weakening edits or sqlite3 bypass attempts. Spec target ≥95%."
|
package/tools/cache-health.js
CHANGED
|
@@ -18,7 +18,7 @@ import { Effect, Option, Schema, SchemaGetter } from "effect";
|
|
|
18
18
|
const ManifestPresent = Schema.Struct({
|
|
19
19
|
manifestPresent: Schema.Literal(true).annotate({ description: "Discriminant — `true` when a cache manifest exists." }),
|
|
20
20
|
manifest: CacheManifest.annotate({ description: "Full cache manifest content as written by the reporter." }),
|
|
21
|
-
ageMs: Schema.
|
|
21
|
+
ageMs: Schema.Finite.annotate({ description: "Milliseconds since the manifest was last updated. Computed at query time, not stored." }),
|
|
22
22
|
stale: Schema.Boolean.annotate({ description: "Convenience flag — `true` when `ageMs` exceeds 24 hours, otherwise `false`." })
|
|
23
23
|
}).annotate({
|
|
24
24
|
identifier: "CacheHealthPresent",
|
package/tools/commit-changes.js
CHANGED
|
@@ -29,7 +29,7 @@ const CommitRow = Schema.Struct({
|
|
|
29
29
|
}).annotate({ identifier: "CommitRow" });
|
|
30
30
|
const CommitChangesResult = Schema.Struct({
|
|
31
31
|
filterSha: Schema.optional(Schema.String).annotate({ description: "Echo of the optional `sha` filter the caller passed; absent when no filter was applied (recent commits returned)." }),
|
|
32
|
-
count: Schema.
|
|
32
|
+
count: Schema.Finite.annotate({ description: "Number of commit rows returned." }),
|
|
33
33
|
commits: Schema.Array(CommitRow).annotate({ description: "Matching commits, newest first when `sha` was omitted; up to 20 rows." })
|
|
34
34
|
}).annotate({
|
|
35
35
|
identifier: "CommitChangesResult",
|
package/tools/errors.js
CHANGED
|
@@ -22,7 +22,7 @@ import { Effect, Schema, SchemaGetter } from "effect";
|
|
|
22
22
|
*/
|
|
23
23
|
/** One row in the structured `errors[]` array. */
|
|
24
24
|
const TestErrorRow = Schema.Struct({
|
|
25
|
-
id: Schema.
|
|
25
|
+
id: Schema.Finite.annotate({
|
|
26
26
|
title: "test_errors.id",
|
|
27
27
|
description: "Numeric primary key of this error row. Pass as `citedTestErrorId` when calling `hypothesis (action: record)`."
|
|
28
28
|
}),
|
|
@@ -57,7 +57,7 @@ const TestErrorsResult = Schema.Struct({
|
|
|
57
57
|
examples: ["playground", "@org/pkg"]
|
|
58
58
|
}),
|
|
59
59
|
errorName: Schema.optional(Schema.String).annotate({ description: "Echo of the optional `errorName` filter the caller passed; absent when no filter was applied." }),
|
|
60
|
-
count: Schema.
|
|
60
|
+
count: Schema.Finite.annotate({ description: "Total error rows in `errors`." }),
|
|
61
61
|
errors: Schema.Array(TestErrorRow).annotate({ description: "Errors from the most recent test run for this project, optionally filtered by `errorName`. Empty when no errors matched." })
|
|
62
62
|
}).annotate({
|
|
63
63
|
identifier: "TestErrorsResult",
|
|
@@ -22,7 +22,7 @@ const SignatureFound = Schema.Struct({
|
|
|
22
22
|
firstSeenRunId: Schema.NullOr(Schema.Number),
|
|
23
23
|
firstSeenAt: Schema.String,
|
|
24
24
|
lastSeenAt: Schema.NullOr(Schema.String),
|
|
25
|
-
occurrenceCount: Schema.
|
|
25
|
+
occurrenceCount: Schema.Finite.annotate({ description: "Total times this signature has been observed." }),
|
|
26
26
|
recentErrors: Schema.Array(RecentError)
|
|
27
27
|
});
|
|
28
28
|
const SignatureMissing = Schema.Struct({
|
package/tools/history.js
CHANGED
|
@@ -16,8 +16,8 @@ const FlakyTestRow = Schema.Struct({
|
|
|
16
16
|
fullName: Schema.String.annotate({ description: "Full hierarchical test name (`describe > it`)." }),
|
|
17
17
|
modulePath: Schema.String.annotate({ description: "Project-relative test module path -- disambiguates same-named tests across files." }),
|
|
18
18
|
project: Schema.String,
|
|
19
|
-
passCount: Schema.
|
|
20
|
-
failCount: Schema.
|
|
19
|
+
passCount: Schema.Finite.annotate({ description: "Number of passing runs in the recent window." }),
|
|
20
|
+
failCount: Schema.Finite.annotate({ description: "Number of failing runs in the recent window." }),
|
|
21
21
|
lastState: Schema.Literals(["passed", "failed"]).annotate({ description: "State of the most recent run." }),
|
|
22
22
|
lastTimestamp: Schema.String.annotate({ description: "ISO-8601 timestamp of the most recent run." })
|
|
23
23
|
}).annotate({
|
|
@@ -28,7 +28,7 @@ const PersistentFailureRow = Schema.Struct({
|
|
|
28
28
|
fullName: Schema.String,
|
|
29
29
|
modulePath: Schema.String.annotate({ description: "Project-relative test module path -- disambiguates same-named tests across files." }),
|
|
30
30
|
project: Schema.String,
|
|
31
|
-
consecutiveFailures: Schema.
|
|
31
|
+
consecutiveFailures: Schema.Finite.annotate({ description: "Length of the current uninterrupted failure streak." }),
|
|
32
32
|
firstFailedAt: Schema.String.annotate({ description: "ISO-8601 timestamp of the first failure in this streak." }),
|
|
33
33
|
lastFailedAt: Schema.String.annotate({ description: "ISO-8601 timestamp of the most recent failure." }),
|
|
34
34
|
lastErrorMessage: Schema.NullOr(Schema.String).annotate({ description: "Last error message reported by the failing test, when captured." })
|
package/tools/hypothesis.js
CHANGED
|
@@ -28,7 +28,7 @@ const HypothesisRowSchema = Schema.Struct({
|
|
|
28
28
|
}).annotate({ identifier: "HypothesisRow" });
|
|
29
29
|
const HypothesisRecordOk = Schema.Struct({
|
|
30
30
|
action: Schema.Literal("record"),
|
|
31
|
-
id: Schema.
|
|
31
|
+
id: Schema.Finite.annotate({ description: "Newly inserted hypothesis row primary key." })
|
|
32
32
|
});
|
|
33
33
|
const HypothesisValidateOk = Schema.Struct({ action: Schema.Literal("validate") });
|
|
34
34
|
const HypothesisListOk = Schema.Struct({
|
package/tools/note.js
CHANGED
|
@@ -12,7 +12,7 @@ const NoteScope = Schema.Literals([
|
|
|
12
12
|
"note"
|
|
13
13
|
]);
|
|
14
14
|
const NoteRowSchema = Schema.Struct({
|
|
15
|
-
id: Schema.
|
|
15
|
+
id: Schema.Finite.annotate({ description: "Note primary key." }),
|
|
16
16
|
title: Schema.String,
|
|
17
17
|
content: Schema.String,
|
|
18
18
|
scope: NoteScope.annotate({ description: "`global` (project-agnostic), `project`, `module`, `suite`, `test` (scoped), or `note` (child note attached via `parentNoteId`)." }),
|
|
@@ -28,7 +28,7 @@ const NoteRowSchema = Schema.Struct({
|
|
|
28
28
|
}).annotate({ identifier: "NoteRow" });
|
|
29
29
|
const NoteCreateOk = Schema.Struct({
|
|
30
30
|
action: Schema.Literal("create"),
|
|
31
|
-
id: Schema.
|
|
31
|
+
id: Schema.Finite.annotate({ description: "Primary key of the newly inserted note." })
|
|
32
32
|
});
|
|
33
33
|
const NoteListOk = Schema.Struct({
|
|
34
34
|
action: Schema.Literal("list"),
|
package/tools/tdd-artifact.js
CHANGED
|
@@ -24,7 +24,7 @@ const ArtifactKindSchema = Schema.Literals([
|
|
|
24
24
|
"test_weakened"
|
|
25
25
|
]);
|
|
26
26
|
const TddArtifactRow = Schema.Struct({
|
|
27
|
-
id: Schema.
|
|
27
|
+
id: Schema.Finite.annotate({
|
|
28
28
|
title: "tdd_artifacts.id",
|
|
29
29
|
description: "Pass as `citedArtifactId` to `tdd_phase_transition_request`."
|
|
30
30
|
}),
|
|
@@ -46,7 +46,7 @@ const RemediationSchema = Schema.Struct({
|
|
|
46
46
|
const PhaseTransitionAccepted = Schema.Struct({
|
|
47
47
|
accepted: Schema.Literal(true).annotate({ description: "Discriminant — `true` when the transition was granted." }),
|
|
48
48
|
phase: phaseLiteral.annotate({ description: "Phase the session is now in (echo of `requestedPhase`)." }),
|
|
49
|
-
newPhaseId: Schema.
|
|
49
|
+
newPhaseId: Schema.Finite.annotate({ description: "`tdd_phases.id` of the freshly opened row." }),
|
|
50
50
|
previousPhaseId: Schema.NullOr(Schema.Number).annotate({ description: "`tdd_phases.id` of the phase that was closed (`null` for the first transition)." }),
|
|
51
51
|
citedArtifactId: Schema.optional(Schema.Number).annotate({ description: "Resolved `tdd_artifacts.id` actually used. Absent only for transitions that need no artifact (e.g. spike→red)." }),
|
|
52
52
|
citedArtifactSource: Schema.optional(Schema.Literals([
|
package/tools/turn-search.js
CHANGED
|
@@ -9,9 +9,9 @@ import { Effect, Schema, SchemaGetter } from "effect";
|
|
|
9
9
|
* @packageDocumentation
|
|
10
10
|
*/
|
|
11
11
|
const TurnRow = Schema.Struct({
|
|
12
|
-
id: Schema.
|
|
13
|
-
sessionId: Schema.
|
|
14
|
-
turnNo: Schema.
|
|
12
|
+
id: Schema.Finite.annotate({ description: "Numeric primary key of this turn row." }),
|
|
13
|
+
sessionId: Schema.Finite.annotate({ description: "Owning `sessions.id` (integer FK)." }),
|
|
14
|
+
turnNo: Schema.Finite.annotate({ description: "Turn ordinal within the session (1-based)." }),
|
|
15
15
|
type: Schema.String.annotate({ description: "Turn category (`user_prompt`, `tool_call`, `tool_result`, `file_edit`, `hook_fire`, `note`, `hypothesis`)." }),
|
|
16
16
|
payload: Schema.String.annotate({ description: "Type-specific payload as a JSON-encoded string. Decode shape depends on `type`." }),
|
|
17
17
|
occurredAt: Schema.String.annotate({ description: "ISO-8601 timestamp the turn was recorded at." })
|
|
@@ -20,7 +20,7 @@ const TurnRow = Schema.Struct({
|
|
|
20
20
|
description: "One row from the turns log."
|
|
21
21
|
});
|
|
22
22
|
const TurnSearchResult = Schema.Struct({
|
|
23
|
-
count: Schema.
|
|
23
|
+
count: Schema.Finite.annotate({ description: "Number of matching turn rows returned." }),
|
|
24
24
|
turns: Schema.Array(TurnRow).annotate({ description: "Matching turns ordered by `occurredAt` ascending." })
|
|
25
25
|
}).annotate({
|
|
26
26
|
identifier: "TurnSearchResult",
|
package/utils/effect-to-zod.js
CHANGED
|
@@ -38,6 +38,17 @@ import { z } from "zod";
|
|
|
38
38
|
* substitution, then drop `$defs`). The schemas don't use
|
|
39
39
|
* `Schema.suspend`, so the substitution is acyclic.
|
|
40
40
|
*
|
|
41
|
+
* Annotation lifting: Effect lowers a checked numeric schema
|
|
42
|
+
* (`Schema.Finite`, `Schema.Int`) to `{ type, allOf: [{ description }] }`
|
|
43
|
+
* rather than putting the annotation on the node itself, and
|
|
44
|
+
* `z.fromJSONSchema` preserves that nesting verbatim. A tool's
|
|
45
|
+
* `outputSchema` would then advertise no description for the field. The
|
|
46
|
+
* bridge therefore lifts annotation-only `allOf` members onto their
|
|
47
|
+
* parent before handing the document to zod. Only the JSON Schema
|
|
48
|
+
* annotation vocabulary is lifted -- a member carrying any constraint
|
|
49
|
+
* keyword (`pattern`, `minimum`, ...) stays put so validation semantics
|
|
50
|
+
* are never changed.
|
|
51
|
+
*
|
|
41
52
|
* MCP SDK constraint: `outputSchema` must normalise to a Zod object
|
|
42
53
|
* schema (`normalizeObjectSchema` returns `undefined` for unions, then
|
|
43
54
|
* `safeParseAsync(undefined, ...)` crashes with "Cannot read properties
|
|
@@ -67,6 +78,57 @@ const isObjectLike = (schema) => {
|
|
|
67
78
|
};
|
|
68
79
|
const REF_PREFIX = "#/$defs/";
|
|
69
80
|
/**
|
|
81
|
+
* The JSON Schema annotation vocabulary -- keywords that describe a
|
|
82
|
+
* schema without constraining what it accepts. Only these are safe to
|
|
83
|
+
* hoist out of an `allOf` member onto the parent node.
|
|
84
|
+
*/
|
|
85
|
+
const ANNOTATION_KEYWORDS = /* @__PURE__ */ new Set([
|
|
86
|
+
"title",
|
|
87
|
+
"description",
|
|
88
|
+
"examples",
|
|
89
|
+
"default",
|
|
90
|
+
"deprecated",
|
|
91
|
+
"readOnly",
|
|
92
|
+
"writeOnly",
|
|
93
|
+
"contentEncoding",
|
|
94
|
+
"contentMediaType"
|
|
95
|
+
]);
|
|
96
|
+
const isPlainObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
97
|
+
/**
|
|
98
|
+
* Hoist the annotation keywords out of every `allOf` member onto
|
|
99
|
+
* `node`, leaving each member's constraint keywords exactly where they
|
|
100
|
+
* were. A member reduced to nothing is dropped, and `allOf` disappears
|
|
101
|
+
* once every member has been consumed.
|
|
102
|
+
*
|
|
103
|
+
* Keys already present on the node win -- a nested annotation never
|
|
104
|
+
* overwrites a more specific one -- and the first member to supply a
|
|
105
|
+
* given key wins among siblings.
|
|
106
|
+
*/
|
|
107
|
+
const liftAnnotations = (node) => {
|
|
108
|
+
const members = node.allOf;
|
|
109
|
+
if (!Array.isArray(members)) return node;
|
|
110
|
+
const kept = [];
|
|
111
|
+
const lifted = {};
|
|
112
|
+
for (const member of members) {
|
|
113
|
+
if (!isPlainObject(member)) {
|
|
114
|
+
kept.push(member);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const constraints = {};
|
|
118
|
+
for (const [key, value] of Object.entries(member)) if (!ANNOTATION_KEYWORDS.has(key)) constraints[key] = value;
|
|
119
|
+
else if (!(key in node) && !(key in lifted)) lifted[key] = value;
|
|
120
|
+
if (Object.keys(constraints).length > 0) kept.push(constraints);
|
|
121
|
+
}
|
|
122
|
+
if (Object.keys(lifted).length === 0) return node;
|
|
123
|
+
const out = {
|
|
124
|
+
...node,
|
|
125
|
+
...lifted
|
|
126
|
+
};
|
|
127
|
+
if (kept.length > 0) out.allOf = kept;
|
|
128
|
+
else delete out.allOf;
|
|
129
|
+
return out;
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
70
132
|
* Walk a JSON Schema tree and replace every `$ref: "#/$defs/X"` node
|
|
71
133
|
* with the contents of `$defs.X`, recursively. The `$defs` table is
|
|
72
134
|
* dropped from the returned root.
|
|
@@ -87,7 +149,7 @@ const inlineAllRefs = (root) => {
|
|
|
87
149
|
if (k === "$defs") continue;
|
|
88
150
|
out[k] = visit(v);
|
|
89
151
|
}
|
|
90
|
-
return out;
|
|
152
|
+
return liftAnnotations(out);
|
|
91
153
|
};
|
|
92
154
|
return visit(root);
|
|
93
155
|
};
|