@gjermundgaraba/effect-actions 0.11.0 → 0.13.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 (215) hide show
  1. package/README.md +102 -41
  2. package/dist/Action.d.mts +31 -117
  3. package/dist/Action.mjs +14 -53
  4. package/dist/ActionCli.d.mts +60 -28
  5. package/dist/ActionCli.mjs +54 -48
  6. package/dist/ActionHttp.d.mts +18 -67
  7. package/dist/ActionHttp.mjs +20 -70
  8. package/dist/ActionMcp.d.mts +23 -29
  9. package/dist/ActionMcp.mjs +56 -114
  10. package/dist/ActionRpc.d.mts +147 -0
  11. package/dist/ActionRpc.mjs +113 -0
  12. package/dist/ActionToolkit.d.mts +28 -9
  13. package/dist/ActionToolkit.mjs +31 -4
  14. package/dist/Authentication.d.mts +15 -15
  15. package/dist/Authentication.mjs +42 -96
  16. package/dist/Testing.d.mts +30 -23
  17. package/dist/Testing.mjs +27 -44
  18. package/dist/{internal/authentication.d.mts → authentication/provider.d.mts} +41 -9
  19. package/dist/authentication/provider.mjs +12 -0
  20. package/dist/authentication/refusal.mjs +36 -0
  21. package/dist/{internal/cli.d.mts → cli/commands.d.mts} +16 -12
  22. package/dist/cli/commands.mjs +302 -0
  23. package/dist/{internal → contract}/call.d.mts +2 -2
  24. package/dist/contract/call.mjs +10 -0
  25. package/dist/{internal → contract}/errors.d.mts +13 -10
  26. package/dist/{internal → contract}/errors.mjs +9 -14
  27. package/dist/{internal → contract}/implementation.d.mts +37 -21
  28. package/dist/{internal → contract}/implementation.mjs +18 -49
  29. package/dist/contract/media.d.mts +13 -0
  30. package/dist/contract/media.mjs +18 -0
  31. package/dist/contract/rules.d.mts +25 -0
  32. package/dist/{internal/actions.mjs → contract/rules.mjs} +6 -42
  33. package/dist/{internal → http}/client.d.mts +7 -9
  34. package/dist/http/client.mjs +17 -0
  35. package/dist/http/declared.mjs +20 -0
  36. package/dist/http/schema-errors.mjs +9 -0
  37. package/dist/mcp/protocol.d.mts +18 -0
  38. package/dist/{internal/mcp.mjs → mcp/protocol.mjs} +29 -62
  39. package/dist/stdio/console.mjs +64 -0
  40. package/dist/{internal → testing}/memory.d.mts +1 -1
  41. package/dist/{internal → testing}/memory.mjs +5 -17
  42. package/dist/toolkit/tools.mjs +29 -0
  43. package/docs/Action-failures.md +47 -0
  44. package/docs/Action-in-process.md +93 -0
  45. package/docs/Action.md +211 -426
  46. package/docs/ActionCli-failures.md +104 -0
  47. package/docs/ActionCli.md +208 -293
  48. package/docs/ActionHttp-failures.md +83 -0
  49. package/docs/ActionHttp.md +205 -282
  50. package/docs/ActionMcp-failures.md +31 -0
  51. package/docs/ActionMcp.md +222 -271
  52. package/docs/ActionRpc-failures.md +21 -0
  53. package/docs/ActionRpc.md +179 -0
  54. package/docs/ActionToolkit-failures.md +15 -0
  55. package/docs/ActionToolkit.md +103 -119
  56. package/docs/Authentication-advanced.md +139 -0
  57. package/docs/Authentication-failures.md +99 -0
  58. package/docs/Authentication.md +143 -392
  59. package/docs/CONTEXT.md +40 -24
  60. package/docs/README.md +75 -35
  61. package/docs/Testing-failures.md +21 -0
  62. package/docs/Testing.md +193 -151
  63. package/docs/guarantees.md +35 -96
  64. package/docs/setup-failures.md +37 -0
  65. package/docs/setup.md +90 -92
  66. package/examples/README.md +68 -0
  67. package/examples/assistant/README.md +92 -0
  68. package/examples/assistant/src/agent.ts +92 -0
  69. package/examples/assistant/src/cli.ts +34 -0
  70. package/examples/assistant/src/commands.ts +25 -0
  71. package/examples/assistant/src/identity/Identity.ts +33 -0
  72. package/examples/assistant/src/identity/authorize.ts +24 -0
  73. package/examples/assistant/src/identity/handlers.ts +10 -0
  74. package/examples/assistant/src/identity/members.ts +9 -0
  75. package/examples/assistant/src/identity/permissions.ts +23 -0
  76. package/examples/assistant/src/scripted.ts +23 -0
  77. package/examples/assistant/src/tasks/Tasks.ts +63 -0
  78. package/examples/assistant/src/tasks/handlers.ts +25 -0
  79. package/examples/assistant/src/tasks/store.ts +96 -0
  80. package/examples/assistant/src/toolkit.ts +30 -0
  81. package/examples/assistant/test/commands.test.ts +57 -0
  82. package/examples/assistant/test/identity/identity.test.ts +43 -0
  83. package/examples/assistant/test/tasks/store.test.ts +48 -0
  84. package/examples/assistant/test/tasks/tasks.test.ts +75 -0
  85. package/examples/assistant/test/toolkit.test.ts +121 -0
  86. package/examples/kitchen-sink/README.md +318 -0
  87. package/examples/kitchen-sink/src/Actions.ts +19 -0
  88. package/examples/kitchen-sink/src/Api.ts +24 -0
  89. package/examples/kitchen-sink/src/Rpc.ts +12 -0
  90. package/examples/kitchen-sink/src/admin/Admin.ts +59 -0
  91. package/examples/kitchen-sink/src/admin/authenticate.ts +18 -0
  92. package/examples/kitchen-sink/src/admin/authorize.ts +27 -0
  93. package/examples/kitchen-sink/src/admin/grants.ts +22 -0
  94. package/examples/kitchen-sink/src/admin/handlers.ts +21 -0
  95. package/examples/kitchen-sink/src/admin/keys.ts +8 -0
  96. package/examples/kitchen-sink/src/agent.ts +47 -0
  97. package/examples/kitchen-sink/src/assistant/Assistant.ts +82 -0
  98. package/examples/kitchen-sink/src/assistant/conversations.ts +71 -0
  99. package/examples/kitchen-sink/src/assistant/demo-model.ts +33 -0
  100. package/examples/kitchen-sink/src/assistant/handlers.ts +126 -0
  101. package/examples/kitchen-sink/src/attachments/Attachments.ts +48 -0
  102. package/examples/kitchen-sink/src/attachments/download.ts +49 -0
  103. package/examples/kitchen-sink/src/attachments/handlers.ts +38 -0
  104. package/examples/kitchen-sink/src/attachments/store.ts +58 -0
  105. package/examples/kitchen-sink/src/browser.ts +33 -0
  106. package/examples/kitchen-sink/src/cli.ts +85 -0
  107. package/examples/kitchen-sink/src/config.ts +18 -0
  108. package/examples/kitchen-sink/src/identity/Identity.ts +61 -0
  109. package/examples/kitchen-sink/src/identity/authenticate.ts +87 -0
  110. package/examples/kitchen-sink/src/identity/authorize.ts +16 -0
  111. package/examples/kitchen-sink/src/identity/directory.ts +52 -0
  112. package/examples/kitchen-sink/src/identity/handlers.ts +9 -0
  113. package/examples/kitchen-sink/src/identity/members.ts +13 -0
  114. package/examples/kitchen-sink/src/identity/quota.ts +62 -0
  115. package/examples/kitchen-sink/src/issues/Issues.ts +111 -0
  116. package/examples/kitchen-sink/src/issues/assistant.ts +23 -0
  117. package/examples/kitchen-sink/src/issues/guide.ts +37 -0
  118. package/examples/kitchen-sink/src/issues/handlers.ts +37 -0
  119. package/examples/kitchen-sink/src/issues/store.ts +96 -0
  120. package/examples/kitchen-sink/src/issues/write-limit.ts +42 -0
  121. package/examples/kitchen-sink/src/memory.ts +30 -0
  122. package/examples/kitchen-sink/src/ops.ts +42 -0
  123. package/examples/kitchen-sink/src/request-policy.ts +63 -0
  124. package/examples/kitchen-sink/src/routes.ts +120 -0
  125. package/examples/kitchen-sink/src/sdk.ts +46 -0
  126. package/examples/kitchen-sink/src/seed.ts +65 -0
  127. package/examples/kitchen-sink/src/server.ts +62 -0
  128. package/examples/kitchen-sink/src/stdio.ts +34 -0
  129. package/examples/kitchen-sink/src/workspaces/Workspaces.ts +28 -0
  130. package/examples/kitchen-sink/src/workspaces/handlers.ts +23 -0
  131. package/examples/kitchen-sink/src/workspaces/registry.ts +46 -0
  132. package/examples/kitchen-sink/src/workspaces/tenant.ts +17 -0
  133. package/examples/kitchen-sink/test/admin/admin.test.ts +67 -0
  134. package/examples/kitchen-sink/test/assistant/assistant.test.ts +435 -0
  135. package/examples/kitchen-sink/test/attachments/attachments.test.ts +161 -0
  136. package/examples/kitchen-sink/test/identity/identity.test.ts +137 -0
  137. package/examples/kitchen-sink/test/identity/session.test.ts +110 -0
  138. package/examples/kitchen-sink/test/issues/assistant.test.ts +75 -0
  139. package/examples/kitchen-sink/test/issues/issues.test.ts +135 -0
  140. package/examples/kitchen-sink/test/request-policy.test.ts +125 -0
  141. package/examples/kitchen-sink/test/routes.test.ts +123 -0
  142. package/examples/kitchen-sink/test/support/scripted.ts +40 -0
  143. package/examples/live-rpc/README.md +107 -0
  144. package/examples/live-rpc/src/Rpc.ts +8 -0
  145. package/examples/live-rpc/src/browser.ts +98 -0
  146. package/examples/live-rpc/src/chat/Chat.ts +77 -0
  147. package/examples/live-rpc/src/chat/authorize.ts +16 -0
  148. package/examples/live-rpc/src/chat/handlers.ts +50 -0
  149. package/examples/live-rpc/src/chat/room.ts +23 -0
  150. package/examples/live-rpc/src/chat/store.ts +54 -0
  151. package/examples/live-rpc/src/client.ts +37 -0
  152. package/examples/live-rpc/src/identity/Identity.ts +19 -0
  153. package/examples/live-rpc/src/identity/members.ts +8 -0
  154. package/examples/live-rpc/src/identity/verifier.ts +23 -0
  155. package/examples/live-rpc/src/routes.ts +22 -0
  156. package/examples/live-rpc/src/server.ts +43 -0
  157. package/examples/live-rpc/test/browser.test.ts +105 -0
  158. package/examples/live-rpc/test/chat/chat.test.ts +69 -0
  159. package/examples/live-rpc/test/routes.test.ts +99 -0
  160. package/examples/live-rpc/test/server.test.ts +75 -0
  161. package/examples/mcp-tools/README.md +141 -0
  162. package/examples/mcp-tools/src/instructions.ts +12 -0
  163. package/examples/mcp-tools/src/printers/Printers.ts +121 -0
  164. package/examples/mcp-tools/src/printers/fake.ts +115 -0
  165. package/examples/mcp-tools/src/printers/farm.ts +136 -0
  166. package/examples/mcp-tools/src/printers/handlers.ts +29 -0
  167. package/examples/mcp-tools/src/routes.ts +52 -0
  168. package/examples/mcp-tools/src/server.ts +31 -0
  169. package/examples/mcp-tools/src/stdio.ts +26 -0
  170. package/examples/mcp-tools/test/printers/printers.test.ts +189 -0
  171. package/examples/mcp-tools/test/routes.test.ts +202 -0
  172. package/examples/notes-service/README.md +111 -0
  173. package/examples/notes-service/src/Api.ts +13 -0
  174. package/examples/notes-service/src/browser.ts +41 -0
  175. package/examples/notes-service/src/cli.ts +22 -0
  176. package/examples/notes-service/src/commands.ts +137 -0
  177. package/examples/notes-service/src/identity/Identity.ts +31 -0
  178. package/examples/notes-service/src/identity/authorize.ts +18 -0
  179. package/examples/notes-service/src/identity/handlers.ts +10 -0
  180. package/examples/notes-service/src/identity/users.ts +9 -0
  181. package/examples/notes-service/src/identity/verifier.ts +36 -0
  182. package/examples/notes-service/src/notes/Notes.ts +119 -0
  183. package/examples/notes-service/src/notes/handlers.ts +39 -0
  184. package/examples/notes-service/src/notes/store.ts +84 -0
  185. package/examples/notes-service/src/routes.ts +45 -0
  186. package/examples/notes-service/src/server.ts +26 -0
  187. package/examples/notes-service/src/status/Status.ts +10 -0
  188. package/examples/notes-service/src/status/handlers.ts +14 -0
  189. package/examples/notes-service/src/stdio.ts +25 -0
  190. package/examples/notes-service/test/commands.test.ts +288 -0
  191. package/examples/notes-service/test/notes/notes.test.ts +103 -0
  192. package/examples/notes-service/test/routes.test.ts +84 -0
  193. package/examples/quickstart/README.md +31 -0
  194. package/examples/quickstart/src/Api.ts +5 -0
  195. package/examples/quickstart/src/client.ts +13 -0
  196. package/examples/quickstart/src/greeting/Greeting.ts +12 -0
  197. package/examples/quickstart/src/greeting/handlers.ts +6 -0
  198. package/examples/quickstart/src/routes.ts +11 -0
  199. package/examples/quickstart/src/server.ts +16 -0
  200. package/examples/quickstart/test/greeting/greeting.test.ts +18 -0
  201. package/package.json +27 -9
  202. package/dist/internal/actions.d.mts +0 -9
  203. package/dist/internal/authentication.mjs +0 -11
  204. package/dist/internal/call.mjs +0 -12
  205. package/dist/internal/cli.mjs +0 -328
  206. package/dist/internal/client.mjs +0 -25
  207. package/dist/internal/console.mjs +0 -77
  208. package/dist/internal/declared.d.mts +0 -13
  209. package/dist/internal/declared.mjs +0 -34
  210. package/dist/internal/mcp.d.mts +0 -14
  211. package/dist/internal/media.d.mts +0 -8
  212. package/dist/internal/media.mjs +0 -26
  213. package/dist/internal/refusal.mjs +0 -67
  214. package/dist/internal/schema-errors.mjs +0 -20
  215. package/dist/internal/tools.mjs +0 -44
