@vitest-agent/mcp 3.0.3 → 4.0.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.
Files changed (54) hide show
  1. package/README.md +7 -6
  2. package/annotations.js +18 -0
  3. package/bin/vitest-agent-mcp.js +5 -140
  4. package/{middleware/idempotency.js → idempotency.js} +48 -49
  5. package/index.d.ts +5610 -1549
  6. package/index.js +35 -17
  7. package/main.d.ts +31 -0
  8. package/main.js +144 -0
  9. package/package.json +9 -6
  10. package/prompts/layer.js +108 -0
  11. package/register-toolkit.js +311 -0
  12. package/server.js +24 -894
  13. package/{context.js → session.js} +38 -22
  14. package/toolkit.js +86 -0
  15. package/tools/acceptance-metrics.js +26 -14
  16. package/tools/cache-health.js +28 -17
  17. package/tools/commit-changes.js +33 -10
  18. package/tools/configure.js +33 -10
  19. package/tools/coverage.js +34 -5
  20. package/tools/errors.js +36 -26
  21. package/tools/failure-signature-get.js +33 -10
  22. package/tools/file-coverage.js +37 -13
  23. package/tools/help.js +45 -4
  24. package/tools/history.js +39 -19
  25. package/tools/hypothesis.js +118 -102
  26. package/tools/inventory.js +149 -146
  27. package/tools/note.js +131 -113
  28. package/tools/overview.js +33 -10
  29. package/tools/ping.js +22 -10
  30. package/tools/register-agent.js +88 -62
  31. package/tools/run-tests.js +76 -23
  32. package/tools/settings-list.js +27 -10
  33. package/tools/status.js +35 -10
  34. package/tools/tdd-artifact.js +37 -22
  35. package/tools/tdd-behavior.js +120 -108
  36. package/tools/tdd-goal.js +98 -85
  37. package/tools/tdd-phase-transition-request.js +201 -166
  38. package/tools/tdd-progress-push.js +102 -0
  39. package/tools/tdd-task.js +140 -138
  40. package/tools/test.js +152 -138
  41. package/tools/trends.js +37 -18
  42. package/tools/triage-brief.js +34 -15
  43. package/tools/turn-search.js +39 -16
  44. package/tools/wrapup-prompt.js +37 -17
  45. package/utils/crash-guards.js +0 -22
  46. package/utils/replay-marker.js +12 -0
  47. package/utils/safe-format-fatal-error.js +0 -16
  48. package/utils/tool-error-envelope.js +3 -3
  49. package/version.js +13 -0
  50. package/layers/McpLive.js +0 -29
  51. package/prompts/index.js +0 -89
  52. package/router.js +0 -74
  53. package/session-env.js +0 -112
  54. package/utils/effect-to-zod.js +0 -158
