@gjermundgaraba/effect-actions 0.15.0 → 0.16.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.
@@ -25,14 +25,21 @@ export interface Options<E = never, R = never, A extends Any = Any> extends Omit
25
25
  readonly features?: Layer.Layer<never, E, R> | undefined;
26
26
  }
27
27
  /**
28
- * One Streamable HTTP MCP endpoint: `Options`, every native `McpServer.layerHttp` option,
29
- * `allowedOrigins` among them, except `protocols` and `allowSessionTermination`, which are fixed,
30
- * with `path` defaulting to `/mcp`, and `requestScoped`.
28
+ * One Streamable HTTP MCP endpoint: `Options`, `authentication`, `requestScoped`, and every native
29
+ * `McpServer.layerHttp` option, `allowedOrigins` among them, except `protocols` and
30
+ * `allowSessionTermination`, which are fixed.
31
31
  */
32
32
  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"> {
33
33
  /** The endpoint's route; defaults to `/mcp`. */
34
34
  readonly path?: HttpRouter.PathInput;
35
35
  readonly authentication?: Any$1;
36
+ /**
37
+ * Whether MCP 2026-07-28 clients may subscribe, holding a stream open for list changes and
38
+ * resource updates. Defaults to whether `features` are given: an endpoint of actions alone
39
+ * registers its tools once, before any client connects, and serves no resource. Pass `true`
40
+ * where a handler changes the server through `McpServer.McpServer`.
41
+ */
42
+ readonly allowSubscriptions?: boolean | undefined;
36
43
  /**
37
44
  * `true` runs a server per request, as Effect's `McpServer.layerHttpRequestScoped` does, for a
38
45
  * host such as Cloudflare Workers where nothing may outlive a request. It speaks MCP 2026-07-28
@@ -148,7 +148,7 @@ const namesItsMethod = (request) => Effect.match(request.json, {
148
148
  onFailure: () => true,
149
149
  onSuccess: (body) => !Predicate.hasProperty(body, "method") || body.method === request.headers["mcp-method"]
150
150
  });
151
- function layerHttp(apps, { actions, features, path = defaultPath, authentication, requestScoped, ...options }) {
151
+ function layerHttp(apps, { actions, features, path = defaultPath, authentication, requestScoped, allowSubscriptions = features !== void 0, ...options }) {
152
152
  const selected = select(toList(apps), actions);
153
153
  const served = selected.flatMap((app) => app.actions);
154
154
  assertAuthentication(served, authentication);
@@ -160,7 +160,8 @@ function layerHttp(apps, { actions, features, path = defaultPath, authentication
160
160
  ...options,
161
161
  path,
162
162
  protocols,
163
- allowSessionTermination: true
163
+ allowSessionTermination: true,
164
+ allowSubscriptions
164
165
  }), features, authentication);
165
166
  if (authentication === void 0) return transport;
166
167
  const required = requiresAuthentication(served);
@@ -19,6 +19,7 @@ Search for a fragment of the message. The tasks are in [ActionMcp.md](ActionMcp.
19
19
  - An MCP client gets an `isError` refusal, not the 401 or 403 it re-authorizes on: the handler sent a notification first, which starts the response, so its refusal is a tool result. Refuse in `authorize`.
20
20
  - A client that opens with `initialize` gets 401 where it lists the tools or calls a public one, on an endpoint that also serves protected tools: a session's every request authenticates. Sign the client in, or serve the public tools on an endpoint of their own.
21
21
  - A client that opens with `initialize`, such as Codex, gets 400 with JSON-RPC error `-32020`, `MCP-Protocol-Version header is required`, or 401 signed out where the endpoint serves a protected tool, and a `DELETE` gets 405: the endpoint is `requestScoped`, which speaks MCP 2026-07-28 alone and keeps no session. Connect that client to an endpoint without it, on a host that keeps a process running.
22
+ - `subscriptions/listen` answered with JSON-RPC error `-32601`, Method not found: an endpoint without `features` has no 2026-07-28 subscriptions unless `allowSubscriptions` is `true`, and a `requestScoped` one has none. Pass `true` where a handler changes the server through `McpServer.McpServer`.
22
23
  - A request over HTTP answers 400, `MCP-Session-Id header is required; send initialize to start a session`: its `MCP-Protocol-Version` header is absent or names a 2025 revision, so it is a session's request, and it follows no `initialize`. One answering 404 names a session that ended, or one of a process since restarted: the client opens another.
23
24
  - A hand-written HTTP request answers 400 with JSON-RPC error `-32602`, `Required request metadata is missing` or `io.modelcontextprotocol/clientCapabilities request metadata is required`: it names 2026-07-28 in `MCP-Protocol-Version` and its `params._meta` lacks `io.modelcontextprotocol/protocolVersion`, for the first message, or `io.modelcontextprotocol/clientCapabilities`, for the second. Build it with `ActionMcp.request`, or copy the `curl` of [mcp-tools](../examples/mcp-tools/README.md#connect-a-client).
24
25
  - A hand-written HTTP request answers 400 with JSON-RPC error `-32020`, `MCP-Protocol-Version header is required`, `Mcp-Method header does not match request method` or `Mcp-Name header does not match request parameters`: those headers repeat a 2026-07-28 request's revision, method and `tools/call` name, and one is absent or differs from its body. Build it with `ActionMcp.request`.
package/docs/ActionMcp.md CHANGED
@@ -25,22 +25,22 @@ Import `@gjermundgaraba/effect-actions/ActionMcp`.
25
25
 
26
26
  `implementations` is one implementation or a list.
27
27
 
28
- | Option | Meaning |
29
- | ------------------------------------ | ---------------------------------------------------------- |
30
- | `name`, `version` | Required server information. |
31
- | `description`, `websiteUrl`, `icons` | Optional server information. |
32
- | `instructions` | Optional text for the model about the server as a whole. |
33
- | `extensions` | Optional native capability extensions. |
34
- | `actions` | The implementations' actions that are tools; default all. |
35
- | `features` | One Layer of native resources, prompts and tools to serve. |
36
- | `path` | HTTP only: the endpoint's path; defaults to `/mcp`. |
37
- | `allowedOrigins` | HTTP only: the exact origins allowed; none by default. |
38
- | `allowSubscriptions` | HTTP only: `false` turns off 2026-07-28 subscriptions. |
39
- | `requestScoped` | HTTP only: `true` runs a server per request. |
40
- | `authentication` | HTTP only: the protected tools' authentication descriptor. |
28
+ | Option | Meaning |
29
+ | ------------------------------------ | ----------------------------------------------------------- |
30
+ | `name`, `version` | Required server information. |
31
+ | `description`, `websiteUrl`, `icons` | Optional server information. |
32
+ | `instructions` | Optional text for the model about the server as a whole. |
33
+ | `extensions` | Optional native capability extensions. |
34
+ | `actions` | The implementations' actions that are tools; default all. |
35
+ | `features` | One Layer of native resources, prompts and tools to serve. |
36
+ | `path` | HTTP only: the endpoint's path; defaults to `/mcp`. |
37
+ | `allowedOrigins` | HTTP only: the exact origins allowed; none by default. |
38
+ | `allowSubscriptions` | HTTP only: 2026-07-28 subscriptions; default if `features`. |
39
+ | `requestScoped` | HTTP only: `true` runs a server per request. |
40
+ | `authentication` | HTTP only: the protected tools' authentication descriptor. |
41
41
 
42
42
  All but `actions`, `features`, `authentication` and `requestScoped` are Effect's own `McpServer`
43
- options, passed through. Its `protocols` and `allowSessionTermination` are not options.
43
+ options. Its `protocols` and `allowSessionTermination` are not options.
44
44
 
45
45
  `client` takes `url`, default `/mcp`, `transformClient`, and the endpoint's `authentication`, so an
46
46
  error its verifier declares decodes; `request` takes `url` and `headers`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjermundgaraba/effect-actions",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "Define Effect action contracts once and project them onto HTTP, MCP, Effect RPC, native Toolkits, and CLIs.",
5
5
  "keywords": [
6
6
  "ai-agents",