@gjermundgaraba/effect-actions 0.19.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 (326) hide show
  1. package/README.md +19 -41
  2. package/dist/Action.d.mts +140 -48
  3. package/dist/Action.mjs +88 -44
  4. package/dist/ActionCli.d.mts +70 -35
  5. package/dist/ActionCli.mjs +53 -34
  6. package/dist/ActionHttp.d.mts +70 -38
  7. package/dist/ActionHttp.mjs +76 -25
  8. package/dist/ActionMcp.d.mts +56 -24
  9. package/dist/ActionMcp.mjs +104 -61
  10. package/dist/ActionRpc.d.mts +222 -57
  11. package/dist/ActionRpc.mjs +301 -49
  12. package/dist/ActionToolkit.d.mts +22 -7
  13. package/dist/ActionToolkit.mjs +37 -14
  14. package/dist/Authentication.d.mts +42 -12
  15. package/dist/Authentication.mjs +38 -32
  16. package/dist/Event.d.mts +370 -0
  17. package/dist/Event.mjs +103 -0
  18. package/dist/Testing.d.mts +1 -3
  19. package/dist/Testing.mjs +2 -3
  20. package/dist/Webhook.d.mts +146 -0
  21. package/dist/Webhook.mjs +72 -0
  22. package/dist/authentication/provider.d.mts +35 -12
  23. package/dist/authentication/provider.mjs +2 -3
  24. package/dist/authentication/refusal.mjs +3 -4
  25. package/dist/cli/commands.d.mts +9 -7
  26. package/dist/cli/commands.mjs +85 -35
  27. package/dist/contract/ast.mjs +5 -3
  28. package/dist/contract/authorizer.d.mts +8 -0
  29. package/dist/contract/authorizer.mjs +20 -0
  30. package/dist/contract/call.d.mts +9 -8
  31. package/dist/contract/call.mjs +49 -8
  32. package/dist/contract/callback.d.mts +7 -0
  33. package/dist/contract/callback.mjs +27 -0
  34. package/dist/contract/caller.d.mts +12 -3
  35. package/dist/contract/caller.mjs +23 -5
  36. package/dist/contract/canonical.mjs +10 -0
  37. package/dist/contract/codec.d.mts +9 -0
  38. package/dist/contract/codec.mjs +9 -0
  39. package/dist/contract/codes.mjs +81 -0
  40. package/dist/contract/declared.d.mts +0 -2
  41. package/dist/contract/declared.mjs +26 -11
  42. package/dist/contract/digest.mjs +6 -0
  43. package/dist/contract/errors.d.mts +9 -5
  44. package/dist/contract/errors.mjs +2 -3
  45. package/dist/contract/feed.d.mts +127 -0
  46. package/dist/contract/feed.mjs +249 -0
  47. package/dist/contract/hints.mjs +1 -2
  48. package/dist/contract/hub.d.mts +94 -0
  49. package/dist/contract/hub.mjs +133 -0
  50. package/dist/contract/idempotency.d.mts +47 -0
  51. package/dist/contract/idempotency.mjs +99 -0
  52. package/dist/contract/implementation.d.mts +73 -51
  53. package/dist/contract/implementation.mjs +94 -60
  54. package/dist/contract/input-check.d.mts +15 -0
  55. package/dist/contract/input-check.mjs +16 -0
  56. package/dist/contract/lease.mjs +28 -0
  57. package/dist/contract/listed.d.mts +4 -0
  58. package/dist/contract/listed.mjs +22 -0
  59. package/dist/contract/log.d.mts +41 -0
  60. package/dist/contract/log.mjs +144 -0
  61. package/dist/contract/media.d.mts +1 -3
  62. package/dist/contract/media.mjs +1 -2
  63. package/dist/contract/names.mjs +15 -6
  64. package/dist/contract/occurrence.d.mts +29 -0
  65. package/dist/contract/occurrence.mjs +45 -0
  66. package/dist/contract/pages.d.mts +13 -0
  67. package/dist/contract/pages.mjs +39 -0
  68. package/dist/contract/progress.d.mts +26 -0
  69. package/dist/contract/progress.mjs +72 -0
  70. package/dist/contract/push.d.mts +20 -0
  71. package/dist/contract/push.mjs +96 -0
  72. package/dist/contract/receiving.d.mts +29 -0
  73. package/dist/contract/receiving.mjs +42 -0
  74. package/dist/contract/recent.mjs +12 -0
  75. package/dist/contract/reported.mjs +16 -0
  76. package/dist/contract/serving.mjs +63 -0
  77. package/dist/contract/status.mjs +15 -7
  78. package/dist/contract/stream.d.mts +34 -0
  79. package/dist/contract/stream.mjs +31 -0
  80. package/dist/contract/subscribe.d.mts +45 -0
  81. package/dist/contract/subscribe.mjs +50 -0
  82. package/dist/contract/transient.mjs +28 -0
  83. package/dist/contract/watch.d.mts +18 -0
  84. package/dist/contract/watch.mjs +105 -0
  85. package/dist/contract/webhooks.d.mts +42 -0
  86. package/dist/contract/webhooks.mjs +53 -0
  87. package/dist/http/client.d.mts +37 -14
  88. package/dist/http/client.mjs +52 -14
  89. package/dist/http/events.d.mts +56 -0
  90. package/dist/http/events.mjs +102 -0
  91. package/dist/http/placed.d.mts +36 -0
  92. package/dist/http/placed.mjs +71 -0
  93. package/dist/http/schema-errors.mjs +2 -3
  94. package/dist/http/streamed.d.mts +21 -0
  95. package/dist/http/streamed.mjs +94 -0
  96. package/dist/http/watch.mjs +50 -0
  97. package/dist/mcp/bridge.mjs +283 -0
  98. package/dist/mcp/client.d.mts +30 -99
  99. package/dist/mcp/client.mjs +60 -134
  100. package/dist/mcp/events.mjs +142 -0
  101. package/dist/mcp/progress.mjs +34 -0
  102. package/dist/mcp/protocol.mjs +40 -15
  103. package/dist/mcp/reply.d.mts +79 -0
  104. package/dist/mcp/reply.mjs +99 -0
  105. package/dist/mcp/request.d.mts +40 -0
  106. package/dist/mcp/request.mjs +51 -0
  107. package/dist/mcp/watch.mjs +115 -0
  108. package/dist/rpc/watch.mjs +39 -0
  109. package/dist/stdio/console.mjs +14 -7
  110. package/dist/testing/memory.d.mts +1 -3
  111. package/dist/testing/memory.mjs +4 -5
  112. package/dist/webhook/address.mjs +47 -0
  113. package/dist/webhook/attempt.mjs +65 -0
  114. package/dist/webhook/receiver.mjs +67 -0
  115. package/dist/webhook/signing.mjs +28 -0
  116. package/dist/webhook/stored.mjs +57 -0
  117. package/dist/webhook/subscriptions.mjs +342 -0
  118. package/docs/Action-failures.md +2 -2
  119. package/docs/Action-streams.md +115 -0
  120. package/docs/Action.md +58 -124
  121. package/docs/ActionCli-failures.md +1 -2
  122. package/docs/ActionCli.md +83 -88
  123. package/docs/ActionHttp-failures.md +3 -3
  124. package/docs/ActionHttp.md +90 -70
  125. package/docs/ActionMcp-failures.md +2 -1
  126. package/docs/ActionMcp.md +120 -92
  127. package/docs/ActionRpc-events.md +126 -0
  128. package/docs/ActionRpc-failures.md +1 -2
  129. package/docs/ActionRpc.md +108 -79
  130. package/docs/ActionToolkit-failures.md +1 -2
  131. package/docs/ActionToolkit.md +33 -90
  132. package/docs/Authentication-advanced.md +57 -61
  133. package/docs/Authentication-failures.md +5 -4
  134. package/docs/Authentication.md +23 -58
  135. package/docs/Event-failures.md +15 -0
  136. package/docs/Event.md +281 -0
  137. package/docs/README.md +51 -82
  138. package/docs/Testing-failures.md +1 -5
  139. package/docs/Testing.md +126 -86
  140. package/docs/Webhook-failures.md +8 -0
  141. package/docs/Webhook.md +319 -0
  142. package/docs/setup-failures.md +2 -3
  143. package/docs/setup.md +32 -53
  144. package/examples/README.md +12 -37
  145. package/examples/assistant/README.md +21 -26
  146. package/examples/assistant/src/agent.ts +66 -24
  147. package/examples/assistant/src/cli.ts +7 -4
  148. package/examples/assistant/src/commands.ts +9 -4
  149. package/examples/assistant/src/identity/Identity.ts +5 -4
  150. package/examples/assistant/src/identity/authorize.ts +13 -5
  151. package/examples/assistant/src/identity/handlers.ts +3 -2
  152. package/examples/assistant/src/identity/permissions.ts +9 -3
  153. package/examples/assistant/src/platform/tasks-file.ts +15 -11
  154. package/examples/assistant/src/scripted.ts +7 -4
  155. package/examples/assistant/src/tasks/Tasks.ts +20 -6
  156. package/examples/assistant/src/tasks/handlers.ts +9 -5
  157. package/examples/assistant/src/tasks/store.ts +38 -11
  158. package/examples/assistant/src/toolkit.ts +11 -7
  159. package/examples/assistant/test/commands.test.ts +53 -27
  160. package/examples/assistant/test/identity/identity.test.ts +27 -11
  161. package/examples/assistant/test/platform/tasks-file.test.ts +39 -23
  162. package/examples/assistant/test/tasks/tasks.test.ts +62 -34
  163. package/examples/assistant/test/toolkit.test.ts +104 -75
  164. package/examples/build-events/README.md +83 -0
  165. package/examples/build-events/src/Api.ts +27 -0
  166. package/examples/build-events/src/Rpc.ts +26 -0
  167. package/examples/build-events/src/builds/Builds.ts +108 -0
  168. package/examples/build-events/src/builds/events.ts +54 -0
  169. package/examples/build-events/src/builds/handlers.ts +58 -0
  170. package/examples/build-events/src/builds/log.ts +30 -0
  171. package/examples/build-events/src/builds/status.ts +28 -0
  172. package/examples/build-events/src/builds/store.ts +130 -0
  173. package/examples/build-events/src/cli.ts +18 -0
  174. package/examples/build-events/src/commands.ts +32 -0
  175. package/examples/build-events/src/deploys/Deploys.ts +44 -0
  176. package/examples/build-events/src/deploys/approvers.ts +55 -0
  177. package/examples/build-events/src/deploys/events.ts +32 -0
  178. package/examples/build-events/src/deploys/handlers.ts +38 -0
  179. package/examples/build-events/src/identity/Identity.ts +29 -0
  180. package/examples/build-events/src/identity/members.ts +7 -0
  181. package/examples/build-events/src/identity/verifier.ts +22 -0
  182. package/examples/build-events/src/notifier/Notices.ts +26 -0
  183. package/examples/build-events/src/notifier/routes.ts +71 -0
  184. package/examples/build-events/src/notifier.ts +48 -0
  185. package/examples/build-events/src/routes.ts +71 -0
  186. package/examples/build-events/src/server.ts +62 -0
  187. package/examples/build-events/test/builds/events.test.ts +68 -0
  188. package/examples/build-events/test/builds/log.test.ts +71 -0
  189. package/examples/build-events/test/builds/progress.test.ts +57 -0
  190. package/examples/build-events/test/builds/status.test.ts +42 -0
  191. package/examples/build-events/test/commands.test.ts +103 -0
  192. package/examples/build-events/test/deploys/events.test.ts +52 -0
  193. package/examples/build-events/test/http.test.ts +95 -0
  194. package/examples/build-events/test/notifier.test.ts +80 -0
  195. package/examples/build-events/test/routes.test.ts +96 -0
  196. package/examples/build-events/test/rpc.test.ts +84 -0
  197. package/examples/build-events/test/support/as.ts +8 -0
  198. package/examples/build-events/test/support/memory.ts +43 -0
  199. package/examples/build-events/test/webhooks.test.ts +143 -0
  200. package/examples/kitchen-sink/README.md +178 -166
  201. package/examples/kitchen-sink/src/Actions.ts +11 -3
  202. package/examples/kitchen-sink/src/Api.ts +12 -6
  203. package/examples/kitchen-sink/src/Rpc.ts +7 -5
  204. package/examples/kitchen-sink/src/admin/Admin.ts +21 -10
  205. package/examples/kitchen-sink/src/admin/authenticate.ts +8 -4
  206. package/examples/kitchen-sink/src/admin/authorize.ts +13 -5
  207. package/examples/kitchen-sink/src/admin/grants.ts +5 -3
  208. package/examples/kitchen-sink/src/admin/handlers.ts +9 -5
  209. package/examples/kitchen-sink/src/agent.ts +14 -6
  210. package/examples/kitchen-sink/src/assistant/Assistant.ts +21 -15
  211. package/examples/kitchen-sink/src/assistant/conversations.ts +14 -9
  212. package/examples/kitchen-sink/src/assistant/demo-model.ts +2 -2
  213. package/examples/kitchen-sink/src/assistant/handlers.ts +42 -22
  214. package/examples/kitchen-sink/src/attachments/Attachments.ts +6 -6
  215. package/examples/kitchen-sink/src/attachments/download.ts +7 -6
  216. package/examples/kitchen-sink/src/attachments/handlers.ts +9 -4
  217. package/examples/kitchen-sink/src/attachments/store.ts +25 -18
  218. package/examples/kitchen-sink/src/browser.ts +10 -4
  219. package/examples/kitchen-sink/src/cli.ts +28 -12
  220. package/examples/kitchen-sink/src/config.ts +1 -1
  221. package/examples/kitchen-sink/src/identity/Identity.ts +24 -10
  222. package/examples/kitchen-sink/src/identity/authenticate.ts +19 -11
  223. package/examples/kitchen-sink/src/identity/authorize.ts +14 -4
  224. package/examples/kitchen-sink/src/identity/directory.ts +16 -7
  225. package/examples/kitchen-sink/src/identity/handlers.ts +1 -0
  226. package/examples/kitchen-sink/src/identity/members.ts +7 -2
  227. package/examples/kitchen-sink/src/identity/quota.ts +23 -20
  228. package/examples/kitchen-sink/src/issues/Issues.ts +91 -15
  229. package/examples/kitchen-sink/src/issues/assistant.ts +4 -2
  230. package/examples/kitchen-sink/src/issues/events.ts +46 -0
  231. package/examples/kitchen-sink/src/issues/export.ts +35 -0
  232. package/examples/kitchen-sink/src/issues/guide.ts +4 -3
  233. package/examples/kitchen-sink/src/issues/handlers.ts +20 -7
  234. package/examples/kitchen-sink/src/issues/store.ts +156 -67
  235. package/examples/kitchen-sink/src/issues/write-limit.ts +44 -18
  236. package/examples/kitchen-sink/src/memory.ts +11 -2
  237. package/examples/kitchen-sink/src/ops.ts +10 -5
  238. package/examples/kitchen-sink/src/request-policy.ts +25 -9
  239. package/examples/kitchen-sink/src/routes.ts +67 -27
  240. package/examples/kitchen-sink/src/sdk.ts +22 -12
  241. package/examples/kitchen-sink/src/seed.ts +15 -9
  242. package/examples/kitchen-sink/src/server.ts +24 -10
  243. package/examples/kitchen-sink/src/stdio.ts +9 -3
  244. package/examples/kitchen-sink/src/workspaces/Workspaces.ts +10 -7
  245. package/examples/kitchen-sink/src/workspaces/handlers.ts +3 -2
  246. package/examples/kitchen-sink/src/workspaces/registry.ts +16 -10
  247. package/examples/kitchen-sink/src/workspaces/tenant.ts +12 -5
  248. package/examples/kitchen-sink/test/admin/admin.test.ts +38 -23
  249. package/examples/kitchen-sink/test/assistant/assistant.test.ts +471 -299
  250. package/examples/kitchen-sink/test/attachments/attachments.test.ts +135 -94
  251. package/examples/kitchen-sink/test/identity/identity.test.ts +110 -82
  252. package/examples/kitchen-sink/test/identity/session.test.ts +65 -43
  253. package/examples/kitchen-sink/test/issues/assistant.test.ts +50 -31
  254. package/examples/kitchen-sink/test/issues/events.test.ts +273 -0
  255. package/examples/kitchen-sink/test/issues/export.test.ts +120 -0
  256. package/examples/kitchen-sink/test/issues/issues.test.ts +105 -75
  257. package/examples/kitchen-sink/test/request-policy.test.ts +130 -33
  258. package/examples/kitchen-sink/test/routes.test.ts +102 -57
  259. package/examples/kitchen-sink/test/support/as.ts +9 -0
  260. package/examples/kitchen-sink/test/support/scripted.ts +6 -5
  261. package/examples/kitchen-sink/test/support/services.ts +17 -0
  262. package/examples/live-rpc/README.md +33 -35
  263. package/examples/live-rpc/src/Rpc.ts +9 -5
  264. package/examples/live-rpc/src/browser.ts +39 -11
  265. package/examples/live-rpc/src/chat/Chat.ts +30 -16
  266. package/examples/live-rpc/src/chat/authorize.ts +2 -1
  267. package/examples/live-rpc/src/chat/events.ts +74 -0
  268. package/examples/live-rpc/src/chat/handlers.ts +13 -5
  269. package/examples/live-rpc/src/chat/room.ts +9 -5
  270. package/examples/live-rpc/src/chat/store.ts +51 -14
  271. package/examples/live-rpc/src/client.ts +22 -5
  272. package/examples/live-rpc/src/identity/Identity.ts +3 -3
  273. package/examples/live-rpc/src/identity/verifier.ts +8 -4
  274. package/examples/live-rpc/src/page.ts +58 -12
  275. package/examples/live-rpc/src/routes.ts +14 -10
  276. package/examples/live-rpc/src/server.ts +15 -5
  277. package/examples/live-rpc/test/browser.test.ts +127 -50
  278. package/examples/live-rpc/test/chat/chat.test.ts +215 -23
  279. package/examples/live-rpc/test/routes.test.ts +130 -46
  280. package/examples/live-rpc/test/server.test.ts +94 -33
  281. package/examples/mcp-tools/README.md +24 -38
  282. package/examples/mcp-tools/src/platform/fake-farm.ts +63 -24
  283. package/examples/mcp-tools/src/platform/print-farm.ts +59 -29
  284. package/examples/mcp-tools/src/printers/Printers.ts +18 -7
  285. package/examples/mcp-tools/src/printers/farm.ts +15 -5
  286. package/examples/mcp-tools/src/printers/handlers.ts +12 -6
  287. package/examples/mcp-tools/src/routes.ts +8 -6
  288. package/examples/mcp-tools/src/server.ts +12 -5
  289. package/examples/mcp-tools/src/stdio.ts +3 -2
  290. package/examples/mcp-tools/test/printers/printers.test.ts +132 -89
  291. package/examples/mcp-tools/test/routes.test.ts +140 -81
  292. package/examples/notes-service/README.md +30 -45
  293. package/examples/notes-service/src/Api.ts +1 -0
  294. package/examples/notes-service/src/browser.ts +15 -14
  295. package/examples/notes-service/src/cli.ts +8 -3
  296. package/examples/notes-service/src/commands.ts +46 -20
  297. package/examples/notes-service/src/identity/Identity.ts +2 -2
  298. package/examples/notes-service/src/identity/authorize.ts +6 -2
  299. package/examples/notes-service/src/identity/handlers.ts +3 -2
  300. package/examples/notes-service/src/identity/users.ts +2 -1
  301. package/examples/notes-service/src/identity/verifier.ts +12 -6
  302. package/examples/notes-service/src/notes/Notes.ts +46 -22
  303. package/examples/notes-service/src/notes/handlers.ts +18 -9
  304. package/examples/notes-service/src/notes/store.ts +70 -43
  305. package/examples/notes-service/src/routes.ts +7 -5
  306. package/examples/notes-service/src/server.ts +12 -5
  307. package/examples/notes-service/src/status/Status.ts +1 -1
  308. package/examples/notes-service/src/status/handlers.ts +5 -3
  309. package/examples/notes-service/src/stdio.ts +7 -3
  310. package/examples/notes-service/src/worker.ts +6 -3
  311. package/examples/notes-service/test/commands.test.ts +164 -91
  312. package/examples/notes-service/test/notes/as.ts +3 -1
  313. package/examples/notes-service/test/notes/idempotency.test.ts +97 -67
  314. package/examples/notes-service/test/notes/in-process.test.ts +26 -15
  315. package/examples/notes-service/test/notes/notes.test.ts +49 -31
  316. package/examples/notes-service/test/routes.test.ts +106 -71
  317. package/examples/notes-service/test/worker.test.ts +21 -13
  318. package/examples/quickstart/README.md +2 -6
  319. package/examples/quickstart/src/Api.ts +1 -0
  320. package/examples/quickstart/src/client.ts +6 -3
  321. package/examples/quickstart/src/greeting/Greeting.ts +1 -1
  322. package/examples/quickstart/src/greeting/handlers.ts +5 -2
  323. package/examples/quickstart/src/routes.ts +3 -2
  324. package/examples/quickstart/src/server.ts +6 -2
  325. package/examples/quickstart/test/routes.test.ts +3 -2
  326. package/package.json +19 -17
