// ---------------------------------------------------------------------------// Handler-level integration test for the MCP group.//// Verifies the layer wiring stays coherent end-to-end: the handlers// pull the wrapped extension from the service, and any un-caught cause// lands in the observability middleware — producing a 500 whose body is// the opaque `InternalError` schema (no internal leakage).// --------------------------------------------------------------------------- import { HttpApiBuilder } from "effect/unstable/httpapi";import { HttpRouter, HttpServer } from "effect/unstable/http";import { describe, expect, it } from "@effect/vitest";import { Effect, Layer, Schema } from "effect"; import { addGroup, observabilityMiddleware } from "@executor-js/api";import { CoreHandlers, ExecutionEngineService, ExecutorService } from "@executor-js/api/server";import type { McpPluginExtension } from "../sdk/plugin";import { McpConnectionError } from "../sdk/errors";import { McpExtensionService, McpHandlers } from "./handlers";import { McpGroup } from "./group"; const unused = Effect.die("unused"); const failingExtension: McpPluginExtension = { // oxlint-disable-next-line executor/no-error-constructor -- boundary: test injects a defect to verify opaque handler error responses probeEndpoint: () => Effect.die(new Error("Not implemented")), addServer: () => unused, removeServer: () => unused, reconcileStdioConnections: () => unused, getServer: () => Effect.succeed(null), configureServer: () => unused, configureAuth: () => unused, listCodexPlugins: () => Effect.succeed([]),}; const Api = addGroup(McpGroup);const UnusedExecutor = Layer.succeed(ExecutorService)({} as ExecutorService["Service"]);const UnusedExecutionEngine = Layer.succeed(ExecutionEngineService)( {} as ExecutionEngineService["Service"],); const webHandlerFor = (extension: McpPluginExtension) => Effect.acquireRelease( Effect.sync(() => HttpRouter.toWebHandler( HttpApiBuilder.layer(Api).pipe( Layer.provide(CoreHandlers), Layer.provide(McpHandlers), Layer.provide(observabilityMiddleware(Api)), Layer.provide(UnusedExecutor), Layer.provide(UnusedExecutionEngine), Layer.provide(Layer.succeed(McpExtensionService, extension)), Layer.provideMerge(HttpServer.layerServices), Layer.provideMerge(Layer.succeed(HttpRouter.RouterConfig)({ maxParamLength: 1000 })), ), ), ), (web) => Effect.promise(() => web.dispose()), ); // `acquireRelease` keeps disposal inside the Effect scope — no// try/finally, no per-test cleanup plumbing. `it.scoped` closes the// scope for us. Each `Layer.provide` satisfies a piece of the api// builder's dependency graph; `provideMerge` at the bottom keeps// framework services available to the router itself.const WebHandler = webHandlerFor(failingExtension); const McpConnectionErrorResponse = Schema.Struct({ _tag: Schema.Literal("McpConnectionError"), message: Schema.String,}); describe("McpHandlers", () => { it.effect("defect-returning methods produce an opaque InternalError, no leakage", () => Effect.gen(function* () { const web = yield* WebHandler; const response = yield* Effect.promise(() => (web.handler as (request: Request) => Promise<Response>)( new Request("http://localhost/mcp/probe", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ endpoint: "https://example.com/mcp" }), }), ), ); expect(response.status).toBe(500); const body = yield* Effect.promise(() => response.text()); expect(body).not.toContain("Not implemented"); }), ); it.effect("domain MCP connection errors are encoded as 400 responses", () => Effect.gen(function* () { const web = yield* webHandlerFor({ ...failingExtension, probeEndpoint: () => Effect.fail( new McpConnectionError({ transport: "remote", message: "Failed to connect to MCP endpoint and no OAuth was detected. Do you need to provide an API key, header, or query parameter?", }), ), }); const response = yield* Effect.promise(() => (web.handler as (request: Request) => Promise<Response>)( new Request("http://localhost/mcp/probe", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ endpoint: "https://ui.sh/mcp" }), }), ), ); expect(response.status).toBe(400); const body = yield* Schema.decodeUnknownEffect(McpConnectionErrorResponse)( yield* Effect.promise(() => response.json()), ); expect(body.message).toContain("Do you need to provide an API key"); }), );});
// ---------------------------------------------------------------------------
// Handler-level integration test for the MCP group.
//
// Verifies the layer wiring stays coherent end-to-end: the handlers
// pull the wrapped extension from the service, and any un-caught cause
// lands in the observability middleware — producing a 500 whose body is
// the opaque `InternalError` schema (no internal leakage).
// ---------------------------------------------------------------------------
import { HttpApiBuilder } from "effect/unstable/httpapi";
import { HttpRouter, HttpServer } from "effect/unstable/http";
import { describe, expect, it } from "@effect/vitest";
import { Effect, Layer, Schema } from "effect";
import { addGroup, observabilityMiddleware } from "@executor-js/api";
import { CoreHandlers, ExecutionEngineService, ExecutorService } from "@executor-js/api/server";
import type { McpPluginExtension } from "../sdk/plugin";
import { McpConnectionError } from "../sdk/errors";
import { McpExtensionService, McpHandlers } from "./handlers";
import { McpGroup } from "./group";
const unused = Effect.die("unused");
const failingExtension: McpPluginExtension = {
// oxlint-disable-next-line executor/no-error-constructor -- boundary: test injects a defect to verify opaque handler error responses
probeEndpoint: () => Effect.die(new Error("Not implemented")),
addServer: () => unused,
removeServer: () => unused,
reconcileStdioConnections: () => unused,
getServer: () => Effect.succeed(null),
configureServer: () => unused,
configureAuth: () => unused,
listCodexPlugins: () => Effect.succeed([]),
};
const Api = addGroup(McpGroup);
const UnusedExecutor = Layer.succeed(ExecutorService)({} as ExecutorService["Service"]);
const UnusedExecutionEngine = Layer.succeed(ExecutionEngineService)(
{} as ExecutionEngineService["Service"],
);
const webHandlerFor = (extension: McpPluginExtension) =>
Effect.acquireRelease(
Effect.sync(() =>
HttpRouter.toWebHandler(
HttpApiBuilder.layer(Api).pipe(
Layer.provide(CoreHandlers),
Layer.provide(McpHandlers),
Layer.provide(observabilityMiddleware(Api)),
Layer.provide(UnusedExecutor),
Layer.provide(UnusedExecutionEngine),
Layer.provide(Layer.succeed(McpExtensionService, extension)),
Layer.provideMerge(HttpServer.layerServices),
Layer.provideMerge(Layer.succeed(HttpRouter.RouterConfig)({ maxParamLength: 1000 })),
),
),
),
(web) => Effect.promise(() => web.dispose()),
);
// `acquireRelease` keeps disposal inside the Effect scope — no
// try/finally, no per-test cleanup plumbing. `it.scoped` closes the
// scope for us. Each `Layer.provide` satisfies a piece of the api
// builder's dependency graph; `provideMerge` at the bottom keeps
// framework services available to the router itself.
const WebHandler = webHandlerFor(failingExtension);
const McpConnectionErrorResponse = Schema.Struct({
_tag: Schema.Literal("McpConnectionError"),
message: Schema.String,
});
describe("McpHandlers", () => {
it.effect("defect-returning methods produce an opaque InternalError, no leakage", () =>
Effect.gen(function* () {
const web = yield* WebHandler;
const response = yield* Effect.promise(() =>
(web.handler as (request: Request) => Promise<Response>)(
new Request("http://localhost/mcp/probe", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ endpoint: "https://example.com/mcp" }),
}),
),
);
expect(response.status).toBe(500);
const body = yield* Effect.promise(() => response.text());
expect(body).not.toContain("Not implemented");
}),
);
it.effect("domain MCP connection errors are encoded as 400 responses", () =>
Effect.gen(function* () {
const web = yield* webHandlerFor({
...failingExtension,
probeEndpoint: () =>
Effect.fail(
new McpConnectionError({
transport: "remote",
message:
"Failed to connect to MCP endpoint and no OAuth was detected. Do you need to provide an API key, header, or query parameter?",
}),
),
});
const response = yield* Effect.promise(() =>
(web.handler as (request: Request) => Promise<Response>)(
new Request("http://localhost/mcp/probe", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ endpoint: "https://ui.sh/mcp" }),
}),
),
);
expect(response.status).toBe(400);
const body = yield* Schema.decodeUnknownEffect(McpConnectionErrorResponse)(
yield* Effect.promise(() => response.json()),
);
expect(body.message).toContain("Do you need to provide an API key");
}),
);
});