1import { OrchestratorMcpFailure } from "@t3tools/contracts";
2import { OrchestratorToolkit } from "./tools.ts";
3import * as Effect from "effect/Effect";
4
5import { readCaller } from "../../threadAccess.ts";
6import * as McpInvocationContext from "../../McpInvocationContext.ts";
7import * as McpToolAccess from "../../McpToolAccess.ts";
8import * as OrchestratorMcpService from "../../OrchestratorMcpService.ts";
9import * as ThreadMetadataMcpService from "../../ThreadMetadataMcpService.ts";
10
11const handlers = {
12 orchestrator_capabilities: McpToolAccess.reads(() =>
13 Effect.gen(function* () {
14 const scope = yield* McpInvocationContext.McpInvocationContext;
15 const service = yield* OrchestratorMcpService.OrchestratorMcpService;
16 return yield* service.capabilities(scope);
17 }),
18 ),
19 delegate_task: McpToolAccess.actsAsCaller((input) =>
20 Effect.gen(function* () {
21 const scope = yield* McpInvocationContext.McpInvocationContext;
22 const service = yield* OrchestratorMcpService.OrchestratorMcpService;
23 return yield* service.delegateTask(scope, input);
24 }),
25 ),
26 task_status: McpToolAccess.actsAsCaller(({ taskId }) =>
27 Effect.gen(function* () {
28 const scope = yield* McpInvocationContext.McpInvocationContext;
29 const service = yield* OrchestratorMcpService.OrchestratorMcpService;
30 return yield* service.taskStatus(scope, taskId);
31 }),
32 ),
33 task_cancel: McpToolAccess.actsAsCaller((input) =>
34 Effect.gen(function* () {
35 const scope = yield* McpInvocationContext.McpInvocationContext;
36 const service = yield* OrchestratorMcpService.OrchestratorMcpService;
37 return yield* service.cancelTask(scope, input);
38 }),
39 ),
40 t3_workflow_stop: McpToolAccess.writesThreads(
41 (input) => [input.threadId],
42 (input) =>
43 Effect.gen(function* () {
44 const { threads } = yield* readCaller();
45 return yield* threads.stopWorkflow(input).pipe(
46 Effect.as({}),
47 Effect.mapError(
48 (cause) =>
49 new OrchestratorMcpFailure({
50 code:
51 cause.reason === "unavailable" || cause.reason === "stop-failed"
52 ? "orchestration_error"
53 : "invalid_request",
54 message: cause.message,
55 }),
56 ),
57 );
58 }),
59 ),
60 schedule_task: McpToolAccess.startsThreads(
61 // A scheduled task runs with the caller's own modes.
62 () => ({}),
63 (input) =>
64 Effect.gen(function* () {
65 const scope = yield* McpInvocationContext.McpInvocationContext;
66 const service = yield* OrchestratorMcpService.OrchestratorMcpService;
67 return yield* service.scheduleTask(scope, input);
68 }),
69 ),
70 list_scheduled_tasks: McpToolAccess.reads((input) =>
71 Effect.gen(function* () {
72 const scope = yield* McpInvocationContext.McpInvocationContext;
73 const service = yield* OrchestratorMcpService.OrchestratorMcpService;
74 return yield* service.listScheduledTasks(scope, input);
75 }),
76 ),
77 update_scheduled_task: McpToolAccess.writes((input) =>
78 Effect.gen(function* () {
79 const scope = yield* McpInvocationContext.McpInvocationContext;
80 const service = yield* OrchestratorMcpService.OrchestratorMcpService;
81 return yield* service.updateScheduledTask(scope, input);
82 }),
83 ),
84 delete_scheduled_task: McpToolAccess.writes((input) =>
85 Effect.gen(function* () {
86 const scope = yield* McpInvocationContext.McpInvocationContext;
87 const service = yield* OrchestratorMcpService.OrchestratorMcpService;
88 return yield* service.deleteScheduledTask(scope, input);
89 }),
90 ),
91 request_secret: McpToolAccess.actsAsCaller((input) =>
92 Effect.gen(function* () {
93 const scope = yield* McpInvocationContext.McpInvocationContext;
94 const service = yield* OrchestratorMcpService.OrchestratorMcpService;
95 return yield* service.requestSecret(scope, input);
96 }),
97 ),
98 create_threads: McpToolAccess.actsAsCaller((input) =>
99 Effect.gen(function* () {
100 const scope = yield* McpInvocationContext.McpInvocationContext;
101 const service = yield* OrchestratorMcpService.OrchestratorMcpService;
102 return yield* service.createThreads(scope, input);
103 }),
104 ),
105 t3_thread_list: McpToolAccess.reads((input) =>
106 Effect.gen(function* () {
107 const scope = yield* McpInvocationContext.McpInvocationContext;
108 const service = yield* OrchestratorMcpService.OrchestratorMcpService;
109 return yield* service.listThreads(scope, input);
110 }),
111 ),
112 // Reading a child's finished result also acknowledges its delivery to the
113 // reader's own thread. That is bookkeeping on the caller's own subagent, not
114 // a change to anything it reads, so this stays a read.
115 t3_thread_read: McpToolAccess.reads((input) =>
116 Effect.gen(function* () {
117 const scope = yield* McpInvocationContext.McpInvocationContext;
118 const service = yield* OrchestratorMcpService.OrchestratorMcpService;
119 return yield* service.readThread(scope, input);
120 }),
121 ),
122 t3_thread_update: McpToolAccess.writesThreads(
123 (input) => [input.threadId],
124 (input) =>
125 Effect.gen(function* () {
126 const scope = yield* McpInvocationContext.McpInvocationContext;
127 const service = yield* ThreadMetadataMcpService.ThreadMetadataMcpService;
128 return yield* service.update(scope, input);
129 }),
130 ),
131 t3_thread_send: McpToolAccess.writesThreads(
132 (input) => [input.threadId],
133 (input) =>
134 Effect.gen(function* () {
135 const scope = yield* McpInvocationContext.McpInvocationContext;
136 const service = yield* OrchestratorMcpService.OrchestratorMcpService;
137 return yield* service.sendToThread(scope, input);
138 }),
139 ),
140 t3_thread_wait: McpToolAccess.reads((input) =>
141 Effect.gen(function* () {
142 const scope = yield* McpInvocationContext.McpInvocationContext;
143 const service = yield* OrchestratorMcpService.OrchestratorMcpService;
144 return yield* service.waitForThread(scope, input);
145 }),
146 ),
147 t3_thread_interrupt: McpToolAccess.writesThreads(
148 (input) => [input.threadId],
149 (input) =>
150 Effect.gen(function* () {
151 const scope = yield* McpInvocationContext.McpInvocationContext;
152 const service = yield* OrchestratorMcpService.OrchestratorMcpService;
153 return yield* service.interruptThread(scope, input);
154 }),
155 ),
156} satisfies McpToolAccess.Handlers<typeof OrchestratorToolkit.tools>;
157
158export const layer = McpToolAccess.toLayer(OrchestratorToolkit, handlers);