@@ -1,26 +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
- //#region src/mcp/client.d.ts
8
- /**
9
- * An MCP request's parameters, as `request` takes them: JSON, with any `_meta`, leaving
10
- * out an `undefined` one, as a caller may. A helper passing on whole params types them as this,
11
- * and a tool's `arguments` as `Schema.Json`, never `unknown`.
12
- */
13
- export interface Params {
14
- readonly _meta?: {
15
- readonly [key: string]: Schema.Json;
16
- };
17
- readonly [key: string]: Schema.Json | undefined;
18
- }
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";
19
11
  /**
20
12
  * Where `client` sends, through what client, and what the endpoint's authentication declares: the
21
13
  * client `transformClient` returns may fail with `E` and require `R`.
22
14
  */
23
- 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> {
24
16
  /**
25
17
  * The endpoint, resolved by the `HttpClient`: relative under `Testing.layer`. Defaults to `/mcp`,
26
18
  * the default `layerHttp` path, so a call to an endpoint on another `path` fails with an
@@ -40,72 +32,19 @@ export interface ClientOptions<D extends Any$1 | undefined = Any$1 | undefined,
40
32
  */
41
33
  readonly authentication?: D;
42
34
  }
43
- /** Where a `request` goes, and with what headers. */
44
- export interface RequestOptions {
45
- /**
46
- * The endpoint, resolved by the `HttpClient` that sends the request: relative under `Testing.layer`.
47
- * Defaults to `/mcp`, as `client`'s does; converting to a web `Request` needs an absolute
48
- * one.
49
- */
50
- readonly url?: string;
51
- /**
52
- * Headers sent beside the routing ones `request` fills in, such as `Authorization`; a routing
53
- * header wins over a given one of its name.
54
- */
55
- readonly headers?: Headers.Input;
56
- }
57
- declare const StatusError_base: Schema.Class<StatusError, Schema.TaggedStruct<"StatusError", {
58
- readonly status: Schema.Int;
59
- readonly body: Schema.String;
60
- }>, import("effect/Cause").YieldableError>;
61
- /** The endpoint answered with an HTTP status other than 200, and a body no declared error decodes. */
62
- declare class StatusError extends StatusError_base {}
63
- declare const ProtocolError_base: Schema.Class<ProtocolError, Schema.TaggedStruct<"ProtocolError", {
64
- readonly code: Schema.Finite;
65
- readonly message: Schema.String;
66
- }>, 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;
67
38
  /**
68
- * 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.
69
43
  */
