@vitest-agent/mcp 4.1.1 → 4.2.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.d.ts +26 -3
- package/index.js +2 -2
- package/package.json +6 -6
- package/register-toolkit.js +80 -33
- package/server.js +36 -9
- package/version.js +1 -1
package/index.d.ts
CHANGED
|
@@ -166,7 +166,7 @@ export declare const PromptsLayer: Layer.Layer<never, never, McpSession>;
|
|
|
166
166
|
*
|
|
167
167
|
* @public
|
|
168
168
|
*/
|
|
169
|
-
export declare const registerStrictToolkit: <Tools extends Record<string, Tool.Any>>(toolkit: Toolkit.Toolkit<Tools>) => Layer.Layer<never, never, Tool.HandlersFor<Tools> | Exclude<Tool.HandlerServices<Tools>, McpSchema.
|
|
169
|
+
export declare const registerStrictToolkit: <Tools extends Record<string, Tool.Any>>(toolkit: Toolkit.Toolkit<Tools>) => Layer.Layer<never, never, Tool.HandlersFor<Tools> | Exclude<Tool.HandlerServices<Tools>, McpSchema.McpRequestContext>>;
|
|
170
170
|
//#endregion
|
|
171
171
|
//#region src/server.d.ts
|
|
172
172
|
/**
|
|
@@ -184,10 +184,33 @@ interface ServerLayerOptions {
|
|
|
184
184
|
/** The `serverInfo.version` advertised on initialize (the package version in production). */
|
|
185
185
|
readonly version: string;
|
|
186
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* The agent-facing orientation every client receives: in the `initialize`
|
|
189
|
+
* result on the stateful protocols and in the `server/discover` result on
|
|
190
|
+
* `2026-07-28`. Says what the server is for, which tool to reach for first,
|
|
191
|
+
* and how to read a result; the one-line human summary stays on
|
|
192
|
+
* `serverInfo.description`.
|
|
193
|
+
*
|
|
194
|
+
* @public
|
|
195
|
+
*/
|
|
196
|
+
export declare const SERVER_INSTRUCTIONS: string;
|
|
187
197
|
/**
|
|
188
198
|
* The server layer: every tool registered under the strict contract, plus
|
|
189
|
-
* the six framing prompts, over `McpServer.layerStdio`.
|
|
190
|
-
*
|
|
199
|
+
* the six framing prompts, over `McpServer.layerStdio`.
|
|
200
|
+
*
|
|
201
|
+
* `protocols` order is load-bearing (rc.116 `unstable/ai/internal/mcpRuntime.ts`):
|
|
202
|
+
* a request carrying `_meta["io.modelcontextprotocol/protocolVersion"]`
|
|
203
|
+
* routes to that adapter, an `initialize` matches the STATEFUL adapters
|
|
204
|
+
* only, and anything else with no session falls to `protocols[0]`. The
|
|
205
|
+
* stateless `2026-07-28` adapter (SEP-2575: no handshake, no session,
|
|
206
|
+
* `server/discover` instead of `initialize`) is listed first, then the two
|
|
207
|
+
* newest stateful ones — every shipping client (Claude Code's default stdio
|
|
208
|
+
* session, Copilot, Cursor, the Inspector) still opens with `initialize`,
|
|
209
|
+
* which a server offering ONLY `2026-07-28` answers with
|
|
210
|
+
* `METHOD_NOT_FOUND`. At most one stateless adapter is allowed; a second
|
|
211
|
+
* fails the layer with `Cause.IllegalArgumentError`, which is why the
|
|
212
|
+
* error channel is `orDie`d: `protocols` is a static literal, so a failure
|
|
213
|
+
* there is an implementer-time defect.
|
|
191
214
|
*
|
|
192
215
|
* @param options - the advertised server version
|
|
193
216
|
* @public
|
package/index.js
CHANGED
|
@@ -30,7 +30,7 @@ import { TriageBriefInput, TriageBriefResult } from "./tools/triage-brief.js";
|
|
|
30
30
|
import { TurnSearchInput, TurnSearchResult } from "./tools/turn-search.js";
|
|
31
31
|
import { WrapupPromptInput, WrapupPromptResult } from "./tools/wrapup-prompt.js";
|
|
32
32
|
import { Kit, ToolsLayer, toolHandlers } from "./toolkit.js";
|
|
33
|
-
import { ServerLayer } from "./server.js";
|
|
33
|
+
import { SERVER_INSTRUCTIONS, ServerLayer } from "./server.js";
|
|
34
34
|
import { CURRENT_MCP_VERSION } from "./version.js";
|
|
35
35
|
|
|
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 };
|
|
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, SERVER_INSTRUCTIONS, 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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitest-agent/mcp",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.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": [
|
|
@@ -43,11 +43,11 @@
|
|
|
43
43
|
"vitest-agent-mcp": "bin/vitest-agent-mcp.js"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@effect/platform-node": "4.0.0-rc.
|
|
47
|
-
"@effect/sql-sqlite-node": "4.0.0-rc.
|
|
48
|
-
"@vitest-agent/engine": "0.2.
|
|
49
|
-
"@vitest-agent/sdk": "5.1.
|
|
50
|
-
"effect": "4.0.0-rc.
|
|
46
|
+
"@effect/platform-node": "4.0.0-rc.116",
|
|
47
|
+
"@effect/sql-sqlite-node": "4.0.0-rc.116",
|
|
48
|
+
"@vitest-agent/engine": "0.2.2",
|
|
49
|
+
"@vitest-agent/sdk": "5.1.1",
|
|
50
|
+
"effect": "4.0.0-rc.116"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"vitest": "^5.0.0"
|
package/register-toolkit.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { RenderText } from "./annotations.js";
|
|
2
2
|
import { buildUnexpectedToolErrorEnvelope } from "./utils/tool-error-envelope.js";
|
|
3
|
-
import { Cause, Context, Effect, Layer, Option, Result, Schema,
|
|
4
|
-
import { AiError, McpSchema, McpServer, Tool } from "effect/unstable/ai";
|
|
3
|
+
import { Cause, Context, Effect, ErrorReporter, Layer, Option, References, Result, Schema, Stream } from "effect";
|
|
4
|
+
import { AiError, McpSchema, McpServer, Tool, Toolkit } from "effect/unstable/ai";
|
|
5
|
+
import { HttpServerRequest } from "effect/unstable/http";
|
|
5
6
|
|
|
6
7
|
//#region src/register-toolkit.ts
|
|
7
8
|
const isPlainObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -214,13 +215,32 @@ const formatUnknownKeys = (levels) => levels.map((level) => {
|
|
|
214
215
|
* primitive encoded result is omitted rather than sent through as-is.
|
|
215
216
|
*/
|
|
216
217
|
const toStructuredContent = (value) => isPlainObject(value) ? value : void 0;
|
|
217
|
-
const
|
|
218
|
+
const toolErrorResult = (message) => new McpSchema.CallToolResult({
|
|
218
219
|
isError: true,
|
|
219
220
|
content: [{
|
|
220
221
|
type: "text",
|
|
221
222
|
text: message
|
|
222
223
|
}]
|
|
223
224
|
});
|
|
225
|
+
const toolResultContent = (encoded) => encoded === void 0 ? [] : [{
|
|
226
|
+
type: "text",
|
|
227
|
+
text: JSON.stringify(encoded)
|
|
228
|
+
}];
|
|
229
|
+
const omitRequestServices = Context.omit(McpSchema.McpRequestContext, McpSchema.McpServerClient, HttpServerRequest.HttpServerRequest, References.CurrentLogLevel);
|
|
230
|
+
const isParameterValidationError = (error) => AiError.isAiError(error) && error.reason._tag === "ToolParameterValidationError";
|
|
231
|
+
/**
|
|
232
|
+
* The input JSON Schema document as rc.116's `toolInputJsonSchema` builds it
|
|
233
|
+
* (`Schema.toJsonSchemaDocument` with `onExcessProperty` following the
|
|
234
|
+
* tool's strict mode, definitions attached as `$defs`); the top-level `$ref`
|
|
235
|
+
* hoist happens in `inlineRootRefs` via `strictifyJsonSchema`.
|
|
236
|
+
*/
|
|
237
|
+
const toolInputJsonSchema = (schema, strict) => {
|
|
238
|
+
const document = Schema.toJsonSchemaDocument(schema, { onExcessProperty: strict ? "error" : "ignore" });
|
|
239
|
+
return Object.keys(document.definitions).length === 0 ? document.schema : {
|
|
240
|
+
...document.schema,
|
|
241
|
+
$defs: document.definitions
|
|
242
|
+
};
|
|
243
|
+
};
|
|
224
244
|
const envelopeResult = (toolName, err) => {
|
|
225
245
|
const envelope = buildUnexpectedToolErrorEnvelope(toolName, err);
|
|
226
246
|
return new McpSchema.CallToolResult({
|
|
@@ -234,26 +254,64 @@ const envelopeResult = (toolName, err) => {
|
|
|
234
254
|
};
|
|
235
255
|
/**
|
|
236
256
|
* Register every tool of `toolkit` with the ambient `McpServer` under the
|
|
237
|
-
* strict contract described in the module docs.
|
|
257
|
+
* strict contract described in the module docs. A port of rc.116's
|
|
258
|
+
* `McpServer.registerToolkit`; the deviations are enumerated in the module
|
|
259
|
+
* header.
|
|
238
260
|
*
|
|
239
261
|
* @internal
|
|
240
262
|
*/
|
|
241
263
|
const registerStrictToolkitEffect = Effect.fnUntraced(function* (toolkit) {
|
|
242
264
|
const registry = yield* McpServer.McpServer;
|
|
243
|
-
const built = yield* toolkit
|
|
244
|
-
|
|
265
|
+
const built = yield* toolkit.pipe(Effect.updateContext((context) => {
|
|
266
|
+
const services = new Map(context.mapUnsafe);
|
|
267
|
+
for (const tool of Object.values(toolkit.tools)) {
|
|
268
|
+
const handler = services.get(tool.id);
|
|
269
|
+
if (handler !== void 0) services.set(tool.id, {
|
|
270
|
+
...handler,
|
|
271
|
+
context: omitRequestServices(handler.context)
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
return Context.makeUnsafe(services);
|
|
275
|
+
}));
|
|
276
|
+
const services = omitRequestServices(yield* Effect.context());
|
|
277
|
+
const reportCause = (cause) => Effect.provideContext(ErrorReporter.report(cause), services);
|
|
278
|
+
const registrations = [];
|
|
245
279
|
for (const tool of Object.values(built.tools)) {
|
|
280
|
+
const internalToolError = (cause) => {
|
|
281
|
+
const failure = Cause.findFail(cause);
|
|
282
|
+
return Result.isFailure(failure) && !Cause.hasDies(cause) ? Effect.failCause(failure.failure) : Effect.logError(`tool ${tool.name} failed`, cause).pipe(Effect.andThen(reportCause(cause)), Effect.as(envelopeResult(tool.name, Result.isSuccess(failure) ? failure.success.error : Cause.squash(cause))));
|
|
283
|
+
};
|
|
284
|
+
const strict = Tool.getStrictMode(tool) === true;
|
|
285
|
+
const rawJsonSchema = Tool.isDynamic(tool) ? tool.jsonSchema : void 0;
|
|
286
|
+
if (strict && rawJsonSchema !== void 0) return yield* Effect.die(`McpServer cannot strictly validate the raw JSON Schema for tool '${tool.name}'; use an Effect Schema instead`);
|
|
287
|
+
const decodeOptions = strict ? { onExcessProperty: "error" } : void 0;
|
|
246
288
|
const annotations = tool.annotations;
|
|
247
289
|
const render = Context.get(annotations, RenderText);
|
|
248
|
-
const isDeclaredFailure = Schema.is(tool.failureSchema);
|
|
249
290
|
const toolMeta = Context.getOrUndefined(annotations, Tool.Meta);
|
|
291
|
+
const isDeclaredFailure = Schema.is(tool.failureSchema);
|
|
292
|
+
const encodeFailure = Schema.encodeUnknownEffect(tool.failureSchema);
|
|
293
|
+
const declaredFailureResult = (error) => error instanceof Error ? Effect.succeed(toolErrorResult(error.message)) : Effect.map(encodeFailure(error), (encoded) => new McpSchema.CallToolResult({
|
|
294
|
+
isError: true,
|
|
295
|
+
content: toolResultContent(encoded)
|
|
296
|
+
}));
|
|
297
|
+
const handleCause = (cause) => {
|
|
298
|
+
const failure = Cause.findFail(cause);
|
|
299
|
+
if (Result.isSuccess(failure)) {
|
|
300
|
+
const error = failure.success.error;
|
|
301
|
+
const origin = Context.get(Cause.reasonAnnotations(failure.success), Toolkit.FailureOrigin);
|
|
302
|
+
if (origin === "parameters" && isParameterValidationError(error)) return Effect.fail(new McpSchema.InvalidParams({ message: error.reason.message }));
|
|
303
|
+
if (origin === "handler" && isDeclaredFailure(error)) return Effect.catchCause(declaredFailureResult(error), internalToolError);
|
|
304
|
+
}
|
|
305
|
+
return internalToolError(cause);
|
|
306
|
+
};
|
|
250
307
|
const outputJsonSchema = inlineRootRefs(Tool.getJsonSchemaFromSchema(tool.successSchema));
|
|
251
|
-
const outputSchema = outputJsonSchema.type === "object" ? yield* Schema.decodeUnknownEffect(McpSchema.
|
|
252
|
-
const servedInput = strictifyJsonSchema(
|
|
253
|
-
const inputSchema = yield* Schema.decodeUnknownEffect(McpSchema.
|
|
308
|
+
const outputSchema = outputJsonSchema.type === "object" ? yield* Schema.decodeUnknownEffect(McpSchema.ToolOutputJson)(outputJsonSchema).pipe(Effect.orDie) : void 0;
|
|
309
|
+
const servedInput = strictifyJsonSchema(rawJsonSchema ?? toolInputJsonSchema(tool.parametersSchema, strict));
|
|
310
|
+
const inputSchema = yield* Schema.decodeUnknownEffect(McpSchema.ToolJson)(servedInput).pipe(Effect.orDie);
|
|
311
|
+
const description = Tool.getDescription(tool);
|
|
254
312
|
const mcpTool = new McpSchema.Tool({
|
|
255
313
|
name: tool.name,
|
|
256
|
-
description:
|
|
314
|
+
...description === void 0 ? {} : { description },
|
|
257
315
|
inputSchema,
|
|
258
316
|
...outputSchema === void 0 ? {} : { outputSchema },
|
|
259
317
|
annotations: {
|
|
@@ -263,38 +321,27 @@ const registerStrictToolkitEffect = Effect.fnUntraced(function* (toolkit) {
|
|
|
263
321
|
idempotentHint: Context.get(annotations, Tool.Idempotent),
|
|
264
322
|
openWorldHint: Context.get(annotations, Tool.OpenWorld)
|
|
265
323
|
},
|
|
266
|
-
_meta: toolMeta
|
|
324
|
+
...toolMeta === void 0 ? {} : { _meta: toolMeta }
|
|
267
325
|
});
|
|
268
|
-
|
|
326
|
+
registrations.push({
|
|
269
327
|
tool: mcpTool,
|
|
270
328
|
annotations,
|
|
271
329
|
handle(payload) {
|
|
272
330
|
const raw = payload ?? {};
|
|
273
331
|
const unknownKeys = collectUnknownKeys(raw, servedInput);
|
|
274
332
|
if (unknownKeys.length > 0) return Effect.fail(new McpSchema.InvalidParams({ message: formatUnknownKeys(unknownKeys) }));
|
|
275
|
-
return built.handle(tool.name, raw).pipe(Stream.unwrap, Stream.
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
text
|
|
284
|
-
}]
|
|
285
|
-
});
|
|
286
|
-
}), Effect.provideContext(services), Effect.catchCause((cause) => {
|
|
287
|
-
const failure = Cause.findError(cause);
|
|
288
|
-
if (Result.isFailure(failure) && !Cause.hasDies(cause)) return Effect.failCause(failure.failure);
|
|
289
|
-
const err = Result.isSuccess(failure) ? failure.success : Cause.squash(cause);
|
|
290
|
-
if (AiError.isAiError(err) && err.reason._tag === "ToolParameterValidationError") return Effect.fail(new McpSchema.InvalidParams({ message: err.reason.message }));
|
|
291
|
-
const logged = Effect.logError(`tool ${tool.name} failed`, cause);
|
|
292
|
-
if (isDeclaredFailure(err) && err instanceof Error) return Effect.as(logged, declaredFailureResult(err.message));
|
|
293
|
-
return Effect.as(logged, envelopeResult(tool.name, err));
|
|
294
|
-
}));
|
|
333
|
+
return built.handle(tool.name, raw, void 0, decodeOptions).pipe(Stream.unwrap, Stream.runLast, Effect.flatMap(Effect.fromOption), Effect.flatMap((result) => result.isFailure && result.failureOrigin !== "handler" ? Effect.failCause(Cause.annotate(Cause.fail(result.result), Context.make(Toolkit.FailureOrigin, result.failureOrigin ?? "result"))) : Effect.succeed(new McpSchema.CallToolResult({
|
|
334
|
+
isError: result.isFailure,
|
|
335
|
+
structuredContent: result.isFailure ? void 0 : toStructuredContent(result.encodedResult),
|
|
336
|
+
content: result.isFailure || result.encodedResult === void 0 ? toolResultContent(result.encodedResult) : [{
|
|
337
|
+
type: "text",
|
|
338
|
+
text: render?.(result.encodedResult) ?? JSON.stringify(result.encodedResult)
|
|
339
|
+
}]
|
|
340
|
+
}))), Effect.catchCause(handleCause), Effect.provideContext(services));
|
|
295
341
|
}
|
|
296
342
|
});
|
|
297
343
|
}
|
|
344
|
+
for (const registration of registrations) yield* registry.addTool(registration);
|
|
298
345
|
});
|
|
299
346
|
/**
|
|
300
347
|
* A layer that registers `toolkit` with the strict contract. Same shape
|
package/server.js
CHANGED
|
@@ -5,16 +5,42 @@ import { Layer, Logger } from "effect";
|
|
|
5
5
|
import { McpProtocol, McpServer } from "effect/unstable/ai";
|
|
6
6
|
|
|
7
7
|
//#region src/server.ts
|
|
8
|
+
/** One-line human summary agents see on `serverInfo.description`. */
|
|
9
|
+
const DESCRIPTION = "vitest-agent MCP server: test results, coverage, history and TDD lifecycle for LLM coding agents.";
|
|
8
10
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
11
|
+
* The agent-facing orientation every client receives: in the `initialize`
|
|
12
|
+
* result on the stateful protocols and in the `server/discover` result on
|
|
13
|
+
* `2026-07-28`. Says what the server is for, which tool to reach for first,
|
|
14
|
+
* and how to read a result; the one-line human summary stays on
|
|
15
|
+
* `serverInfo.description`.
|
|
16
|
+
*
|
|
17
|
+
* @public
|
|
12
18
|
*/
|
|
13
|
-
const
|
|
19
|
+
const SERVER_INSTRUCTIONS = [
|
|
20
|
+
"vitest-agent serves the test landscape of one Vitest project to LLM coding agents: run results, failure detail and history, coverage, flakiness, and the TDD lifecycle (tasks, goals, behaviors, phase transitions, hypotheses, notes).",
|
|
21
|
+
"Call the help tool first for the full tool reference; triage_brief orients on the current landscape; run_tests executes Vitest and persists the run so every query tool reads it back.",
|
|
22
|
+
"Every input is strict at every object level: an unknown key is rejected with a message naming the unrecognized key(s) and the accepted params, so fix the call rather than retrying it.",
|
|
23
|
+
"Every successful result carries the typed object in structuredContent and a text rendering (markdown where the tool renders one, otherwise the same JSON) in content[0].text; read structuredContent when you need fields.",
|
|
24
|
+
"An expected domain error is NOT an isError result: it is a normal result whose structuredContent has ok: false and an error object. tdd_goal and tdd_behavior put _tag, the failing ids and a remediation naming the tool to call next in that object; register_agent puts a code and a message in it, plus existingAgentId or expectedPrefix when the code calls for one.",
|
|
25
|
+
"An isError result means the call itself failed: invalid params (bare text in content[0].text, no structuredContent) or an unexpected crash inside the tool (structuredContent is an UnexpectedToolError envelope with ok: false and a remediation). Read the message and fix the call."
|
|
26
|
+
].join("\n");
|
|
14
27
|
/**
|
|
15
28
|
* The server layer: every tool registered under the strict contract, plus
|
|
16
|
-
* the six framing prompts, over `McpServer.layerStdio`.
|
|
17
|
-
*
|
|
29
|
+
* the six framing prompts, over `McpServer.layerStdio`.
|
|
30
|
+
*
|
|
31
|
+
* `protocols` order is load-bearing (rc.116 `unstable/ai/internal/mcpRuntime.ts`):
|
|
32
|
+
* a request carrying `_meta["io.modelcontextprotocol/protocolVersion"]`
|
|
33
|
+
* routes to that adapter, an `initialize` matches the STATEFUL adapters
|
|
34
|
+
* only, and anything else with no session falls to `protocols[0]`. The
|
|
35
|
+
* stateless `2026-07-28` adapter (SEP-2575: no handshake, no session,
|
|
36
|
+
* `server/discover` instead of `initialize`) is listed first, then the two
|
|
37
|
+
* newest stateful ones — every shipping client (Claude Code's default stdio
|
|
38
|
+
* session, Copilot, Cursor, the Inspector) still opens with `initialize`,
|
|
39
|
+
* which a server offering ONLY `2026-07-28` answers with
|
|
40
|
+
* `METHOD_NOT_FOUND`. At most one stateless adapter is allowed; a second
|
|
41
|
+
* fails the layer with `Cause.IllegalArgumentError`, which is why the
|
|
42
|
+
* error channel is `orDie`d: `protocols` is a static literal, so a failure
|
|
43
|
+
* there is an implementer-time defect.
|
|
18
44
|
*
|
|
19
45
|
* @param options - the advertised server version
|
|
20
46
|
* @public
|
|
@@ -23,12 +49,13 @@ const ServerLayer = (options) => Layer.mergeAll(registerStrictToolkit(Kit).pipe(
|
|
|
23
49
|
name: "vitest-agent",
|
|
24
50
|
version: options.version,
|
|
25
51
|
description: DESCRIPTION,
|
|
52
|
+
instructions: SERVER_INSTRUCTIONS,
|
|
26
53
|
protocols: [
|
|
54
|
+
McpProtocol.v2026_07_28,
|
|
27
55
|
McpProtocol.v2025_11_25,
|
|
28
|
-
McpProtocol.v2025_06_18
|
|
29
|
-
McpProtocol.v2025_03_26
|
|
56
|
+
McpProtocol.v2025_06_18
|
|
30
57
|
]
|
|
31
58
|
})), Layer.provide(Layer.succeed(Logger.LogToStderr, true)), Layer.orDie);
|
|
32
59
|
|
|
33
60
|
//#endregion
|
|
34
|
-
export { ServerLayer };
|
|
61
|
+
export { SERVER_INSTRUCTIONS, ServerLayer };
|