@gjermundgaraba/effect-actions 0.20.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (321) hide show
  1. package/README.md +19 -41
  2. package/dist/Action.d.mts +140 -46
  3. package/dist/Action.mjs +87 -42
  4. package/dist/ActionCli.d.mts +69 -33
  5. package/dist/ActionCli.mjs +51 -32
  6. package/dist/ActionHttp.d.mts +69 -35
  7. package/dist/ActionHttp.mjs +75 -23
  8. package/dist/ActionMcp.d.mts +56 -22
  9. package/dist/ActionMcp.mjs +94 -69
  10. package/dist/ActionRpc.d.mts +222 -55
  11. package/dist/ActionRpc.mjs +299 -46
  12. package/dist/ActionToolkit.d.mts +22 -5
  13. package/dist/ActionToolkit.mjs +36 -12
  14. package/dist/Authentication.d.mts +42 -10
  15. package/dist/Authentication.mjs +37 -30
  16. package/dist/Event.d.mts +370 -0
  17. package/dist/Event.mjs +103 -0
  18. package/dist/Testing.mjs +1 -1
  19. package/dist/Webhook.d.mts +146 -0
  20. package/dist/Webhook.mjs +72 -0
  21. package/dist/authentication/provider.d.mts +35 -10
  22. package/dist/authentication/provider.mjs +1 -1
  23. package/dist/authentication/refusal.mjs +2 -2
  24. package/dist/cli/commands.d.mts +9 -5
  25. package/dist/cli/commands.mjs +84 -33
  26. package/dist/contract/ast.mjs +4 -1
  27. package/dist/contract/authorizer.d.mts +8 -0
  28. package/dist/contract/authorizer.mjs +20 -0
  29. package/dist/contract/call.d.mts +9 -6
  30. package/dist/contract/call.mjs +48 -6
  31. package/dist/contract/callback.d.mts +7 -0
  32. package/dist/contract/callback.mjs +27 -0
  33. package/dist/contract/caller.d.mts +13 -2
  34. package/dist/contract/caller.mjs +22 -3
  35. package/dist/contract/canonical.mjs +10 -0
  36. package/dist/contract/codec.d.mts +9 -0
  37. package/dist/contract/codec.mjs +9 -0
  38. package/dist/contract/codes.mjs +81 -0
  39. package/dist/contract/declared.mjs +25 -9
  40. package/dist/contract/digest.mjs +6 -0
  41. package/dist/contract/errors.d.mts +9 -3
  42. package/dist/contract/errors.mjs +1 -1
  43. package/dist/contract/feed.d.mts +127 -0
  44. package/dist/contract/feed.mjs +249 -0
  45. package/dist/contract/hub.d.mts +94 -0
  46. package/dist/contract/hub.mjs +133 -0
  47. package/dist/contract/idempotency.d.mts +47 -0
  48. package/dist/contract/idempotency.mjs +99 -0
  49. package/dist/contract/implementation.d.mts +73 -47
  50. package/dist/contract/implementation.mjs +92 -58
  51. package/dist/contract/input-check.d.mts +15 -0
  52. package/dist/contract/input-check.mjs +16 -0
  53. package/dist/contract/lease.mjs +28 -0
  54. package/dist/contract/listed.d.mts +4 -0
  55. package/dist/contract/listed.mjs +22 -0
  56. package/dist/contract/log.d.mts +41 -0
  57. package/dist/contract/log.mjs +144 -0
  58. package/dist/contract/names.mjs +14 -4
  59. package/dist/contract/occurrence.d.mts +29 -0
  60. package/dist/contract/occurrence.mjs +45 -0
  61. package/dist/contract/pages.d.mts +13 -0
  62. package/dist/contract/pages.mjs +39 -0
  63. package/dist/contract/progress.d.mts +26 -0
  64. package/dist/contract/progress.mjs +72 -0
  65. package/dist/contract/push.d.mts +20 -0
  66. package/dist/contract/push.mjs +96 -0
  67. package/dist/contract/receiving.d.mts +29 -0
  68. package/dist/contract/receiving.mjs +42 -0
  69. package/dist/contract/recent.mjs +12 -0
  70. package/dist/contract/reported.mjs +16 -0
  71. package/dist/contract/serving.mjs +63 -0
  72. package/dist/contract/status.mjs +14 -5
  73. package/dist/contract/stream.d.mts +34 -0
  74. package/dist/contract/stream.mjs +31 -0
  75. package/dist/contract/subscribe.d.mts +45 -0
  76. package/dist/contract/subscribe.mjs +50 -0
  77. package/dist/contract/transient.mjs +28 -0
  78. package/dist/contract/watch.d.mts +18 -0
  79. package/dist/contract/watch.mjs +105 -0
  80. package/dist/contract/webhooks.d.mts +42 -0
  81. package/dist/contract/webhooks.mjs +53 -0
  82. package/dist/http/client.d.mts +37 -12
  83. package/dist/http/client.mjs +51 -12
  84. package/dist/http/events.d.mts +56 -0
  85. package/dist/http/events.mjs +102 -0
  86. package/dist/http/placed.d.mts +36 -0
  87. package/dist/http/placed.mjs +71 -0
  88. package/dist/http/schema-errors.mjs +1 -1
  89. package/dist/http/streamed.d.mts +21 -0
  90. package/dist/http/streamed.mjs +94 -0
  91. package/dist/http/watch.mjs +50 -0
  92. package/dist/mcp/bridge.mjs +283 -0
  93. package/dist/mcp/client.d.mts +30 -97
  94. package/dist/mcp/client.mjs +59 -132
  95. package/dist/mcp/events.mjs +142 -0
  96. package/dist/mcp/progress.mjs +34 -0
  97. package/dist/mcp/protocol.mjs +39 -13
  98. package/dist/mcp/reply.d.mts +79 -0
  99. package/dist/mcp/reply.mjs +99 -0
  100. package/dist/mcp/request.d.mts +40 -0
  101. package/dist/mcp/request.mjs +51 -0
  102. package/dist/mcp/watch.mjs +115 -0
  103. package/dist/rpc/watch.mjs +39 -0
  104. package/dist/stdio/console.mjs +13 -5
  105. package/dist/testing/memory.d.mts +1 -1
  106. package/dist/testing/memory.mjs +3 -3
  107. package/dist/webhook/address.mjs +47 -0
  108. package/dist/webhook/attempt.mjs +65 -0
  109. package/dist/webhook/receiver.mjs +67 -0
  110. package/dist/webhook/signing.mjs +28 -0
  111. package/dist/webhook/stored.mjs +57 -0
  112. package/dist/webhook/subscriptions.mjs +342 -0
  113. package/docs/Action-failures.md +2 -2
  114. package/docs/Action-streams.md +115 -0
  115. package/docs/Action.md +58 -124
  116. package/docs/ActionCli-failures.md +1 -2
  117. package/docs/ActionCli.md +83 -88
  118. package/docs/ActionHttp-failures.md +3 -3
  119. package/docs/ActionHttp.md +90 -70
  120. package/docs/ActionMcp-failures.md +2 -1
  121. package/docs/ActionMcp.md +120 -92
  122. package/docs/ActionRpc-events.md +126 -0
  123. package/docs/ActionRpc-failures.md +1 -2
  124. package/docs/ActionRpc.md +108 -79
  125. package/docs/ActionToolkit-failures.md +1 -2
  126. package/docs/ActionToolkit.md +33 -90
  127. package/docs/Authentication-advanced.md +52 -60
  128. package/docs/Authentication-failures.md +5 -4
  129. package/docs/Authentication.md +23 -58
  130. package/docs/Event-failures.md +15 -0
  131. package/docs/Event.md +281 -0
  132. package/docs/README.md +51 -82
  133. package/docs/Testing-failures.md +1 -5
  134. package/docs/Testing.md +126 -86
  135. package/docs/Webhook-failures.md +8 -0
  136. package/docs/Webhook.md +319 -0
  137. package/docs/setup-failures.md +2 -3
  138. package/docs/setup.md +32 -52
  139. package/examples/README.md +12 -37
  140. package/examples/assistant/README.md +21 -26
  141. package/examples/assistant/src/agent.ts +66 -24
  142. package/examples/assistant/src/cli.ts +7 -4
  143. package/examples/assistant/src/commands.ts +9 -4
  144. package/examples/assistant/src/identity/Identity.ts +5 -4
  145. package/examples/assistant/src/identity/authorize.ts +13 -5
  146. package/examples/assistant/src/identity/handlers.ts +3 -2
  147. package/examples/assistant/src/identity/permissions.ts +9 -3
  148. package/examples/assistant/src/platform/tasks-file.ts +15 -11
  149. package/examples/assistant/src/scripted.ts +7 -4
  150. package/examples/assistant/src/tasks/Tasks.ts +20 -6
  151. package/examples/assistant/src/tasks/handlers.ts +9 -5
  152. package/examples/assistant/src/tasks/store.ts +38 -11
  153. package/examples/assistant/src/toolkit.ts +11 -7
  154. package/examples/assistant/test/commands.test.ts +53 -27
  155. package/examples/assistant/test/identity/identity.test.ts +27 -11
  156. package/examples/assistant/test/platform/tasks-file.test.ts +39 -23
  157. package/examples/assistant/test/tasks/tasks.test.ts +62 -34
  158. package/examples/assistant/test/toolkit.test.ts +104 -75
  159. package/examples/build-events/README.md +83 -0
  160. package/examples/build-events/src/Api.ts +27 -0
  161. package/examples/build-events/src/Rpc.ts +26 -0
  162. package/examples/build-events/src/builds/Builds.ts +108 -0
  163. package/examples/build-events/src/builds/events.ts +54 -0
  164. package/examples/build-events/src/builds/handlers.ts +58 -0
  165. package/examples/build-events/src/builds/log.ts +30 -0
  166. package/examples/build-events/src/builds/status.ts +28 -0
  167. package/examples/build-events/src/builds/store.ts +130 -0
  168. package/examples/build-events/src/cli.ts +18 -0
  169. package/examples/build-events/src/commands.ts +32 -0
  170. package/examples/build-events/src/deploys/Deploys.ts +44 -0
  171. package/examples/build-events/src/deploys/approvers.ts +55 -0
  172. package/examples/build-events/src/deploys/events.ts +32 -0
  173. package/examples/build-events/src/deploys/handlers.ts +38 -0
  174. package/examples/build-events/src/identity/Identity.ts +29 -0
  175. package/examples/build-events/src/identity/members.ts +7 -0
  176. package/examples/build-events/src/identity/verifier.ts +22 -0
  177. package/examples/build-events/src/notifier/Notices.ts +26 -0
  178. package/examples/build-events/src/notifier/routes.ts +71 -0
  179. package/examples/build-events/src/notifier.ts +48 -0
  180. package/examples/build-events/src/routes.ts +71 -0
  181. package/examples/build-events/src/server.ts +62 -0
  182. package/examples/build-events/test/builds/events.test.ts +68 -0
  183. package/examples/build-events/test/builds/log.test.ts +71 -0
  184. package/examples/build-events/test/builds/progress.test.ts +57 -0
  185. package/examples/build-events/test/builds/status.test.ts +42 -0
  186. package/examples/build-events/test/commands.test.ts +103 -0
  187. package/examples/build-events/test/deploys/events.test.ts +52 -0
  188. package/examples/build-events/test/http.test.ts +95 -0
  189. package/examples/build-events/test/notifier.test.ts +80 -0
  190. package/examples/build-events/test/routes.test.ts +96 -0
  191. package/examples/build-events/test/rpc.test.ts +84 -0
  192. package/examples/build-events/test/support/as.ts +8 -0
  193. package/examples/build-events/test/support/memory.ts +43 -0
  194. package/examples/build-events/test/webhooks.test.ts +143 -0
  195. package/examples/kitchen-sink/README.md +178 -166
  196. package/examples/kitchen-sink/src/Actions.ts +11 -3
  197. package/examples/kitchen-sink/src/Api.ts +12 -6
  198. package/examples/kitchen-sink/src/Rpc.ts +7 -5
  199. package/examples/kitchen-sink/src/admin/Admin.ts +21 -10
  200. package/examples/kitchen-sink/src/admin/authenticate.ts +8 -4
  201. package/examples/kitchen-sink/src/admin/authorize.ts +13 -5
  202. package/examples/kitchen-sink/src/admin/grants.ts +5 -3
  203. package/examples/kitchen-sink/src/admin/handlers.ts +9 -5
  204. package/examples/kitchen-sink/src/agent.ts +14 -6
  205. package/examples/kitchen-sink/src/assistant/Assistant.ts +21 -15
  206. package/examples/kitchen-sink/src/assistant/conversations.ts +14 -9
  207. package/examples/kitchen-sink/src/assistant/demo-model.ts +2 -2
  208. package/examples/kitchen-sink/src/assistant/handlers.ts +42 -22
  209. package/examples/kitchen-sink/src/attachments/Attachments.ts +6 -6
  210. package/examples/kitchen-sink/src/attachments/download.ts +7 -6
  211. package/examples/kitchen-sink/src/attachments/handlers.ts +9 -4
  212. package/examples/kitchen-sink/src/attachments/store.ts +25 -18
  213. package/examples/kitchen-sink/src/browser.ts +10 -4
  214. package/examples/kitchen-sink/src/cli.ts +28 -12
  215. package/examples/kitchen-sink/src/config.ts +1 -1
  216. package/examples/kitchen-sink/src/identity/Identity.ts +24 -10
  217. package/examples/kitchen-sink/src/identity/authenticate.ts +19 -11
  218. package/examples/kitchen-sink/src/identity/authorize.ts +14 -4
  219. package/examples/kitchen-sink/src/identity/directory.ts +16 -7
  220. package/examples/kitchen-sink/src/identity/handlers.ts +1 -0
  221. package/examples/kitchen-sink/src/identity/members.ts +7 -2
  222. package/examples/kitchen-sink/src/identity/quota.ts +23 -20
  223. package/examples/kitchen-sink/src/issues/Issues.ts +91 -15
  224. package/examples/kitchen-sink/src/issues/assistant.ts +4 -2
  225. package/examples/kitchen-sink/src/issues/events.ts +46 -0
  226. package/examples/kitchen-sink/src/issues/export.ts +35 -0
  227. package/examples/kitchen-sink/src/issues/guide.ts +4 -3
  228. package/examples/kitchen-sink/src/issues/handlers.ts +20 -7
  229. package/examples/kitchen-sink/src/issues/store.ts +156 -67
  230. package/examples/kitchen-sink/src/issues/write-limit.ts +44 -18
  231. package/examples/kitchen-sink/src/memory.ts +11 -2
  232. package/examples/kitchen-sink/src/ops.ts +10 -5
  233. package/examples/kitchen-sink/src/request-policy.ts +21 -9
  234. package/examples/kitchen-sink/src/routes.ts +67 -27
  235. package/examples/kitchen-sink/src/sdk.ts +22 -12
  236. package/examples/kitchen-sink/src/seed.ts +15 -9
  237. package/examples/kitchen-sink/src/server.ts +24 -10
  238. package/examples/kitchen-sink/src/stdio.ts +9 -3
  239. package/examples/kitchen-sink/src/workspaces/Workspaces.ts +10 -7
  240. package/examples/kitchen-sink/src/workspaces/handlers.ts +3 -2
  241. package/examples/kitchen-sink/src/workspaces/registry.ts +16 -10
  242. package/examples/kitchen-sink/src/workspaces/tenant.ts +12 -5
  243. package/examples/kitchen-sink/test/admin/admin.test.ts +38 -23
  244. package/examples/kitchen-sink/test/assistant/assistant.test.ts +471 -299
  245. package/examples/kitchen-sink/test/attachments/attachments.test.ts +135 -94
  246. package/examples/kitchen-sink/test/identity/identity.test.ts +110 -82
  247. package/examples/kitchen-sink/test/identity/session.test.ts +65 -43
  248. package/examples/kitchen-sink/test/issues/assistant.test.ts +50 -31
  249. package/examples/kitchen-sink/test/issues/events.test.ts +273 -0
  250. package/examples/kitchen-sink/test/issues/export.test.ts +120 -0
  251. package/examples/kitchen-sink/test/issues/issues.test.ts +105 -75
  252. package/examples/kitchen-sink/test/request-policy.test.ts +108 -47
  253. package/examples/kitchen-sink/test/routes.test.ts +102 -57
  254. package/examples/kitchen-sink/test/support/as.ts +9 -0
  255. package/examples/kitchen-sink/test/support/scripted.ts +6 -5
  256. package/examples/kitchen-sink/test/support/services.ts +17 -0
  257. package/examples/live-rpc/README.md +33 -35
  258. package/examples/live-rpc/src/Rpc.ts +9 -5
  259. package/examples/live-rpc/src/browser.ts +39 -11
  260. package/examples/live-rpc/src/chat/Chat.ts +30 -16
  261. package/examples/live-rpc/src/chat/authorize.ts +2 -1
  262. package/examples/live-rpc/src/chat/events.ts +74 -0
  263. package/examples/live-rpc/src/chat/handlers.ts +13 -5
  264. package/examples/live-rpc/src/chat/room.ts +9 -5
  265. package/examples/live-rpc/src/chat/store.ts +51 -14
  266. package/examples/live-rpc/src/client.ts +22 -5
  267. package/examples/live-rpc/src/identity/Identity.ts +3 -3
  268. package/examples/live-rpc/src/identity/verifier.ts +8 -4
  269. package/examples/live-rpc/src/page.ts +58 -12
  270. package/examples/live-rpc/src/routes.ts +14 -10
  271. package/examples/live-rpc/src/server.ts +15 -5
  272. package/examples/live-rpc/test/browser.test.ts +127 -50
  273. package/examples/live-rpc/test/chat/chat.test.ts +215 -23
  274. package/examples/live-rpc/test/routes.test.ts +130 -46
  275. package/examples/live-rpc/test/server.test.ts +94 -33
  276. package/examples/mcp-tools/README.md +24 -38
  277. package/examples/mcp-tools/src/platform/fake-farm.ts +63 -24
  278. package/examples/mcp-tools/src/platform/print-farm.ts +59 -29
  279. package/examples/mcp-tools/src/printers/Printers.ts +18 -7
  280. package/examples/mcp-tools/src/printers/farm.ts +15 -5
  281. package/examples/mcp-tools/src/printers/handlers.ts +12 -6
  282. package/examples/mcp-tools/src/routes.ts +8 -6
  283. package/examples/mcp-tools/src/server.ts +12 -5
  284. package/examples/mcp-tools/src/stdio.ts +3 -2
  285. package/examples/mcp-tools/test/printers/printers.test.ts +132 -89
  286. package/examples/mcp-tools/test/routes.test.ts +140 -81
  287. package/examples/notes-service/README.md +30 -45
  288. package/examples/notes-service/src/Api.ts +1 -0
  289. package/examples/notes-service/src/browser.ts +15 -14
  290. package/examples/notes-service/src/cli.ts +8 -3
  291. package/examples/notes-service/src/commands.ts +46 -20
  292. package/examples/notes-service/src/identity/Identity.ts +2 -2
  293. package/examples/notes-service/src/identity/authorize.ts +6 -2
  294. package/examples/notes-service/src/identity/handlers.ts +3 -2
  295. package/examples/notes-service/src/identity/users.ts +2 -1
  296. package/examples/notes-service/src/identity/verifier.ts +12 -6
  297. package/examples/notes-service/src/notes/Notes.ts +46 -22
  298. package/examples/notes-service/src/notes/handlers.ts +18 -9
  299. package/examples/notes-service/src/notes/store.ts +70 -43
  300. package/examples/notes-service/src/routes.ts +7 -5
  301. package/examples/notes-service/src/server.ts +12 -5
  302. package/examples/notes-service/src/status/Status.ts +1 -1
  303. package/examples/notes-service/src/status/handlers.ts +5 -3
  304. package/examples/notes-service/src/stdio.ts +7 -3
  305. package/examples/notes-service/src/worker.ts +6 -3
  306. package/examples/notes-service/test/commands.test.ts +164 -91
  307. package/examples/notes-service/test/notes/as.ts +3 -1
  308. package/examples/notes-service/test/notes/idempotency.test.ts +97 -67
  309. package/examples/notes-service/test/notes/in-process.test.ts +26 -15
  310. package/examples/notes-service/test/notes/notes.test.ts +49 -31
  311. package/examples/notes-service/test/routes.test.ts +106 -71
  312. package/examples/notes-service/test/worker.test.ts +21 -13
  313. package/examples/quickstart/README.md +2 -6
  314. package/examples/quickstart/src/Api.ts +1 -0
  315. package/examples/quickstart/src/client.ts +6 -3
  316. package/examples/quickstart/src/greeting/Greeting.ts +1 -1
  317. package/examples/quickstart/src/greeting/handlers.ts +5 -2
  318. package/examples/quickstart/src/routes.ts +3 -2
  319. package/examples/quickstart/src/server.ts +6 -2
  320. package/examples/quickstart/test/routes.test.ts +3 -2
  321. package/package.json +10 -4