70
- declare class ProtocolError extends ProtocolError_base {}
71
- declare const NoReply_base: Schema.Class<NoReply, Schema.TaggedStruct<"NoReply", {
72
- readonly body: Schema.String;
73
- }>, import("effect/Cause").YieldableError>;
74
- /** The endpoint answered 200 without a JSON-RPC reply to read, as JSON or as an event stream. */
75
- declare class NoReply extends NoReply_base {}
76
- declare const UndeclaredError_base: Schema.Class<UndeclaredError, Schema.TaggedStruct<"UndeclaredError", {
77
- readonly text: Schema.String;
78
- }>, import("effect/Cause").YieldableError>;
79
- /** The tool returned an error whose text decodes as none of the action's declared errors. */
80
- declare class UndeclaredError extends UndeclaredError_base {}
81
- declare const NoStructuredContent_base: Schema.Class<NoStructuredContent, Schema.TaggedStruct<"NoStructuredContent", {
82
- readonly body: Schema.String;
83
- }>, import("effect/Cause").YieldableError>;
84
- /** The tool returned a result without the structured content its action's success needs. */
85
- declare class NoStructuredContent extends NoStructuredContent_base {}
86
- declare const McpCallError_base: Schema.Class<McpCallError, Schema.TaggedStruct<"McpCallError", {
87
- readonly tool: Schema.String;
88
- readonly reason: Schema.Union<readonly [typeof StatusError, typeof ProtocolError, typeof NoReply, typeof UndeclaredError, typeof NoStructuredContent]>;
89
- readonly message: Schema.String;
90
- }>, import("effect/Cause").YieldableError>;
91
- /**
92
- * An answer a client method cannot read as the action's success or a declared error: `reason`
93
- * says which, with what the endpoint sent, so a caller branches on it with
94
- * `Effect.catchReason("McpCallError", "StatusError", ...)`. `message` names the tool and the
95
- * reason. Structured content that does not decode as the success is a `SchemaError` instead,
96
- * and a request that fails an `HttpClientError`.
97
- *
98
- * **Gotchas**
99
- *
100
- * - `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.
101
- */
102
- export declare class McpCallError extends McpCallError_base {}
103
- type CallError<A extends Any, D> = A["error"][number]["Type"] | VerifierError<A, D>["Type"] | BuiltIn | Schema.SchemaError | HttpClientError.HttpClientError | McpCallError;
104
- /**
105
- * Every action of `Actions` as `client.<action>(input)`, calling its tool. Each call also fails
106
- * with `E` and requires `R`, what its `transformClient` adds.
107
- */
108
- 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
+ };
109
48
  /**
110
49
  * A client of an MCP endpoint served by `layerHttp`, one method per action calling
111
50
  * its tool with one stateless request, as `ActionHttp.client` calls routes: the input is
@@ -114,27 +53,19 @@ export type Client<Actions extends ReadonlyArray<Any>, D = undefined, E = never,
114
53
  * every field is, from its blocks alone. A declared error the tool returns, the action's own
115
54
  * or a refusal, is its decoded value, and so is a refusal the endpoint's authentication answers
116
55
  * with: a test telling a 401 or 403 from a tool result asserts the status of a request `request`
117
- * builds. The argument may be omitted when `{}` is a valid input. Requires the native
118
- * `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.
119
60
  *
120
61
  * **Gotchas**
121
62
  *
122
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.
123
68
  */
