import { type EnvironmentId, type OrchestrationV2LinkOrigin, OrchestratorMcpEnvironmentTarget, OrchestratorMcpFailure, type ProviderInteractionMode, type RuntimeMode, type ThreadId, } from "@t3tools/contracts"; import * as Effect from "effect/Effect"; import * as Option from "effect/Option"; import * as Predicate from "effect/Predicate"; import * as Ref from "effect/Ref"; import * as Struct from "effect/Struct"; import type * as Layer from "effect/Layer"; import type * as Scope from "effect/Scope"; import type { Tool, Toolkit } from "effect/ai"; import { DispatchModeLimit, type DispatchModeRefusal, } from "../orchestration-v2/DispatchModeLimit.ts"; import * as PeerForwarding from "../peer/PeerForwarding.ts"; import { assertNotLinked, assertSameLink, callerLinkOrigin } from "./linkOrigin.ts"; import * as McpInvocationContext from "./McpInvocationContext.ts"; import { resolveInteractionMode, resolveRuntimeMode } from "./delegatedTaskModes.ts"; import { assertFullAccess, assertLiveCaller, assertTargetWithinLimits, type Caller, loadCaller, readCaller, unavailable, } from "./threadAccess.ts"; /** Private constructors only enforce types; the token also blocks runtime construction. */ const builtHere: unique symbol = Symbol("t3/mcp/McpToolAccess/builtHere"); const refuseOutsideConstruction = (token: symbol) => { if (token !== builtHere) { throw new TypeError("Only McpToolAccess builds MCP tool declarations and handler layers."); } }; let declare: ( handle: (params: P) => Effect.Effect, remote: RemoteCheck

| undefined, ) => Declaration<(params: P) => Effect.Effect>; /** * What a call bound for a linked environment checks here before it goes: the * caller, not the thread or project it names, which live there and are * checked there. `undefined` for tools that act as the calling thread itself. */ type RemoteCheck