@@ -0,0 +1,283 @@
1
+ import { Anyone, principalOf } from "../contract/caller.mjs";
2
+ import { inputParseOptions } from "../contract/call.mjs";
3
+ import { reported } from "../contract/reported.mjs";
4
+ import { encodeUntaggedOf } from "../contract/occurrence.mjs";
5
+ import { acquireFeeds, unkeyedHook } from "../contract/feed.mjs";
6
+ import { NotServed, SubscriptionError, Unsupported } from "../Event.mjs";
7
+ import { sentError } from "../contract/codes.mjs";
8
+ import { Webhooks, offersWebhook, servedWebhooks, webhookSubscriber } from "../contract/webhooks.mjs";
9
+ import { pushed, refusalIn } from "../contract/push.mjs";
10
+ import { promote } from "../authentication/provider.mjs";
11
+ import { recordStepUp } from "../authentication/refusal.mjs";
12
+ import { Active, EventsError, Failed, Heartbeat, Notifications, Occurred, Requests, Terminated, codes, extension, subscriptionIdKey } from "./events.mjs";
13
+ import { isObjectSchema, routingFormsOf, toolJsonSchema } from "./protocol.mjs";
14
+ import { Cause, Context, Duration, Effect, Exit, Match, Option, Predicate, Result, Schema } from "effect";
15
+ import { HttpEffect, HttpServerRequest, HttpServerResponse } from "effect/http";
16
+ import { McpSchema } from "effect/ai";
17
+ // src/mcp/bridge.ts
18
+ const isGroup = (u) => Predicate.isObjectKeyword(u) && "merge" in u && Predicate.isFunction(u.merge) && "requests" in u && u.requests instanceof Map;
19
+ const groupOf = (adapter, key) => {
20
+ const group = Predicate.hasProperty(adapter, key) ? adapter[key] : void 0;
21
+ if (!isGroup(group)) throw new Error(`Effect's MCP ${adapter.protocolVersion} adapter holds no ${key} group: effect-actions supports MCP events on effect 4.0.x (install the effect version its peer range names)`);
22
+ return group;
23
+ };
24
+ /**
25
+ * The JSON Schemas `events/list` lists for `event`, refusing an input or a payload that is no
26
+ * object.
27
+ */
28
+ const eventSchemasOf = (event) => {
29
+ const inputSchema = toolJsonSchema(Schema.toCodecJson(event.input), true);
30
+ if (!isObjectSchema(inputSchema)) throw new Error(`MCP event input must be one object with keys, such as a struct: ${event.name} (wrap a union, an array or a scalar in a field, or omit input for no arguments)`);
31
+ const payloadSchema = toolJsonSchema(Schema.toCodecJson(event.payload), false);
32
+ if (!isObjectSchema(payloadSchema)) throw new Error(`MCP event payload must be one object, such as a struct: ${event.name} (wrap an array or a scalar in a field)`);
33
+ return {
34
+ inputSchema,
35
+ payloadSchema
36
+ };
37
+ };
38
+ const encodeServerInfo = Schema.encodeEffect(McpSchema.Implementation);
39
+ const internalError = new EventsError({
40
+ code: codes.internal,
41
+ message: "Internal error"
42
+ });
43
+ const isSubscriptionError = Schema.is(SubscriptionError);
44
+ const wireErrorOf = (cause) => {
45
+ const refusal = refusalIn(cause);
46
+ if (refusal !== void 0) return Effect.succeed(new EventsError(sentError(refusal)));
47
+ const failure = Cause.hasDies(cause) ? void 0 : Result.getOrUndefined(Cause.findFail(cause));
48
+ return isSubscriptionError(failure?.error) ? Effect.succeed(new EventsError(sentError(failure.error))) : Effect.as(reported(cause), internalError);
49
+ };
50
+ const headerMismatch = (request) => Effect.andThen(HttpEffect.appendPreResponseHandler((_request, response) => Effect.succeed(HttpServerResponse.setStatus(response, 400))).pipe(Effect.provideService(HttpServerRequest.HttpServerRequest, request)), Effect.fail(new EventsError({
51
+ code: codes.headerMismatch,
52
+ message: "Mcp-Name header does not match request parameters"
53
+ })));
54
+ const routedTo = (name) => Effect.flatMap(Effect.serviceOption(HttpServerRequest.HttpServerRequest), (request) => Option.isNone(request) || routingFormsOf(name).includes(request.value.headers["mcp-name"] ?? "") ? Effect.void : headerMismatch(request.value));
55
+ const servedOf = (feed, delivery) => {
56
+ const { event } = feed;
57
+ const decode = Schema.decodeUnknownEffect(Schema.toCodecJson(event.input), inputParseOptions);
58
+ return {
59
+ feed,
60
+ decode: (args) => Effect.mapError(decode(args ?? {}), ({ message }) => new EventsError({
61
+ code: codes.invalidParams,
62
+ message: `Invalid arguments for event '${event.name}': ${message}`
63
+ })),
64
+ wire: encodeUntaggedOf(event),
65
+ listing: {
66
+ name: event.name,
67
+ description: event.description,
68
+ delivery,
69
+ ...eventSchemasOf(event)
70
+ }
71
+ };
72
+ };
73
+ /**
74
+ * Dies with the reason the endpoint of `serving` cannot serve one of its events, run where the
75
+ * endpoint's layer is built: a protected one with a `subscription` hook, under an authentication
76
+ * without a principal to key its polls by.
77
+ */
78
+ const refuseUnservable = ({ events, authentication }) => authentication === void 0 ? Effect.void : Effect.flatMap(acquireFeeds(events), (feeds) => {
79
+ const refusal = unkeyedHook("MCP", feeds, authentication.principal !== void 0);
80
+ return refusal === void 0 ? Effect.void : Effect.die(new Error(refusal));
81
+ });
82
+ /**
83
+ * Serves the feeds the endpoint of `serving` offers by webhook to `Webhooks`, where it is
84
+ * provided, run where the endpoint's layer is built: it restores their kept subscriptions, and
85
+ * dies where they cannot be kept.
86
+ */
87
+ const serveWebhooks = (serving) => Effect.asVoid(Effect.flatMap(acquireFeeds(serving.events), (feeds) => servedWebhooks(feeds, serving.authentication)));
88
+ /**
89
+ * One push subscription, as `events/stream` holds it: what the feed's `pushed` sends, as
90
+ * SEP-3415's notifications. Its confirmation is `active`, a later gap a fresh `active`, a
91
+ * position a heartbeat, and a failure it reads on through `error`. A failure before `active` is
92
+ * the request's error; a refusal after it ends the subscription with `terminated`.
93
+ */
94
+ const delivered = ({ send, clientId, requestId, wire, _meta: resultMeta }, run) => {
95
+ const notify = (rpc, payload) => send("2026-07-28", clientId, {
96
+ tag: rpc._tag,
97
+ payload
98
+ });
99
+ return Effect.gen(function* () {
100
+ const _meta = { [subscriptionIdKey]: requestId };
101
+ let active = false;
102
+ const activate = (cursor, truncated) => {
103
+ active = true;
104
+ return notify(Active, {
105
+ _meta,
106
+ cursor,
107
+ truncated
108
+ });
109
+ };
110
+ const deliver = (sent) => Match.valueTags(sent, {
111
+ Gap: ({ cursor }) => activate(cursor, true),
112
+ Checked: ({ cursor }) => active ? notify(Heartbeat, {
113
+ _meta,
114
+ cursor
115
+ }) : activate(cursor, false),
116
+ Occurrence: (occurrence) => Effect.flatMap(wire(occurrence), (event) => notify(Occurred, {
117
+ ...event,
118
+ _meta
119
+ })),
120
+ Failed: () => notify(Failed, {
121
+ _meta,
122
+ error: {
123
+ code: internalError.code,
124
+ message: internalError.message
125
+ }
126
+ })
127
+ });
128
+ const exit = yield* Effect.exit(run(deliver));
129
+ if (Exit.isFailure(exit)) {
130
+ if (Cause.hasInterruptsOnly(exit.cause)) return yield* Effect.interrupt;
131
+ const error = yield* wireErrorOf(exit.cause);
132
+ if (!active) return yield* error;
133
+ const { code, message, data } = error;
134
+ yield* notify(Terminated, {
135
+ _meta,
136
+ error: {
137
+ code,
138
+ message,
139
+ ...data === void 0 ? {} : { data }
140
+ }
141
+ });
142
+ }
143
+ return {
144
+ resultType: "complete",
145
+ _meta: {
146
+ ...resultMeta,
147
+ ..._meta
148
+ }
149
+ };
150
+ });
151
+ };
152
+ /**
153
+ * `base`, the 2026-07-28 adapter, also serving SEP-3415's `events/list`, `events/poll` and
154
+ * `events/stream` for the event implementations of `serving`, and advertising the extension in
155
+ * its discovery.
156
+ */
157
+ const withEvents = (base, serving) => {
158
+ const { authentication } = serving;
159
+ const asSubscriber = (event, run) => authentication === void 0 || event.caller === Anyone ? run : recordStepUp(promote(authentication, run));
160
+ const installHandlers = (core, lifecycle, target) => Effect.gen(function* () {
161
+ const feeds = yield* acquireFeeds(serving.events);
162
+ const built = yield* Effect.context();
163
+ const send = target.context.sendNotification;
164
+ const meta = Effect.map(Effect.orDie(encodeServerInfo(target.context.serverInfo)), (serverInfo) => ({ "io.modelcontextprotocol/serverInfo": serverInfo }));
165
+ const webhooks = Context.getOption(built, Webhooks);
166
+ const webhookOffered = (event) => Option.isSome(webhooks) && offersWebhook(event, authentication);
167
+ const deliveryOf = (event) => [
168
+ "poll",
169
+ ...send === void 0 ? [] : ["push"],
170
+ ...webhookOffered(event) ? ["webhook"] : []
171
+ ];
172
+ const served = new Map(feeds.map((feed) => [feed.event.name, servedOf(feed, deliveryOf(feed.event))]));
173
+ const servedNamed = (name) => {
174
+ const found = served.get(name);
175
+ return found === void 0 ? Effect.fail(new EventsError(sentError(SubscriptionError.of(name, new NotServed())))) : Effect.succeed(found);
176
+ };
177
+ const asWebhook = (feed, run) => Effect.catchCause(asSubscriber(feed.event, Effect.flatMap(webhookSubscriber(feed, authentication, webhooks), run)), (cause) => Effect.flatMap(wireErrorOf(cause), Effect.fail));
178
+ const fromBuilt = (handled) => Effect.updateContext(handled, (request) => Context.merge(built, request));
179
+ const handlers = Requests.of({
180
+ "events/list": () => Effect.map(meta, (_meta) => ({
181
+ resultType: "complete",
182
+ events: [...served.values()].map(({ listing }) => listing),
183
+ _meta
184
+ })),
185
+ "events/poll": ({ name, arguments: args, cursor, maxEvents }) => Effect.gen(function* () {
186
+ yield* routedTo(name);
187
+ const { feed, decode, wire } = yield* servedNamed(name);
188
+ const input = yield* decode(args);
189
+ const { page, events } = yield* Effect.gen(function* () {
190
+ const polled = yield* asSubscriber(feed.event, Effect.flatMap(principalOf(feed.event, authentication), (subscriber) => feed.poll(input, cursor ?? null, {
191
+ subscriber,
192
+ maxEvents
193
+ })));
194
+ return {
195
+ page: polled,
196
+ events: yield* Effect.forEach(polled.events, wire)
197
+ };
198
+ }).pipe(Effect.catchCause((cause) => Effect.flatMap(wireErrorOf(cause), Effect.fail)));
199
+ return {
200
+ resultType: "complete",
201
+ events,
202
+ cursor: page.cursor,
203
+ truncated: page.truncated,
204
+ hasMore: page.hasMore,
205
+ nextPollMs: Duration.toMillis(page.nextPoll),
206
+ _meta: yield* meta
207
+ };
208
+ }).pipe(fromBuilt),
209
+ "events/stream": ({ name, arguments: args, cursor }, { client, requestId }) => Effect.gen(function* () {
210
+ yield* routedTo(name);
211
+ const { feed, decode, wire } = yield* servedNamed(name);
212
+ const input = yield* decode(args);
213
+ if (send === void 0) return yield* new EventsError(sentError(SubscriptionError.of(name, new Unsupported({
214
+ feature: "deliveryMode",
215
+ value: "push"
216
+ }))));
217
+ return yield* delivered({
218
+ send,
219
+ clientId: client.id,
220
+ requestId,
221
+ wire,
222
+ _meta: yield* meta
223
+ }, (deliver) => asSubscriber(feed.event, Effect.flatMap(feed.stream(input), (open) => pushed(open, cursor ?? null, deliver))));
224
+ }).pipe(fromBuilt),
225
+ "events/subscribe": ({ name, arguments: args, delivery: { url, secret }, cursor, ttlMs }) => Effect.gen(function* () {
226
+ yield* routedTo(name);
227
+ const { feed, decode } = yield* servedNamed(name);
228
+ const input = yield* decode(args);
229
+ return {
230
+ resultType: "complete",
231
+ ...yield* asWebhook(feed, ({ webhooks: service, ...subscriber }) => service.subscribe({
232
+ ...subscriber,
233
+ input,
234
+ delivery: {
235
+ url,
236
+ secret
237
+ },
238
+ cursor: cursor ?? null,
239
+ ttlMs
240
+ })),
241
+ _meta: yield* meta
242
+ };
243
+ }).pipe(fromBuilt),
244
+ "events/unsubscribe": ({ name, arguments: args, delivery }) => Effect.gen(function* () {
245
+ yield* routedTo(name);
246
+ const { feed, decode } = yield* servedNamed(name);
247
+ const input = yield* decode(args);
248
+ yield* asWebhook(feed, ({ webhooks: service, feed: subscribed, principal }) => service.unsubscribe({
249
+ feed: subscribed,
250
+ principal,
251
+ input,
252
+ delivery
253
+ }));
254
+ return {
255
+ resultType: "complete",
256
+ _meta: yield* meta
257
+ };
258
+ }).pipe(fromBuilt)
259
+ });
260
+ yield* Effect.asVoid(base.installHandlers(core, lifecycle, {
261
+ ...target,
262
+ context: {
263
+ ...target.context,
264
+ serverInfo: {
265
+ ...target.context.serverInfo,
266
+ extensions: {
267
+ ...target.context.serverInfo.extensions,
268
+ [extension]: { listChanged: false }
269
+ }
270
+ }
271
+ }
272
+ }));
273
+ yield* target.install(base, Requests, handlers);
274
+ });
275
+ return {
276
+ ...base,
277
+ clientRpcs: groupOf(base, "clientRpcs").merge(Requests),
278
+ handlerRpcs: groupOf(base, "handlerRpcs").merge(Requests),
279
+ serverNotificationRpcs: groupOf(base, "serverNotificationRpcs").merge(Notifications),
280
+ installHandlers
281
+ };
282
+ };
283
+ export { eventSchemasOf, refuseUnservable, serveWebhooks, withEvents };
@@ -1,25 +1,18 @@
1
1
  import { BuiltIn } from "../contract/errors.mjs";