124
- 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>;
125
- 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>;
126
- /**
127
- * One stateless MCP request of `method` with `params`, as `layerHttp` serves it,
128
- * as the native request value: the JSON-RPC envelope, the 2026-07-28 headers (`Accept`,
129
- * `MCP-Protocol-Version`, `MCP-Method`), `mcp-name` from
130
- * `params.uri` for `resources/read` and `params.name` otherwise, and the client metadata in
131
- * `_meta` are filled in, under any `_meta` given, such as a `progressToken`; the protocol
132
- * version is always the request's own. The test sends it: `HttpClient.execute` answers the
133
- * response as the endpoint sent it, whatever its status, and `HttpClientRequest.toWebResult`
134
- * gives the web `Request` a web handler or `fetch` takes, once `url` is absolute. It is for a test
135
- * asserting on what `client` decodes away, such as `tools/list`, a refusal's challenge, or
136
- * a call its types would not send.
137
- */
138
- export declare const request: (method: string, params?: Params, { headers, url }?: RequestOptions) => HttpClientRequest.HttpClientRequest;
139
- //#endregion
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>;
140
71
  export {};
@@ -1,163 +1,89 @@
1
1
  import { Anyone } from "../contract/caller.mjs";
2
2
  import { Refusal } from "../contract/errors.mjs";
