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", ) {} // --------------------------------------------------------------------------- // Composed API // --------------------------------------------------------------------------- const ExecutorApiWithMcp = addGroup(McpGroup); // --------------------------------------------------------------------------- // Convert API payload → McpServerInput // --------------------------------------------------------------------------- const toServerInput = ( payload: { transport?: "remote" | "stdio" } & Record, ): 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; 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; headers?: Record; 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[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] }; }), ), ), );