@effected/mcp 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -1,10 +1,3 @@
1
- import { McpStdio } from "./McpStdio.js";
2
- import { ToolFailure } from "./ToolFailure.js";
3
- import { ToolInputSchema } from "./ToolInputSchema.js";
4
- import { McpToolkit } from "./McpToolkit.js";
5
- import { ToolOutputSchema } from "./ToolOutputSchema.js";
6
- import { ToolOutcome } from "./ToolOutcome.js";
7
- import { ToolRefusal } from "./ToolRefusal.js";
8
- import { ToolReplay } from "./ToolReplay.js";
1
+ import { MCP_VERSION } from "./version.js";
9
2
 
10
- export { McpStdio, McpToolkit, ToolFailure, ToolInputSchema, ToolOutcome, ToolOutputSchema, ToolRefusal, ToolReplay };
3
+ export { MCP_VERSION };
package/main.d.ts ADDED
@@ -0,0 +1,28 @@
1
+ import { Distribution } from "@effected/kit";
2
+ /**
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`.
6
+ *
7
+ * @public
8
+ */
9
+ export interface MainOptions {
10
+ readonly distribution?: Distribution;
11
+ }
12
+ /**
13
+ * Run the `effected-mcp` server over stdio. Owns the process.
14
+ *
15
+ * `McpGuard.run` installs the crash guards before `load` evaluates the server
16
+ * graph, so a throw during module evaluation is still reported on stderr; this
17
+ * module therefore carries no static runtime import (`Distribution` is
18
+ * type-only). Policy is `exitBeforeConnect` for both events: the server holds
19
+ * no state between calls, and a stray error while serving is likely a config's
20
+ * own (config modules run in this process), so the guard logs and keeps
21
+ * serving, while the same error before serving means a broken boot and exits
22
+ * `1`. Nothing here writes to stdout; it is the JSON-RPC wire. A config can, so
23
+ * a tool call runs under `StdioCapture`, its stdout diverted to stderr.
24
+ *
25
+ * @public
26
+ */
27
+ export declare const main: (options?: MainOptions) => Promise<void>;
28
+ //# sourceMappingURL=main.d.ts.map
package/main.js ADDED
@@ -0,0 +1,46 @@
1
+ import { McpGuard } from "@effected/mcp-kit/guard";
2
+
3
+ // src/main.ts
4
+ /**
5
+ * Run the `effected-mcp` server over stdio. Owns the process.
6
+ *
7
+ * `McpGuard.run` installs the crash guards before `load` evaluates the server
8
+ * graph, so a throw during module evaluation is still reported on stderr; this
9
+ * module therefore carries no static runtime import (`Distribution` is
10
+ * type-only). Policy is `exitBeforeConnect` for both events: the server holds
11
+ * no state between calls, and a stray error while serving is likely a config's
12
+ * own (config modules run in this process), so the guard logs and keeps
13
+ * serving, while the same error before serving means a broken boot and exits
14
+ * `1`. Nothing here writes to stdout; it is the JSON-RPC wire. A config can, so
15
+ * a tool call runs under `StdioCapture`, its stdout diverted to stderr.
16
+ *
17
+ * @public
18
+ */
19
+ const main = (options = {}) => McpGuard.run({
20
+ label: "effected-mcp",
21
+ host: process,
22
+ policy: {
23
+ onUncaught: "exitBeforeConnect",
24
+ onRejection: "exitBeforeConnect"
25
+ },
26
+ load: async () => {
27
+ const NodeRuntime = await import("@effect/platform-node/NodeRuntime");
28
+ const NodeStdio = await import("@effect/platform-node/NodeStdio");
29
+ const NodeFileSystem = await import("@effect/platform-node/NodeFileSystem");
30
+ const NodePath = await import("@effect/platform-node/NodePath");
31
+ const { StdioCapture } = await import("@effected/mcp-kit/node");
32
+ const { Layer } = await import("effect");
33
+ const { ServerLayer } = await import("./server.js");
34
+ const capture = StdioCapture.install(process);
35
+ return {
36
+ layer: ServerLayer({
37
+ ...options,
38
+ cwd: process.cwd(),
39
+ confineStdout: capture.capture({ stdout: capture.toStderr })
40
+ }).pipe(Layer.provide(Layer.mergeAll(NodeStdio.layer, NodeFileSystem.layer, NodePath.layer))),
41
+ runMain: NodeRuntime.runMain
42
+ };
43
+ }
44
+ });
45
+
46
+ export { main };
package/package.json CHANGED
@@ -1,14 +1,12 @@
1
1
  {
2
2
  "name": "@effected/mcp",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "private": false,
5
- "description": "The boundary layer of an effect/ai MCP server: stdio wiring that keeps stdout the wire, tool-failure shaping, strict-input walkers, and test clients",
5
+ "description": "The effected-mcp server: the tool's MCP tools on the kit's MCP boundary",
6
6
  "keywords": [
7
7
  "effect",
8
8
  "mcp",
9
9
  "model-context-protocol",
10
- "stdio",
11
- "json-rpc",
12
10
  "effected"
13
11
  ],
14
12
  "homepage": "https://github.com/spencerbeggs/effected/tree/main/packages/mcp#readme",
@@ -34,26 +32,24 @@
34
32
  "import": "./index.js",
35
33
  "default": "./index.js"
36
34
  },