@@ -1,10 +1,10 @@
1
- import { ActionOf, BuildError, BuildServices, Holding, Known, Member, Protected, SelectedOf, Selection, Served, ServedRequest } from "./internal/implementation.mjs";
1
+ import { ActionOf, BuildError, BuildServices, Holding, Known, Member, Protected, SelectedOf, Selection, Served, ServedRequest } from "./contract/implementation.mjs";
2
2
  import { Action_d_exports, Any } from "./Action.mjs";
3
- import { Any as Any$1, Matching, ProviderOf, RemoteRequest, Required } from "./internal/authentication.mjs";
3
+ import { Any as Any$1, Matching, ProviderOf, RemoteRequest, Required, ServerBuildServices } from "./authentication/provider.mjs";
4
4
  import { Cause, Effect, Layer, Stdio } from "effect";
5
- import { HttpRouter } from "effect/http";
6
5
  import { McpSchema, McpServer } from "effect/ai";
7
- //#region src/ActionMcp.d.ts
6
+ import { HttpRouter } from "effect/http";
7
+ //#region src/mcp/ActionMcp.d.ts
8
8
  /**
9
9
  * An MCP server, over HTTP or stdio: every native `McpServer.layerStdio` option except
10
10
  * `protocols`, the server information, `instructions` and `extensions`, the actions `A` that
@@ -24,53 +24,47 @@ export interface Options<E = never, R = never, A extends Any = Any> extends Omit
24
24
  readonly features?: Layer.Layer<never, E, R> | undefined;
25
25
  }
26
26
  /**
27
- * One Streamable HTTP MCP endpoint: `Options`, and every native `McpServer.layerHttp` option
28
- * except `protocols`, `allowedOrigins` among them, with `path` defaulting to `/mcp`.
27
+ * One Streamable HTTP MCP endpoint, speaking MCP 2026-07-28, 2025-11-25 or 2025-06-18, as the
28
+ * client negotiates: `Options`, and every native `McpServer.layerHttp` option, `allowedOrigins`
29
+ * among them, except `protocols` and `allowSessionTermination`, which are fixed, with `path`
30
+ * defaulting to `/mcp`. A client of a 2025 revision opens a session with `initialize`, which
31
+ * lasts until that client ends it with a DELETE, answered 204, or the process stops.
29
32
  */