package/index.js CHANGED
@@ -1,18 +1,36 @@
1
- import { createCallerFactory, createCurrentSessionIdRef, createSessionContextRef } from "./context.js";
2
- import { McpLive } from "./layers/McpLive.js";
3
- import { appRouter } from "./router.js";
4
- import { buildMcpServer, startMcpServer } from "./server.js";
5
- import { parseSessionEnvExports, recoverSessionContextFromSessionEnv } from "./session-env.js";
1
+ import { RenderText } from "./annotations.js";
2
+ import { withIdempotency } from "./idempotency.js";
3
+ import { McpSession, createCurrentSessionIdRef, createSessionContextRef, sessionContextFromEnv } from "./session.js";
4
+ import { PromptsLayer } from "./prompts/layer.js";
5
+ import { registerStrictToolkit } from "./register-toolkit.js";
6
+ import { AcceptanceMetricsResult } from "./tools/acceptance-metrics.js";
7
+ import { CacheHealthResult } from "./tools/cache-health.js";
8
+ import { CommitChangesInput, CommitChangesResult } from "./tools/commit-changes.js";
9
+ import { ConfigureInput, ConfigureResult } from "./tools/configure.js";
10
+ import { TestCoverageInput, TestCoverageResult } from "./tools/coverage.js";
11
+ import { TestErrorsInput, TestErrorsResult } from "./tools/errors.js";
12
+ import { FailureSignatureGetInput, FailureSignatureGetResult } from "./tools/failure-signature-get.js";
13
+ import { FileCoverageInput, FileCoverageResult } from "./tools/file-coverage.js";
14
+ import { HelpResult } from "./tools/help.js";
15
+ import { TestHistoryInput, TestHistoryResult } from "./tools/history.js";
16
+ import { InventoryInput, InventoryResult } from "./tools/inventory.js";
17
+ import { NoteParams, NoteResult } from "./tools/note.js";
18
+ import { TestOverviewInput, TestOverviewResult } from "./tools/overview.js";
19
+ import { PingResult } from "./tools/ping.js";
20
+ import { RegisterAgentInput, RegisterAgentResult } from "./tools/register-agent.js";
21
+ import { RunTestsInput, RunTestsResult } from "./tools/run-tests.js";
22
+ import { SettingsListResult } from "./tools/settings-list.js";
23
+ import { TestStatusInput, TestStatusResult } from "./tools/status.js";
24
+ import { TddArtifactListInput, TddArtifactListResult } from "./tools/tdd-artifact.js";
25
+ import { PhaseTransitionInput, PhaseTransitionResult } from "./tools/tdd-phase-transition-request.js";
26
+ import { TddProgressPushInput, TddProgressPushResult } from "./tools/tdd-progress-push.js";
27
+ import { TestInput, TestResult } from "./tools/test.js";
28
+ import { TestTrendsInput, TestTrendsResult } from "./tools/trends.js";
29
+ import { TriageBriefInput, TriageBriefResult } from "./tools/triage-brief.js";
30
+ import { TurnSearchInput, TurnSearchResult } from "./tools/turn-search.js";
31
+ import { WrapupPromptInput, WrapupPromptResult } from "./tools/wrapup-prompt.js";
32
+ import { Kit, ToolsLayer, toolHandlers } from "./toolkit.js";
33
+ import { ServerLayer } from "./server.js";
34
+ import { CURRENT_MCP_VERSION } from "./version.js";
6
35
 