3
- import { assertOnce } from "../contract/names.mjs";
4
3
  import { projectedErrors } from "../contract/declared.mjs";
5
- import { checked } from "../contract/call.mjs";
6
- import { defaultPath, httpProtocol, liftOf, unliftedFrom } from "./protocol.mjs";
7
- import { Effect, Match, Option, Predicate, Schema } from "effect";
4
+ import { assertOnce } from "../contract/names.mjs";
5
+ import { progressSink } from "../contract/progress.mjs";
6
+ import { elementOf } from "../contract/stream.mjs";
7
+ import { checked, checkedStream, sendingJson, watchInput } from "../contract/call.mjs";
8
+ import { split } from "../contract/listed.mjs";
9
+ import { watching } from "../contract/watch.mjs";
10
+ import { defaultPath, liftOf, unliftedFrom } from "./protocol.mjs";
11
+ import { NoReply, NoStructuredContent, ProtocolError, StatusError, UndeclaredError, bodyOf, callError, failWith } from "./reply.mjs";
12
+ import { request } from "./request.mjs";
13
+ import { transportOf } from "./watch.mjs";
14
+ import { Effect, Option, Schema, Stream } from "effect";
15
+ import { HttpClient } from "effect/http";
8
16
  import { McpSchema } from "effect/ai";
9
- import { HttpClient, HttpClientRequest } from "effect/http";
10
- import { Sse } from "effect/encoding";
11
- //#region src/mcp/client.ts
12
- /** The routing headers and JSON-RPC body of the request `request` builds. */
13
- const statelessRequest = (method, params) => {
14
- const { protocolVersion } = httpProtocol;
15
- const name = method === "resources/read" ? params.uri : params.name;
16
- return {
17
- headers: {
18
- accept: "application/json, text/event-stream",
19
- "mcp-protocol-version": protocolVersion,
20
- "mcp-method": method,
21
- ...Predicate.isString(name) ? { "mcp-name": name } : {}
22
- },
23
- body: {
24
- jsonrpc: "2.0",
25
- id: 1,
26
- method,
27
- params: {
28
- ...params,
29
- _meta: {
30
- "io.modelcontextprotocol/clientCapabilities": {},
31
- "io.modelcontextprotocol/clientInfo": {
32
- name: "effect-actions",
33
- version: "0"
34
- },
35
- ...params._meta,
36
- "io.modelcontextprotocol/protocolVersion": protocolVersion
37
- }
38
- }
39
- }
40
- };
41
- };
42
- /** The endpoint answered with an HTTP status other than 200, and a body no declared error decodes. */
43
- var StatusError = class extends Schema.TaggedError()("StatusError", {
44
- status: Schema.Int,
45
- body: Schema.String
46
- }) {};
47
- /**
48
- * The endpoint answered with a JSON-RPC error, such as the native server's for an unknown tool.
49
- */
50
- var ProtocolError = class extends Schema.TaggedError()("ProtocolError", {
51
- code: Schema.Finite,
52
- message: Schema.String
53
- }) {};
54
- /** The endpoint answered 200 without a JSON-RPC reply to read, as JSON or as an event stream. */
55
- var NoReply = class extends Schema.TaggedError()("NoReply", { body: Schema.String }) {};
56
- /** The tool returned an error whose text decodes as none of the action's declared errors. */
57
- var UndeclaredError = class extends Schema.TaggedError()("UndeclaredError", { text: Schema.String }) {};
58
- /** The tool returned a result without the structured content its action's success needs. */
59
- var NoStructuredContent = class extends Schema.TaggedError()("NoStructuredContent", { body: Schema.String }) {};
60
- const Reason = Schema.Union([
61
- StatusError,
62
- ProtocolError,
63
- NoReply,
64
- UndeclaredError,
65
- NoStructuredContent
66
- ]);
67
- const sentenceOf = Match.typeTags()({
68
- StatusError: ({ status, body }) => `answered ${status}: ${body}`,
69
- ProtocolError: ({ code, message }) => `failed with ${code}: ${message}`,
70
- NoReply: ({ body }) => `had no reply: ${body}`,
71
- UndeclaredError: ({ text }) => `returned an error: ${text}`,
72
- NoStructuredContent: ({ body }) => `returned no structured content: ${body}`
73
- });
74
- /**
75
- * An answer a client method cannot read as the action's success or a declared error: `reason`
76
- * says which, with what the endpoint sent, so a caller branches on it with
77
- * `Effect.catchReason("McpCallError", "StatusError", ...)`. `message` names the tool and the
78
- * reason. Structured content that does not decode as the success is a `SchemaError` instead,
79
- * and a request that fails an `HttpClientError`.
80
- *
81
- * **Gotchas**
82
- *
83
- * - `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.
84
- */
85
- var McpCallError = class extends Schema.TaggedError()("McpCallError", {
86
- tool: Schema.String,
87
- reason: Reason,
88
- message: Schema.String
89
- }) {};
90
- const callError = (tool, reason) => new McpCallError({
91
- tool,
92
- reason,
93
- message: `MCP tools/call "${tool}" ${sentenceOf(reason)}`
94
- });
17
+ // src/mcp/client.ts
95
18
  const textOf = (block) => block?.type === "text" ? block.text : void 0;
96
19
  const ToolCallReply = Schema.Union([Schema.Struct({ result: McpSchema.CallToolResult }), Schema.Struct({ error: Schema.Struct({
97
20
  code: Schema.Finite,
98
21
  message: Schema.String
99
22
  }) })]);
100
23
  const decodeReply = Schema.decodeUnknownOption(Schema.fromJsonString(ToolCallReply));