30
- export interface LayerHttpOptions<E = never, R = never, A extends Any = Any> extends Options<E, R, A>, Omit<Parameters<typeof McpServer.layerHttp>[0], "protocols" | "path"> {
33
+ export interface LayerHttpOptions<E = never, R = never, A extends Any = Any> extends Options<E, R, A>, Omit<Parameters<typeof McpServer.layerHttp>[0], "protocols" | "path" | "allowSessionTermination"> {
31
34
  /** The endpoint's route; defaults to `/mcp`. */
32
35
  readonly path?: HttpRouter.PathInput;
33
36
  readonly authentication?: Any$1;
34
37
  }
35
- /**
36
- * The native server supplies its own request context to every tool call, and the registry
37
- * it registers the tools on, the endpoint's own; over HTTP the router its own, such as the
38
- * request.
39
- */
40
38
  type ToolRequestContext<R> = Exclude<R, McpSchema.McpRequestContext | McpServer.McpServer>;
41
39
  type HttpToolRequestContext<R> = Exclude<ToolRequestContext<R>, HttpRouter.Provided>;
42
- /**
43
- * What the native features owe, `R`, beside the registry, which the endpoint provides them,
44
- * so one registering through `McpServer.registerResource` owes it no host.
45
- */
46
40
  type Features<R> = Exclude<R, McpServer.McpServer>;
47
- /**
48
- * Where `D`, the descriptor given, is inferred, so the provider owed is that descriptor's.
49
- * Without one, tools `A` all public take none, even past a `D` given explicitly; protected
50
- * ones state theirs in `RequiredAuthentication` alone, as an absent `authentication` and the
51
- * one they require would intersect to `never`, reading as every option being wrong.
52
- */
53
41
  type Inferring<D, A extends Any> = [D] extends [undefined] ? [Protected<A>] extends [never] ? {
54
42
  readonly authentication?: undefined;
55
43
  } : unknown : {
56
44
  readonly authentication?: D;
57
45
  };
58
46
  /**
59
- * Serve MCP tools over one Streamable HTTP endpoint, speaking MCP 2026-07-28 only.
47
+ * Serve MCP tools over one Streamable HTTP endpoint, speaking MCP 2026-07-28, 2025-11-25 or
48
+ * 2025-06-18, as the client negotiates. A 2026-07-28 request stands alone; a client of a 2025
49
+ * revision opens a session with `initialize`, which holds what it sent there and no identity,
50
+ * and lasts until that client ends it with a DELETE, answered 204, or the process stops.
60
51
  *
61
52
  * Protected tools need the `authentication` descriptor their contracts name, and the
62
53
  * provider `Authentication.layer` builds from it. On an endpoint of protected tools only,
63
54
  * every request authenticates before it is decoded. On a mixed endpoint, anyone may discover
64
- * the server, list its tools, cancel, and call a public tool; everything else authenticates
65
- * first, so a protected tool's description is listed to anyone but run only for its caller.
66
- * A call refused with `Unauthenticated`, or with `Forbidden` naming scopes, is answered with
67
- * its HTTP status and challenge, 401 or 403, as MCP authorization requires; any other
68
- * failure is a tool result. In a tool call, what middleware provides per request wins over
55
+ * the server, list its tools, cancel, and call a public tool in a 2026-07-28 request;
56
+ * everything else authenticates first, a session's every request included, so a protected
57
+ * tool's description is listed to anyone but run only for its caller.
58
+ * On a Bearer endpoint, a protected tool's call refused with `Unauthenticated`, or with
59
+ * `Forbidden` naming scopes, is answered with its HTTP status and challenge, 401 or 403, as MCP
60
+ * authorization requires, unless its response has started; any other failure is a tool result. Claude Code connects with
61
+ * `claude mcp add --transport http <name> <url>`. In a tool call, what middleware provides per request wins over
69
62
  * what the endpoint was built with; native `features` read only what they were built with,
70
63
  * never a request's. Never provide an identity at startup: a tool call takes its identity
71
64
  * only from the request it authenticated.
65
+ * Guide: `docs/ActionMcp.md` in this package; in an application: `examples/mcp-tools/`.
72
66
  */
73
- export declare function layerHttp<const Apps extends Served, const O extends Selection<ActionOf<Member<Apps>>> = {}, E = never, R = never, const D extends Any$1 | undefined = undefined>(implementations: Apps, options: LayerHttpOptions<E, R, ActionOf<Member<Apps>>> & O & NoInfer<Known<O, LayerHttpOptions<E, R, Any>>> & NoInfer<Required<SelectedOf<O, Apps>>> & Matching<NoInfer<SelectedOf<O, Apps>>, NoInfer<D>> & Inferring<D, NoInfer<SelectedOf<O, Apps>>>): Layer.Layer<never, BuildError<Holding<Member<Apps>, SelectedOf<O, Apps>>, SelectedOf<O, Apps>> | Cause.IllegalArgumentError | E, BuildServices<Holding<Member<Apps>, SelectedOf<O, Apps>>, SelectedOf<O, Apps>> | HttpRouter.HttpRouter | HttpRouter.Request.From<"Requires", HttpToolRequestContext<RemoteRequest<Member<Apps>, SelectedOf<O, Apps>>>> | ProviderOf<D> | Features<R>>;
67
+ export declare function layerHttp<const Apps extends Served, const O extends Selection<ActionOf<Member<Apps>>> = {}, E = never, R = never, const D extends Any$1 | undefined = undefined>(implementations: Apps, options: LayerHttpOptions<E, R, ActionOf<Member<Apps>>> & O & NoInfer<Known<O, LayerHttpOptions<E, R, Any>>> & NoInfer<Required<SelectedOf<O, Apps>>> & Matching<NoInfer<SelectedOf<O, Apps>>, NoInfer<D>> & Inferring<D, NoInfer<SelectedOf<O, Apps>>>): Layer.Layer<never, BuildError<Holding<Member<Apps>, SelectedOf<O, Apps>>, SelectedOf<O, Apps>> | Cause.IllegalArgumentError | E, ServerBuildServices<Holding<Member<Apps>, SelectedOf<O, Apps>>, SelectedOf<O, Apps>> | HttpRouter.HttpRouter | HttpRouter.Request.From<"Requires", HttpToolRequestContext<RemoteRequest<Member<Apps>, SelectedOf<O, Apps>>>> | ProviderOf<D> | Features<R>>;
74
68
  /**
75
69
  * Serve MCP tools through newline-delimited JSON-RPC on standard I/O, speaking MCP
76
70
  * 2026-07-28, 2025-11-25 or 2025-06-18, as the host negotiates: the whole
@@ -1,44 +1,20 @@
1
- import { Anyone } from "./internal/actions.mjs";
2
- import { provideHandlers, select, toList } from "./internal/implementation.mjs";
3
- import { assertAuthentication, promote } from "./internal/authentication.mjs";
4
- import { fieldKey, httpProtocol, isMisplaced, liftOf, liftedFrom } from "./internal/mcp.mjs";
5
- import { recordStepUp } from "./internal/refusal.mjs";
6
- import { logToStderr } from "./internal/console.mjs";
7
- import { bindTools } from "./internal/tools.mjs";
1
+ import { Anyone } from "./contract/rules.mjs";
2
+ import { provideHandlers, select, toList } from "./contract/implementation.mjs";
3
+ import { bindTools } from "./toolkit/tools.mjs";
4
+ import { fieldKey, httpProtocol, isMisplaced, liftOf, liftedFrom, protocols } from "./mcp/protocol.mjs";
5
+ import { assertAuthentication, promote, providerOf } from "./authentication/provider.mjs";
6
+ import { recordStepUp } from "./authentication/refusal.mjs";
7
+ import { withStderrConsole } from "./stdio/console.mjs";
8
8
  import { Cause, Context, Effect, ErrorReporter, Exit, Fiber, JsonPointer, Layer, Option, Predicate, References, Result, Schema, Stream } from "effect";
9
+ import { AiError, McpSchema, McpServer, Tool, Toolkit } from "effect/ai";
9
10
  import { HttpRouter, HttpServerRequest } from "effect/http";
10
- import { AiError, McpProtocol, McpSchema, McpServer, Tool, Toolkit } from "effect/ai";
11
- //#region src/ActionMcp.ts
12
- /**
13
- * The revisions served over stdio: 2026-07-28 and the stateful revisions hosts open stdio
14
- * with, newest first; a host asking for another is offered 2025-11-25. A success is sent as it
15
- * is on each. 2026-07-28 carries any success as `structuredContent`; the adapters of 2025-11-25
16
- * and 2025-06-18 carry only an object there, and list only an object-rooted `outputSchema`. A
17
- * success they do not structure is text alone, a string as itself.
18
- */
19
- const stdioProtocols = [
20
- McpProtocol.v2026_07_28,
21
- McpProtocol.v2025_11_25,
22
- McpProtocol.v2025_06_18
23
- ];
24
- /**
25
- * MCP has a JSON-only wire contract. Success is the encoded success as structured content,
26
- * and its JSON as text, but for its media, each lifted into a block of its own; declared
27
- * failures are returned as JSON text. The native server refuses undeclared arguments and
28
- * publishes closed input schemas.
29
- */
30
- const projection = {
11
+ //#region src/mcp/ActionMcp.ts
12
+ const strictToolProjection = {
31
13
  label: "MCP tool",
32
14
  tool: (tool) => tool.annotate(Tool.Strict, true)
33
15
  };
34
- /** Whether a JSON Schema is one the native server takes for a tool's arguments. */
35
- const isToolJson = Schema.is(McpSchema.ToolJson);
36
- /**
37
- * The JSON Schema of `schema`, a JSON codec, as the native server lists it for a tool: a
38
- * `$ref` at its root resolved, as MCP requires an object root, and its definitions as `$defs`.
39
- * A strict one refuses undeclared keys.
40
- */
41
- const toolJsonSchema = (schema, strict) => {
16
+ const isToolArgumentsSchema = Schema.is(McpSchema.ToolJson);
17
+ const nativeToolJsonSchema = (schema, strict) => {
42
18
  const document = Schema.toJsonSchemaDocument(schema, { onExcessProperty: strict ? "error" : "ignore" });
43
19
  const [scope, key] = Predicate.isString(document.schema.$ref) ? JsonPointer.parseUriFragment(document.schema.$ref) ?? [] : [];
44
20
  const root = scope === "$defs" && key !== void 0 ? document.definitions[key] : document.schema;
@@ -47,58 +23,33 @@ const toolJsonSchema = (schema, strict) => {
47
23
  $defs: document.definitions
48
24
  };
49
25
  };
50
- /**
51
- * Refuse, when the server is made, the actions whose input is not one object, as the root of a
52
- * tool's arguments must be, naming every one, reading the JSON Schema the native
53
- * server reads: not a union, an array, a scalar, nor `Schema.Struct({})`, which takes any value
54
- * but `null`, where the native server dies when the layer builds, suggesting
55
- * `Tool.EmptyParams`. Then refuse those holding media where no tool lifts it, as each action's
56
- * `Lift` says.
57
- */
58
- const assertShapes = (lifted) => {
59
- const inputs = lifted.map(([action]) => action).filter((action) => !isToolJson(toolJsonSchema(Schema.toCodecJson(action.input), true))).map(({ name }) => name);
26
+ const assertToolShapes = (lifted) => {
27
+ const inputs = lifted.map(([action]) => action).filter((action) => !isToolArgumentsSchema(nativeToolJsonSchema(Schema.toCodecJson(action.input), true))).map(({ name }) => name);
60
28
  if (inputs.length > 0) throw new Error(`MCP tool input must be one object with keys, such as a struct: ${inputs.join(", ")}`);
61
29
  const misplaced = lifted.filter(([action, lift]) => isMisplaced(action, lift)).map(([{ name }]) => name);
62
30
  if (misplaced.length > 0) throw new Error(`MCP media must be the success or an array of it, or a top-level field of a struct success, one, optional or a required array: ${misplaced.join(", ")}`);
63
31
  };
64
- /**
65
- * What the native server answers a call that fails other than as its contract declares: a
66
- * defect, an invalid success or error, or an unknown tool.
67
- */
68
- const internalError = "Tool execution failed due to an internal server error.";
69
- /** A tool's error result, `text` its one text block. */
70
- const errorResult = (text) => new McpSchema.CallToolResult({
32
+ const nativeInternalErrorText = "Tool execution failed due to an internal server error.";
33
+ const textErrorResult = (text) => new McpSchema.CallToolResult({
71
34
  isError: true,
72
35
  content: [{
73
36
  type: "text",
74
37
  text
75
38
  }]
76
39
  });
77
- /** The one text block holding `encoded`'s JSON, none for `undefined`, as the native server sends it. */
78
- const textOf = (encoded) => encoded === void 0 ? [] : [{
40
+ const nativeJsonTextOf = (encoded) => encoded === void 0 ? [] : [{
79
41
  type: "text",
80
42
  text: JSON.stringify(encoded)
81
43
  }];
82
- /** The content block a lifted media value is sent as, naming its field where it has one. */
83
- const blockOf = ({ kind, field, value }) => ({
44
+ const contentBlockOf = ({ kind, field, value }) => ({
84
45
  type: kind,
85
46
  data: value.data,
86
47
  mimeType: value.mimeType,
87
48
  ...field === void 0 ? {} : { _meta: { [fieldKey]: field } }
88
49
  });
89
- /** Why a tool call's arguments failed to decode, as the native server tells it, if they did. */
90
- const invalidParameters = (error) => AiError.isAiError(error) && Predicate.isTagged(error.reason, "ToolParameterValidationError") ? error.reason : void 0;
50
+ const parameterValidationErrorOf = (error) => AiError.isAiError(error) && Predicate.isTagged(error.reason, "ToolParameterValidationError") ? error.reason : void 0;
91
51
  const omitRequestServices = Context.omit(McpSchema.McpRequestContext, McpSchema.McpServerClient, HttpServerRequest.HttpServerRequest, References.CurrentLogLevel);
92
- /**
93
- * Register the tools of `toolkit` on the endpoint's registry, as `McpServer.registerToolkit`
94
- * does, lifting the media `lifts` finds in a tool's success into blocks of their own. The native
95
- * registration sends every success as structured content and one JSON text block, and takes
96
- * no option, so this one follows it: its decode options, failure classification, defect
97
- * scrubbing and listing, and the request services it leaves out of what it retains. A tool
98
- * whose action holds no media is registered as the native one would be: a test compares their
99
- * listings and results, not the services each call runs with.
100
- */
101
- const registerTools = Effect.fnUntraced(function* (toolkit, lifts) {
52
+ const registerToolsLiftingMedia = Effect.fnUntraced(function* (toolkit, lifts) {
102
53
  const registry = yield* McpServer.McpServer;
103
54
  const built = yield* toolkit.pipe(Effect.updateContext((context) => Context.merge(context, Context.mergeAll(...Object.values(toolkit.tools).flatMap((tool) => {
104
55
  const key = Context.Service(tool.id);
@@ -107,19 +58,19 @@ const registerTools = Effect.fnUntraced(function* (toolkit, lifts) {
107
58
  context: omitRequestServices(handler.context)
108
59
  }));
109
60
  })))));
110
- const services = omitRequestServices(yield* Effect.context());
111
- const internalToolError = (cause) => {
61
+ const retainedServices = omitRequestServices(yield* Effect.context());
62
+ const propagateInterruptOrReportInternal = (cause) => {
112
63
  const failure = Cause.findFail(cause);
113
- return Result.isFailure(failure) && !Cause.hasDies(cause) ? Effect.failCause(failure.failure) : Effect.logError(cause).pipe(Effect.andThen(Effect.provideContext(ErrorReporter.report(cause), services)), Effect.as(errorResult(internalError)));
64
+ return Result.isFailure(failure) && !Cause.hasDies(cause) ? Effect.failCause(failure.failure) : Effect.logError(cause).pipe(Effect.andThen(Effect.provideContext(ErrorReporter.report(cause), retainedServices)), Effect.as(textErrorResult(nativeInternalErrorText)));
114
65
  };
115
- const handleCause = (cause) => {
66
+ const invalidParamsOrInternal = (cause) => {
116
67
  const failure = Cause.findFail(cause);
117
68
  if (Result.isSuccess(failure)) {
118
69
  const { error } = failure.success;
119
- const invalid = Context.get(Cause.reasonAnnotations(failure.success), Toolkit.FailureOrigin) === "parameters" ? invalidParameters(error) : void 0;
70
+ const invalid = Context.get(Cause.reasonAnnotations(failure.success), Toolkit.FailureOrigin) === "parameters" ? parameterValidationErrorOf(error) : void 0;
120
71
  if (invalid !== void 0) return Effect.fail(new McpSchema.InvalidParams({ message: invalid.message }));
121
72
  }
122
- return internalToolError(cause);
73
+ return propagateInterruptOrReportInternal(cause);
123
74
  };
124
75
  const registrations = yield* Effect.forEach(Object.entries(built.tools), ([name, tool]) => Effect.gen(function* () {
125
76
  const strict = Tool.getStrictMode(tool) === true;
@@ -130,25 +81,25 @@ const registerTools = Effect.fnUntraced(function* (toolkit, lifts) {
130
81
  const lift = lifts.get(name);
131
82
  const media = lift === void 0 || lift.fields.length === 0 ? void 0 : lift;
132
83
  const structured = media === void 0 ? tool.successSchema : media.rest === void 0 ? void 0 : Schema.toCodecJson(media.rest);
133
- const outputSchema = structured === void 0 ? void 0 : yield* Schema.decodeUnknownEffect(McpSchema.ToolOutputJson)(toolJsonSchema(structured, false)).pipe(Effect.orDie);
134
- const inputSchema = yield* Schema.decodeUnknownEffect(McpSchema.ToolJson)(toolJsonSchema(tool.parametersSchema, strict)).pipe(Effect.orDie);
135
- const lifted = new Set(media?.fields.map(({ name }) => name));
136
- const succeeded = ({ encodedResult, result }) => {
84
+ const outputSchema = structured === void 0 ? void 0 : yield* Schema.decodeUnknownEffect(McpSchema.ToolOutputJson)(nativeToolJsonSchema(structured, false)).pipe(Effect.orDie);
85
+ const inputSchema = yield* Schema.decodeUnknownEffect(McpSchema.ToolJson)(nativeToolJsonSchema(tool.parametersSchema, strict)).pipe(Effect.orDie);
86
+ const liftedFieldNames = new Set(media?.fields.map(({ name }) => name));
87
+ const resultWithLiftedMedia = ({ encodedResult, result }) => {
137
88
  if (media === void 0) return new McpSchema.CallToolResult({
138
89
  isError: false,
139
90
  structuredContent: encodedResult,
140
- content: textOf(encodedResult)
91
+ content: nativeJsonTextOf(encodedResult)
141
92
  });
142
- const blocks = liftedFrom(media, result).map(blockOf);
93
+ const blocks = liftedFrom(media, result).map(contentBlockOf);
143
94
  if (structured === void 0) return new McpSchema.CallToolResult({
144
95
  isError: false,
145
96
  content: blocks
146
97
  });
147
- const rest = Predicate.isObject(encodedResult) ? Object.fromEntries(Object.entries(encodedResult).filter(([key]) => !lifted.has(key))) : encodedResult;
98
+ const encodedRestOfStruct = Predicate.isObject(encodedResult) ? Object.fromEntries(Object.entries(encodedResult).filter(([key]) => !liftedFieldNames.has(key))) : encodedResult;
148
99
  return new McpSchema.CallToolResult({
149
100
  isError: false,
150
- structuredContent: rest,
151
- content: [...textOf(rest), ...blocks]
101
+ structuredContent: encodedRestOfStruct,
102
+ content: [...nativeJsonTextOf(encodedRestOfStruct), ...blocks]
152
103
  });
153
104
  };
154
105
  return {
@@ -167,63 +118,53 @@ const registerTools = Effect.fnUntraced(function* (toolkit, lifts) {
167
118
  _meta: Context.getOrUndefined(tool.annotations, Tool.Meta)
168
119
  }),
169
120
  annotations: tool.annotations,
170
- handle: (payload) => built.handle(name, payload ?? {}, void 0, decodeOptions).pipe(Stream.unwrap, Stream.runLast, Effect.flatMap(Effect.fromOption), Effect.flatMap((result) => !result.isFailure ? Effect.succeed(succeeded(result)) : result.failureOrigin === "handler" ? Effect.succeed(new McpSchema.CallToolResult({
121
+ handle: (payload) => built.handle(name, payload ?? {}, void 0, decodeOptions).pipe(Stream.unwrap, Stream.runLast, Effect.flatMap(Effect.fromOption), Effect.flatMap((result) => !result.isFailure ? Effect.succeed(resultWithLiftedMedia(result)) : result.failureOrigin === "handler" ? Effect.succeed(new McpSchema.CallToolResult({
171
122
  isError: true,
172
- content: textOf(result.encodedResult)
173
- })) : Effect.failCause(Cause.annotate(Cause.fail(result.result), Context.make(Toolkit.FailureOrigin, result.failureOrigin ?? "result")))), Effect.catchCause(handleCause), Effect.provideContext(services))
123
+ content: nativeJsonTextOf(result.encodedResult)
124
+ })) : Effect.failCause(Cause.annotate(Cause.fail(result.result), Context.make(Toolkit.FailureOrigin, result.failureOrigin ?? "result")))), Effect.catchCause(invalidParamsOrInternal), Effect.provideContext(retainedServices))
174
125
  };
175
126
  }));
176
127
  for (const registration of registrations) yield* registry.addTool(registration);
177
128
  });
178
129
  const server = (apps, transport, features = Layer.empty, authentication) => {
179
- const binding = bindTools(apps, authentication === void 0 ? projection : {
180
- ...projection,
130
+ const binding = bindTools(apps, authentication === void 0 ? strictToolProjection : {
131
+ ...strictToolProjection,
181
132
  handler: (run, action) => (input) => action.caller === Anyone ? run(input) : recordStepUp(promote(authentication, run(input)))
182
133
  });
183
134
  const lifted = apps.flatMap((app) => app.actions.map((action) => [action, liftOf(action.success)]));
184
- assertShapes(lifted);
135
+ assertToolShapes(lifted);
185
136
  const lifts = new Map(lifted.map(([{ name }, lift]) => [name, lift]));
186
137
  const register = Effect.gen(function* () {
187
138
  const registry = yield* McpServer.McpServer;
188
- const claimed = registry.tools.flatMap(({ tool }) => lifts.has(tool.name) ? [tool.name] : []);
189
- if (claimed.length > 0) return yield* Effect.die(/* @__PURE__ */ new Error(`Duplicate MCP tool: ${claimed.join(", ")}, claimed by an action and a native feature`));
139
+ const namesClaimedByFeatures = registry.tools.flatMap(({ tool }) => lifts.has(tool.name) ? [tool.name] : []);
140
+ if (namesClaimedByFeatures.length > 0) return yield* Effect.die(/* @__PURE__ */ new Error(`Duplicate MCP tool: ${namesClaimedByFeatures.join(", ")}, claimed by an action and a native feature`));
190
141
  const handlers = yield* Layer.build(binding.layer);
191
- yield* registerTools(binding.toolkit, lifts).pipe(Effect.setContext(Context.add(handlers, McpServer.McpServer, registry)));
142
+ yield* registerToolsLiftingMedia(binding.toolkit, lifts).pipe(Effect.setContext(Context.add(handlers, McpServer.McpServer, registry)));
192
143
  });
193
144
  return Layer.effectDiscard(register).pipe(Layer.provide(features), Layer.provide(transport), Layer.fresh, provideHandlers(apps));
194
145
  };
195
- /**
196
- * What an anonymous caller of a mixed endpoint may send besides a public tool's call:
197
- * discovering the server and its tools, which the contracts describe, and cancelling.
198
- * Everything else, a native feature's listing, completion or subscription among them, and
199
- * any method a later revision adds, authenticates.
200
- */
201
- const anonymous = /* @__PURE__ */ new Set([
146
+ const anonymousMethods = /* @__PURE__ */ new Set([
202
147
  "server/discover",
203
148
  "tools/list",
204
149
  "notifications/cancelled"
205
150
  ]);
206
- /**
207
- * Whether a request to an endpoint serving `actions` must authenticate before the native
208
- * server reads its body. An endpoint whose every tool is protected authenticates every
209
- * request, so a host signs in when it connects. Otherwise the request's routing headers
210
- * decide, which the native server refuses when they disagree with the body: discovery and a
211
- * call of a public tool authenticate only a caller presenting credentials, and everything
212
- * else authenticates, so native features are protected too. A request whose headers name
213
- * no public tool as it is authenticates: it fails closed.
214
- */
215
151
  const requiresAuthentication = (actions) => {
216
152
  const open = new Set(actions.filter(({ caller }) => caller === Anyone).map(({ name }) => name));
217
153
  return (headers) => {
218
154
  if (open.size === 0) return true;
155
+ if (headers["mcp-protocol-version"] !== httpProtocol.protocolVersion) return true;
219
156
  const method = headers["mcp-method"];
220
157
  if (method === void 0) return true;
221
- if (anonymous.has(method)) return false;
158
+ if (anonymousMethods.has(method)) return false;
222
159
  if (method !== "tools/call") return true;
223
160
  const name = headers["mcp-name"];
224
161
  return name === void 0 || !open.has(name);
225
162
  };
226
163
  };
164
+ const namesItsMethod = (request) => Effect.match(request.json, {
165
+ onFailure: () => true,
166
+ onSuccess: (body) => !Predicate.hasProperty(body, "method") || body.method === request.headers["mcp-method"]
167
+ });
227
168
  function layerHttp(apps, { actions, features, path, authentication, ...options }) {
228
169
  const selected = select(toList(apps), actions);
229
170
  const served = selected.flatMap((app) => app.actions);
@@ -231,19 +172,20 @@ function layerHttp(apps, { actions, features, path, authentication, ...options }
231
172
  const transport = server(selected, McpServer.layerHttp({
232
173
  ...options,
233
174
  path: path ?? "/mcp",
234
- protocols: [httpProtocol]
175
+ protocols,
176
+ allowSessionTermination: true
235
177
  }), features, authentication);
236
178
  if (authentication === void 0) return transport;
237
179
  const required = requiresAuthentication(served);
238
- const middleware = HttpRouter.middleware(Effect.map(authentication["~provider"], (runtime) => (route) => Effect.flatMap(HttpServerRequest.HttpServerRequest, (request) => runtime.http(route, !required(request.headers)))));
180
+ const middleware = HttpRouter.middleware(Effect.map(providerOf(authentication), (runtime) => (route) => Effect.flatMap(HttpServerRequest.HttpServerRequest, (request) => request.method !== "POST" || required(request.headers) ? runtime.http(route, false) : Effect.flatMap(namesItsMethod(request), (named) => runtime.http(route, named)))));
239
181
  return transport.pipe(Layer.provide(middleware.layer));
240
182
  }
241
183
  function runStdio(apps, { actions, features, ...options }) {
242
184
  const transport = server(select(toList(apps), actions), McpServer.layerStdio({
243
185
  ...options,
244
- protocols: stdioProtocols
186
+ protocols
245
187
  }), features);
246
- return Layer.launch(transport).pipe(logToStderr, Effect.forkChild, Effect.flatMap(Fiber.await), Effect.flatMap((exit) => Exit.isFailure(exit) && !Cause.hasInterruptsOnly(exit.cause) ? Effect.failCause(exit.cause) : Effect.void));
188
+ return Layer.launch(transport).pipe(withStderrConsole, Effect.forkChild, Effect.flatMap(Fiber.await), Effect.flatMap((exit) => Exit.isFailure(exit) && !Cause.hasInterruptsOnly(exit.cause) ? Effect.failCause(exit.cause) : Effect.void));
247
189
  }
248
190
  //#endregion
249
191
  export { layerHttp, runStdio };
@@ -0,0 +1,147 @@
1
+ import { Anyone, Certain, ErrorsOf } from "./contract/rules.mjs";
2
+ import { BuiltIn, BuiltIns } from "./contract/errors.mjs";
3
+ import { BuildError, Holding, Known, Member, MiddlewareOf, Protected, Selected, Served, Serving } from "./contract/implementation.mjs";
4
+ import { Call } from "./contract/call.mjs";
5
+ import { Action_d_exports, Any as Any$1, Errors } from "./Action.mjs";
6
+ import { Any as Any$2, DescriptorOf, Identity, Matching, PublicOnly, RemoteRequest, Required, ServedProvider, ServerBuildServices, VerifierError } from "./authentication/provider.mjs";
7
+ import { Context, Effect, Layer, Schema, Scope, Types } from "effect";
8
+ import { HttpRouter } from "effect/http";
9
+ import { Rpc, RpcClient, RpcClientError, RpcGroup, RpcMiddleware, RpcServer } from "effect/rpc";
10
+ //#region src/rpc/ActionRpc.d.ts
11
+ type BindingError = Errors[number];
12
+ type ErrorOf<A extends Any$1, E extends Errors, D> = Schema.Union<ReadonlyArray<A["error"][number] | E[number] | VerifierError<A, D> | BuiltIns>>;
13
+ type RpcOf<A extends Any$1, E extends Errors, D> = A extends Any$1 ? Rpc.Rpc<A["name"], A["input"], A["success"], ErrorOf<A, E, D>> : never;
14
+ /**
15
+ * An RPC binding: actions, the errors every rpc declares, and the descriptor authenticating
16
+ * its protected ones. Plain data, so a copy of it, or one made by another installed copy of
17
+ * this package, serves the same; `layer` serves it and `client` calls it.
18
+ */
19
+ export interface Binding<Actions extends ReadonlyArray<Any$1>, E extends Errors = [], D extends Any$2 | undefined = undefined> {
20
+ readonly authentication: D;
21
+ /** The exact actions bound to this binding. */
22
+ readonly actions: Actions;
23
+ /** The errors every rpc declares besides its action's own, as a list. */
24
+ readonly error: E;
25
+ /**
26
+ * The native group of one rpc per action, named after it, as clients see it:
27
+ * `RpcClient.make(Rpc.group)` is the native client.
28
+ */
29
+ readonly group: RpcGroup.RpcGroup<RpcOf<Actions[number], E, D>>;
30
+ }
31
+ /**
32
+ * Any RPC binding. Its group is any native group here, since native groups are invariant in
33
+ * their rpcs; `actions` carries the types.
34
+ */
35
+ export interface Any {
36
+ readonly authentication?: Any$2 | undefined;
37
+ readonly actions: ReadonlyArray<Any$1>;
38
+ readonly error: Errors;
39
+ readonly group: RpcGroup.Any;
40
+ }
41
+ /** Contract-level configuration shared by servers and clients. */
42
+ export interface Options<E extends Errors = Errors> {
43
+ /** Layer middleware's errors, which every rpc declares: one schema, or a list. */
44
+ readonly error?: E | E[number];
45
+ readonly authentication?: Any$2;
46
+ }
47
+ /**
48
+ * Bind actions once, for servers and clients alike, each as an rpc named after it, taking
49
+ * its action's input and answering its success. Every rpc declares its action's errors, the
50
+ * binding's `error`, and the built-in `InvalidInput`, `Unauthenticated` and `Forbidden`, so
51
+ * clients decode each as a typed failure; a protected one also declares what its descriptor's
52
+ * verifier fails with. Browser-safe, as an HTTP binding is.
53
+ * Guide: `docs/ActionRpc.md` in this package; in an application: `examples/live-rpc/`.
54
+ */
55
+ export declare function make<const Actions extends ReadonlyArray<Any$1 & {
56
+ readonly caller: typeof Anyone;
57
+ }>>(actions: Actions): Binding<Actions, [], undefined>;
58
+ export declare function make<const Actions extends ReadonlyArray<Any$1>, const O extends Options = {}>(actions: Actions, options: O & Required<Actions[number]> & Matching<Actions[number], NoInfer<DescriptorOf<O>>> & NoInfer<Known<O, Options>>): Binding<Actions, ErrorsOf<O>, DescriptorOf<O>>;
59
+ export declare function make<const Actions extends ReadonlyArray<Any$1>>(actions: Actions & PublicOnly<Actions>): Binding<Actions, [], undefined>;
60
+ type Middleware = ReadonlyArray<RpcMiddleware.AnyService>;
61
+ type IdOf<K> = K extends Context.Key<infer I extends RpcMiddleware.AnyId, unknown> ? I : never;
62
+ type Through<M extends Middleware, R> = number extends M["length"] ? R | RpcMiddleware.Requires<IdOf<M[number]>> : M extends readonly [infer K, ...infer Rest extends Middleware] ? Through<Rest, true extends Types.IsUnion<IdOf<K>> ? R | RpcMiddleware.Requires<IdOf<K>> : RpcMiddleware.ApplyServices<IdOf<K>, R>> : R;
63
+ type ForClient<K> = K extends {
64
+ readonly requiredForClient: infer F;
65
+ } ? true extends F ? K : never : never;
66
+ type ServerOnly<M extends Middleware, E extends Errors> = [Exclude<RpcMiddleware.Error<IdOf<M[number]>>, Extract<Certain<E>, BindingError>["Type"] | BuiltIn> | ForClient<M[number]>] extends [never] ? unknown : {
67
+ readonly "Layer middleware fails only with the binding's errors and needs no client": never;
68
+ };
69
+ type LayerRequest<App, A extends Any$1, M extends Middleware> = Exclude<Through<M, RemoteRequest<App, A>>, [Extract<Serving<App, A>, {
70
+ readonly caller: typeof Anyone;
71
+ }>] extends [never] ? Identity<Protected<Serving<App, A>>> : never>;
72
+ type RpcLayer<B extends Any, Apps extends Served, M extends Middleware, A extends Any$1 = B["actions"][number]> = Layer.Layer<never, BuildError<Holding<Member<Apps>, A>, A>, ServerBuildServices<Holding<Member<Apps>, A>, A> | RpcServer.Protocol | HttpRouter.Request.From<"Requires", LayerRequest<Member<Apps>, A, M>> | ServedProvider<Member<Apps>, A, B extends {
73
+ readonly authentication: infer D;
74
+ } ? D : never> | IdOf<M[number]>>;
75
+ /** `ActionRpc.layer`'s options. */
76
+ export interface LayerOptions<M extends Middleware = [], A extends Any$1 = never> {
77
+ /**
78
+ * The actions it serves, among the binding's actions the implementations hold: `[GetUser]`.
79
+ * Defaults to every one of them. A layer whose middleware requires the identity lists
80
+ * protected actions only.
81
+ */
82
+ readonly actions?: ReadonlyArray<A> | undefined;
83
+ /**
84
+ * Native rpc middleware every served rpc runs, the first innermost: inside the
85
+ * authentication of a protected rpc, outside its decoding. Server-only: the binding's group,
86
+ * which clients use, carries none. A middleware requiring the identity needs a layer
87
+ * serving only protected actions.
88
+ */
89
+ readonly middleware?: M;
90
+ }
91
+ /**
92
+ * Serve the binding's actions among `implementations` with Effect's own `RpcServer`, or those
93
+ * its `actions` lists, as `ActionHttp.layer` selects them. The host provides the protocol,
94
+ * such as `RpcServer.layerProtocolWebsocket` or `layerProtocolHttp` and its JSON serialization:
95
+ * the layer refuses to build under a protocol that does not speak JSON. A protected rpc
96
+ * authenticates each message from its headers with the binding's descriptor, outermost; then
97
+ * the layer's `middleware` runs, then the input is decoded, a failure answering `InvalidInput`,
98
+ * then the implementation's `authorize` and the handler, as on every surface.
99
+ *
100
+ * The server serves its own copy of the binding's group, whose payloads, successes and errors
101
+ * are the actions' JSON and which carries no native middleware: the protocol decodes a payload before
102
+ * any middleware runs, and answers one that does not decode with a defect, so each handler
103
+ * runs the authentication and the middleware itself, then decodes the input. A defect is
104
+ * logged with its cause, reported to the call's `ErrorReporter`s, and answered as its
105
+ * request's generic failure, never failing the connection's other calls; a declared failure is
106
+ * the caller's answer, and not reported.
107
+ *
108
+ * The layer fails as the builders do, needs at startup what they need, and per request what
109
+ * each implementation's authorization and the handlers of the actions it serves need, as
110
+ * router request markers, until its `middleware` provides them: over a router-mounted
111
+ * protocol, the route's request context does too. The identity of a protected action is
112
+ * provided by its authentication alone.
113
+ */
114
+ export declare function layer<const B extends Any, const Apps extends Served>(binding: B, implementations: Apps): RpcLayer<B, Apps, []>;
115
+ export declare function layer<const B extends Any, const Apps extends Served, const O extends LayerOptions<Middleware, B["actions"][number]>>(binding: B, implementations: Apps, options: O & ServerOnly<MiddlewareOf<O, Middleware>, B["error"]> & NoInfer<Known<O, LayerOptions<Middleware>>>): RpcLayer<B, Apps, MiddlewareOf<O, Middleware>, Selected<O, B["actions"][number]>>;
116
+ /**
117
+ * What one call of an action `A` fails with, beside the binding's errors `E`: its declared and
118
+ * built-in errors, and Effect's own `RpcClientError`. Input that does not encode is
119
+ * `InvalidInput`, and nothing is sent; an answer that does not decode is a defect, as the
120
+ * native client makes it.
121
+ */
122
+ export type MethodError<A extends Any$1, E extends BindingError = never> = A["error"][number]["Type"] | E["Type"] | BuiltIns["Type"] | RpcClientError.RpcClientError;
123
+ type Method<A extends Any$1, E extends BindingError> = Call<A, Effect.Effect<A["success"]["Type"], MethodError<A, E>>>;
124
+ /**
125
+ * Every action of the binding `B`, as `client.<action>(input)`: a protected one may also fail
126
+ * with what its descriptor's verifier declares.
127
+ */
128
+ export type Client<B extends Any> = { readonly [A in B["actions"][number] as A["name"]]: Method<A, B["error"][number] | VerifierError<A, B["authentication"]>>; };
129
+ /**
130
+ * The native `RpcClient.make` options `client` takes: `spanPrefix`, `spanAttributes`,
131
+ * `generateRequestId` and `disableTracing`. `flatten` changes the client's shape, which the
132
+ * method types cannot follow.
133
+ */
134
+ export type ClientOptions = Omit<NonNullable<Parameters<typeof RpcClient.make>[1]>, "flatten">;
135
+ /**
136
+ * Effect's native `RpcClient` for a binding, one method per action taking the action's input
137
+ * directly: `client.greet({ name })`, as `ActionHttp.client`'s. The argument may be omitted
138
+ * when `{}` is a valid input. Requires the native client `Protocol` and a `Scope`, as
139
+ * `RpcClient.make` does; headers set with `RpcClient.withHeaders` around a call go with its
140
+ * message. The native client itself stays available: `RpcClient.make(Rpc.group)`.
141
+ *
142
+ * A call fails with a declared error value (the action's own, the binding's, a built-in
143
+ * `InvalidInput`, `Unauthenticated` or `Forbidden`, or on a protected rpc its descriptor's),
144
+ * or with Effect's own `RpcClientError` when the server could not be reached.
145
+ */
146
+ export declare function client<const B extends Any>(binding: B, options?: ClientOptions): Effect.Effect<Client<B>, never, RpcClient.Protocol | Scope.Scope>;
147
+ //#endregion