7
- //#region src/index.ts
8
- /**
9
- * The version of this package, inlined at build time from
10
- * package.json#version via rslib-builder's __PACKAGE_VERSION__ substitution.
11
- * Exported for version introspection by downstream tooling.
12
- *
13
- * @public
14
- */
15
- const CURRENT_MCP_VERSION = "3.0.3";
16
-
17
- //#endregion
18
- export { CURRENT_MCP_VERSION, McpLive, appRouter, buildMcpServer, createCallerFactory, createCurrentSessionIdRef, createSessionContextRef, parseSessionEnvExports, recoverSessionContextFromSessionEnv, startMcpServer };
36
+ export { AcceptanceMetricsResult, CURRENT_MCP_VERSION, CacheHealthResult, CommitChangesInput, CommitChangesResult, ConfigureInput, ConfigureResult, FailureSignatureGetInput, FailureSignatureGetResult, FileCoverageInput, FileCoverageResult, HelpResult, InventoryInput, InventoryResult, Kit, McpSession, NoteParams, NoteResult, PhaseTransitionInput, PhaseTransitionResult, PingResult, PromptsLayer, RegisterAgentInput, RegisterAgentResult, RenderText, RunTestsInput, RunTestsResult, ServerLayer, SettingsListResult, TddArtifactListInput, TddArtifactListResult, TddProgressPushInput, TddProgressPushResult, TestCoverageInput, TestCoverageResult, TestErrorsInput, TestErrorsResult, TestHistoryInput, TestHistoryResult, TestInput, TestOverviewInput, TestOverviewResult, TestResult, TestStatusInput, TestStatusResult, TestTrendsInput, TestTrendsResult, ToolsLayer, TriageBriefInput, TriageBriefResult, TurnSearchInput, TurnSearchResult, WrapupPromptInput, WrapupPromptResult, createCurrentSessionIdRef, createSessionContextRef, registerStrictToolkit, sessionContextFromEnv, toolHandlers, withIdempotency };
package/main.d.ts ADDED
@@ -0,0 +1,31 @@
1
+ //#region src/main.d.ts
2
+ /**
3
+ * The assembled vitest-agent MCP server program over stdio. Owns the process.
4
+ *
5
+ * This module deliberately carries NO static imports of the server graph:
6
+ * the `unhandledRejection` and `uncaughtException` guards are registered
7
+ * before `NodeRuntime`, the engine platform and `ServerLayer` are ever
8
+ * evaluated, so a throw during module evaluation is still reported on stderr
9
+ * rather than crashing silently. Adding a static import here would defeat
10
+ * that. `bin.ts` is the published bin shim; this module is also published as
11
+ * the `./main` subpath so the carrier (`@vitest-agent/plugin`) can ship its
12
+ * own `vitest-agent-mcp` bin over it.
13
+ *
14
+ * Every `process` read lives here, not in the server layer or the tools.
15
+ *
16
+ * @packageDocumentation
17
+ */
18
+ /**
19
+ * Run the vitest-agent MCP server over stdio. Owns the process: registers
20
+ * the crash guards, resolves the project directory and `data.db` path,
21
+ * recovers the host session context, and launches the server layer under
22
+ * `NodeRuntime.runMain`.
23
+ *
24
+ * Not re-exported from `index.ts` — a library consumer's import graph must
25
+ * not pull in the process-owning module.
26
+ *
27
+ * @public
28
+ */
29
+ export declare const main: () => Promise<void>;
30
+ //#endregion
31
+ //# sourceMappingURL=main.d.ts.map
package/main.js ADDED
@@ -0,0 +1,144 @@
1
+ import { shouldExitOnUncaughtException } from "./utils/crash-guards.js";
2
+
3
+ //#region src/main.ts
4
+ /**
5
+ * The assembled vitest-agent MCP server program over stdio. Owns the process.
6
+ *
7
+ * This module deliberately carries NO static imports of the server graph:
8
+ * the `unhandledRejection` and `uncaughtException` guards are registered
9
+ * before `NodeRuntime`, the engine platform and `ServerLayer` are ever
10
+ * evaluated, so a throw during module evaluation is still reported on stderr
11
+ * rather than crashing silently. Adding a static import here would defeat
12
+ * that. `bin.ts` is the published bin shim; this module is also published as
13
+ * the `./main` subpath so the carrier (`@vitest-agent/plugin`) can ship its
14
+ * own `vitest-agent-mcp` bin over it.
15
+ *
16
+ * Every `process` read lives here, not in the server layer or the tools.
17
+ *
18
+ * @packageDocumentation
19
+ */
20
+ /**
21
+ * Whether the stdio transport is live for this process — set once the
22
+ * whole server layer graph (the stdio protocol included) has been built.
23
+ * Read by the `uncaughtException` guard via `shouldExitOnUncaughtException`;
24
+ * see that function's doc comment for the judgment call this flag backs
25
+ * (issue #191).
26
+ */
27
+ let transportConnected = false;
28
+ /**
29
+ * Formatter for the crash guards. Starts as a dependency-free fallback so a
30
+ * throw during module evaluation of the server graph is still described,
31
+ * and is swapped for the SDK's `safeFormatFatalError` once that import
32
+ * has resolved.
33
+ */
34
+ let formatFatal = (error) => {
35
+ try {
36
+ return error instanceof Error ? error.stack ?? error.message : String(error);
37
+ } catch {
38
+ return "<unformattable error value>";
39
+ }
40
+ };
41
+ /**
42
+ * Test-only crash injection, gated by an env var so it can never fire in a
43
+ * normal install. Exists so `__test__/bin-crash-resilience.e2e.test.ts` can
44
+ * exercise the guards against a *real* child process. Fires exactly once,
45
+ * on the next event-loop turn after the transport is known to be connected,
46
+ * so ordering relative to `transportConnected` is deterministic regardless
47
+ * of client-side handshake timing.
48
+ */
49
+ const scheduleTestCrashInjection = () => {
50
+ const kind = process.env.VITEST_AGENT_MCP_TEST_INJECT_CRASH;
51
+ if (kind !== "unhandledRejection" && kind !== "uncaughtException") return;
52
+ setImmediate(() => {
53
+ if (kind === "unhandledRejection") Promise.reject(/* @__PURE__ */ new Error("[test-injected] unhandledRejection"));
54
+ else throw new Error("[test-injected] uncaughtException");
55
+ });
56
+ };
57
+ /**
58
+ * Optional first positional argument: an initial Claude Code chat UUID (the
59
+ * host's `chatId`) to seed the MCP server's session association. Claude Code
60
+ * substitutes unknown `${...}` variables to literal text in some surfaces, so
61
+ * a literal substitution is treated as absent rather than seeding garbage.
62
+ *
63
+ * @param argv - the raw `process.argv`
64
+ * @returns the trimmed seed, or `null` when absent, empty, or a literal `${...}`
65
+ */
66
+ const resolveInitialSessionId = (argv) => {
67
+ const first = argv[2];
68
+ if (first === void 0) return null;
69
+ const trimmed = first.trim();
70
+ if (trimmed.length === 0) return null;
71
+ if (trimmed.startsWith("${") && trimmed.endsWith("}")) return null;
72
+ return trimmed;
73
+ };
74
+ /**
75
+ * Run the vitest-agent MCP server over stdio. Owns the process: registers
76
+ * the crash guards, resolves the project directory and `data.db` path,
77
+ * recovers the host session context, and launches the server layer under
78
+ * `NodeRuntime.runMain`.
79
+ *
80
+ * Not re-exported from `index.ts` — a library consumer's import graph must
81
+ * not pull in the process-owning module.
82
+ *
83
+ * @public
84
+ */
85
+ const main = async () => {
86
+ process.on("unhandledRejection", (reason) => {
87
+ process.stderr.write(`vitest-agent-mcp: unhandledRejection: ${formatFatal(reason)}\n`);
88
+ });
89
+ process.on("uncaughtException", (err, origin) => {
90
+ process.stderr.write(`vitest-agent-mcp: uncaughtException (${origin}): ${formatFatal(err)}\n`);
91
+ if (shouldExitOnUncaughtException(transportConnected)) {
92
+ process.exitCode = 1;
93
+ process.exit(1);
94
+ }
95
+ });
96
+ try {
97
+ const { safeFormatFatalError } = await import("./utils/safe-format-fatal-error.js");
98
+ formatFatal = safeFormatFatalError;
99
+ const NodeRuntime = await import("@effect/platform-node/NodeRuntime");
100
+ const NodeServices = await import("@effect/platform-node/NodeServices");
101
+ const NodeStdio = await import("@effect/platform-node/NodeStdio");
102
+ const { Cause, Effect, Exit, Layer, Logger, Runtime } = await import("effect");
103
+ const { PathResolutionLive, PlatformLive, recoverSessionContextFromSessionEnv, resolveDataPath, resolveLogFile, resolveLogLevel, resolveProjectDir } = await import("@vitest-agent/engine");
104
+ const { McpSession, sessionContextFromEnv } = await import("./session.js");
105
+ const { ServerLayer } = await import("./server.js");
106
+ const { CURRENT_MCP_VERSION } = await import("./version.js");
107
+ const env = process.env;
108
+ const projectDir = resolveProjectDir({
109
+ env,
110
+ cwd: process.cwd()
111
+ });
112
+ const initialSessionId = resolveInitialSessionId(process.argv);
113
+ const dbPath = await Effect.runPromise(resolveDataPath(projectDir).pipe(Effect.provide(PathResolutionLive(projectDir)), Effect.provide(NodeServices.layer)));
114
+ const recovered = sessionContextFromEnv(env);
115
+ const homeDir = env.HOME ?? env.USERPROFILE ?? "";
116
+ const Session = McpSession.layer({
117
+ cwd: projectDir,
118
+ initialSessionId: initialSessionId ?? recovered?.chatId ?? null,
119
+ initialContext: recovered,
120
+ recover: () => recoverSessionContextFromSessionEnv({
121
+ projectDir,
122
+ homeDir
123
+ })
124
+ });
125
+ const Main = ServerLayer({ version: CURRENT_MCP_VERSION }).pipe(Layer.provide(Session), Layer.provide(PlatformLive({
126
+ dbPath,
127
+ env,
128
+ logLevel: resolveLogLevel(env),
129
+ logFile: resolveLogFile(env)
130
+ })), Layer.provide(NodeStdio.layer), Layer.provide(Layer.succeed(Logger.LogToStderr, true)));
131
+ const Connected = Layer.effectDiscard(Effect.sync(() => {
132
+ transportConnected = true;
133
+ scheduleTestCrashInjection();
134
+ })).pipe(Layer.provide(Main));
135
+ const program = Layer.launch(Connected).pipe(Effect.provideService(Logger.LogToStderr, true));
136
+ NodeRuntime.runMain(program, { teardown: (exit, onExit) => Exit.isSuccess(exit) || Cause.hasInterruptsOnly(exit.cause) ? onExit(0) : Runtime.defaultTeardown(exit, onExit) });
137
+ } catch (err) {
138
+ process.stderr.write(`vitest-agent-mcp: startup failed: ${formatFatal(err)}\n`);
139
+ process.exit(1);
140
+ }
141
+ };
142
+
143
+ //#endregion
144
+ export { main };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitest-agent/mcp",
3
- "version": "3.0.3",
3
+ "version": "4.0.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": [
@@ -32,6 +32,11 @@
32
32
  "import": "./index.js",
33
33
  "default": "./index.js"
34
34
  },