2
+ import { Any, Received, SubscriptionError, WatchOptions } from "../Event.mjs";
2
3
  import { Call } from "../contract/call.mjs";
3
- import { Any } from "../Action.mjs";
4
- import { Any as Any$1, VerifierError } from "../authentication/provider.mjs";
5
- import { Effect, Schema } from "effect";
6
- import { Headers, HttpClient, HttpClientError, HttpClientRequest } from "effect/http";
7
- /**
8
- * An MCP request's parameters, as `request` takes them: JSON, with any `_meta`, leaving
9
- * out an `undefined` one, as a caller may. A helper passing on whole params types them as this,
10
- * and a tool's `arguments` as `Schema.Json`, never `unknown`.
11
- */
12
- export interface Params {
13
- readonly _meta?: {
14
- readonly [key: string]: Schema.Json;
15
- };
16
- readonly [key: string]: Schema.Json | undefined;
17
- }
4
+ import { Answer } from "../contract/stream.mjs";
5
+ import { WatchArgs, WatchRequirement } from "../contract/watch.mjs";
6
+ import { Any as Any$1 } from "../Action.mjs";
7
+ import { Any as Any$2, VerifierError } from "../authentication/provider.mjs";
8
+ import { McpCallError } from "./reply.mjs";
9
+ import { Effect, Schema, Stream } from "effect";
10
+ import { HttpClient, HttpClientError } from "effect/http";
18
11
  /**
19
12
  * Where `client` sends, through what client, and what the endpoint's authentication declares: the
20
13
  * client `transformClient` returns may fail with `E` and require `R`.
21
14
  */