37
- "./guard": {
38
- "types": "./guard.d.ts",
39
- "import": "./guard.js",
40
- "default": "./guard.js"
41
- },
42
- "./node": {
43
- "types": "./node.d.ts",
44
- "import": "./node.js",
45
- "default": "./node.js"
46
- },
47
- "./testing": {
48
- "types": "./testing.d.ts",
49
- "import": "./testing.js",
50
- "default": "./testing.js"
35
+ "./main": {
36
+ "types": "./main.d.ts",
37
+ "import": "./main.js",
38
+ "default": "./main.js"
51
39
  },
52
40
  "./package.json": "./package.json"
53
41
  },
54
- "peerDependencies": {
55
- "@effected/engine": "^0.11.0",
56
- "effect": "~4.1.0"
42
+ "bin": {
43
+ "effected-mcp": "bin/effected-mcp.js"
44
+ },
45
+ "dependencies": {
46
+ "@effect/platform-node": "~4.1.2",
47
+ "@effected/engine": "0.13.0",
48
+ "@effected/glob": "^0.19.0",
49
+ "@effected/kit": "^0.13.0",
50
+ "@effected/mcp-kit": "^0.14.0",
51
+ "@effected/walker": "^0.25.1",
52
+ "effect": "~4.1.2"
57
53
  },
58
54
  "engines": {
59
55
  "node": ">=24.11.0"
package/server.js ADDED
@@ -0,0 +1,48 @@
1
+ import { handleIdentity, identity } from "./tools/identity.js";
2
+ import { makeSchemaHandlers, schemaBuild, schemaCheck, schemaValidate } from "./tools/schema.js";
3
+ import { MCP_VERSION } from "./version.js";
4
+ import { McpStdio, McpToolkit } from "@effected/mcp-kit";
5
+ import { Effect, Layer } from "effect";
6
+ import { McpProtocol, Toolkit } from "effect/ai";
7
+
8
+ // src/server.ts
9
+ /**
10
+ * Agent-facing orientation, surfaced as `instructions` in both the
11
+ * `initialize` and `server/discover` results.
12
+ *
13
+ * @public
14
+ */
15
+ const SERVER_INSTRUCTIONS = "effected-mcp serves the effected tool. Call identity to learn the tool name, this server's version, the distribution it was launched through and the resolved effect version. schema_check reports whether the generated schema documents match the config and writes nothing, schema_build writes them, and schema_validate checks a JSON payload against one. Drift, staleness and a failed validation arrive as results (a verdict, or valid: false), not as errors; an error means the config could not be found or loaded, a path was outside the workspace root, or the run could not finish. Each call reloads the config and the TypeScript and JSON it imports, but an imported .mjs, .cjs or built .js under type: module stays cached, so restart this server after rebuilding a sibling package the config uses.";
16
+ /**
17
+ * The whole server as one layer: the toolkit over the kit's `McpStdio.layer`,
18
+ * its schema handlers capturing `FileSystem` and `Path` when they are built.
19
+ * The adapters are listed newest first, as the kit's
20
+ * default lists them; a client that opens with `initialize` needs the
21
+ * stateful ones.
22
+ *
23
+ * @public
24
+ */
25
+ const ServerLayer = (options = {}) => {
26
+ const EffectedToolkit = Toolkit.make(identity, schemaCheck, schemaBuild, schemaValidate);
27
+ const handlers = EffectedToolkit.toLayer(Effect.map(makeSchemaHandlers(options), (schema) => ({
28
+ identity: () => handleIdentity(options.distribution),
29
+ ...schema
30
+ })));
31
+ return McpStdio.layer({
32
+ name: "effected",
33
+ version: MCP_VERSION,
34
+ instructions: SERVER_INSTRUCTIONS,
35
+ cacheHints: { tools: {
36
+ ttl: "1 hour",
37
+ cacheScope: "private"
38
+ } },
39
+ protocols: [
40
+ McpProtocol.v2026_07_28,
41
+ McpProtocol.v2025_11_25,
42
+ McpProtocol.v2025_06_18
43
+ ],
44
+ registrations: McpToolkit.layer(EffectedToolkit).pipe(Layer.provide(handlers))
45
+ });
46
+ };
47
+
48
+ export { SERVER_INSTRUCTIONS, ServerLayer };
@@ -0,0 +1,28 @@
1
+ import { MCP_VERSION } from "../version.js";
2
+ import { Effect } from "effect";
3
+ import { Tool } from "effect/ai";
4
+ import { Identity, IdentityReport } from "@effected/engine";
5
+
6
+ // src/tools/identity.ts
7
+ /**
8
+ * The `identity` tool: what the `effected` tool reports about itself, the
9
+ * same report the `effected` command prints.
10
+ *
11
+ * @public
12
+ */
13
+ const identity = Tool.make("identity", {
14
+ description: "Reports the effected tool's identity: the tool name, this server's version, the carrier distribution it was launched through (null for a direct install), and the resolved effect version.",
15
+ parameters: Tool.EmptyParams,
16
+ success: IdentityReport
17
+ }).annotate(Tool.Title, "Effected identity").annotate(Tool.Readonly, true).annotate(Tool.Idempotent, true).annotate(Tool.OpenWorld, false);
18
+ /**
19
+ * The `identity` handler, closed over the carrier distribution.
20
+ *
21
+ * @public
22
+ */
23
+ const handleIdentity = (distribution) => Effect.suspend(() => Identity.report({
24
+ version: MCP_VERSION,
25
+ distribution: distribution ?? null
26
+ }));
27
+
28
+ export { handleIdentity, identity };
@@ -0,0 +1,158 @@
1
+ import { ToolFailure, ToolRefusal } from "@effected/mcp-kit";
2
+ import { Effect, FileSystem, Option, Path, Ref, Schema } from "effect";
3
+ import { Tool } from "effect/ai";
4
+ import { ConfigLoader, SchemaRun, SchemaRunResult, SchemaValidate, ValidateReport } from "@effected/engine/schema";
5
+ import { Exclusive, PathContainment } from "@effected/kit";
6
+ import { Walker } from "@effected/walker";
7
+
8
+ // src/tools/schema.ts
9
+ const CONFIG_DESCRIPTION = "The schemastore config, resolved against the workspace root and refused outside it. Omitted, discovery walks upward from the server's working directory, stopping at the workspace root.";
10
+ const RunParameters = Schema.Struct({
11
+ config: Schema.optionalKey(Schema.String.annotate({ description: CONFIG_DESCRIPTION })),
12
+ drift: Schema.optionalKey(Schema.Literals([
13
+ "strict",
14
+ "semantic",
15
+ "allow"
16
+ ]).annotate({ description: "One drift tolerance over every schema's own." })),
17
+ onDrift: Schema.optionalKey(Schema.Literals(["error", "warn"]).annotate({ description: "Overrides the config's onDrift." })),
18
+ force: Schema.optionalKey(Schema.Boolean.annotate({ description: "Shorthand for drift \"allow\", and refused beside any other drift." }))
19
+ });
20
+ /**
21
+ * The `schema_check` tool: what `schema_build` would do, writing nothing.
22
+ *
23
+ * @public
24
+ */
25
+ const schemaCheck = Tool.make("schema_check", {
26
+ description: "Checks the generated schema documents against the config, writing nothing. Drift and staleness arrive as the result's verdict, with each changed document's full diff; they are not errors. Reloads the config on every call.",
27
+ parameters: RunParameters,
28
+ success: SchemaRunResult,
29
+ failure: ToolRefusal
30
+ }).annotate(Tool.Title, "Check schema documents").annotate(Tool.Readonly, true).annotate(Tool.Idempotent, true).annotate(Tool.OpenWorld, false);
31
+ /**
32
+ * The `schema_build` tool: writes the generated documents and catalogs.
33
+ *
34
+ * @public
35
+ */
36
+ const schemaBuild = Tool.make("schema_build", {
37
+ description: "Generates the schema documents and catalogs the config declares and writes the ones that changed, wherever the config puts them. A refused drift or a failed gate writes nothing and arrives as the result's verdict. Reloads the config on every call.",
38
+ parameters: RunParameters,
39
+ success: SchemaRunResult,
40
+ failure: ToolRefusal
41
+ }).annotate(Tool.Title, "Build schema documents").annotate(Tool.Readonly, false).annotate(Tool.Idempotent, true).annotate(Tool.OpenWorld, false);
42
+ /**
43
+ * The `schema_validate` tool: does one payload conform to a published document.
44
+ *
45
+ * @public
46
+ */
47
+ const schemaValidate = Tool.make("schema_validate", {
48
+ description: "Validates a JSON payload file against a local schema document, with each finding's line and column. A payload that does not conform is valid: false, not an error.",
49
+ parameters: Schema.Struct({
50
+ payload: Schema.String.annotate({ description: "The payload file, resolved against the workspace root and refused outside it." }),
51
+ config: Schema.optionalKey(Schema.String.annotate({ description: CONFIG_DESCRIPTION })),
52
+ schema: Schema.optionalKey(Schema.String.annotate({ description: "The document: a file path under the workspace root, or an $id or url the config derives. Omitted, the payload's own $schema." }))
53
+ }),
54
+ success: ValidateReport,
55
+ failure: ToolRefusal
56
+ }).annotate(Tool.Title, "Validate a payload").annotate(Tool.Readonly, true).annotate(Tool.Idempotent, true).annotate(Tool.OpenWorld, false);
57
+ const WALK_TIMEOUT = "2 minutes";
58
+ const findRoot = (cwd) => Effect.gen(function* () {
59
+ const fs = yield* FileSystem.FileSystem;
60
+ const path = yield* Path.Path;
61
+ const chain = yield* Walker.ascend(cwd);
62
+ const holding = (marker) => Walker.findRoot(chain, (dir) => fs.exists(path.join(dir, marker)));
63
+ const workspace = yield* holding("pnpm-workspace.yaml");
64
+ return Option.isSome(workspace) ? workspace.value : Option.getOrElse(yield* holding(".git"), () => cwd);
65
+ });
66
+ const refusalOf = (error, root, mismatch) => {
67
+ const refuse = (reason, hint, suggestedTool) => ToolRefusal.refuse([ToolFailure.truncate(reason, ToolFailure.ENGINE_ECHO_LIMIT), ...mismatch !== void 0 ? [`Version mismatch: ${mismatch.message}`] : []].join(" "), {
68
+ hint,
69
+ ...suggestedTool !== void 0 ? { suggestedTool } : {}
70
+ });
71
+ switch (error._tag) {
72
+ case "ConfigNotFoundError": return refuse(error.message, `Add a schemastore.config.ts, or pass config with its path under the workspace root ${root}.`);
73
+ case "ConfigLoadError": return refuse(`Failed to load schemastore config ${error.path}: ${error.reason.split("\n", 1)[0]}`, "Fix the config and call the tool again; it reloads on every call.");
74
+ case "ConflictingFlagsError": return refuse(`force means drift "allow", so it conflicts with drift "${error.policy}".`, "Pass one or the other.");
75
+ case "MissingSchemaRefError": return refuse(`Payload ${error.path} has no "$schema" property.`, "Name the document with the schema parameter: a file path, or an $id or url the config derives.");
76
+ case "ExclusiveTimeout": return refuse(error.message, "Documents may be part-written; call the tool to see what differs.", "schema_check");
77
+ default: return refuse(error.message, "Fix what the message names and call the tool again.");
78
+ }
79
+ };
80
+ const payloadRef = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.Struct({ $schema: Schema.String })));
81
+ /**
82
+ * The three handlers, closed over the filesystem and path the server was built with, the workspace root found once
83
+ * from `options.cwd`, and one lane.
84
+ *
85
+ * @remarks
86
+ * `schema_build` and `schema_check` load the config OUTSIDE the lane and hand the engine what was loaded. A timed-out
87
+ * lane interrupts its body, and the engine's `Effect.tryPromise` around the import abandons the promise on interrupt,
88
+ * so a load inside would leave a config still evaluating while the next call holds the permit. A hanging config
89
+ * therefore never holds up another call. `schema_validate` stays out: it only reads one document, and each document is
90
+ * replaced atomically, so it sees the old text or the new.
91
+ *
92
+ * @public
93
+ */
94
+ const makeSchemaHandlers = (options = {}) => Effect.gen(function* () {
95
+ const fs = yield* FileSystem.FileSystem;
96
+ const path = yield* Path.Path;
97
+ const cwd = path.resolve(options.cwd ?? ".");
98
+ const root = yield* findRoot(cwd);
99
+ const lane = yield* Exclusive.make;
100
+ const importer = options.importModule !== void 0 ? { importModule: options.importModule } : {};
101
+ const inside = (label, candidate) => Effect.flatMap(PathContainment.isWithin(root, candidate), (within) => within ? Effect.void : Effect.fail(ToolRefusal.refuse(`The ${label} "${ToolFailure.truncate(candidate)}" is outside the workspace root ${root}.`, { hint: `Pass a path under ${root}.` })));
102
+ const configOf = (config) => config !== void 0 ? Effect.as(inside("config", config), { config }) : Effect.match(ConfigLoader.discover(cwd, root), {
103
+ onFailure: () => ({}),
104
+ onSuccess: (found) => ({ config: found })
105
+ });
106
+ const call = (run) => Effect.gen(function* () {
107
+ const warned = yield* Ref.make(void 0);
108
+ return yield* run((event) => event._tag === "Started" && event.versionMismatch !== void 0 ? Ref.set(warned, event.versionMismatch) : Effect.void).pipe(Effect.catch((error) => error._tag === "ToolRefusal" ? Effect.fail(error) : Effect.gen(function* () {
109
+ yield* Effect.logDebug("schema tool refused", { error: error.message });
110
+ return yield* Effect.fail(refusalOf(error, root, yield* Ref.get(warned)));
111
+ })));
112
+ }).pipe(Effect.provideService(FileSystem.FileSystem, fs), Effect.provideService(Path.Path, path), options.confineStdout ?? ((self) => self));
113
+ const walk = (mode) => (params) => call((onProgress) => Effect.gen(function* () {
114
+ const scoped = yield* configOf(params.config);
115
+ const loaded = yield* ConfigLoader.load({
116
+ cwd: root,
117
+ ceiling: root,
118
+ ...scoped.config !== void 0 ? { explicit: scoped.config } : {},
119
+ ...importer
120
+ });
121
+ return yield* lane.run(SchemaRun.run({
122
+ mode,
123
+ config: loaded.path,
124
+ ...params.drift !== void 0 ? { drift: params.drift } : {},
125
+ ...params.onDrift !== void 0 ? { onDrift: params.onDrift } : {},
126
+ force: params.force ?? false,
127
+ versionMismatch: "warn"
128
+ }, {
129
+ cwd: root,
130
+ discoveryCeiling: root,
131
+ importModule: () => Promise.resolve(loaded.config),
132
+ onProgress
133
+ }), { timeout: WALK_TIMEOUT });
134
+ }));
135
+ return {
136
+ schema_check: walk("check"),
137
+ schema_build: walk("build"),
138
+ schema_validate: (params) => call((onProgress) => Effect.gen(function* () {
139
+ yield* inside("payload", params.payload);
140
+ const ref = params.schema ?? Option.getOrUndefined(Option.map(yield* fs.readFileString(path.resolve(root, params.payload)).pipe(Effect.map((text) => payloadRef(text)), Effect.orElseSucceed(() => Option.none())), (payload) => payload.$schema));
141
+ if (ref !== void 0) yield* inside("schema", ref);
142
+ const scoped = yield* configOf(params.config);
143
+ return yield* SchemaValidate.run({
144
+ payload: params.payload,
145
+ ...ref !== void 0 ? { schema: ref } : {},
146
+ ...scoped,
147
+ versionMismatch: "warn"
148
+ }, {
149
+ cwd: root,
150
+ discoveryCeiling: root,
151
+ ...importer,
152
+ onProgress
153
+ });
154
+ }))
155
+ };
156
+ });
157
+
158
+ export { makeSchemaHandlers, schemaBuild, schemaCheck, schemaValidate };
@@ -5,7 +5,7 @@
5
5
  "toolPackages": [
6
6
  {
7
7
  "packageName": "@microsoft/api-extractor",
8
- "packageVersion": "7.60.0"
8
+ "packageVersion": "7.60.1"
9
9
  }
10
10
  ]
