@effected/mcp 0.11.0 → 0.13.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 CHANGED
@@ -1,939 +1,21 @@
1
- import { Array, Context, Effect, JsonSchema, Layer, Option, Runtime, Schema, Stdio, Struct } from "effect";
2
- import { McpProtocol, McpSchema, McpServer, Tool, Toolkit } from "effect/ai";
3
- import { Remediation } from "@effected/engine";
4
- import { KeyValueStore } from "effect/persistence";
1
+ import { Distribution } from "@effected/kit";
5
2
  /**
6
- * Options for {@link McpStdio.layer}.
3
+ * Options a carrier's bin shim passes to `main`. `distribution` names
4
+ * the package the `effected-mcp` bin was installed through; omitted for a
5
+ * direct install of `@effected/mcp`.
7
6
  *
8
7
  * @public
9
8
  */
10
- interface McpStdioOptions {
11
- /** The server name reported to the client. */
12
- readonly name: string;
13
- /** The server version reported to the client. */
14
- readonly version: string;
15
- /** Surfaced in `initialize` and `server/discover`; tell the agent how to use the tools. */
16
- readonly instructions?: string | undefined;
17
- /** A one-line description of the server, reported to the client. */
18
- readonly description?: string | undefined;
19
- /** Defaults to {@link McpStdio.protocols}. Read its ordering rules before overriding. */
20
- readonly protocols?: Array.NonEmptyReadonlyArray<McpProtocol.ProtocolAdapter> | undefined;
9
+ interface MainOptions {
10
+ readonly distribution?: Distribution;
21
11
  }
22
12
  /**
23
- * Options for {@link McpStdio.launch}.
13
+ * The `effected-mcp` server's version. The bundler replaces the exact expression
14
+ * `process.env.__PACKAGE_VERSION__` with the package version at build time, so
15
+ * it must stay spelled this way.
24
16
  *
25
17
  * @public
26
18
  */
27
- interface McpLaunchOptions {
28
- /**
29
- * Runs once the whole layer has built, and before the launch waits
30
- * forever. By then the stdio protocol is reading stdin: the server is
31
- * serving, although no client may have sent `initialize` yet.
32
- *
33
- * @remarks
34
- * `McpGuard.run` (`@effected/mcp/guard`) uses it as its "connected"
35
- * signal. A layer built after the server layer, as in
36
- * `Layer.effectDiscard(ready).pipe(Layer.provide(Main))`, gives the same
37
- * signal, but only if every consumer re-derives that `Layer.provide`
38
- * builds its dependency first.
39
- */
40
- readonly onReady?: Effect.Effect<void> | undefined;
41
- }
42
- /**
43
- * Serve an MCP server over stdio without ever writing a log line or a failure
44
- * report onto stdout, which is the JSON-RPC wire.
45
- *
46
- * @remarks
47
- * The one-line `main.ts`:
48
- *
49
- * ```ts
50
- * NodeRuntime.runMain(McpStdio.launch(Main), { teardown: McpStdio.teardown })
51
- * ```
52
- *
53
- * - {@link McpStdio.layer} is `McpServer.layerStdio` with `LogToStderr`
54
- * provided and merged into its output, and `Layer.orDie`. It answers a
55
- * stdin line that is not JSON with a JSON-RPC `-32700` parse error, and
56
- * JSON that is not a JSON-RPC message with `-32600`, and keeps serving.
57
- * - {@link McpStdio.launch} reports a launch failure itself, on stderr, and
58
- * hides it from `runMain`, whose own report is written outside anything
59
- * the program can provide.
60
- * - {@link McpStdio.teardown} maps stdin EOF, the normal end of every
61
- * session, to exit 0 instead of 130.
62
- *
63
- * Never `NodeRuntime.runMain(Layer.launch(Main).pipe(Effect.provideService(References.LogToStderr, true)))`:
64
- * it typechecks and serves, but a launch failure is still reported by
65
- * `runMain` through `console.log`, straight onto the wire, where the client
66
- * reads it as a malformed frame instead of an error.
67
- *
68
- * @public
69
- */
70
- export declare class McpStdio {
71
- private constructor();
72
- /**
73
- * `[v2026_07_28, v2025_11_25, v2025_06_18]` — the stateless revision
74
- * first, then the two newest stateful ones.
75
- *
76
- * @remarks
77
- * Real clients open with either handshake: some (Claude Code, for one)
78
- * open a stdio server with the stateless `server/discover`, while others
79
- * still send `initialize`. The order is load-bearing:
80
- *
81
- * - Stateless first: a request with no session and no `_meta` falls to
82
- * `protocols[0]`.
83
- * - Never a single entry: `initialize` only matches stateful adapters, so
84
- * a stateless-only list refuses every client that opens with it.
85
- * - At most one stateless adapter; a second fails the layer.
86
- */
87
- static readonly protocols: Array.NonEmptyReadonlyArray<McpProtocol.ProtocolAdapter>;
88
- /**
89
- * `McpServer.layerStdio` with `LogToStderr` provided and merged into the
90
- * output, so every layer it provides logs to stderr too. A bad
91
- * `protocols` list dies: it is the implementer's defect.
92
- *
93
- * @remarks
94
- * Only a layer this one provides inherits `LogToStderr`: compose with
95
- * `Layer.provideMerge(McpStdio.layer(...))`. A sibling merged beside it
96
- * with `Layer.mergeAll` is not provided by it, so its build logs still
97
- * go through `console.log`, onto the wire.
98
-
99
- *
100
- * `LogToStderr` only reaches a logger that reads it: Effect's default
101
- * logger and `Logger.consolePretty`. `Logger.consoleJson`,
102
- * `Logger.consoleLogFmt` and `Logger.consoleStructured` are built with
103
- * `Logger.withConsoleLog`, which writes through `console.log` whatever
104
- * `LogToStderr` says, so a server that installs one puts every log line on
105
- * the wire. Never install them under this layer; wrap the formatter in
106
- * `Logger.withConsoleError` instead, as in
107
- * `Logger.layer([Logger.withConsoleError(Logger.formatJson)])`.
108
- *
109
- * The server reads stdin through a guard that frames it exactly as core's
110
- * decoder does: one streaming UTF-8 decoder, a byte-order mark stripped
111
- * only at the start of the stream, lines split on `\n`. Core itself skips
112
- * a line it cannot use and keeps serving, but sends no reply, where
113
- * JSON-RPC 2.0 requires one; the guard supplies it. A line that is not
114
- * JSON is answered on stdout with a parse error, code `-32700` and
115
- * `id: null`, and never reaches core. So is a line longer than core's cap
116
- * of 16 Mi UTF-16 code units: it is answered once, as soon as it passes
117
- * the cap, and the rest of it is discarded up to its newline (core would
118
- * log it on stderr and drop it). A line of JSON whitespace (space, tab,
119
- * carriage return) is ignored.
120
- *
121
- * A line that is JSON but no JSON-RPC message core can handle is answered
122
- * with an Invalid Request, code `-32600` and `id: null`, and never reaches
123
- * core: a value that is neither an object nor an array, an object whose
124
- * `method` is not a string and whose `id` is absent or `null`, and an
125
- * object with neither `method` nor `id`, which is neither a request nor a
126
- * response.
127
- *
128
- * Everything else goes to core: an array, which core answers `-32600`
129
- * itself because it serves no batches; an object with an `id` and no
130
- * `method`, which is a response and gets no reply; and a request with an
131
- * `id`, which core answers even when its `method` is malformed.
132
- *
133
- * Give each server a fresh layer memo map. Core's stdio protocol layer
134
- * is a shared constant, so a second `McpStdio.layer` server whose build
135
- * sees the first one's memo map shares its protocol: only the first
136
- * server reads stdin, and the second never answers. Merging both into
137
- * one graph does that, and so does building or providing the second
138
- * anywhere under the first one's `Effect.provide`: nested `Layer.build`
139
- * and `Effect.provide` fork the ambient memo map rather than starting a
140
- * new one. The tool registry is shared the same way. Isolate each server
141
- * by wrapping its whole bundle (its toolkit layers together with this
142
- * layer) in `Layer.fresh`, or with its own `ManagedRuntime`,
143
- * `Effect.provide(layer, { local: true })` or its own process. Never put
144
- * the `Layer.fresh` boundary between a toolkit and this layer: the server
145
- * would serve an empty registry.
146
- *
147
- * Code after a completed `Effect.provide` of a stdio server never runs:
148
- * core's stdio protocol interrupts the fiber that built it when its stdin
149
- * loop ends, which closing the provide's scope does. In a test, serve the
150
- * layer through `McpHarness` from `@effected/mcp/testing` instead.
151
- *
152
- * Each call mints a fresh layer; bind the result to a `const` or the
153
- * server builds twice.
154
- */
155
- static readonly layer: (options: McpStdioOptions) => Layer.Layer<McpServer.McpServer | McpSchema.McpServerClient, never, Stdio.Stdio>;
156
- /**
157
- * `Layer.launch`, with any failure other than an interrupt logged on
158
- * stderr here and re-raised marked as already reported, keeping its exit
159
- * code.
160
- *
161
- * @remarks
162
- * `runMain` logs an unhandled failure from outside the program, where no
163
- * `Effect.provideService` reaches, and Effect's default logger writes
164
- * through `console.log` unless `LogToStderr` is set. For an MCP server
165
- * that is stdout, which is the wire.
166
- *
167
- * `options.onReady` runs after the layer has built and before the launch
168
- * waits forever, as {@link McpLaunchOptions.onReady} describes.
169
- */
170
- static readonly launch: <ROut, E, R>(layer: Layer.Layer<ROut, E, R>, options?: McpLaunchOptions) => Effect.Effect<never, Error, R>;
171
- /**
172
- * Success or an interrupt-only exit maps to 0. Anything else goes to
173
- * `Runtime.defaultTeardown`.
174
- *
175
- * @remarks
176
- * Stdin EOF, the normal end of every session, ends in an interrupt-only
177
- * exit, and so do SIGINT and SIGTERM: `runMain` interrupts the program on
178
- * either signal. All three exit 0 here, not the 130 a default teardown
179
- * reports for an interrupt.
180
- */
181
- static readonly teardown: Runtime.Teardown;
182
- }
183
- /**
184
- * The unknown keys found at one object level of a tool-call payload.
185
- *
186
- * @public
187
- */
188
- interface UnknownKeysLevel {
189
- /** Keys from the payload root to this object; empty at the root. Array positions are decimal strings. */
190
- readonly path: ReadonlyArray<string>;
191
- /** Keys the payload carries that this level does not accept, in payload order. */
192
- readonly unknown: ReadonlyArray<string>;
193
- /** Keys this level declares, in schema order. */
194
- readonly accepted: ReadonlyArray<string>;
195
- /**
196
- * The `patternProperties` patterns this level also accepts keys by, in
197
- * schema order. Present only when the level has at least one.
198
- */
199
- readonly acceptedPatterns?: ReadonlyArray<string>;
200
- }
201
- /**
202
- * Options for {@link ToolInputSchema.formatUnknownKeys}.
203
- *
204
- * @public
205
- */
206
- interface FormatUnknownKeysOptions {
207
- /** The cap on each echoed key path, in UTF-16 code units. Defaults to `ToolFailure.ECHO_LIMIT`. */
208
- readonly echoLimit?: number | undefined;
209
- }
210
- /**
211
- * Pure walkers over a tool's served JSON Schema: every unknown key at every
212
- * depth, a message naming them all, and an object-rooted form of a union.
213
- *
214
- * @remarks
215
- * For a `Tool.make` tool, core's strict decode already names every unknown
216
- * key, with every missing or invalid field, in one response. Core never
217
- * validates a `Tool.dynamic` tool's raw JSON Schema, so these walkers are
218
- * for that case: a handler runs them against the schema it registered.
219
- * They run over the schema as served, so what they accept matches what the
220
- * tool advertises:
221
- *
222
- * - Only `additionalProperties: false` closes a node, as in JSON Schema; core
223
- * emits it on every object node of a strict tool and `true` otherwise.
224
- * - `allOf` members' properties merge; `oneOf`/`anyOf` members are chosen by an
225
- * `action`, `kind`, `_tag` or `type` literal (tried in that order), or by
226
- * being the only object member. A payload whose discriminant matches no
227
- * member, or is missing, is skipped and left for decoding.
228
- * - `items`, `prefixItems`, `$ref` into `$defs` (JSON Pointer escapes
229
- * honoured), and `patternProperties` are followed; a key matched by a
230
- * pattern is accepted and walked with the first matching pattern's schema.
231
- * Effect serves a pattern-keyed `Schema.Record` with `patternProperties`
232
- * only when the key pattern's RegExp has the `u` flag; without it the
233
- * record is served open, and every key is accepted.
234
- * - The payload is untrusted: the walk stops descending at depth 256 rather
235
- * than overflowing the stack, so a deeper unknown key goes unreported and
236
- * is left for decoding. Every hop counts toward that cap: each property or
237
- * array position, and also each union member selection, which adds a hop
238
- * without adding a path segment.
239
- *
240
- * @public
241
- */
242
- export declare class ToolInputSchema {
243
- private constructor();
244
- /** Every level of `payload` that carries a key `schema` does not accept. */
245
- static readonly unknownKeys: (payload: unknown, schema: JsonSchema.JsonSchema) => ReadonlyArray<UnknownKeysLevel>;
246
- /**
247
- * `Unrecognized parameter(s): a, b.c. Accepted params: x, y.` per level,
248
- * followed by `Accepted keys matching: ^x-.` when the level also accepts
249
- * keys by pattern. A level that accepts keys only by pattern names its
250
- * patterns alone, never `Accepted params: (none).` Each echoed key and
251
- * pattern is truncated, and at most 20 keys, 20 patterns and 20 levels are
252
- * named; a trailing `(and N more levels).` counts the levels left out.
253
- */
254
- static readonly formatUnknownKeys: (levels: ReadonlyArray<UnknownKeysLevel>, options?: FormatUnknownKeysOptions) => string;
255
- /**
256
- * `schema` with a root `$ref` inlined and a top-level union of objects
257
- * sharing an `action`, `kind`, `_tag` or `type` literal rewritten to an
258
- * object root carrying `type: "object"`, `oneOf` and `"x-discriminator"` —
259
- * MCP requires an object root, and core dies at boot on a union root.
260
- * Returned as the same value when nothing changes.
261
- */
262
- static readonly objectRooted: (schema: JsonSchema.JsonSchema) => JsonSchema.JsonSchema;
263
- }
264
- /**
265
- * Options for {@link McpToolkit.layer}.
266
- *
267
- * @public
268
- */
269
- interface McpToolkitOptions {
270
- /**
271
- * `"all"` (the default): every tool without its own `Tool.Strict`
272
- * annotation is served and decoded strict. `"annotated"`: only tools
273
- * annotated `Tool.Strict` true are. In both modes an explicit annotation,
274
- * true or false, always wins, and a `Tool.dynamic` tool is never
275
- * re-annotated (core dies at registration on a strict dynamic tool).
276
- */
277
- readonly strict?: "all" | "annotated" | undefined;
278
- /**
279
- * Ignored. Core's strict decode reports every excess key at every depth,
280
- * together with every missing or invalid field, in one `InvalidParams`,
281
- * and the layer appends a fixed `Accepted params at <path>: …` line per
282
- * level that carries an unknown key; this option changes neither.
283
- *
284
- * @deprecated A no-op, scheduled for removal in a later minor.
285
- */
286
- readonly unknownKeyMessage?: ((levels: ReadonlyArray<UnknownKeysLevel>) => string) | undefined;
287
- }
288
- /**
289
- * A `Tool.dynamic` made by {@link McpToolkit.unionTool}: served with the
290
- * strict, object-rooted JSON Schema of a `Schema.Union` of objects, and
291
- * carrying that union so {@link McpToolkit.unionHandler} can decode it.
292
- *
293
- * @remarks
294
- * `annotate` and `addDependency` keep the type, so a chained
295
- * `.annotate(Tool.Title, …)` still hands `unionHandler` the union. The
296
- * declared failure is `failure` together with `McpSchema.InvalidParams`.
297
- *
298
- * @public
299
- */
300
- interface UnionTool<Name extends string, P extends Schema.Decoder<unknown>, S extends Schema.Constraint, F extends Schema.Constraint, R = never> extends Tool.Dynamic<Name, {
301
- readonly parameters: JsonSchema.JsonSchema;
302
- readonly success: S;
303
- readonly failure: Schema.Union<readonly [F, typeof McpSchema.InvalidParams]>;
304
- readonly failureMode: "error";
305
- }, R> {
306
- /** The union the tool decodes its arguments with. */
307
- readonly unionParameters: P;
308
- /** Add an annotation, keeping the union. */
309
- annotate<I, V>(tag: Context.Key<I, V>, value: V): UnionTool<Name, P, S, F, R>;
310
- /** Add a request-level dependency, keeping the union. */
311
- addDependency<Identifier, Service>(tag: Context.Key<Identifier, Service>): UnionTool<Name, P, S, F, Identifier | R>;
312
- }
313
- /**
314
- * Options for {@link McpToolkit.unionTool}.
315
- *
316
- * @public
317
- */
318
- interface UnionToolOptions<P extends Schema.Decoder<unknown>, S extends Schema.Constraint, F extends Schema.Constraint, Dependencies extends ReadonlyArray<Context.Key<unknown, unknown>>> {
319
- /** What the tool does, for the agent. */
320
- readonly description?: string | undefined;
321
- /** A `Schema.Union` of object schemas, discriminated by an `action`, `kind`, `_tag` or `type` literal. */
322
- readonly parameters: P;
323
- /** The success schema. Defaults to `Schema.Void`; object-root a union whose members are all objects with `ToolOutputSchema.objectRooted`. */
324
- readonly success?: S | undefined;
325
- /** Declared failures besides `InvalidParams`, such as `ToolRefusal`. Defaults to `Schema.Never`. */
326
- readonly failure?: F | undefined;
327
- /** Services the handler needs, as with `Tool.make`. */
328
- readonly dependencies?: Dependencies | undefined;
329
- }
330
- /**
331
- * Options for {@link McpToolkit.unionHandler}.
332
- *
333
- * @public
334
- */
335
- interface UnionHandlerOptions {
336
- /**
337
- * Ignored. The union decode reports every excess, missing and invalid
338
- * field in one `InvalidParams`, followed by the same fixed
339
- * `Accepted params at <path>: …` lines {@link McpToolkit.layer} appends;
340
- * this option changes neither.
341
- *
342
- * @deprecated A no-op, scheduled for removal in a later minor.
343
- */
344
- readonly unknownKeyMessage?: ((levels: ReadonlyArray<UnknownKeysLevel>) => string) | undefined;
345
- }
346
- /**
347
- * Register a toolkit exactly as core's `McpServer.toolkit` does, except that
348
- * every tool is strict by default and a {@link McpToolkit.unionTool} gets its
349
- * union decoded.
350
- *
351
- * @remarks
352
- * Rejection and reporting are core's: a tool annotated `Tool.Strict` is
353
- * served with `additionalProperties: false` on every object node and decoded
354
- * with `onExcessProperty: "error"` and `errors: "all"`, so one
355
- * `InvalidParams` names every excess key at every depth together with every
356
- * missing or invalid field. The layer appends to that report, never in place
357
- * of it: for every object level of the payload that carries a key the served
358
- * input schema does not accept, one line naming what that level does accept,
359
- * `Accepted params at the root: query, filter.` or
360
- * `Accepted params at ["filter"]: kind, tag.` (the path as core writes it),
361
- * with `keys matching <pattern>` for keys a `patternProperties` level
362
- * accepts, and `This tool accepts no params.` for a zero-parameter tool. An
363
- * agent can fix the call from the reply alone. A failure with no unknown key
364
- * (a missing or mistyped field only) is core's report unchanged. The lines
365
- * are appended to core's `InvalidParams` as it leaves the tool's registered
366
- * handler, the one failure that handler has for bad parameters.
367
- *
368
- * On top of that is the policy: under
369
- * `strict: "all"` (the default) it annotates every tool that carries no
370
- * `Tool.Strict` annotation of its own as strict, skipping `Tool.dynamic`
371
- * tools (core dies at registration on a strict dynamic tool). It runs core's
372
- * `registerToolkit` unchanged under a registration-scoped `McpServer` whose
373
- * `addTool` puts the union decode in front of a union tool's handler and
374
- * passes every other registration through untouched. It patches no core
375
- * internals: it uses only `McpServer.registerToolkit` and `addTool`.
376
- *
377
- * Registration goes through core's module-level `McpServer.McpServer.layer`,
378
- * shared by reference with `McpStdio.layer`'s own copy — provide both into
379
- * the same graph, as with `McpServer.toolkit`. Never wrap that layer in
380
- * `Layer.fresh`: tools would register into a second registry nobody serves.
381
- * To give two servers separate registries, wrap each whole bundle (this
382
- * layer together with `McpStdio.layer`) in `Layer.fresh` instead.
383
- *
384
- * Each call mints a fresh layer; bind the result to a `const` or the
385
- * registration runs twice.
386
- *
387
- * @example
388
- * ```ts
389
- * import { Layer } from "effect";
390
- * import { McpStdio, McpToolkit } from "@effected/mcp";
391
- *
392
- * const ToolsLayer = McpToolkit.layer(MyToolkit).pipe(Layer.provide(MyHandlers));
393
- * const ServerLayer = ToolsLayer.pipe(Layer.provideMerge(McpStdio.layer({ name: "my-server", version: "1.0.0" })));
394
- * ```
395
- *
396
- * @public
397
- */
398
- export declare class McpToolkit {
399
- private constructor();
400
- /**
401
- * A tool whose parameters are a `Schema.Union` of objects, which
402
- * `Tool.make` cannot take: core dies at registration on a non-object
403
- * `parameters` root.
404
- *
405
- * @remarks
406
- * The tool is a `Tool.dynamic` served with Effect's strict JSON Schema
407
- * document for `parameters` (`additionalProperties: false` on every
408
- * object node, definitions as `$defs`), rewritten by
409
- * `ToolInputSchema.objectRooted` to an object root carrying `type`,
410
- * `oneOf` and `x-discriminator`. Write its handler with
411
- * {@link McpToolkit.unionHandler}, which receives the decoded member.
412
- *
413
- * Registered through {@link McpToolkit.layer}, a union tool gets the same
414
- * treatment as a strict `Tool.make` tool: a strict decode with
415
- * `errors: "all"` before the handler runs, reporting every excess, missing
416
- * and invalid field of the matched member in one `InvalidParams`, with the
417
- * same `Accepted params at <path>: …` lines appended, so bad
418
- * arguments answer JSON-RPC `-32602` on `2025-06-18` and an `isError`
419
- * result on the later revisions, exactly as a `Tool.make` decode failure
420
- * does. Registered through core's `McpServer.toolkit` it still decodes
421
- * strictly, in the handler, but the `InvalidParams` is then a declared
422
- * failure: an `isError` result on every revision.
423
- *
424
- * Never annotate it `Tool.Strict` true: core dies at registration on a
425
- * strict tool with a raw JSON Schema.
426
- *
427
- * @example
428
- * ```ts
429
- * import { McpToolkit, ToolOutputSchema, ToolRefusal } from "@effected/mcp";
430
- * import { Schema } from "effect";
431
- * import { Tool } from "effect/ai";
432
- *
433
- * // `AddNote`, `ListNotes`, `Added`, `Listed` are object schemas; `Kit` is a Toolkit that includes `Note`.
434
- * const Note = McpToolkit.unionTool("note", {
435
- * parameters: Schema.Union([AddNote, ListNotes]),
436
- * success: ToolOutputSchema.objectRooted(Schema.Union([Added, Listed])),
437
- * failure: ToolRefusal,
438
- * }).annotate(Tool.Title, "Note")
439
- * const handlers = Kit.toLayer({ note: McpToolkit.unionHandler(Note, (params) => handleNote(params)) })
440
- * ```
441
- */
442
- static readonly unionTool: <const Name extends string, P extends Schema.Decoder<unknown>, S extends Schema.Constraint = typeof Schema.Void, F extends Schema.Constraint = typeof Schema.Never, const Dependencies extends ReadonlyArray<Context.Key<unknown, unknown>> = []>(name: Name, options: UnionToolOptions<P, S, F, Dependencies>) => UnionTool<Name, P, S, F, Context.Service.Identifier<Dependencies[number]>>;
443
- /**
444
- * The handler for a {@link McpToolkit.unionTool}: decodes the raw payload
445
- * exactly as {@link McpToolkit.layer} does, and passes the decoded member
446
- * to `handler`.
447
- *
448
- * @remarks
449
- * A payload that does not decode strictly fails with one
450
- * `McpSchema.InvalidParams`, worded as core words a `Tool.make` decode
451
- * failure, naming every excess key at every depth together with every
452
- * missing or invalid field of the matched member, then one
453
- * `Accepted params at <path>: …` line per level with an unknown key. It
454
- * is the same
455
- * implementation the layer runs, so a handler called directly (a test
456
- * helper, or a toolkit registered through core's `McpServer.toolkit`)
457
- * reports what a client of the layer sees. Under {@link McpToolkit.layer}
458
- * the layer has already rejected a bad call before the handler runs.
459
- * `options.unknownKeyMessage` is deprecated and ignored.
460
- */
461
- static readonly unionHandler: <Name extends string, P extends Schema.Decoder<unknown>, S extends Schema.Constraint, F extends Schema.Constraint, R, A, E, RH>(tool: UnionTool<Name, P, S, F, R>, handler: (params: P["Type"]) => Effect.Effect<A, E, RH>, _options?: UnionHandlerOptions) => ((payload: unknown) => Effect.Effect<A, E | McpSchema.InvalidParams, RH>);
462
- /**
463
- * `McpServer.toolkit`'s registration layer, strict by default.
464
- * `options.unknownKeyMessage` is deprecated and ignored.
465
- */
466
- static readonly layer: <Tools extends Record<string, Tool.Any>>(toolkit: Toolkit.Toolkit<Tools>, options?: McpToolkitOptions) => Layer.Layer<never, never, Tool.HandlersFor<Tools> | Exclude<Tool.HandlerServices<Tools>, McpSchema.McpRequestContext>>;
467
- }
468
- /**
469
- * How a tool's declared failure should read on the wire.
470
- *
471
- * @remarks
472
- * Core sends a declared failure that is an `Error` instance — every
473
- * `Schema.TaggedError` is — as `isError: true` with `error.message` as the only
474
- * text and no `structuredContent` (effect `ai/McpServer.ts`,
475
- * `declaredFailureResult`). So the remediation must be folded into `message`
476
- * when the error is constructed: whatever is not in the message never reaches
477
- * the agent.
478
- *
479
- * Spread {@link ToolFailure.fields} into your tool's `Schema.TaggedError`, build
480
- * `message` with {@link ToolFailure.message}, and pass every caller-supplied
481
- * value through {@link ToolFailure.truncate} before echoing it. For a plain
482
- * refusal with no fields of its own, `ToolRefusal` is this shape ready-made.
483
- *
484
- * Only a declared failure keeps its message: core scrubs an undeclared
485
- * failure or a defect to "Tool execution failed due to an internal server
486
- * error.", so audit every throw path a tool can take and declare each one the
487
- * agent should read.
488
- *
489
- * @example
490
- * ```ts
491
- * import { ToolFailure } from "@effected/mcp";
492
- * import { Schema } from "effect";
493
- *
494
- * class NotFound extends Schema.TaggedError<NotFound>()("NotFound", { ...ToolFailure.fields, id: Schema.String }) {}
495
- * const remediation = { hint: "List the ids first.", suggestedTool: "list_things" }
496
- * new NotFound({ id, remediation, message: ToolFailure.message(`No thing "${ToolFailure.truncate(id)}".`, remediation) })
497
- * ```
498
- *
499
- * @public
500
- */
501
- export declare class ToolFailure {
502
- private constructor();
503
- /** The cap, in UTF-16 code units, on a caller-supplied value echoed into a message. */
504
- static readonly ECHO_LIMIT: number;
505
- /** The larger cap for a value the engine produced (a path, a diagnostic), not one a caller typed. */
506
- static readonly ENGINE_ECHO_LIMIT: number;
507
- /**
508
- * The `message` and `remediation` fields to spread into a consumer's
509
- * `Schema.TaggedError`, each carrying a default `description` that a served
510
- * JSON Schema (a tool's `outputSchema`) shows the agent.
511
- */
512
- static readonly fields: {
513
- readonly message: Schema.String;
514
- readonly remediation: Schema.Struct<{
515
- readonly hint: Schema.String;
516
- readonly suggestedTool: Schema.optionalKey<Schema.String>;
517
- readonly suggestedArgs: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
518
- }>;
519
- };
520
- /**
521
- * `"<raw> <hint> Try <suggestedTool>."`, dropping any empty part, so a
522
- * missing tool or an empty hint never leaves a double space.
523
- */
524
- static readonly message: (raw: string, remediation: Remediation) => string;
525
- /**
526
- * `value` cut to `limit` code units with a trailing ellipsis, backing off one
527
- * unit rather than splitting a surrogate pair. A hostile multi-megabyte
528
- * argument is otherwise echoed into the agent's context whole.
529
- */
530
- static readonly truncate: (value: string, limit?: number) => string;
531
- }
532
- /**
533
- * What a {@link ToolOutcome.catchAsEnvelope} case returns for one caught error:
534
- * the same `message` and `remediation` a {@link ToolRefusal} carries, plus any
535
- * structured fields the agent branches on.
536
- *
537
- * @remarks
538
- * `message` is the plain sentence. The hint stays in `remediation`, where it
539
- * reaches the agent as data; unlike a refusal's message, nothing is folded in.
540
- *
541
- * @public
542
- */
543
- interface ToolOutcomeErrorInput {
544
- readonly message: string;
545
- readonly remediation: Remediation;
546
- readonly [field: string]: unknown;
547
- }
548
- /**
549
- * The `ok: false` member of an envelope: `{ ok: false, error: { _tag, message, remediation, ...fields } }`.
550
- *
551
- * @public
552
- */
553
- interface ToolOutcomeFailure<Tag extends string, I extends ToolOutcomeErrorInput> {
554
- readonly ok: false;
555
- readonly error: {
556
- readonly _tag: Tag;
557
- } & I;
558
- }
559
- /**
560
- * The failure values {@link ToolOutcome.catchAsEnvelope} produces for a set of
561
- * cases: one {@link ToolOutcomeFailure} per handled tag.
562
- *
563
- * @public
564
- */
565
- type ToolOutcomeFailures<Cases> = { [K in keyof Cases]: Cases[K] extends ((...args: ReadonlyArray<never>) => infer I) ? I extends ToolOutcomeErrorInput ? ToolOutcomeFailure<K & string, I> : never : never; }[keyof Cases];
566
- /**
567
- * {@link ToolOutcome.envelope}'s options: annotations for the `ok: false`
568
- * member, so a served `outputSchema` can name and describe it.
569
- *
570
- * @public
571
- */
572
- interface ToolOutcomeEnvelopeOptions {
573
- /**
574
- * The failure member's schema identifier. Core's JSON Schema output then
575
- * emits it once under `$defs` by this name and refers to it with `$ref`.
576
- */
577
- readonly identifier?: string;
578
- /**
579
- * The failure member's description, replacing the default one (which says
580
- * that `ok: false` carries an expected error to branch on).
581
- */
582
- readonly description?: string;
583
- }
584
- /**
585
- * The `ok: false` structured-outcome envelope: an expected error an agent
586
- * should branch on, returned on a tool's success channel so it reaches the
587
- * client as `structuredContent` instead of message text.
588
- *
589
- * @remarks
590
- * Two channels, two jobs, one error shape:
591
- *
592
- * - {@link ToolRefusal} is a declared failure: the call itself was wrong, and
593
- * the agent reads one sentence (`isError: true`, message text only; core
594
- * never sends a failure's `structuredContent`).
595
- * - `ToolOutcome` is an expected domain outcome: an id that does not exist, a
596
- * transition that is not allowed. The agent branches on its `_tag` and
597
- * fields (`isError: false`, `structuredContent` carries the envelope).
598
- *
599
- * Both carry {@link ToolFailure.fields} (`message`, `remediation`), so an error
600
- * can move between channels without reshaping.
601
- *
602
- * Success members are not reshaped. Spread {@link ToolOutcome.ok} into each
603
- * success struct so a client can discriminate on `ok`; a member that
604
- * discriminates another way (`found: true | false`) stays legal.
605
- *
606
- * @example
607
- * ```ts
608
- * import { ToolOutcome } from "@effected/mcp"
609
- * import { Effect, Schema } from "effect"
610
- * import { Tool } from "effect/ai"
611
- *
612
- * const NotFound = ToolOutcome.error("GoalNotFound", { id: Schema.Number })
613
- * const Lookup = Tool.make("goal", {
614
- * parameters: Schema.Struct({ id: Schema.Number }),
615
- * success: ToolOutcome.envelope(Schema.Struct({ ...ToolOutcome.ok, goal: Goal }), [NotFound]),
616
- * })
617
- * // in the handler:
618
- * findGoal(id).pipe(
619
- * Effect.map((goal) => ({ ok: true as const, goal })),
620
- * ToolOutcome.catchAsEnvelope({
621
- * GoalNotFound: (e) => ({ id: e.id, message: `No goal ${e.id}.`, remediation: { hint: "List goals first.", suggestedTool: "goal" } }),
622
- * }),
623
- * )
624
- * ```
625
- *
626
- * @public
627
- */
628
- export declare class ToolOutcome {
629
- private constructor();
630
- /** `{ ok: true }` as schema fields, to spread into each success member. */
631
- static readonly ok: {
632
- readonly ok: Schema.Literal<true>;
633
- };
634
- /**
635
- * The schema of one envelope error: `_tag`, {@link ToolFailure.fields} and
636
- * `fields`. A field named `_tag`, `message` or `remediation` is rejected by
637
- * the type, because it would replace the shared one.
638
- */
639
- static readonly error: <const Tag extends string, const Fields extends Schema.Struct.Fields = Record<never, never>>(tag: Tag, fields?: Fields & { readonly [K in "_tag" | "message" | "remediation"]?: never; }) => Schema.Struct<{
640
- readonly _tag: Schema.Literal<Tag>;
641
- } & typeof ToolFailure.fields & Fields>;
642
- /**
643
- * A tool's success schema: `success`, or `{ ok: false, error }` where `error`
644
- * is one of `errors`. Object-rooted with {@link ToolOutputSchema.objectRooted},
645
- * so core serves its `outputSchema` on every protocol revision.
646
- *
647
- * @remarks
648
- * The failure member, its `ok` and its `error` carry default descriptions.
649
- * `options` names the member (`identifier`, served once under `$defs`) or
650
- * replaces its description.
651
- */
652
- static readonly envelope: <S extends Schema.Top, const Errors extends ReadonlyArray<Schema.Top>>(success: S, errors: Errors, options?: ToolOutcomeEnvelopeOptions) => Schema.Union<readonly [S, Schema.Struct<{
653
- readonly ok: Schema.Literal<false>;
654
- readonly error: Schema.Union<Errors>;
655
- }>]>;
656
- /** One `ok: false` value, for a handler that decides on a failure without catching an error. */
657
- static readonly failure: <const Tag extends string, const I extends ToolOutcomeErrorInput>(tag: Tag, error: I) => ToolOutcomeFailure<Tag, I>;
658
- /**
659
- * Turns each handled tagged error into its `ok: false` value on the success
660
- * channel; every other error stays in the error channel. Typed like core's
661
- * `Effect.catchTags`: a case for a tag the effect cannot fail with is a type
662
- * error, and the handled tags leave `E`.
663
- *
664
- * @remarks
665
- * Write it inline in the effect's `pipe`. `E` is inferred from the effect
666
- * being piped, so `effect.pipe(ToolOutcome.catchAsEnvelope({ … }))` checks
667
- * every case name against the effect's real tags. Bound to a `const` first,
668
- * the combinator never sees that effect: `E` infers as `unknown`, the
669
- * unknown-tag check switches off, and a misspelled tag compiles and never
670
- * matches. Core's `catchTags` looks a case up only among `cases`' own keys,
671
- * so an error tagged `"toString"` or `"constructor"` never hits an inherited
672
- * `Object.prototype` member and stays in the error channel.
673
- */
674
- static readonly catchAsEnvelope: <E, Cases extends { [K in Extract<E, {
675
- readonly _tag: string;
676
- }>["_tag"]]+?: (error: Extract<E, {
677
- readonly _tag: K;
678
- }>) => ToolOutcomeErrorInput; } & (unknown extends E ? Record<never, never> : { [K in Exclude<keyof Cases, Extract<E, {
679
- readonly _tag: string;
680
- }>["_tag"]>]: never; })>(cases: Cases) => <A, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A | ToolOutcomeFailures<Cases>, Exclude<E, {
681
- readonly _tag: keyof Cases;
682
- }>, R>;
683
- /**
684
- * Whether `value` is an `ok: false` envelope member: an object whose `ok` is
685
- * exactly `false`. A success member that discriminates some other way, or a
686
- * non-object, is not a failure. Narrows to `{ ok: false }` only: it checks
687
- * the discriminant, not the `error` payload.
688
- */
689
- static readonly isFailure: (value: unknown) => value is {
690
- readonly ok: false;
691
- };
692
- }
693
- /**
694
- * Shaping for a tool's `success` schema, so the `outputSchema` core serves
695
- * is one every client accepts.
696
- *
697
- * @remarks
698
- * MCP requires a tool's `outputSchema` to be rooted in an object, and a
699
- * top-level `Schema.Union` emits a bare `anyOf`. Core drops such an
700
- * `outputSchema` from `tools/list` on the stateful revisions and serves it
701
- * verbatim on the stateless one, where strict clients reject it.
702
- * `McpToolAudit`'s `objectRootedOutput` check reports it either way.
703
- *
704
- * {@link ToolOutputSchema.objectRooted} is the fix when every member of the
705
- * union is an object shape; never apply it to a union with a primitive or
706
- * array member. It works on the Effect
707
- * schema, not on JSON Schema — unlike `ToolInputSchema.objectRooted`, which
708
- * rewrites the served input document of a `Tool.dynamic`.
709
- *
710
- * @public
711
- */
712
- export declare class ToolOutputSchema {
713
- private constructor();
714
- /**
715
- * `schema` with `type: "object"` added beside the `anyOf` at its JSON
716
- * Schema root, through a check that always passes. Decoding, encoding and
717
- * the schema's type are unchanged.
718
- *
719
- * @remarks
720
- * Order against `.annotate({ identifier })` does not matter. A check added
721
- * after an identifier starts a new node that would drop the definition
722
- * name, so the identifier the schema already resolves is carried onto the
723
- * new check, and the served documents are equal either way (key order
724
- * can differ when the schema carries a title or description). Applying it
725
- * twice is a no-op.
726
- *
727
- * Every union member must be an object shape: the added `type: "object"`
728
- * is a claim about the whole union, and nothing checks it. Do not use it
729
- * on a union with a primitive or array member — the served schema would
730
- * contradict the values the tool returns; give such a tool an object
731
- * envelope instead.
732
- *
733
- * @example
734
- * ```ts
735
- * const Result = ToolOutputSchema.objectRooted(Schema.Union([Found, Missing])).annotate({ identifier: "Result" })
736
- * Tool.make("lookup", { parameters, success: Result })
737
- * ```
738
- */
739
- static readonly objectRooted: <S extends Schema.Top>(schema: S) => S;
740
- }
741
- declare const ToolRefusal_base: Schema.Class<ToolRefusal, Schema.TaggedStruct<"ToolRefusal", {
742
- readonly message: Schema.String;
743
- readonly remediation: Schema.Struct<{
744
- readonly hint: Schema.String;
745
- readonly suggestedTool: Schema.optionalKey<Schema.String>;
746
- readonly suggestedArgs: Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>;
747
- }>;
748
- }>, import("effect/Cause").YieldableError>;
749
- /**
750
- * A tool call refused for a reason the caller can fix: the kit's ready-made
751
- * declared failure, built on {@link ToolFailure.fields}.
752
- *
753
- * @remarks
754
- * Core scrubs every undeclared failure and every defect to one generic
755
- * internal-error sentence, so a refusal the agent must act on (an unknown id,
756
- * a missing argument, a precondition) reaches it only when the tool declares
757
- * it. Declare `ToolRefusal` in the tool's `failure` schema and fail with
758
- * {@link ToolRefusal.refuse}; its message already carries the remediation,
759
- * because a declared `Error` failure sends `message` and nothing else.
760
- *
761
- * Audit every throw path of a tool: any failure left undeclared loses its
762
- * message on the wire, however carefully it was worded.
763
- *
764
- * **Structured data on a refusal is deliberately unreachable: put it in the
765
- * message.** Core's `McpServer.registerToolkit` builds every failed call's
766
- * result with `structuredContent` unset and no `_meta`, on every protocol
767
- * revision. Under the default `failureMode: "error"` a declared `Error`
768
- * failure sends `message` as the only text; under `failureMode: "return"`
769
- * it sends the whole encoded failure JSON-stringified into the text, still
770
- * with no `structuredContent`, which buries the human message in JSON a
771
- * client must parse. So `refuse` takes no data argument: a list of valid
772
- * names, the root that was searched, the id that was not found all belong
773
- * in `reason`, truncated with {@link ToolFailure.truncate}. When an agent
774
- * must branch on the data itself, return it on the success channel as an
775
- * `ok: false` envelope, where `structuredContent` does reach the client.
776
- *
777
- * @example
778
- * ```ts
779
- * import { ToolFailure, ToolRefusal } from "@effected/mcp";
780
- * import { Tool } from "effect/ai";
781
- *
782
- * const Lookup = Tool.make("lookup", { parameters, success, failure: ToolRefusal })
783
- * // in the handler:
784
- * return yield* ToolRefusal.refuse(`No run "${ToolFailure.truncate(id)}".`, { hint: "List runs first.", suggestedTool: "list_runs" })
785
- * ```
786
- *
787
- * @public
788
- */
789
- export declare class ToolRefusal extends ToolRefusal_base {
790
- /**
791
- * A refusal for `reason`, with `remediation` folded into its message by
792
- * {@link ToolFailure.message}. `reason` must already truncate any
793
- * caller-supplied value it echoes, with {@link ToolFailure.truncate}.
794
- */
795
- static readonly refuse: (reason: string, remediation: Remediation) => ToolRefusal;
796
- }
797
- /**
798
- * The narrow store {@link ToolReplay.wrap} reads and writes: `get` and `set`
799
- * on string values, nothing else.
800
- *
801
- * @remarks
802
- * Core's `KeyValueStore` already satisfies it structurally, so a
803
- * `KeyValueStore` value can be passed as is. An application backed by its
804
- * own table implements just these two methods, instead of stubbing the
805
- * `remove`, `clear` and `size` that `KeyValueStore.makeStringOnly` demands.
806
- *
807
- * `get` answers `undefined` for a missing key, the same contract as
808
- * `KeyValueStore.get`. A failure from either method is never a tool error:
809
- * a failed `get` is a miss and a failed `set` is a skipped write, both
810
- * logged at debug level, so the error channel is `unknown`.
811
- *
812
- * @public
813
- */
814
- interface ToolReplayStore {
815
- /** The stored value for `key`, or `undefined` when nothing is stored under it. */
816
- readonly get: (key: string) => Effect.Effect<string | undefined, unknown>;
817
- /** Stores `value` under `key`, replacing any earlier value. */
818
- readonly set: (key: string, value: string) => Effect.Effect<void, unknown>;
819
- }
820
- /**
821
- * What {@link ToolReplay.wrap} needs to know about one tool.
822
- *
823
- * @typeParam Store - The type of {@link ToolReplayOptions.store}; when it is
824
- * omitted, `wrap` requires `KeyValueStore` in `R` instead.
825
- *
826
- * @public
827
- */
828
- interface ToolReplayOptions<P, S extends Schema.Top, Store extends ToolReplayStore | undefined = undefined> {
829
- /** The served tool name. It must not contain `:`, the separator of the stored key ({@link ToolReplay.storeKey}). */
830
- readonly tool: string;
831
- /**
832
- * The tool's success schema. Results are stored encoded through it and
833
- * decoded through it on replay. It must declare {@link ToolReplay.marker} on
834
- * each object member, or core's encoder drops the marker from the reply:
835
- * build each member with {@link ToolReplay.success} so it cannot be left out.
836
- */
837
- readonly success: S;
838
- /**
839
- * The idempotency key for one call; `Option.none()` runs the handler with
840
- * nothing stored or replayed.
841
- *
842
- * @remarks
843
- * The key must identify the request completely. A replay compares nothing
844
- * else: not the params, not the schema version. So a reused key with
845
- * different params returns the FIRST call's result. When the params under
846
- * one caller-supplied id can vary, fold a digest of them into the key.
847
- */
848
- readonly key: (params: P) => Option.Option<string>;
849
- /**
850
- * Where results are stored. Omitted, `wrap` reads core's `KeyValueStore`
851
- * from `R`; given, it uses this store and `KeyValueStore` leaves `R`.
852
- */
853
- readonly store?: Store;
854
- }
855
- /**
856
- * Idempotent replay for a write tool: a repeated call with the same key returns
857
- * the first call's stored result, marked `_idempotentReplay: true`, instead of
858
- * running the mutation again.
859
- *
860
- * @remarks
861
- * The store is core's `KeyValueStore` (`effect/persistence`), required in `R`
862
- * (`KeyValueStore.layerMemory`, `layerFileSystem` or `layerSql`), unless
863
- * {@link ToolReplayOptions.store} supplies a {@link ToolReplayStore}: just
864
- * `get` and `set`, for an application's own table. Then `R` carries no
865
- * `KeyValueStore`.
866
- *
867
- * Semantics, per call:
868
- *
869
- * - A key of `Option.none()` runs the handler; nothing is read or stored.
870
- * - A stored value that decodes through `success` is returned with the marker,
871
- * and the handler does not run.
872
- * - A miss runs the handler and stores its result, best-effort. A store that
873
- * fails, a row that no longer decodes (written under an older schema, or
874
- * corrupt), and a failed write are each a miss or a skipped write, never a
875
- * tool error. Each is logged at debug level with the tool name.
876
- * - A handler failure stores nothing, so a retry runs it again.
877
- * - An `ok: false` result (a {@link ToolOutcome} envelope failure) is never
878
- * stored: a "not found" must not replay after the thing exists.
879
- *
880
- * This is replay, not exactly-once: two concurrent calls with one key both
881
- * miss and both run.
882
- *
883
- * The key is the whole identity of a request. A hit compares neither the
884
- * params nor the schema version (a row written under an older schema replays
885
- * whenever it still decodes), and rows never expire: the store keeps a result
886
- * until something removes it. A key reused with different params therefore
887
- * replays the first result, so {@link ToolReplayOptions.key} must fold in a
888
- * digest of any params that can vary under one id.
889
- *
890
- * Wrap it inside {@link ToolOutcome.catchAsEnvelope}, so the envelope sees the
891
- * handler's real errors and replay sees only real successes.
892
- *
893
- * @example
894
- * ```ts
895
- * import { ToolReplay } from "@effected/mcp"
896
- * import { Option, Schema } from "effect"
897
- *
898
- * const Created = ToolReplay.success(Schema.Struct({ ok: Schema.Literal(true), id: Schema.Number }))
899
- * const create = ToolReplay.wrap({
900
- * tool: "goal_create",
901
- * success: Created,
902
- * key: (params: { readonly requestId?: string }) => Option.fromUndefinedOr(params.requestId),
903
- * })(createGoal)
904
- * ```
905
- *
906
- * @public
907
- */
908
- export declare class ToolReplay {
909
- private constructor();
910
- /**
911
- * The marker field, to spread into each object member of a replayable
912
- * success schema. {@link ToolReplay.success} adds it for you.
913
- */
914
- static readonly marker: {
915
- readonly _idempotentReplay: Schema.optionalKey<Schema.Literal<true>>;
916
- };
917
- /**
918
- * A struct with {@link ToolReplay.marker} added. Use it for each object
919
- * success member, so core's encoder keeps the marker on a replayed reply.
920
- *
921
- * @remarks
922
- * Built with core's `fieldsAssign`, which, like `mapFields`, does not carry
923
- * the struct's own `.check(...)` refinements over: apply checks to the
924
- * result, not to the struct passed in.
925
- */
926
- static readonly success: <const Fields extends Schema.Struct.Fields>(schema: Schema.Struct<Fields>) => Schema.Struct<Struct.Simplify<Struct.Assign<Fields, typeof ToolReplay.marker>>>;
927
- /**
928
- * The `KeyValueStore` key one call is stored under: `<tool>:<key>`. Split on
929
- * the first `:` to recover both parts; MCP tool names are `[A-Za-z0-9_.-]`.
930
- */
931
- static readonly storeKey: (tool: string, key: string) => string;
932
- /**
933
- * Wraps a handler with replay. Throws at wrap time when `tool` contains
934
- * `:`, a wiring mistake, so it surfaces when the server is built.
935
- */
936
- static readonly wrap: <P, S extends Schema.Top, Store extends ToolReplayStore | undefined = undefined>(options: ToolReplayOptions<P, S, Store>) => (<E, R>(handler: (params: P) => Effect.Effect<S["Type"], E, R>) => (params: P) => Effect.Effect<S["Type"], E, R | ([Store] extends [ToolReplayStore] ? never : KeyValueStore.KeyValueStore) | S["DecodingServices"] | S["EncodingServices"]>);
937
- }
938
- export type { FormatUnknownKeysOptions, McpLaunchOptions, McpStdioOptions, McpToolkitOptions, ToolOutcomeEnvelopeOptions, ToolOutcomeErrorInput, ToolOutcomeFailure, ToolOutcomeFailures, ToolReplayOptions, ToolReplayStore, UnionHandlerOptions, UnionTool, UnionToolOptions, UnknownKeysLevel };
19
+ export declare const MCP_VERSION: string;
20
+ export type { MainOptions };
939
21
  //# sourceMappingURL=index.d.ts.map