101
- const replyOf = (response, text) => {
102
- if (!(response.headers["content-type"] ?? "").startsWith("text/event-stream")) return decodeReply(text);
103
- const data = [];
104
- Sse.makeParser((event) => {
105
- if (Predicate.isTagged(event, "Event")) data.push(event.data);
106
- }).feed(text);
107
- return Option.fromNullishOr(data.flatMap((message) => Option.toArray(decodeReply(message))).at(-1));
24
+ /** `onProgress` given each `notifications/progress` value decoded, one that does not logged and dropped. */
25
+ const decodedSink = (progress, onProgress) => {
26
+ const sink = progressSink(onProgress);
27
+ if (progress === void 0 || sink === void 0) return;
28
+ const decode = Schema.decodeUnknownEffect(Schema.toCodecJson(progress));
29
+ return { progress: (json) => Effect.matchEffect(decode(json), {
30
+ onFailure: (error) => Effect.logWarning("A progress value did not decode", error),
31
+ onSuccess: sink.progress
32
+ }) };
108
33
  };
109
- const failWith = (errors, text, otherwise) => Schema.decodeUnknownEffect(Schema.fromJsonString(Schema.toCodecJson(Schema.Union(errors))))(text).pipe(Effect.mapError(() => otherwise), Effect.flatMap(Effect.fail));
110
- const callTool = (client, url, action, lift, refusedWith, input) => {
34
+ const callTool = (client, url, action, success, refusedWith, input, sink) => {
111
35
  const { name } = action;
36
+ const lift = liftOf(success);
37
+ const method = "tools/call";
112
38
  return Effect.gen(function* () {
113
- const encoded = yield* Schema.encodeEffect(Schema.toCodecJson(action.input))(input);
114
- const response = yield* client.execute(request("tools/call", {
39
+ const response = yield* client.execute(request(method, {
115
40
  name,
116
- arguments: encoded
41
+ arguments: input,
42
+ ...sink === void 0 ? {} : { _meta: { progressToken: "progress" } }
117
43
  }, { url }));
118
- const text = yield* response.text;
119
- if (response.status !== 200) return yield* failWith(refusedWith, text, callError(name, new StatusError({
44
+ const messages = yield* bodyOf(method, name, response, sink);
45
+ const text = messages.join("\n");
46
+ if (response.status !== 200) return yield* failWith(refusedWith, text, callError(method, name, new StatusError({
120
47
  status: response.status,
121
48
  body: text
122
49
  })));
123
- const reply = replyOf(response, text);
124
- if (Option.isNone(reply)) return yield* Effect.fail(callError(name, new NoReply({ body: text })));
125
- if ("error" in reply.value) return yield* Effect.fail(callError(name, new ProtocolError(reply.value.error)));
50
+ const reply = Option.fromNullishOr(messages.flatMap((message) => Option.toArray(decodeReply(message))).at(-1));
51
+ if (Option.isNone(reply)) return yield* callError(method, name, new NoReply({ body: text }));
52
+ if ("error" in reply.value) return yield* callError(method, name, new ProtocolError(reply.value.error));
126
53
  const { result } = reply.value;
127
54
  if (result.isError === true) {
128
55
  const error = textOf(result.content.find((block) => block.type === "text")) ?? "";
129
- return yield* failWith(projectedErrors(action), error, callError(name, new UndeclaredError({ text: error })));
56
+ return yield* failWith(projectedErrors(action), error, callError(method, name, new UndeclaredError({ text: error })));
130
57
  }
131
- if (result.structuredContent === void 0 && lift.rest !== void 0) return yield* Effect.fail(callError(name, new NoStructuredContent({ body: text })));
132
- return yield* Schema.decodeUnknownEffect(Schema.toCodecJson(action.success))(unliftedFrom(lift, result));
58
+ if (result.structuredContent === void 0 && lift.rest !== void 0) return yield* callError(method, name, new NoStructuredContent({ body: text }));
59
+ return yield* Schema.decodeUnknownEffect(Schema.toCodecJson(success))(unliftedFrom(lift, result));
133
60
  });
134
61
  };
135
- function client(actions, { url = defaultPath, transformClient, authentication } = {}) {
62
+ function client(contracts, { url = defaultPath, transformClient, authentication } = {}) {
63
+ const { actions, events } = split(contracts);
136
64
  assertOnce("action", actions);
65
+ assertOnce("event", events);
66
+ const refusedWith = (contract) => [...Refusal.members, ...contract.caller === Anyone ? [] : authentication?.error ?? []];
137
67
  return Effect.map(HttpClient.HttpClient, (native) => {
138
68
  const http = transformClient === void 0 ? native : transformClient(native);
139
- return Object.fromEntries(actions.map((action) => {
140
- const lift = liftOf(action.success);
141
- const refusedWith = [...Refusal.members, ...action.caller === Anyone ? [] : authentication?.error ?? []];
142
- return [action.name, checked(action, (input) => callTool(http, url, action, lift, refusedWith, input))];
143
- }));
69
+ const watch = (event, input, options) => Stream.unwrap(Effect.map(watchInput(event, input), ({ json }) => watching(transportOf({
70
+ http,
71
+ url,
72
+ event,
73
+ refusedWith: refusedWith(event)
74
+ }, json), options)));
75
+ return {
76
+ ...Object.fromEntries(actions.map((action) => {
77
+ const call = (success, input, onProgress) => callTool(http, url, action, success, refusedWith(action), input, decodedSink(action.progress, onProgress));
78
+ const element = elementOf(action.success);
79
+ if (element !== void 0) {
80
+ const elements = Schema.Array(element);
81
+ return [action.name, checkedStream(action, sendingJson, (input, onProgress) => Stream.fromIterableEffect(call(elements, input, onProgress)))];
82
+ }
83
+ return [action.name, checked(action, sendingJson, (input, onProgress) => call(action.success, input, onProgress))];
84
+ })),
85
+ ...events.length === 0 ? {} : { watch }
86
+ };
144
87
  });
145
88
  }
146
- /**
147
- * One stateless MCP request of `method` with `params`, as `layerHttp` serves it,
148
- * as the native request value: the JSON-RPC envelope, the 2026-07-28 headers (`Accept`,
149
- * `MCP-Protocol-Version`, `MCP-Method`), `mcp-name` from
150
- * `params.uri` for `resources/read` and `params.name` otherwise, and the client metadata in
151
- * `_meta` are filled in, under any `_meta` given, such as a `progressToken`; the protocol
152
- * version is always the request's own. The test sends it: `HttpClient.execute` answers the
153
- * response as the endpoint sent it, whatever its status, and `HttpClientRequest.toWebResult`
154
- * gives the web `Request` a web handler or `fetch` takes, once `url` is absolute. It is for a test
155
- * asserting on what `client` decodes away, such as `tools/list`, a refusal's challenge, or
156
- * a call its types would not send.
157
- */
158
- const request = (method, params = {}, { headers = {}, url = defaultPath } = {}) => {
159
- const { headers: routingHeaders, body } = statelessRequest(method, params);
160
- return HttpClientRequest.post(url).pipe(HttpClientRequest.setHeaders(headers), HttpClientRequest.setHeaders(routingHeaders), HttpClientRequest.bodyJsonUnsafe(body));
161
- };
162
- //#endregion
163
- export { McpCallError, client, request, statelessRequest };
89
+ export { client };
@@ -0,0 +1,142 @@
1
+ import { eventCodes } from "../contract/codes.mjs";
2
+ import { Subscribed, WebhookCallback, WebhookTarget, webhookFields } from "../contract/subscribe.mjs";
3
+ import { ErrorReporter, Schema, Struct } from "effect";
4
+ import { McpSchema } from "effect/ai";
5
+ import { Rpc, RpcGroup } from "effect/rpc";
6
+ // src/mcp/events.ts
7
+ /** The extension's identifier, its key in `capabilities.extensions`. */
8
+ const extension = "io.modelcontextprotocol/events";
9
+ /** The `_meta` key a push stream's notifications carry its request id under. */
10
+ const subscriptionIdKey = "io.modelcontextprotocol/subscriptionId";
11
+ const codes = {
12
+ headerMismatch: -32020,
13
+ ...eventCodes,
14
+ invalidParams: -32602,
15
+ internal: -32603
16
+ };
17
+ const Meta = Schema.optional(Schema.Record(Schema.String, Schema.Json));
18
+ const Arguments = Schema.optional(Schema.Record(Schema.String, Schema.Json));
19
+ const Cursor = Schema.optional(Schema.NullOr(Schema.String));
20
+ /** A JSON-RPC error as an `events/*` request fails with, and a termination carries. */
21
+ const WireError = Schema.Struct({
22
+ code: Schema.Int,
23
+ message: Schema.String,
24
+ data: Schema.optional(Schema.Json)
25
+ });
26
+ /** What an `events/*` handler fails with: the JSON-RPC error it answers. */
27
+ var EventsError = class extends Schema.Error("effect-actions/McpEventsError")(WireError.fields) {
28
+ [ErrorReporter.ignore] = true;
29
+ };
30
+ /** One event type as `events/list` lists it. */
31
+ const EventDescriptor = Schema.Struct({
32
+ name: Schema.String,
33
+ description: Schema.String,
34
+ delivery: Schema.Array(Schema.Literals([
35
+ "poll",
36
+ "push",
37
+ "webhook"
38
+ ])),
39
+ inputSchema: McpSchema.ToolJson,
40
+ payloadSchema: McpSchema.ToolOutputJson
41
+ });
42
+ /** An occurrence on the wire: in a poll's `events`, and the params of an event notification. */
43
+ const WireOccurrence = Schema.Struct({
44
+ eventId: Schema.String,
45
+ name: Schema.String,
46
+ timestamp: Schema.String,
47
+ data: Schema.Json,
48
+ cursor: Cursor
49
+ });
50
+ const subscription = {
51
+ _meta: Meta,
52
+ name: Schema.String,
53
+ arguments: Arguments,
54
+ cursor: Cursor,
55
+ maxAgeMs: Schema.optional(Schema.Finite)
56
+ };
57
+ var ListEvents = class extends Rpc.make("events/list", {
58
+ payload: {
59
+ _meta: Meta,
60
+ cursor: Schema.optional(Schema.String)
61
+ },
62
+ success: Schema.Struct({
63
+ resultType: Schema.Literal("complete"),
64
+ events: Schema.Array(EventDescriptor),
65
+ _meta: Meta
66
+ }),
67
+ error: EventsError
68
+ }) {};
69
+ var PollEvents = class extends Rpc.make("events/poll", {
70
+ payload: {
71
+ ...subscription,
72
+ maxEvents: Schema.optional(Schema.Finite)
73
+ },
74
+ success: Schema.Struct({
75
+ resultType: Schema.Literal("complete"),
76
+ events: Schema.Array(WireOccurrence),
77
+ cursor: Cursor,
78
+ truncated: Schema.optional(Schema.Boolean),
79
+ hasMore: Schema.optional(Schema.Boolean),
80
+ nextPollMs: Schema.optional(Schema.Finite),
81
+ _meta: Meta
82
+ }),
83
+ error: EventsError
84
+ }) {};
85
+ var StreamEvents = class extends Rpc.make("events/stream", {
86
+ payload: subscription,
87
+ success: Schema.Struct({
88
+ resultType: Schema.Literal("complete"),
89
+ _meta: Meta
90
+ }),
91
+ error: EventsError
92
+ }) {};
93
+ var SubscribeEvents = class extends Rpc.make("events/subscribe", {
94
+ payload: {
95
+ ...subscription,
96
+ delivery: WebhookCallback.mapFields(Struct.assign({ mode: Schema.Literal("webhook") })),
97
+ ttlMs: webhookFields.ttlMs
98
+ },
99
+ success: Schema.Struct({
100
+ resultType: Schema.Literal("complete"),
101
+ ...Subscribed.fields,
102
+ _meta: Meta
103
+ }),
104
+ error: EventsError
105
+ }) {};
106
+ var UnsubscribeEvents = class extends Rpc.make("events/unsubscribe", {
107
+ payload: {
108
+ _meta: Meta,
109
+ name: Schema.String,
110
+ arguments: Arguments,
111
+ delivery: WebhookTarget
112
+ },
113
+ success: Schema.Struct({
114
+ resultType: Schema.Literal("complete"),
115
+ _meta: Meta
116
+ }),
117
+ error: EventsError
118
+ }) {};
119
+ const Active = Rpc.make("notifications/events/active", { payload: {
120
+ _meta: Meta,
121
+ cursor: Cursor,
122
+ truncated: Schema.optional(Schema.Boolean)
123
+ } });
124
+ const Occurred = Rpc.make("notifications/events/event", { payload: {
125
+ ...WireOccurrence.fields,
126
+ _meta: Meta
127
+ } });
128
+ const Heartbeat = Rpc.make("notifications/events/heartbeat", { payload: {
129
+ _meta: Meta,
130
+ cursor: Cursor
131
+ } });
132
+ const Failed = Rpc.make("notifications/events/error", { payload: {
133
+ _meta: Meta,
134
+ error: WireError
135
+ } });
136
+ const Terminated = Rpc.make("notifications/events/terminated", { payload: {
137
+ _meta: Meta,
138
+ error: WireError
139
+ } });
140
+ const Requests = RpcGroup.make(ListEvents, PollEvents, StreamEvents, SubscribeEvents, UnsubscribeEvents);
141
+ const Notifications = RpcGroup.make(Active, Occurred, Heartbeat, Failed, Terminated);
142
+ export { Active, EventDescriptor, EventsError, Failed, Heartbeat, ListEvents, Notifications, Occurred, PollEvents, Requests, StreamEvents, SubscribeEvents, Terminated, UnsubscribeEvents, WireError, WireOccurrence, codes, extension, subscriptionIdKey };
@@ -0,0 +1,34 @@
1
+ import { Effect, Option, Schema } from "effect";
2
+ import { McpSchema } from "effect/ai";
3
+ // src/mcp/progress.ts
4
+ /** The `_meta` key a `notifications/progress` carries its value under, as JSON. */
5
+ const progressKey = "effect-actions/progress";
6
+ const tokenOf = Schema.decodeUnknownOption(McpSchema.ProgressToken);
7
+ /**
8
+ * Where a tool call's progress goes: a `notifications/progress` for each value, on the
9
+ * `progressToken` its request carried, `progress` counting from 1, `message` the value's JSON and
10
+ * `_meta` the value; none for a request without a token, so the values are checked and dropped.
11
+ */
12
+ const progressNotifier = (notifications, progress) => Effect.map(Effect.service(McpSchema.McpRequestContext), ({ requestMetadata }) => {
13
+ const token = tokenOf(requestMetadata?.progressToken);
14
+ if (Option.isNone(token)) return;
15
+ const encode = Schema.encodeUnknownEffect(Schema.toCodecJson(progress));
16
+ let sent = 0;
17
+ return { progress: (value) => Effect.flatMap(Effect.orDie(encode(value)), (json) => {
18
+ sent += 1;
19
+ return notifications["notifications/progress"]({
20
+ progressToken: token.value,
21
+ progress: sent,
22
+ message: JSON.stringify(json),
23
+ _meta: { [progressKey]: json }
24
+ });
25
+ }) };
26
+ });
27
+ const Notified = Schema.fromJsonString(Schema.Struct({
28
+ method: Schema.Literal("notifications/progress"),
29
+ params: Schema.Struct({ _meta: Schema.Struct({ [progressKey]: Schema.Json }) })
30
+ }));
31
+ const notifiedOf = Schema.decodeUnknownOption(Notified);
32
+ /** The value a frame's `notifications/progress` carries, or none for another frame. */
33
+ const progressOf = (data) => Option.map(notifiedOf(data), (notified) => notified.params._meta[progressKey]);
34
+ export { progressKey, progressNotifier, progressOf };
@@ -1,9 +1,9 @@
1
1
  import { unsuspended } from "../contract/ast.mjs";
2
2
  import { Media, isMarked, kindOf } from "../contract/media.mjs";
3
- import { Predicate, Schema, SchemaAST } from "effect";
4
- import { McpProtocol } from "effect/ai";
3
+ import { JsonPointer, Predicate, Schema, SchemaAST } from "effect";
5
4
  import { Base64 } from "effect/encoding";
6
- //#region src/mcp/protocol.ts
5
+ import { McpProtocol, McpSchema } from "effect/ai";
6
+ // src/mcp/protocol.ts
7
7
  const httpProtocol = McpProtocol.v2026_07_28;
8
8
  const protocols = [
9
9
  McpProtocol.v2026_07_28,
@@ -11,6 +11,31 @@ const protocols = [
11
11
  McpProtocol.v2025_06_18
12
12
  ];
13
13
  const defaultPath = "/mcp";
14
+ const isObjectSchema = Schema.is(McpSchema.ToolJson);
15
+ const isJson = Schema.is(Schema.Json);
16
+ const definitionsReferenced = (value) => {
17
+ if (value === null || Predicate.isString(value) || Predicate.isNumber(value) || Predicate.isBoolean(value)) return [];
18
+ if (Array.isArray(value)) return value.flatMap(definitionsReferenced);
19
+ return Object.entries(value).flatMap(([field, nested]) => {
20
+ if (field !== "$ref") return definitionsReferenced(nested);
21
+ const [scope, key] = Predicate.isString(nested) ? JsonPointer.parseUriFragment(nested) ?? [] : [];
22
+ return scope === "$defs" && key !== void 0 ? [key] : [];
23
+ });
24
+ };
25
+ const toolJsonSchema = (schema, strict) => {
26
+ const document = Schema.toJsonSchemaDocument(schema, { onExcessProperty: strict ? "error" : "ignore" });
27
+ const [scope, key] = Predicate.isString(document.schema["$ref"]) ? JsonPointer.parseUriFragment(document.schema["$ref"]) ?? [] : [];
28
+ const inlined = scope === "$defs" && key !== void 0 ? key : void 0;
29
+ const root = inlined === void 0 ? document.schema : document.definitions[inlined];
30
+ if (root === void 0) return;
31
+ const definitions = inlined !== void 0 && ![root, ...Object.values(document.definitions)].flatMap((definition) => isJson(definition) ? definitionsReferenced(definition) : []).includes(inlined) ? Object.fromEntries(Object.entries(document.definitions).filter(([name]) => name !== inlined)) : document.definitions;
32
+ return Object.keys(definitions).length === 0 ? root : {
33
+ ...root,
34
+ $defs: definitions
35
+ };
36
+ };
37
+ /** The forms an MCP routing header may carry `name` in: as sent, or base64-encoded in its sentinel. */
38
+ const routingFormsOf = (name) => [name, `=?base64?${Base64.encode(name)}?=`];
14
39
  const fieldKey = "effect-actions/field";
15
40
  const isMedia = Schema.is(Media);
16
41
  const isMediaArray = Schema.is(Schema.Array(Media));
@@ -22,7 +47,7 @@ const wholeSuccessMediaOf = (suspended) => {
22
47
  kind: one,
23
48
  many: false
24
49
  };
25
- if (!SchemaAST.isArrays(ast) || ast.encoding !== void 0) return void 0;
50
+ if (!SchemaAST.isArrays(ast) || ast.encoding !== void 0) return;
26
51
  const [kind, ...others] = new Set([...ast.elements, ...ast.rest].map(kindOfUnsuspended));
27
52
  return kind === void 0 || others.length > 0 ? void 0 : {
28
53
  kind,
@@ -95,16 +120,17 @@ const holdsMediaAnywhere = (ast) => {
95
120
  }
96
121
  return false;
97
122
  };
98
- const isMisplaced = (action, { rest }) => {
99
- return [
100
- action.input,
101
- ...action.error,
102
- ...rest === void 0 ? [] : [rest]
103
- ].some(({ ast }) => holdsMediaAnywhere(ast));
104
- };
123
+ const isMisplaced = (action, { rest }) => [
124
+ action.input,
125
+ ...action.error,
126
+ ...rest === void 0 ? [] : [rest]
127
+ ].some(({ ast }) => holdsMediaAnywhere(ast));
105
128
  const liftedFrom = (lift, success) => lift.fields.flatMap(({ name, kind, many }) => {
106
- const value = name === void 0 ? success : Predicate.hasProperty(success, name) ? success[name] : void 0;
107
- return (many ? isMediaArray(value) ? value : [] : isMedia(value) ? [value] : []).map((media) => ({
129
+ const field = name !== void 0 && Predicate.hasProperty(success, name) ? success[name] : void 0;
130
+ const value = name === void 0 ? success : field;
131
+ const listed = isMediaArray(value) ? value : [];
132
+ const single = isMedia(value) ? [value] : [];
133
+ return (many ? listed : single).map((media) => ({
108
134
  kind,
109
135
  field: name,
110
136
  value: media
@@ -133,5 +159,4 @@ const unliftedFrom = (lift, result) => {
133
159
  return field.name === void 0 || value === void 0 ? [] : [[field.name, value]];
134
160
  })]);
135
161
  };
136
- //#endregion
137
- export { defaultPath, fieldKey, httpProtocol, isMisplaced, liftOf, liftedFrom, protocols, unliftedFrom };
162
+ export { defaultPath, fieldKey, httpProtocol, isMisplaced, isObjectSchema, liftOf, liftedFrom, protocols, routingFormsOf, toolJsonSchema, unliftedFrom };