35
+ "./main": {
36
+ "types": "./main.d.ts",
37
+ "import": "./main.js",
38
+ "default": "./main.js"
39
+ },
35
40
  "./package.json": "./package.json"
36
41
  },
37
42
  "bin": {
@@ -40,11 +45,9 @@
40
45
  "dependencies": {
41
46
  "@effect/platform-node": "4.0.0-rc.115",
42
47
  "@effect/sql-sqlite-node": "4.0.0-rc.115",
43
- "@modelcontextprotocol/sdk": "^1.30.0",
44
- "@trpc/server": "^11.18.0",
45
- "@vitest-agent/sdk": "3.1.2",
46
- "effect": "4.0.0-rc.115",
47
- "zod": "^4.5.4"
48
+ "@vitest-agent/engine": "0.1.0",
49
+ "@vitest-agent/sdk": "4.0.0",
50
+ "effect": "4.0.0-rc.115"
48
51
  },
49
52
  "peerDependencies": {
50
53
  "vitest": "^5.0.0"
@@ -0,0 +1,108 @@
1
+ import { McpSession } from "../session.js";
2
+ import { explainFailurePrompt } from "./explain-failure.js";
3
+ import { regressionSincePassPrompt } from "./regression-since-pass.js";
4
+ import { tddResumePrompt } from "./tdd-resume.js";
5
+ import { triagePrompt } from "./triage.js";
6
+ import { whyFlakyPrompt } from "./why-flaky.js";
7
+ import { wrapupPrompt } from "./wrapup.js";
8
+ import { Effect, Layer, Schema } from "effect";
9
+ import { McpSchema, McpServer } from "effect/unstable/ai";
10
+
11
+ //#region src/prompts/layer.ts
12
+ /**
13
+ * The six prompt names `PromptsLayer` registers, in registration order.
14
+ * Pinned against the `help` text by `__test__/help-drift.test.ts`.
15
+ *
16
+ * @internal
17
+ */
18
+ const PROMPT_NAMES = [
19
+ "triage",
20
+ "why-flaky",
21
+ "regression-since-pass",
22
+ "explain-failure",
23
+ "tdd-resume",
24
+ "wrapup"
25
+ ];
26
+ /** The `wrapup.kind` literal set, served as the argument's closed vocabulary. */
27
+ const WRAPUP_KINDS = [
28
+ "stop",
29
+ "session_end",
30
+ "pre_compact",
31
+ "tdd_handoff",
32
+ "user_prompt_nudge"
33
+ ];
34
+ const toMessages = (result) => result.messages.map((m) => ({
35
+ role: m.role,
36
+ content: McpSchema.TextContent.make({ text: m.content.text })
37
+ }));
38
+ const projectArg = Schema.optionalKey(Schema.String.annotate({ description: "Filter to a specific project" }));
39
+ const Triage = McpServer.prompt({
40
+ name: PROMPT_NAMES[0],
41
+ description: "Orient toward a triage workflow over the most recent test run; compose triage_brief, failure_signature_get, hypothesis_record.",
42
+ parameters: { project: projectArg },
43
+ content: (args) => Effect.succeed(toMessages(triagePrompt(args.project !== void 0 ? { project: args.project } : {})))
44
+ });
45
+ const WhyFlaky = McpServer.prompt({
46
+ name: PROMPT_NAMES[1],
47
+ description: "Diagnose why a named test is flaky; compose test_history and failure_signature_get with timing/shared-state framing.",
48
+ parameters: {
49
+ test: Schema.String.annotate({ description: "Full hierarchical test name (e.g. 'Suite > nested > test')" }),
50
+ project: projectArg
51
+ },
52
+ content: (args) => Effect.succeed(toMessages(whyFlakyPrompt(args.project !== void 0 ? {
53
+ test: args.test,
54
+ project: args.project
55
+ } : { test: args.test })))
56
+ });
57
+ const RegressionSincePass = McpServer.prompt({
58
+ name: PROMPT_NAMES[2],
59
+ description: "Walk back from the test's most recent passing run to identify the change that broke it; compose test_history, commit_changes, turn_search.",
60
+ parameters: {
61
+ test: Schema.String.annotate({ description: "Full hierarchical test name" }),
62
+ project: projectArg
63
+ },
64
+ content: (args) => Effect.succeed(toMessages(regressionSincePassPrompt(args.project !== void 0 ? {
65
+ test: args.test,
66
+ project: args.project
67
+ } : { test: args.test })))
68
+ });
69
+ const ExplainFailure = McpServer.prompt({
70
+ name: PROMPT_NAMES[3],
71
+ description: "Synthesize a root-cause explanation from the recurrence history of a failure signature.",
72
+ parameters: { signature: Schema.String.annotate({ description: "16-char failure signature hex" }) },
73
+ content: (args) => Effect.succeed(toMessages(explainFailurePrompt({ signature: args.signature })))
74
+ });
75
+ const TddResume = McpServer.prompt({
76
+ name: PROMPT_NAMES[4],
77
+ description: "Resume the active TDD task from its current phase; iron-law reminder for evidence-bound transitions.",
78
+ parameters: { sessionId: Schema.optionalKey(Schema.String.annotate({ description: "Host session id (defaults to MCP server's recovered SessionContext)" })) },
79
+ content: (args) => Effect.gen(function* () {
80
+ const session = yield* McpSession;
81
+ const sessionId = args.sessionId ?? session.sessionContext.get()?.chatId ?? session.currentSessionId.get();
82
+ return toMessages(tddResumePrompt(sessionId === null ? {} : { sessionId }));
83
+ })
84
+ });
85
+ const Wrapup = McpServer.prompt({
86
+ name: PROMPT_NAMES[5],
87
+ description: "Surface the same wrapup content the post-hooks emit automatically.",
88
+ parameters: {
89
+ kind: Schema.optionalKey(Schema.Literals(WRAPUP_KINDS).annotate({ description: "Wrapup variant (default: user_prompt_nudge)" })),
90
+ since: Schema.optionalKey(Schema.String.annotate({ description: "ISO 8601 timestamp lower bound for activity to summarize" }))
91
+ },
92
+ content: (args) => {
93
+ const wrapupArgs = {};
94
+ if (args.kind !== void 0) wrapupArgs.kind = args.kind;
95
+ if (args.since !== void 0) wrapupArgs.since = args.since;
96
+ return Effect.succeed(toMessages(wrapupPrompt(wrapupArgs)));
97
+ }
98
+ });
99
+ /**
100
+ * Every framing prompt as one layer; merged into `ServerLayer` beside the
101
+ * toolkit registration. Requires `McpSession` for `tdd-resume`'s default.
102
+ *
103
+ * @public
104
+ */
105
+ const PromptsLayer = Layer.mergeAll(Triage, WhyFlaky, RegressionSincePass, ExplainFailure, TddResume, Wrapup);
106
+
107
+ //#endregion
108
+ export { PROMPT_NAMES, PromptsLayer, WRAPUP_KINDS };