= ( params: P, ) => Effect.Effect>; let remoteCheckOf: (declaration: Declaration) => RemoteCheck | undefined; type CheckedHandlerOf = D extends Declaration ? Handler : never; interface CheckedHandler extends Struct.Lambda { (declaration: Declaration): Handler; readonly "~lambda.out": CheckedHandlerOf; } let checkedHandler: CheckedHandler; let handlersLayer: , EX, RX>( layer: Layer.Layer, EX, RX>, ) => HandlersLayer; /** t3code/no-raw-mcp-registration confines Effect's unchecked registration API to McpHttpServer. */ export class Declaration { readonly #handle: Handler; readonly #remote: RemoteCheck | undefined; private constructor( token: typeof builtHere, handle: Handler, remote: RemoteCheck | undefined, ) { refuseOutsideConstruction(token); this.#handle = handle; this.#remote = remote; } static { declare = (handle, remote) => new Declaration(builtHere, handle, remote); checkedHandler = Struct.lambda((declaration) => declaration.#handle); remoteCheckOf = (declaration) => declaration.#remote; } } const refuseReadOnlyClient = McpInvocationContext.McpInvocationContext.pipe( Effect.flatMap((scope) => scope.client?.access === "read-only" ? Effect.fail( new OrchestratorMcpFailure({ code: "capability_denied", message: "This tool changes the environment, and this MCP client was approved for read-only access.", }), ) : Effect.void, ), ); const writingCaller = refuseReadOnlyClient.pipe( Effect.andThen(loadCaller()), Effect.tap(assertLiveCaller), ); const orchestratingCaller = refuseReadOnlyClient.pipe( Effect.andThen(readCaller()), Effect.tap(assertLiveCaller), ); const requireThreadCaller = McpInvocationContext.McpInvocationContext.pipe( Effect.flatMap((scope) => McpInvocationContext.requireThreadScope(scope, "This tool")), ); export const reads = (handle: (params: P) => Effect.Effect) => declare( (params: P) => handle(params), () => Effect.void, ); export const readsAsCaller = (handle: (params: P) => Effect.Effect) => declare((params: P) => requireThreadCaller.pipe(Effect.flatMap(() => handle(params))), undefined); export const actsAsCaller = (handle: (params: P) => Effect.Effect) => declare( (params: P) => requireThreadCaller.pipe( Effect.andThen(writingCaller), Effect.flatMap(() => handle(params)), ), undefined, ); /** * Changes something that belongs to no thread, such as a pending upload or a * scheduled task. Work a linked environment started may not: none of it * carries the link's origin, so none of it is that link's. */ export const writes = ( handle: (params: P) => Effect.Effect, options?: { /** Whether a linked caller may make it, as for uploads its own sends need. */ readonly linkedCallers?: "allowed"; }, ) => declare( (params: P) => orchestratingCaller.pipe( Effect.tap((caller) => options?.linkedCallers === "allowed" ? Effect.void : assertNotLinked(caller, "change this environment's scheduled tasks"), ), Effect.flatMap(() => handle(params)), ), () => orchestratingCaller, ); /** Requires handler capability checks, not thread control; linked work is refused the same way. */ export const writesWithCapability = (handle: (params: P) => Effect.Effect) => declare( (params: P) => writingCaller.pipe( Effect.tap((caller) => assertNotLinked(caller, "start work here that is not its link's")), Effect.flatMap(() => handle(params)), ), () => writingCaller, ); export const writesThreads = ( threads: (params: P) => ReadonlyArray, handle: (params: P) => Effect.Effect, ) => declare( (params: P) => Effect.gen(function* () { const caller = yield* writingCaller; for (const threadId of threads(params)) { if (threadId === undefined || threadId === caller.scope.thread?.threadId) continue; const target = yield* caller.threads .getThreadShell(threadId) .pipe(Effect.mapError(unavailable)); if (target !== null && target.deletedAt === null) { yield* assertTargetWithinLimits(caller.limits, target); yield* assertSameLink(caller, target); } } // Modes can rise after this check; the orchestrator rechecks under lock and records refusal before handlers wrap errors. const refused = yield* Ref.make(undefined); return yield* handle(params).pipe( Effect.provideService(DispatchModeLimit, { ...caller.limits, refused }), Effect.catch((error) => Effect.flatMap(Ref.get(refused), (refusal) => Effect.fail( refusal === undefined ? error : escalationDenied(refusal), ), ), ), ); }), () => writingCaller, ); const escalationDenied = (refusal: DispatchModeRefusal) => new OrchestratorMcpFailure({ code: refusal.mode === "runtime" ? "runtime_mode_escalation_denied" : "interaction_mode_escalation_denied", message: `Thread ${refusal.threadId} now runs in ${refusal.runtimeMode}/${refusal.interactionMode} mode, above this caller's. Its user changed it while this call ran.`, }); export interface StartedModes { readonly runtimeMode: RuntimeMode; readonly interactionMode: ProviderInteractionMode; /** The linked environment the caller acts for. The thread must carry it. */ readonly linkOrigin: OrchestrationV2LinkOrigin | undefined; } /** * Starts threads with the modes `modes` requests, which may not be broader * than the caller's. `linked` says what a linked caller gets: threads stamped * with its `linkOrigin`, or a refusal where the tool cannot carry one. */ export const startsThreads = ( modes: (params: P) => { readonly runtimeMode?: RuntimeMode | undefined; readonly interactionMode?: ProviderInteractionMode | undefined; }, handle: (params: P, modes: StartedModes) => Effect.Effect, linked: "stamped" | { readonly refused: string }, ) => { const started = Effect.fn("McpToolAccess.started")(function* (caller: Caller, params: P) { const requested = modes(params); return { runtimeMode: yield* resolveRuntimeMode(caller.limits.runtimeMode, requested.runtimeMode), interactionMode: yield* resolveInteractionMode( caller.limits.interactionMode, requested.interactionMode, ), linkOrigin: callerLinkOrigin(caller), } satisfies StartedModes; }); return declare( (params: P) => Effect.gen(function* () { const caller = yield* writingCaller; if (linked !== "stamped") yield* assertNotLinked(caller, linked.refused); return yield* handle(params, yield* started(caller, params)); }), (params: P) => Effect.flatMap(writingCaller, (caller) => started(caller, params)), ); }; /** One tool with two access rules, picked per call, such as create versus update. */ export const either = ( useFirst: (params: P) => boolean, first: Declaration<(params: P) => Effect.Effect>, second: Declaration<(params: P) => Effect.Effect>, ) => { const remotes = [remoteCheckOf(first), remoteCheckOf(second)] as ReadonlyArray< RemoteCheck

| undefined >; return declare( (params: P): Effect.Effect => useFirst(params) ? checkedHandler(first)(params) : checkedHandler(second)(params), (params) => remotes[useFirst(params) ? 0 : 1]?.(params) ?? Effect.fail( new OrchestratorMcpFailure({ code: "invalid_request", message: "This action acts as the calling thread, which is here. Omit environmentId.", }), ), ); }; const fullAccessRequired = "Changing projects or environment settings needs a live full-access/default calling thread or a full-access client."; /** Recheck after waits before writing; caller modes may have changed. */ export const writesEnvironment = ( handle: ( params: P, check: Effect.Effect, ) => Effect.Effect, options?: { /** * Whether a linked caller may make it, as for adding a project: it leaves * everything already here as it was. */ readonly linkedCallers?: "allowed"; }, ) => { const check = orchestratingCaller.pipe( Effect.tap((caller) => options?.linkedCallers === "allowed" ? Effect.void : assertNotLinked(caller, "change projects or environment settings"), ), Effect.tap((caller) => assertFullAccess(caller, fullAccessRequired)), ); return declare( (params: P) => check.pipe(Effect.flatMap(() => handle(params, check))), // Linked work never reaches a link at all; that is checked as the call goes. () => orchestratingCaller.pipe( Effect.tap((caller) => assertFullAccess(caller, fullAccessRequired)), ), ); }; type CheckServices = Effect.Services; type Declarations = { readonly [Name in keyof Handlers]: Declaration & NotCallable; }; type NotCallable = { readonly call?: never } & { readonly apply?: never }; export type Handlers> = Declarations< Toolkit.HandlersFrom >; export class HandlersLayer, EX = never, RX = never> { readonly #layer: Layer.Layer, EX, RX>; private constructor( token: typeof builtHere, layer: Layer.Layer, EX, RX>, ) { refuseOutsideConstruction(token); this.#layer = layer; } static { handlersLayer = (layer) => new HandlersLayer(builtHere, layer); } static layer, EX, RX>( handlers: HandlersLayer, ) { return handlers.#layer; } } const checkedHandlers = (declarations: Declarations): Handlers => Struct.map(declarations, checkedHandler); /** * A tool runs in a linked environment when its parameters take this target * as `environmentId`. Other tools may name an environment for their own * reasons, such as the one to link or to move a thread to. */ const takesLinkedEnvironment = (tool: Tool.Any) => { const parameters: unknown = tool.parametersSchema; return ( Predicate.hasProperty(parameters, "fields") && Predicate.hasProperty(parameters.fields, "environmentId") && parameters.fields.environmentId === OrchestratorMcpEnvironmentTarget ); }; const linksUnavailable = new OrchestratorMcpFailure({ code: "orchestration_error", message: "This environment cannot reach linked environments.", }); /** * Every tool that takes a linked environment runs there the same way: its * declaration checks the caller here, and the same call goes to the other * environment, which checks the rest. The handler here never sees * `environmentId`, so it cannot leak into what it writes. */ const routed = Effect.fn("McpToolAccess.routed")(function* >( toolkit: Toolkit.Toolkit, declarations: Handlers, checked: Toolkit.HandlersFrom, ) { // Optional so toolkits that never leave this environment build without it. const forwarding = yield* Effect.serviceOption(PeerForwarding.PeerForwarding); const handlers: Record = { ...checked }; for (const [name, declaration] of Object.entries>(declarations)) { const tool = toolkit.tools[name]; if (tool === undefined || !takesLinkedEnvironment(tool)) continue; const remote = remoteCheckOf(declaration); if (remote === undefined) return yield* Effect.die( `${name} takes a linked environment, but it acts as its calling thread, which is here.`, ); const here = handlers[name] as ( params: object, ) => Effect.Effect>; handlers[name] = ({ environmentId, ...params }: { readonly environmentId?: EnvironmentId }) => Effect.gen(function* () { const scope = yield* McpInvocationContext.McpInvocationContext; const target = PeerForwarding.remoteTarget(scope, environmentId); if (target === undefined) return yield* here(params); if (scope.linkedEnvironments === false) return yield* PeerForwarding.linkedEnvironmentsOff; yield* (remote as RemoteCheck)(params); if (Option.isNone(forwarding)) return yield* linksUnavailable; return yield* forwarding.value.call(scope, tool, target, { ...params, environmentId }); }); } return handlers as Toolkit.HandlersFrom; }); export const toLayer = , EX = never, RX = never>( toolkit: Toolkit.Toolkit, build: Handlers | Effect.Effect, EX, RX>, ): HandlersLayer> => { // Checked as soon as they are given, so a forged declaration fails here. const withDeclarations = (declarations: Handlers) => routed(toolkit, declarations, checkedHandlers(declarations)); return handlersLayer( toolkit.toLayer( Effect.isEffect(build) ? Effect.flatMap(build, withDeclarations) : withDeclarations(build), ), ); };