import { HttpApiBuilder } from "effect/unstable/httpapi";import { Context, Effect } from "effect"; import { addGroup, capture } from "@executor-js/api";import type { McpPluginExtension, McpProbeEndpointInput, McpServerInput } from "../sdk/plugin";import { parseMcpIntegrationConfig } from "../sdk/types";import { McpGroup } from "./group"; // ---------------------------------------------------------------------------// Service tag — holds the raw extension shape the executor produces. Handlers// wrap their generator bodies with `capture(...)` from `@executor-js/api`,// which translates `StorageError` to `InternalError` at the edge.// --------------------------------------------------------------------------- export class McpExtensionService extends Context.Service<McpExtensionService, McpPluginExtension>()( "McpExtensionService",) {} // ---------------------------------------------------------------------------// Composed API// --------------------------------------------------------------------------- const ExecutorApiWithMcp = addGroup(McpGroup); // ---------------------------------------------------------------------------// Convert API payload → McpServerInput// --------------------------------------------------------------------------- const toServerInput = ( payload: { transport?: "remote" | "stdio" } & Record<string, unknown>,): McpServerInput => { if (payload.transport === "stdio") { const p = payload as { transport: "stdio"; name: string; description?: string; command: string; args?: readonly string[]; envVars?: readonly string[]; env?: Record<string, string>; cwd?: string; slug?: string; }; return { transport: "stdio", name: p.name, description: p.description, command: p.command, args: p.args ? [...p.args] : undefined, envVars: p.envVars ? [...p.envVars] : undefined, env: p.env, cwd: p.cwd, slug: p.slug, }; } const p = payload as { transport?: "remote"; name: string; description?: string; endpoint: string; remoteTransport?: "streamable-http" | "sse" | "auto"; queryParams?: Record<string, string>; headers?: Record<string, string>; slug?: string; authenticationTemplate?: McpServerInput extends { authenticationTemplate?: infer T } ? T : never; auth?: McpServerInput extends { auth?: infer A } ? A : never; }; return { transport: "remote", name: p.name, description: p.description, endpoint: p.endpoint, remoteTransport: p.remoteTransport, queryParams: p.queryParams, headers: p.headers, slug: p.slug, authenticationTemplate: p.authenticationTemplate, auth: p.auth, };}; // ---------------------------------------------------------------------------// Handlers// --------------------------------------------------------------------------- export const McpHandlers = HttpApiBuilder.group(ExecutorApiWithMcp, "mcp", (handlers) => handlers .handle("probeEndpoint", ({ payload }) => capture( Effect.gen(function* () { const ext = yield* McpExtensionService; return yield* ext.probeEndpoint(payload as McpProbeEndpointInput); }), ), ) .handle("addServer", ({ payload }) => capture( Effect.gen(function* () { const ext = yield* McpExtensionService; return yield* ext.addServer( toServerInput(payload as Parameters<typeof toServerInput>[0]), ); }), ), ) .handle("removeServer", ({ params: path }) => capture( Effect.gen(function* () { const ext = yield* McpExtensionService; yield* ext.removeServer(path.slug); return { removed: true }; }), ), ) .handle("getServer", ({ params: path }) => capture( Effect.gen(function* () { const ext = yield* McpExtensionService; const integration = yield* ext.getServer(path.slug); if (integration === null) return null; const config = parseMcpIntegrationConfig(integration.config); if (config === null) return null; return { slug: integration.slug, description: integration.description, kind: integration.kind, canRemove: integration.canRemove, canRefresh: integration.canRefresh, config, }; }), ), ) .handle("configureServer", ({ params: path, payload }) => capture( Effect.gen(function* () { const ext = yield* McpExtensionService; yield* ext.configureServer(path.slug, payload.config); return { config: payload.config }; }), ), ) .handle("configureAuth", ({ params: path, payload }) => capture( Effect.gen(function* () { const ext = yield* McpExtensionService; const authenticationTemplate = yield* ext.configureAuth(path.slug, { authenticationTemplate: payload.authenticationTemplate, mode: payload.mode ?? "merge", }); return { authenticationTemplate: [...authenticationTemplate] }; }), ), ),);
import { HttpApiBuilder } from "effect/unstable/httpapi";
import { Context, Effect } from "effect";
import { addGroup, capture } from "@executor-js/api";
import type { McpPluginExtension, McpProbeEndpointInput, McpServerInput } from "../sdk/plugin";
import { parseMcpIntegrationConfig } from "../sdk/types";
import { McpGroup } from "./group";
// ---------------------------------------------------------------------------
// Service tag — holds the raw extension shape the executor produces. Handlers
// wrap their generator bodies with `capture(...)` from `@executor-js/api`,
// which translates `StorageError` to `InternalError` at the edge.
// ---------------------------------------------------------------------------
export class McpExtensionService extends Context.Service<McpExtensionService, McpPluginExtension>()(
"McpExtensionService",
) {}
// ---------------------------------------------------------------------------
// Composed API
// ---------------------------------------------------------------------------
const ExecutorApiWithMcp = addGroup(McpGroup);
// ---------------------------------------------------------------------------
// Convert API payload → McpServerInput
// ---------------------------------------------------------------------------
const toServerInput = (
payload: { transport?: "remote" | "stdio" } & Record<string, unknown>,
): McpServerInput => {
if (payload.transport === "stdio") {
const p = payload as {
transport: "stdio";
name: string;
description?: string;
command: string;
args?: readonly string[];
envVars?: readonly string[];
env?: Record<string, string>;
cwd?: string;
slug?: string;
};
return {
transport: "stdio",
name: p.name,
description: p.description,
command: p.command,
args: p.args ? [...p.args] : undefined,
envVars: p.envVars ? [...p.envVars] : undefined,
env: p.env,
cwd: p.cwd,
slug: p.slug,
};
}
const p = payload as {
transport?: "remote";
name: string;
description?: string;
endpoint: string;
remoteTransport?: "streamable-http" | "sse" | "auto";
queryParams?: Record<string, string>;
headers?: Record<string, string>;
slug?: string;
authenticationTemplate?: McpServerInput extends { authenticationTemplate?: infer T }
? T
: never;
auth?: McpServerInput extends { auth?: infer A } ? A : never;
};
return {
transport: "remote",
name: p.name,
description: p.description,
endpoint: p.endpoint,
remoteTransport: p.remoteTransport,
queryParams: p.queryParams,
headers: p.headers,
slug: p.slug,
authenticationTemplate: p.authenticationTemplate,
auth: p.auth,
};
};
// ---------------------------------------------------------------------------
// Handlers
// ---------------------------------------------------------------------------
export const McpHandlers = HttpApiBuilder.group(ExecutorApiWithMcp, "mcp", (handlers) =>
handlers
.handle("probeEndpoint", ({ payload }) =>
capture(
Effect.gen(function* () {
const ext = yield* McpExtensionService;
return yield* ext.probeEndpoint(payload as McpProbeEndpointInput);
}),
),
)
.handle("addServer", ({ payload }) =>
capture(
Effect.gen(function* () {
const ext = yield* McpExtensionService;
return yield* ext.addServer(
toServerInput(payload as Parameters<typeof toServerInput>[0]),
);
}),
),
)
.handle("removeServer", ({ params: path }) =>
capture(
Effect.gen(function* () {
const ext = yield* McpExtensionService;
yield* ext.removeServer(path.slug);
return { removed: true };
}),
),
)
.handle("getServer", ({ params: path }) =>
capture(
Effect.gen(function* () {
const ext = yield* McpExtensionService;
const integration = yield* ext.getServer(path.slug);
if (integration === null) return null;
const config = parseMcpIntegrationConfig(integration.config);
if (config === null) return null;
return {
slug: integration.slug,
description: integration.description,
kind: integration.kind,
canRemove: integration.canRemove,
canRefresh: integration.canRefresh,
config,
};
}),
),
)
.handle("configureServer", ({ params: path, payload }) =>
capture(
Effect.gen(function* () {
const ext = yield* McpExtensionService;
yield* ext.configureServer(path.slug, payload.config);
return { config: payload.config };
}),
),
)
.handle("configureAuth", ({ params: path, payload }) =>
capture(
Effect.gen(function* () {
const ext = yield* McpExtensionService;
const authenticationTemplate = yield* ext.configureAuth(path.slug, {
authenticationTemplate: payload.authenticationTemplate,
mode: payload.mode ?? "merge",
});
return { authenticationTemplate: [...authenticationTemplate] };
}),
),
),
);