22
- export interface ClientOptions<D extends Any$1 | undefined = Any$1 | undefined, E = HttpClientError.HttpClientError, R = never> {
15
+ export interface ClientOptions<D extends Any$2 | undefined = Any$2 | undefined, E = HttpClientError.HttpClientError, R = never> {
23
16
  /**
24
17
  * The endpoint, resolved by the `HttpClient`: relative under `Testing.layer`. Defaults to `/mcp`,
25
18
  * the default `layerHttp` path, so a call to an endpoint on another `path` fails with an
@@ -39,72 +32,19 @@ export interface ClientOptions<D extends Any$1 | undefined = Any$1 | undefined,
39
32
  */
40
33
  readonly authentication?: D;
41
34
  }
42
- /** Where a `request` goes, and with what headers. */
43
- export interface RequestOptions {
44
- /**
45
- * The endpoint, resolved by the `HttpClient` that sends the request: relative under `Testing.layer`.
46
- * Defaults to `/mcp`, as `client`'s does; converting to a web `Request` needs an absolute
47
- * one.
48
- */
49
- readonly url?: string;
50
- /**
51
- * Headers sent beside the routing ones `request` fills in, such as `Authorization`; a routing
52
- * header wins over a given one of its name.
53
- */
54
- readonly headers?: Headers.Input;
55
- }
56
- declare const StatusError_base: Schema.Class<StatusError, Schema.TaggedStruct<"StatusError", {
57
- readonly status: Schema.Int;
58
- readonly body: Schema.String;
59
- }>, import("effect/Cause").YieldableError>;
60
- /** The endpoint answered with an HTTP status other than 200, and a body no declared error decodes. */
61
- declare class StatusError extends StatusError_base {}
62
- declare const ProtocolError_base: Schema.Class<ProtocolError, Schema.TaggedStruct<"ProtocolError", {
63
- readonly code: Schema.Finite;
64
- readonly message: Schema.String;
65
- }>, import("effect/Cause").YieldableError>;
35
+ type CallError<A extends Any$1, D> = A["error"][number]["Type"] | VerifierError<A, D>["Type"] | BuiltIn | Schema.SchemaError | HttpClientError.HttpClientError | McpCallError;
36
+ /** What a watch of `W` may fail with, under the endpoint's descriptor `D`. */
37
+ type WatchError<W extends Any, D> = SubscriptionError | BuiltIn | VerifierError<W, D>["Type"] | Schema.SchemaError | HttpClientError.HttpClientError | McpCallError;
66
38
  /**
67
- * The endpoint answered with a JSON-RPC error, such as the native server's for an unknown tool.
39
+ * Every action of `Contracts` as `client.<action>(input)`, calling its tool; one answering a
40
+ * stream returns a `Stream` of the elements its tool collected. Where it lists event types,
41
+ * `watch(event, input, options)` subscribes to one. Each call and watch also fails with `E` and
42
+ * requires `R`, what its `transformClient` adds.
68
43
  */
69
- declare class ProtocolError extends ProtocolError_base {}
70
- declare const NoReply_base: Schema.Class<NoReply, Schema.TaggedStruct<"NoReply", {
71
- readonly body: Schema.String;
72
- }>, import("effect/Cause").YieldableError>;
73
- /** The endpoint answered 200 without a JSON-RPC reply to read, as JSON or as an event stream. */
74
- declare class NoReply extends NoReply_base {}
75
- declare const UndeclaredError_base: Schema.Class<UndeclaredError, Schema.TaggedStruct<"UndeclaredError", {
76
- readonly text: Schema.String;
77
- }>, import("effect/Cause").YieldableError>;
78
- /** The tool returned an error whose text decodes as none of the action's declared errors. */
79
- declare class UndeclaredError extends UndeclaredError_base {}
80
- declare const NoStructuredContent_base: Schema.Class<NoStructuredContent, Schema.TaggedStruct<"NoStructuredContent", {
81
- readonly body: Schema.String;
82
- }>, import("effect/Cause").YieldableError>;
83
- /** The tool returned a result without the structured content its action's success needs. */
84
- declare class NoStructuredContent extends NoStructuredContent_base {}
85
- declare const McpCallError_base: Schema.Class<McpCallError, Schema.TaggedStruct<"McpCallError", {
86
- readonly tool: Schema.String;
87
- readonly reason: Schema.Union<readonly [typeof StatusError, typeof ProtocolError, typeof NoReply, typeof UndeclaredError, typeof NoStructuredContent]>;
88
- readonly message: Schema.String;
89
- }>, import("effect/Cause").YieldableError>;
90
- /**
91
- * An answer a client method cannot read as the action's success or a declared error: `reason`
92
- * says which, with what the endpoint sent, so a caller branches on it with
93
- * `Effect.catchReason("McpCallError", "StatusError", ...)`. `message` names the tool and the
94
- * reason. Structured content that does not decode as the success is a `SchemaError` instead,
95
- * and a request that fails an `HttpClientError`.
96
- *
97
- * **Gotchas**
98
- *
99
- * - `returned an error: Invalid parameters for tool …`, an `UndeclaredError`, from a call its types allowed: the endpoint serves another contract under that name; give `client` the served contract value.
100
- */
101
- export declare class McpCallError extends McpCallError_base {}
102
- type CallError<A extends Any, D> = A["error"][number]["Type"] | VerifierError<A, D>["Type"] | BuiltIn | Schema.SchemaError | HttpClientError.HttpClientError | McpCallError;
103
- /**
104
- * Every action of `Actions` as `client.<action>(input)`, calling its tool. Each call also fails
105
- * with `E` and requires `R`, what its `transformClient` adds.
106
- */
107
- export type Client<Actions extends ReadonlyArray<Any>, D = undefined, E = never, R = never> = { readonly [A in Actions[number] as A["name"]]: Call<A, Effect.Effect<A["success"]["Type"], CallError<A, D> | E, R>>; };
44
+ export type Client<Contracts extends ReadonlyArray<Any$1 | Any>, D = undefined, E = never, R = never> = { readonly [A in Extract<Contracts[number], Any$1> as A["name"]]: Call<A, Answer<A["success"], CallError<A, D> | E, R>>; } & Watching<Extract<Contracts[number], Any>, D, E, R>;
45
+ type Watching<Listed extends Any, D, E, R> = [Listed] extends [never] ? unknown : {
46
+ readonly watch: <W extends Listed, SE = never, const O extends WatchOptions<unknown> = {}>(event: W, ...args: WatchArgs<W, SE, O>) => Stream.Stream<Received<W>, WatchError<W, D> | E | SE, R | WatchRequirement<O>>;
47
+ };
108
48
  /**
109
49
  * A client of an MCP endpoint served by `layerHttp`, one method per action calling
110
50
  * its tool with one stateless request, as `ActionHttp.client` calls routes: the input is
@@ -113,26 +53,19 @@ export type Client<Actions extends ReadonlyArray<Any>, D = undefined, E = never,
113
53
  * every field is, from its blocks alone. A declared error the tool returns, the action's own
114
54
  * or a refusal, is its decoded value, and so is a refusal the endpoint's authentication answers
115
55
  * with: a test telling a 401 or 403 from a tool result asserts the status of a request `request`
116
- * builds. The argument may be omitted when `{}` is a valid input. Requires the native
117
- * `HttpClient`, such as the one `Testing.layer` provides.
56
+ * builds. The argument may be omitted when `{}` is a valid input. Where `contracts` lists event
57
+ * types, `watch` subscribes to one as `Action.client`'s does, holding `events/stream` open, or
58
+ * polling `events/poll` with `mode: "poll"` or where the endpoint cannot push. Requires the
59
+ * native `HttpClient`, such as the one `Testing.layer` provides.
118
60
  *
119
61
  * **Gotchas**
120
62
  *
121
63
  * - It drops undeclared input fields as it encodes, and fails with `InvalidInput`, sending nothing, for an input that does not encode.
64
+ * - A call of an action answering a stream returns a `Stream` of the elements its tool collected, the first arriving once the whole stream has ended on the server.
65
+ * - A watch of an event the endpoint does not serve fails with `Event.SubscriptionError`, reason `NotServed`, as an endpoint serving events answers its first request; one serving none answers `events/*` with a 404, failing with `McpCallError`'s `StatusError`.
66
+ * - A watch reconnects after the transport fails, a status such as 503, or 60 seconds without an answer or a frame, a second later and doubling to 30 seconds, each reconnect logged as a warning; any other failure ends it.
67
+ * - A call given `onProgress` sends a `progressToken` and reads each `notifications/progress` as its frame arrives; a call without one sends none, so the endpoint sends no progress.
122
68
  */
123
- export declare function client<const Actions extends ReadonlyArray<Any>, D extends Any$1 | undefined = undefined>(actions: Actions, options?: ClientOptions<D>): Effect.Effect<Client<Actions, D>, never, HttpClient.HttpClient>;
124
- export declare function client<const Actions extends ReadonlyArray<Any>, E, R, D extends Any$1 | undefined = undefined>(actions: Actions, options: ClientOptions<D, E, R>): Effect.Effect<Client<Actions, D, E, R>, never, HttpClient.HttpClient>;
125
- /**
126
- * One stateless MCP request of `method` with `params`, as `layerHttp` serves it,
127
- * as the native request value: the JSON-RPC envelope, the 2026-07-28 headers (`Accept`,
128
- * `MCP-Protocol-Version`, `MCP-Method`), `mcp-name` from
129
- * `params.uri` for `resources/read` and `params.name` otherwise, and the client metadata in
130
- * `_meta` are filled in, under any `_meta` given, such as a `progressToken`; the protocol
131
- * version is always the request's own. The test sends it: `HttpClient.execute` answers the
132
- * response as the endpoint sent it, whatever its status, and `HttpClientRequest.toWebResult`
133
- * gives the web `Request` a web handler or `fetch` takes, once `url` is absolute. It is for a test
134
- * asserting on what `client` decodes away, such as `tools/list`, a refusal's challenge, or
135
- * a call its types would not send.
136
- */
137
- export declare const request: (method: string, params?: Params, { headers, url }?: RequestOptions) => HttpClientRequest.HttpClientRequest;
69
+ export declare function client<const Contracts extends ReadonlyArray<Any$1 | Any>, D extends Any$2 | undefined = undefined>(contracts: Contracts, options?: ClientOptions<D>): Effect.Effect<Client<Contracts, D>, never, HttpClient.HttpClient>;
70
+ export declare function client<const Contracts extends ReadonlyArray<Any$1 | Any>, E, R, D extends Any$2 | undefined = undefined>(contracts: Contracts, options: ClientOptions<D, E, R>): Effect.Effect<Client<Contracts, D, E, R>, never, HttpClient.HttpClient>;
138
71
  export {};