11
11
  }
package/version.js ADDED
@@ -0,0 +1,11 @@
1
+ // src/version.ts
2
+ /**
3
+ * The `effected-mcp` server's version. The bundler replaces the exact expression
4
+ * `process.env.__PACKAGE_VERSION__` with the package version at build time, so
5
+ * it must stay spelled this way.
6
+ *
7
+ * @public
8
+ */
9
+ const MCP_VERSION = "0.14.0";
10
+
11
+ export { MCP_VERSION };
package/McpGuard.js DELETED
@@ -1,81 +0,0 @@
1
- import { ProcessGuard } from "@effected/engine/guard";
2
-
3
- // src/McpGuard.ts
4
- /**
5
- * Crash guards for an MCP server process, installed before the server's
6
- * module graph is loaded. Imported from `@effected/mcp/guard`.
7
- *
8
- * @remarks
9
- * {@link McpGuard.run} is `ProcessGuard.run` from `@effected/engine/guard`
10
- * with an MCP launch in its `load`: it registers `uncaughtException` and
11
- * `unhandledRejection` listeners on `host`, then awaits `load()`, then
12
- * launches the server with `McpStdio.launch` and `McpStdio.teardown` under
13
- * the `runMain` it returned. A server on another transport (an LSP over
14
- * `vscode-languageserver`, say) uses `ProcessGuard.run` directly and calls
15
- * its `markConnected` itself. This entrypoint statically imports only
16
- * `@effected/engine/guard`, which imports nothing: it loads `effect` and the
17
- * stdio wiring only after the guards are listening, so a throw while any of
18
- * it evaluates is still reported on stderr.
19
- *
20
- * - "Connected" means the whole server layer has built, so stdin is being
21
- * read. That is before any client sends `initialize`.
22
- * - `injectCrash` drives either half of the policy from a test, before
23
- * `load()` or once connected; unset, it does nothing.
24
- * - A `load()` that rejects is reported as `startup failed` and exits 1
25
- * whatever the policy. Left to a log-only rejection listener, it would
26
- * let the event loop drain and exit 0 with no server.
27
- * - Installing an `unhandledRejection` listener switches off Node's default
28
- * of throwing on one, so `"log"` really does keep the process running.
29
- * - An exit from a guard skips Effect's finalizers and the teardown.
30
- * - Keeping on serving after a crash works because the platform `runMain`
31
- * holds the process open while the server fiber lives.
32
- * - A layer that fails to build is still reported by `McpStdio.launch`.
33
- *
34
- * @example
35
- * ```ts
36
- * import { McpGuard } from "@effected/mcp/guard";
37
- *
38
- * await McpGuard.run({
39
- * label: "my-server",
40
- * host: process,
41
- * policy: { onUncaught: "exitBeforeConnect", onRejection: "log" },
42
- * load: async () => {
43
- * const { NodeRuntime } = await import("@effect/platform-node");
44
- * const { Main } = await import("./server.js");
45
- * return { layer: Main, runMain: NodeRuntime.runMain };
46
- * },
47
- * });
48
- * ```
49
- *
50
- * @public
51
- */
52
- var McpGuard = class {
53
- constructor() {}
54
- /**
55
- * Parse a test-only crash-injection setting into {@link McpGuardRunOptions.injectCrash}: `<at>:<kind>`, where `at`
56
- * is `"load"` or `"connected"` and `kind` is `"uncaughtException"` or `"unhandledRejection"`. Anything else, or no
57
- * value, is `undefined`. The same grammar as `ProcessGuard.parseInjectCrash` from `@effected/engine/guard`, which
58
- * this is, so an MCP launcher need not import the engine guard for it.
59
- *
60
- * @example
61
- * ```ts
62
- * McpGuard.parseInjectCrash("load:uncaughtException"); // => { at: "load", kind: "uncaughtException" }
63
- * ```
64
- */
65
- static parseInjectCrash = ProcessGuard.parseInjectCrash;
66
- /** Install the guards, load the server, and launch it. Resolves once the server is launched. */
67
- static run = (options) => ProcessGuard.run({
68
- label: options.label,
69
- host: options.host,
70
- policy: options.policy,
71
- injectCrash: options.injectCrash,
72
- load: async (guard) => {
73
- const server = await options.load();
74
- if (server.format !== void 0) guard.useFormat(server.format);
75
- const { launchGuarded } = await import("./internal/guardLaunch.js");
76
- launchGuarded(server, guard.markConnected);
77
- }
78
- });
79
- };
80
-
81
- export { McpGuard };