@gjermundgaraba/effect-actions 0.5.0 → 0.7.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.
- package/README.md +7 -6
- package/dist/Action.d.mts +16 -8
- package/dist/Action.mjs +0 -1
- package/dist/ActionCatalog.d.mts +2 -3
- package/dist/ActionCatalog.mjs +4 -5
- package/dist/ActionCli.mjs +5 -1
- package/dist/ActionCliClient.d.mts +8 -9
- package/dist/ActionCliClient.mjs +4 -11
- package/dist/ActionGroup.d.mts +15 -11
- package/dist/ActionGroup.mjs +8 -2
- package/dist/ActionHttp.d.mts +9 -5
- package/dist/ActionHttp.mjs +14 -9
- package/dist/ActionHttpClient.d.mts +33 -0
- package/dist/ActionHttpClient.mjs +16 -0
- package/dist/ActionMcp.d.mts +16 -26
- package/dist/ActionMcp.mjs +12 -8
- package/dist/ActionToolkit.d.mts +10 -11
- package/dist/Testing.d.mts +35 -4
- package/dist/Testing.mjs +53 -6
- package/dist/TestingClient.d.mts +6 -5
- package/dist/TestingClient.mjs +6 -3
- package/dist/internal/actions.d.mts +18 -7
- package/dist/internal/actions.mjs +11 -1
- package/dist/internal/fetchClient.d.mts +6 -0
- package/dist/internal/fetchClient.mjs +8 -0
- package/dist/internal/implementation.d.mts +14 -15
- package/dist/internal/implementation.mjs +6 -5
- package/dist/internal/tools.mjs +1 -1
- package/docs/Action.md +11 -10
- package/docs/ActionCatalog.md +9 -9
- package/docs/ActionCli.md +7 -7
- package/docs/ActionCliClient.md +4 -4
- package/docs/ActionGroup.md +24 -16
- package/docs/ActionHttp.md +14 -12
- package/docs/ActionHttpClient.md +80 -0
- package/docs/ActionMcp.md +19 -22
- package/docs/ActionToolkit.md +1 -1
- package/docs/CONTEXT.md +4 -4
- package/docs/README.md +18 -18
- package/docs/Testing.md +27 -16
- package/docs/guarantees.md +5 -5
- package/docs/setup.md +7 -6
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -7,7 +7,6 @@ Effect's own servers and clients underneath.
|
|
|
7
7
|
## Looks like this
|
|
8
8
|
|
|
9
9
|
```ts
|
|
10
|
-
import { McpProtocol } from "effect/unstable/ai";
|
|
11
10
|
import { Effect, Layer, Schema } from "effect";
|
|
12
11
|
import * as Action from "@gjermundgaraba/effect-actions/Action";
|
|
13
12
|
import * as ActionGroup from "@gjermundgaraba/effect-actions/ActionGroup";
|
|
@@ -32,7 +31,6 @@ const app = Actions.implement({
|
|
|
32
31
|
export const routes = Layer.mergeAll(
|
|
33
32
|
Http.layer([app]),
|
|
34
33
|
ActionMcp.layerHttp([app], {
|
|
35
|
-
protocols: [McpProtocol.v2026_07_28],
|
|
36
34
|
name: "greetings",
|
|
37
35
|
version: "1.0.0",
|
|
38
36
|
path: "/mcp",
|
|
@@ -60,6 +58,9 @@ const greeting = Effect.gen(function* () {
|
|
|
60
58
|
});
|
|
61
59
|
```
|
|
62
60
|
|
|
61
|
+
Code that does not run Effects, such as a browser page, gets the same calls as Promises:
|
|
62
|
+
`await ActionHttpClient.promise(Http, { baseUrl }).greetings.greet({ name: "Ada" })`.
|
|
63
|
+
|
|
63
64
|
## Why
|
|
64
65
|
|
|
65
66
|
- Input, success, and errors are declared once. Routes, tools, commands, clients, OpenAPI, and the catalog are derived from that declaration, so they cannot disagree.
|
|
@@ -72,10 +73,10 @@ const greeting = Effect.gen(function* () {
|
|
|
72
73
|
## Install
|
|
73
74
|
|
|
74
75
|
```sh
|
|
75
|
-
pnpm add @gjermundgaraba/effect-actions effect@4.0.0-rc.
|
|
76
|
+
pnpm add @gjermundgaraba/effect-actions effect@4.0.0-rc.117
|
|
76
77
|
```
|
|
77
78
|
|
|
78
|
-
Add `@effect/platform-node@4.0.0-rc.
|
|
79
|
+
Add `@effect/platform-node@4.0.0-rc.117` to serve from Node. Every module is a subpath import
|
|
79
80
|
(`.../Action`, `.../ActionHttp`, ...); there is no package root, so a contracts-only bundle
|
|
80
81
|
never loads a server.
|
|
81
82
|
|
|
@@ -100,8 +101,8 @@ in `node_modules/@gjermundgaraba/effect-actions/docs`) gives the same content.
|
|
|
100
101
|
|
|
101
102
|
## Status
|
|
102
103
|
|
|
103
|
-
The `effect` peer
|
|
104
|
-
|
|
104
|
+
The `effect` peer accepts any Effect 4.0 release candidate from `4.0.0-rc.116` on; the package
|
|
105
|
+
is built and tested against the release candidate in its `devDependencies` (see [docs/setup.md](docs/setup.md)). Actions are unary JSON over HTTP and MCP: no streaming, uploads, prompts, or resources.
|
|
105
106
|
Authentication and authorization belong to the application. See [docs/setup.md](docs/setup.md).
|
|
106
107
|
|
|
107
108
|
## Acknowledgements
|
package/dist/Action.d.mts
CHANGED
|
@@ -30,7 +30,7 @@ type ResolvedMcp<Name extends string, Mcp extends false | McpOptions | undefined
|
|
|
30
30
|
readonly openWorld: boolean;
|
|
31
31
|
};
|
|
32
32
|
/** What `make` needs to define an action. */
|
|
33
|
-
export interface Options<Input extends Codec, Output extends Codec, Errors extends ReadonlyArray<Codec>, Acc extends Access = Access,
|
|
33
|
+
export interface Options<Input extends Codec, Output extends Codec, Errors extends ReadonlyArray<Codec>, Acc extends Access = Access, Mcp extends false | McpOptions | undefined = McpOptions | undefined> {
|
|
34
34
|
readonly description: string;
|
|
35
35
|
/** Omit for an action without arguments. */
|
|
36
36
|
readonly input?: Input;
|
|
@@ -43,32 +43,40 @@ export interface Options<Input extends Codec, Output extends Codec, Errors exten
|
|
|
43
43
|
* library itself authorizes nothing.
|
|
44
44
|
*/
|
|
45
45
|
readonly access: Acc;
|
|
46
|
-
/** `false` hides the action from HTTP routes and clients. */
|
|
47
|
-
readonly http?: Http;
|
|
48
46
|
/** `false` hides the action from MCP; otherwise tool metadata. */
|
|
49
47
|
readonly mcp?: Mcp;
|
|
50
48
|
}
|
|
51
49
|
/** A pure contract: schemas and transport metadata. Implementations are bound by `ActionGroup.implement`. */
|
|
52
|
-
export interface Action<Name extends string, Input extends Codec, Output extends Codec, Errors extends ReadonlyArray<Codec>, Acc extends Access = Access,
|
|
50
|
+
export interface Action<Name extends string, Input extends Codec, Output extends Codec, Errors extends ReadonlyArray<Codec>, Acc extends Access = Access, Mcp extends false | McpOptions | undefined = McpOptions | undefined> {
|
|
53
51
|
readonly name: Name;
|
|
54
52
|
readonly description: string;
|
|
55
53
|
readonly input: Input;
|
|
56
54
|
readonly success: Output;
|
|
57
55
|
readonly errors: Errors;
|
|
58
56
|
readonly access: Acc;
|
|
59
|
-
readonly http: Http;
|
|
60
57
|
readonly mcp: ResolvedMcp<Name, Mcp>;
|
|
61
58
|
}
|
|
62
59
|
/** Any action, with its schemas erased. */
|
|
63
|
-
export type Any = Action<string, Codec, Codec, ReadonlyArray<Codec>, Access,
|
|
60
|
+
export type Any = Action<string, Codec, Codec, ReadonlyArray<Codec>, Access, false> | Action<string, Codec, Codec, ReadonlyArray<Codec>, Access, McpOptions> | Action<string, Codec, Codec, ReadonlyArray<Codec>, Access, undefined>;
|
|
64
61
|
/** Receives decoded input; may fail only with the declared errors. */
|
|
65
62
|
export type Handler<A extends Any, R = never> = (input: A["input"]["Type"]) => Effect.Effect<A["success"]["Type"], A["errors"][number]["Type"], R>;
|
|
66
63
|
/** An empty object schema that also produces the object root MCP requires. */
|
|
67
64
|
declare const NoInput: Schema.$Record<Schema.String, Schema.Never>;
|
|
65
|
+
type AnyOptions = Options<Codec, Codec, ReadonlyArray<Codec>, Access, false | McpOptions | undefined>;
|
|
66
|
+
/**
|
|
67
|
+
* The `mcp` option `make` received. Only a required literal `false` hides the action:
|
|
68
|
+
* options that may carry something else, such as a conditional spread, `false | undefined`
|
|
69
|
+
* or a broadly typed variable, may serve it, so its requirements are kept.
|
|
70
|
+
*/
|
|
71
|
+
type McpOf<O extends AnyOptions> = [O] extends [{
|
|
72
|
+
readonly mcp: false;
|
|
73
|
+
}] ? false : O extends {
|
|
74
|
+
readonly mcp: infer Mcp extends false | McpOptions | undefined;
|
|
75
|
+
} ? Mcp : "mcp" extends keyof O ? O["mcp"] | undefined : undefined;
|
|
68
76
|
/**
|
|
69
77
|
* Define an action contract. Names are `[A-Za-z0-9_-]+`, other than `then`.
|
|
70
|
-
*
|
|
78
|
+
* HTTP serves every action of a group it binds; `mcp: false` hides one from MCP.
|
|
71
79
|
*/
|
|
72
|
-
export declare function make<const Name extends string,
|
|
80
|
+
export declare function make<const Name extends string, const O extends AnyOptions>(name: Name, options: O & { readonly [K in Exclude<keyof O, keyof AnyOptions>]: never; }): Action<Name, "input" extends keyof O ? Exclude<O["input"], undefined> : typeof NoInput, O["success"], "errors" extends keyof O ? Exclude<O["errors"], undefined> : [], O["access"], McpOf<O>>;
|
|
73
81
|
//#endregion
|
|
74
82
|
export { Action_d_exports };
|
package/dist/Action.mjs
CHANGED
package/dist/ActionCatalog.d.mts
CHANGED
|
@@ -8,7 +8,6 @@ export interface Entry {
|
|
|
8
8
|
readonly group: string;
|
|
9
9
|
readonly name: string;
|
|
10
10
|
readonly description: string;
|
|
11
|
-
readonly http: boolean;
|
|
12
11
|
readonly mcp: Any["mcp"];
|
|
13
12
|
readonly input: JsonSchema.JsonSchema;
|
|
14
13
|
readonly success: JsonSchema.JsonSchema;
|
|
@@ -19,13 +18,13 @@ export interface Entry {
|
|
|
19
18
|
}
|
|
20
19
|
/** An offline contract document, not an authorization decision or a mounted endpoint. */
|
|
21
20
|
export interface Catalog {
|
|
22
|
-
readonly version: "
|
|
21
|
+
readonly version: "4";
|
|
23
22
|
readonly actions: ReadonlyArray<Entry>;
|
|
24
23
|
}
|
|
25
24
|
/**
|
|
26
25
|
* Describe the supplied groups without acquiring implementations. Schemas describe
|
|
27
26
|
* encoded action values, not MCP's `{ value }` envelope or deployment-specific URLs.
|
|
28
|
-
*
|
|
27
|
+
* The host chooses what, if anything, to publish.
|
|
29
28
|
*/
|
|
30
29
|
export declare const make: (...groups: ReadonlyArray<Any$1>) => Catalog;
|
|
31
30
|
//#endregion
|
package/dist/ActionCatalog.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { assertDistinct } from "./internal/actions.mjs";
|
|
1
|
+
import { assertDistinct, policyErrors } from "./internal/actions.mjs";
|
|
2
2
|
import { JsonSchema, Schema } from "effect";
|
|
3
3
|
//#region src/ActionCatalog.ts
|
|
4
4
|
/** Describe the JSON wire form; decoding on the server remains authoritative. */
|
|
@@ -13,23 +13,22 @@ const describe = (codec) => {
|
|
|
13
13
|
/**
|
|
14
14
|
* Describe the supplied groups without acquiring implementations. Schemas describe
|
|
15
15
|
* encoded action values, not MCP's `{ value }` envelope or deployment-specific URLs.
|
|
16
|
-
*
|
|
16
|
+
* The host chooses what, if anything, to publish.
|
|
17
17
|
*/
|
|
18
18
|
const make = (...groups) => {
|
|
19
19
|
assertDistinct("catalog group", groups.map((group) => group.name));
|
|
20
20
|
return {
|
|
21
|
-
version: "
|
|
21
|
+
version: "4",
|
|
22
22
|
actions: groups.flatMap((group) => group.actions.map((action) => ({
|
|
23
23
|
id: `${group.name}.${action.name}`,
|
|
24
24
|
group: group.name,
|
|
25
25
|
name: action.name,
|
|
26
26
|
description: action.description,
|
|
27
|
-
http: action.http,
|
|
28
27
|
mcp: action.mcp,
|
|
29
28
|
input: describe(action.input),
|
|
30
29
|
success: describe(action.success),
|
|
31
30
|
errors: action.errors.map(describe),
|
|
32
|
-
httpSchemaErrors:
|
|
31
|
+
httpSchemaErrors: group.schemaError === void 0 ? [] : policyErrors(group.schemaError).map(describe)
|
|
33
32
|
})))
|
|
34
33
|
};
|
|
35
34
|
};
|
package/dist/ActionCli.mjs
CHANGED
|
@@ -4,6 +4,10 @@ import { command as command$1 } from "./internal/cli.mjs";
|
|
|
4
4
|
import { Effect } from "effect";
|
|
5
5
|
import { Command } from "effect/unstable/cli";
|
|
6
6
|
//#region src/ActionCli.ts
|
|
7
|
+
/**
|
|
8
|
+
* Acquire the implementation in a scope of its own, run one action through the hook
|
|
9
|
+
* and its handler, and release it: every local command, selected or grouped.
|
|
10
|
+
*/
|
|
7
11
|
const local = (app, action, before, input) => Effect.scoped(Effect.flatMap(app.build, (handlers) => dispatch(app.group, action, handlers, before)(input)));
|
|
8
12
|
/**
|
|
9
13
|
* Project one explicitly selected local action into a native Effect CLI command.
|
|
@@ -15,7 +19,7 @@ const command = (app, name, options) => {
|
|
|
15
19
|
};
|
|
16
20
|
/** Project every local action below its group namespace with default CLI options. */
|
|
17
21
|
const group = (app, options) => {
|
|
18
|
-
const commands = app.group.actions.map((action) => command$1(action, (input) =>
|
|
22
|
+
const commands = app.group.actions.map((action) => command$1(action, (input) => local(app, action, options?.before, input)));
|
|
19
23
|
return Command.make(options?.name ?? app.group.name).pipe(Command.withSubcommands(commands));
|
|
20
24
|
};
|
|
21
25
|
//#endregion
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Action_d_exports, Codec } from "./Action.mjs";
|
|
2
2
|
import { Actions } from "./internal/actions.mjs";
|
|
3
|
-
import { ServedNames } from "./internal/implementation.mjs";
|
|
4
3
|
import { Options as Options$1 } from "./internal/cli.mjs";
|
|
5
4
|
import { ActionHttp_d_exports, Api, Http } from "./ActionHttp.mjs";
|
|
6
5
|
import { Effect } from "effect";
|
|
@@ -30,20 +29,20 @@ type Group<Groups extends ReadonlyArray<Actions>, Name extends Groups[number]["n
|
|
|
30
29
|
type Selected<G extends Actions, Name extends G["actions"][number]["name"]> = Extract<G["actions"][number], {
|
|
31
30
|
readonly name: Name;
|
|
32
31
|
}>;
|
|
33
|
-
type
|
|
32
|
+
type ActionNames<G extends Actions> = G["actions"][number]["name"];
|
|
34
33
|
type ApiGroups<Groups extends ReadonlyArray<Actions>, Errors extends ReadonlyArray<Codec>> = Api<Groups[number], Errors[number]> extends HttpApi.HttpApi<"actions", infer ApiGroups> ? ApiGroups : never;
|
|
35
|
-
type Endpoint<Groups extends ReadonlyArray<Actions>, Errors extends ReadonlyArray<Codec>, GroupName extends Groups[number]["name"], ActionName extends
|
|
34
|
+
type Endpoint<Groups extends ReadonlyArray<Actions>, Errors extends ReadonlyArray<Codec>, GroupName extends Groups[number]["name"], ActionName extends ActionNames<Group<Groups, GroupName>>> = Extract<HttpApiGroup.EndpointsWithIdentifier<ApiGroups<Groups, Errors>, Extract<GroupName, HttpApiGroup.Identifier<ApiGroups<Groups, Errors>>>>, {
|
|
36
35
|
readonly identifier: ActionName;
|
|
37
36
|
}>;
|
|
38
|
-
type NativeMethod<Groups extends ReadonlyArray<Actions>, Errors extends ReadonlyArray<Codec>, GroupName extends Groups[number]["name"], ActionName extends
|
|
39
|
-
type RemoteCommand<Groups extends ReadonlyArray<Actions>, Errors extends ReadonlyArray<Codec>, GroupName extends Groups[number]["name"], ActionName extends
|
|
40
|
-
type RemoteGroupCommand<Groups extends ReadonlyArray<Actions>, Errors extends ReadonlyArray<Codec>, GroupName extends Groups[number]["name"]> = Command.Command<string, {}, {}, Effect.Error<ReturnType<NativeMethod<Groups, Errors, GroupName,
|
|
37
|
+
type NativeMethod<Groups extends ReadonlyArray<Actions>, Errors extends ReadonlyArray<Codec>, GroupName extends Groups[number]["name"], ActionName extends ActionNames<Group<Groups, GroupName>>> = HttpApiClient.Client.Method<Extract<Endpoint<Groups, Errors, GroupName, ActionName>, HttpApiEndpoint.ConstraintRequest>, never, never>;
|
|
38
|
+
type RemoteCommand<Groups extends ReadonlyArray<Actions>, Errors extends ReadonlyArray<Codec>, GroupName extends Groups[number]["name"], ActionName extends ActionNames<Group<Groups, GroupName>>> = Command.Command<string, never, {}, Effect.Error<ReturnType<NativeMethod<Groups, Errors, GroupName, ActionName>>>, HttpClient.HttpClient | Effect.Services<ReturnType<NativeMethod<Groups, Errors, GroupName, ActionName>>>>;
|
|
39
|
+
type RemoteGroupCommand<Groups extends ReadonlyArray<Actions>, Errors extends ReadonlyArray<Codec>, GroupName extends Groups[number]["name"]> = Command.Command<string, {}, {}, Effect.Error<ReturnType<NativeMethod<Groups, Errors, GroupName, ActionNames<Group<Groups, GroupName>>>>>, HttpClient.HttpClient | Effect.Services<ReturnType<NativeMethod<Groups, Errors, GroupName, ActionNames<Group<Groups, GroupName>>>>>>;
|
|
41
40
|
/**
|
|
42
|
-
* Project one
|
|
41
|
+
* Project one action of the binding into a native Effect CLI command.
|
|
43
42
|
* Group and action selectors resolve exclusively from `http.groups`; runtime guards
|
|
44
43
|
* keep dynamically supplied selector strings from reaching the native client.
|
|
45
44
|
*/
|
|
46
|
-
export declare const command: <const Groups extends ReadonlyArray<Actions>, const Errors extends ReadonlyArray<Codec>, const GroupName extends Groups[number]["name"], const ActionName extends
|
|
47
|
-
/** Project
|
|
45
|
+
export declare const command: <const Groups extends ReadonlyArray<Actions>, const Errors extends ReadonlyArray<Codec>, const GroupName extends Groups[number]["name"], const ActionName extends ActionNames<Group<Groups, GroupName>>, ParsedParameters extends Command.Command.Config = never>(http: Http<Groups, Errors>, groupName: GroupName, actionName: ActionName, options?: Options<Selected<Group<Groups, GroupName>, ActionName>["success"]["Type"], ParsedParameters>) => RemoteCommand<Groups, Errors, GroupName, ActionName>;
|
|
46
|
+
/** Project every action of one bound group below its group namespace. */
|
|
48
47
|
export declare const group: <const Groups extends ReadonlyArray<Actions>, const Errors extends ReadonlyArray<Codec>, const GroupName extends Groups[number]["name"]>(http: Http<Groups, Errors>, groupName: GroupName, options?: GroupOptions) => RemoteGroupCommand<Groups, Errors, GroupName>;
|
|
49
48
|
//#endregion
|
package/dist/ActionCliClient.mjs
CHANGED
|
@@ -13,21 +13,14 @@ import { Command } from "effect/unstable/cli";
|
|
|
13
13
|
const erasedApi = (api) => {
|
|
14
14
|
return api;
|
|
15
15
|
};
|
|
16
|
-
/** An action hidden from HTTP is unknown to this binding, exactly like a missing one. */
|
|
17
|
-
const selectAction = (group, name) => {
|
|
18
|
-
const what = `HTTP action "${group.name}.${name}"`;
|
|
19
|
-
const action = selectNamed(group.actions, name, what);
|
|
20
|
-
if (!action.http) throw new Error(`Unknown ${what}`);
|
|
21
|
-
return action;
|
|
22
|
-
};
|
|
23
16
|
/**
|
|
24
|
-
* Project one
|
|
17
|
+
* Project one action of the binding into a native Effect CLI command.
|
|
25
18
|
* Group and action selectors resolve exclusively from `http.groups`; runtime guards
|
|
26
19
|
* keep dynamically supplied selector strings from reaching the native client.
|
|
27
20
|
*/
|
|
28
21
|
const command = (http, groupName, actionName, options) => {
|
|
29
22
|
const group = selectNamed(http.groups, groupName, `HTTP group "${groupName}"`);
|
|
30
|
-
const action =
|
|
23
|
+
const action = selectNamed(group.actions, actionName, `HTTP action "${groupName}.${actionName}"`);
|
|
31
24
|
return command$1(action, (input) => Effect.flatMap(HttpClient.HttpClient, (httpClient) => {
|
|
32
25
|
const endpoint = HttpApiClient.endpoint(erasedApi(http.api), {
|
|
33
26
|
...options?.connection,
|
|
@@ -40,10 +33,10 @@ const command = (http, groupName, actionName, options) => {
|
|
|
40
33
|
});
|
|
41
34
|
}), options);
|
|
42
35
|
};
|
|
43
|
-
/** Project
|
|
36
|
+
/** Project every action of one bound group below its group namespace. */
|
|
44
37
|
const group = (http, groupName, options) => {
|
|
45
38
|
const actions = selectNamed(http.groups, groupName, `HTTP group "${groupName}"`);
|
|
46
|
-
const commands = actions.actions.
|
|
39
|
+
const commands = actions.actions.map((action) => options?.connection === void 0 ? command(http, groupName, action.name) : command(http, groupName, action.name, { connection: options.connection }));
|
|
47
40
|
return Command.make(options?.name ?? actions.name).pipe(Command.withSubcommands(commands));
|
|
48
41
|
};
|
|
49
42
|
//#endregion
|
package/dist/ActionGroup.d.mts
CHANGED
|
@@ -1,31 +1,35 @@
|
|
|
1
1
|
import { Action, Action_d_exports, Any as Any$1, Codec, Handler } from "./Action.mjs";
|
|
2
|
-
import { Actions, SchemaErrorPolicy } from "./internal/actions.mjs";
|
|
2
|
+
import { Actions, SchemaErrorAnswer, SchemaErrorPolicy } from "./internal/actions.mjs";
|
|
3
3
|
import { Implementation } from "./internal/implementation.mjs";
|
|
4
4
|
import { Effect, Scope, Types } from "effect";
|
|
5
5
|
declare namespace ActionGroup_d_exports {
|
|
6
|
-
export { Any, Contracts, Group, Implementation, Options, SchemaErrorPolicy, contracts, make };
|
|
6
|
+
export { Any, Contracts, Group, Implementation, Options, SchemaErrorAnswer, SchemaErrorPolicy, contracts, make };
|
|
7
7
|
}
|
|
8
8
|
type HandlersFrom<Actions extends ReadonlyArray<Any$1>> = { readonly [A in Actions[number] as A["name"]]: Handler<A, any>; };
|
|
9
9
|
/** Group-level errors join each action's own, so a shared set is declared once. */
|
|
10
|
-
type WithErrors<Actions extends ReadonlyArray<Any$1>, Errors extends ReadonlyArray<Codec>> = { readonly [K in keyof Actions]: Actions[K] extends Action<infer Name, infer Input, infer Output, infer Own, infer Access, infer
|
|
10
|
+
type WithErrors<Actions extends ReadonlyArray<Any$1>, Errors extends ReadonlyArray<Codec>> = { readonly [K in keyof Actions]: Actions[K] extends Action<infer Name, infer Input, infer Output, infer Own, infer Access, infer Mcp> ? Action<Name, Input, Output, readonly [...Own, ...Errors], Access, Mcp> : never; };
|
|
11
11
|
/** What `make` needs to define a group. */
|
|
12
|
-
export interface Options<Name extends string, Errors extends ReadonlyArray<Codec>,
|
|
12
|
+
export interface Options<Name extends string, Errors extends ReadonlyArray<Codec>, Invalid extends Codec = never, Internal extends Codec = never> {
|
|
13
13
|
/** The `HttpApiGroup` identifier, and so the OpenAPI tag and operation-ID prefix. */
|
|
14
14
|
readonly name: Name;
|
|
15
15
|
/** Failures every action of the group may have, added to each action's own. */
|
|
16
16
|
readonly errors?: Errors;
|
|
17
|
-
/**
|
|
18
|
-
|
|
17
|
+
/**
|
|
18
|
+
* How HTTP answers a request that fails decoding (`invalid`) and a result that fails
|
|
19
|
+
* encoding (`internal`); without one, both are Effect's empty 400. MCP keeps its
|
|
20
|
+
* native answers.
|
|
21
|
+
*/
|
|
22
|
+
readonly schemaError?: SchemaErrorPolicy<Invalid, Internal>;
|
|
19
23
|
}
|
|
20
24
|
/** A named set of action contracts: what adapters serve and what `implement` binds. */
|
|
21
|
-
export interface Group<Name extends string, Actions$1 extends ReadonlyArray<Any$1>,
|
|
25
|
+
export interface Group<Name extends string, Actions$1 extends ReadonlyArray<Any$1>, PolicyError extends Codec = Codec> extends Actions<Name, Actions$1, PolicyError> {
|
|
22
26
|
/** Bind every handler at once, resolving build-time services once per adapter layer. */
|
|
23
|
-
readonly implement: <H extends HandlersFrom<Actions$1>, EX = never, RX = never>(build: H | Effect.Effect<H, EX, RX>) => Implementation<Group<Name, Actions$1,
|
|
27
|
+
readonly implement: <H extends HandlersFrom<Actions$1>, EX = never, RX = never>(build: H | Effect.Effect<H, EX, RX>) => Implementation<Group<Name, Actions$1, PolicyError>, H, NoInfer<EX>, NoInfer<Exclude<RX, Scope.Scope>>>;
|
|
24
28
|
}
|
|
25
29
|
/** Any group, with its actions and error schemas erased. */
|
|
26
|
-
export type Any = Group<string, ReadonlyArray<Any$1>,
|
|
30
|
+
export type Any = Group<string, ReadonlyArray<Any$1>, Codec>;
|
|
27
31
|
/** Define a group. Invalid or duplicate action and MCP names fail here, at definition time. */
|
|
28
|
-
export declare function make<const Name extends string, const Actions extends ReadonlyArray<Any$1>, const Errors extends ReadonlyArray<Codec> = [],
|
|
32
|
+
export declare function make<const Name extends string, const Actions extends ReadonlyArray<Any$1>, const Errors extends ReadonlyArray<Codec> = [], Invalid extends Codec = never, Internal extends Codec = never>(options: Options<Name, Errors, Invalid, Internal>, ...actions: Actions): Group<Name, WithErrors<Actions, Errors>, Invalid | Internal>;
|
|
29
33
|
/** `G extends unknown` distributes, so each group maps only its own actions. */
|
|
30
34
|
type PerGroup<G extends Actions> = G extends unknown ? { readonly [A in G["actions"][number] as `${G["name"]}.${A["name"]}`]: A; } : never;
|
|
31
35
|
/** Every action of `Groups`, keyed `<group>.<action>`: the per-group maps merged into one. */
|
|
@@ -37,4 +41,4 @@ export type Contracts<Groups extends ReadonlyArray<Actions>> = Types.UnionToInte
|
|
|
37
41
|
*/
|
|
38
42
|
export declare function contracts<const Groups extends ReadonlyArray<Actions>>(...groups: Groups): Contracts<Groups>;
|
|
39
43
|
//#endregion
|
|
40
|
-
export { ActionGroup_d_exports, type Implementation, type SchemaErrorPolicy };
|
|
44
|
+
export { ActionGroup_d_exports, type Implementation, type SchemaErrorAnswer, type SchemaErrorPolicy };
|
package/dist/ActionGroup.mjs
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { assertDistinct, assertName } from "./internal/actions.mjs";
|
|
2
2
|
import { Implementation } from "./internal/implementation.mjs";
|
|
3
|
-
import { Effect } from "effect";
|
|
3
|
+
import { Effect, Predicate } from "effect";
|
|
4
4
|
//#region src/ActionGroup.ts
|
|
5
|
+
/** Refuse a record without an own-property function for every action, before anything is served. */
|
|
6
|
+
const assertHandlers = (group, handlers) => {
|
|
7
|
+
const missing = group.actions.filter((action) => !(Object.hasOwn(handlers, action.name) && Predicate.isFunction(handlers[action.name])));
|
|
8
|
+
if (missing.length > 0) throw new Error(`Missing handlers for group "${group.name}": ${missing.map((action) => action.name).join(", ")}`);
|
|
9
|
+
return handlers;
|
|
10
|
+
};
|
|
5
11
|
function make(options, ...declared) {
|
|
6
12
|
const { name } = options;
|
|
7
13
|
assertName("action group name", name);
|
|
@@ -16,7 +22,7 @@ function make(options, ...declared) {
|
|
|
16
22
|
actions,
|
|
17
23
|
schemaError: options.schemaError,
|
|
18
24
|
implement: (build) => {
|
|
19
|
-
const built = Effect.isEffect(build) ? build : Effect.succeed(build);
|
|
25
|
+
const built = Effect.isEffect(build) ? Effect.map(build, (handlers) => assertHandlers(group, handlers)) : Effect.succeed(assertHandlers(group, build));
|
|
20
26
|
return Implementation.make(group, built);
|
|
21
27
|
}
|
|
22
28
|
};
|
package/dist/ActionHttp.d.mts
CHANGED
|
@@ -31,11 +31,9 @@ export interface LayerOptions<Errors extends ReadonlyArray<Codec>, R> {
|
|
|
31
31
|
*/
|
|
32
32
|
readonly before?: (action: Any) => Effect.Effect<void, Errors[number]["Type"], R>;
|
|
33
33
|
}
|
|
34
|
-
type Endpoint<A extends Any, E extends Codec> = A extends {
|
|
35
|
-
readonly http: false;
|
|
36
|
-
} ? never : A extends Any ? HttpApiEndpoint.HttpApiEndpoint<A["name"], "POST", `/${string}`, never, never, Schema.toCodecJson<A["input"]>, never, Schema.toCodecJson<A["success"]>, Schema.toCodecJson<A["errors"][number] | E>, never> : never;
|
|
34
|
+
type Endpoint<A extends Any, E extends Codec> = A extends Any ? HttpApiEndpoint.HttpApiEndpoint<A["name"], "POST", `/${string}`, never, never, Schema.toCodecJson<A["input"]>, never, Schema.toCodecJson<A["success"]>, Schema.toCodecJson<A["errors"][number] | E>, never> : never;
|
|
37
35
|
type ApiGroup<G extends Actions, E extends Codec> = G extends Actions ? [Endpoint<G["actions"][number], never>] extends [never] ? never : HttpApiGroup.HttpApiGroup<G["name"], Endpoint<G["actions"][number], PolicyError<G> | E>> : never;
|
|
38
|
-
/** The native `HttpApi` of every
|
|
36
|
+
/** The native `HttpApi` of every action of `G`, one group per action group. */
|
|
39
37
|
export type Api<G extends Actions, E extends Codec = never> = HttpApi.HttpApi<"actions", ApiGroup<G, E>>;
|
|
40
38
|
/** A namespaced HTTP binding. */
|
|
41
39
|
export interface Http<Groups extends ReadonlyArray<Actions>, Errors extends ReadonlyArray<Codec> = []> {
|
|
@@ -47,7 +45,13 @@ export interface Http<Groups extends ReadonlyArray<Actions>, Errors extends Read
|
|
|
47
45
|
* around every request they answer. Build failures, build services and request
|
|
48
46
|
* services are unions over precisely those apps and that hook.
|
|
49
47
|
*/
|
|
50
|
-
readonly layer: <const Apps extends ReadonlyArray<AnyImplementation<Groups[number]>>, RB = never>(apps: readonly [...Apps], options?: LayerOptions<Errors, RB>) => Layer.Layer<never, BuildError<Apps[number]
|
|
48
|
+
readonly layer: <const Apps extends ReadonlyArray<AnyImplementation<Groups[number]>>, RB = never>(apps: readonly [...Apps], options?: LayerOptions<Errors, RB>) => Layer.Layer<never, BuildError<Apps[number]>, BuildContext<Apps[number]> | HttpRouter.HttpRouter | HttpRouter.Request.From<"Requires", RequestContext<Apps[number]> | RB> | Etag.Generator | FileSystem | HttpPlatform.HttpPlatform | Path>;
|
|
49
|
+
/**
|
|
50
|
+
* Serve the OpenAPI document of `api` with `GET path`, by default
|
|
51
|
+
* `<apiPath>/openapi.json`. It is a plain route: middleware provided to this layer
|
|
52
|
+
* covers it, and none is applied otherwise.
|
|
53
|
+
*/
|
|
54
|
+
readonly openApi: (path?: HttpRouter.PathInput) => Layer.Layer<never, never, HttpRouter.HttpRouter>;
|
|
51
55
|
}
|
|
52
56
|
/**
|
|
53
57
|
* Bind the contract-level configuration once. Every route is `POST
|
package/dist/ActionHttp.mjs
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { assertDistinct, projectedErrors } from "./internal/actions.mjs";
|
|
1
|
+
import { answerSchemaError, assertDistinct, policyErrors, projectedErrors } from "./internal/actions.mjs";
|
|
2
2
|
import { dispatch } from "./internal/implementation.mjs";
|
|
3
3
|
import { Effect, Layer } from "effect";
|
|
4
|
-
import "effect/unstable/http";
|
|
4
|
+
import { HttpRouter, HttpServerResponse } from "effect/unstable/http";
|
|
5
5
|
import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup, HttpApiMiddleware, OpenApi } from "effect/unstable/httpapi";
|
|
6
6
|
//#region src/ActionHttp.ts
|
|
7
|
-
const httpActions = (group) => group.actions.filter((action) => action.http);
|
|
8
7
|
const endpoint = (options, group, action) => HttpApiEndpoint.post(action.name, `${options.apiPath}/${group.name}/${action.name}`, {
|
|
9
8
|
payload: action.input,
|
|
10
9
|
success: action.success,
|
|
@@ -12,14 +11,14 @@ const endpoint = (options, group, action) => HttpApiEndpoint.post(action.name, `
|
|
|
12
11
|
}).annotate(OpenApi.Description, action.description);
|
|
13
12
|
/** The native middleware declares the policy's errors on every endpoint it wraps. */
|
|
14
13
|
const schemaErrorMiddleware = (name, policy) => {
|
|
15
|
-
class SchemaErrors extends HttpApiMiddleware.Service()(`effect-actions/http/SchemaErrors/${name}`, { error: policy
|
|
14
|
+
class SchemaErrors extends HttpApiMiddleware.Service()(`effect-actions/http/SchemaErrors/${name}`, { error: policyErrors(policy) }) {}
|
|
16
15
|
return {
|
|
17
16
|
SchemaErrors,
|
|
18
|
-
layer: HttpApiMiddleware.layerSchemaErrorTransform(SchemaErrors, (failure) => Effect.fail(policy
|
|
17
|
+
layer: HttpApiMiddleware.layerSchemaErrorTransform(SchemaErrors, (failure) => Effect.fail(answerSchemaError(policy, failure)))
|
|
19
18
|
};
|
|
20
19
|
};
|
|
21
20
|
function bind(options, group) {
|
|
22
|
-
const [first, ...rest] =
|
|
21
|
+
const [first, ...rest] = group.actions.map((action) => endpoint(options, group, action));
|
|
23
22
|
if (first === void 0) return void 0;
|
|
24
23
|
const bound = HttpApiGroup.make(group.name).add(first, ...rest);
|
|
25
24
|
if (group.schemaError === void 0) return {
|
|
@@ -49,7 +48,10 @@ const erasedHandlers = (handlers) => {
|
|
|
49
48
|
};
|
|
50
49
|
/** Register one group's handlers. Handler erasure is limited to native dynamic endpoint registration. */
|
|
51
50
|
const groupHandlers = (api, app, middleware, before) => {
|
|
52
|
-
const entries = (record) => Object.fromEntries(
|
|
51
|
+
const entries = (record) => Object.fromEntries(app.group.actions.map((action) => {
|
|
52
|
+
const run = dispatch(app.group, action, record, before);
|
|
53
|
+
return [action.name, (request) => run(request.payload)];
|
|
54
|
+
}));
|
|
53
55
|
return Layer.unwrap(Effect.map(app.build, (record) => HttpApiBuilder.group(api, app.group.name, (builder) => builder.handleAll(entries(erasedHandlers(record)))))).pipe(Layer.provide(middleware));
|
|
54
56
|
};
|
|
55
57
|
function make(options, ...groups) {
|
|
@@ -72,10 +74,13 @@ function make(options, ...groups) {
|
|
|
72
74
|
const [first, ...rest] = served.map(({ app, middleware }) => groupHandlers(api, app, middleware, layerOptions.before));
|
|
73
75
|
return first === void 0 ? HttpApiBuilder.layer(api) : HttpApiBuilder.layer(api).pipe(Layer.provide([first, ...rest]));
|
|
74
76
|
};
|
|
77
|
+
const api = apiOf([...bound.values()].map(({ native }) => native));
|
|
78
|
+
const openApi = (path = `${options.apiPath}/openapi.json`) => HttpRouter.add("GET", path, HttpServerResponse.jsonUnsafe(OpenApi.fromApi(api)));
|
|
75
79
|
return {
|
|
76
80
|
groups,
|
|
77
|
-
api
|
|
78
|
-
layer
|
|
81
|
+
api,
|
|
82
|
+
layer,
|
|
83
|
+
openApi
|
|
79
84
|
};
|
|
80
85
|
}
|
|
81
86
|
//#endregion
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Action_d_exports, Any, Codec } from "./Action.mjs";
|
|
2
|
+
import { Actions } from "./internal/actions.mjs";
|
|
3
|
+
import { ActionHttp_d_exports, Http } from "./ActionHttp.mjs";
|
|
4
|
+
import { ClientOptions } from "./internal/fetchClient.mjs";
|
|
5
|
+
//#region src/ActionHttpClient.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* The native `HttpApiClient.make` options except `transformResponse`, plus `fetch`;
|
|
8
|
+
* see the options table in docs/ActionHttpClient.md.
|
|
9
|
+
*/
|
|
10
|
+
export interface Options extends Omit<ClientOptions, "transformResponse"> {
|
|
11
|
+
readonly fetch?: typeof globalThis.fetch;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* One action as a Promise of its decoded success. An action whose input may be empty,
|
|
15
|
+
* such as one declared without `input`, may be called without an argument.
|
|
16
|
+
*/
|
|
17
|
+
export type Method<A extends Any> = {} extends A["input"]["Type"] ? (input?: A["input"]["Type"]) => Promise<A["success"]["Type"]> : (input: A["input"]["Type"]) => Promise<A["success"]["Type"]>;
|
|
18
|
+
/** Every action of the binding, as `client.<group>.<action>(input)`. */
|
|
19
|
+
export type Client<Groups extends ReadonlyArray<Actions>> = { readonly [G in Groups[number] as [G["actions"][number]] extends [never] ? never : G["name"]]: { readonly [A in G["actions"][number] as A["name"]]: Method<A>; }; };
|
|
20
|
+
/**
|
|
21
|
+
* A Promise client for an HTTP binding, for code that does not run Effects, such as a
|
|
22
|
+
* browser page. It is Effect's native `HttpApiClient` over `fetch`, built once, and
|
|
23
|
+
* each call runs one request: nothing is retried.
|
|
24
|
+
*
|
|
25
|
+
* A call resolves with the action's decoded success. It rejects with what the native
|
|
26
|
+
* client fails with: a declared error value (the action's, its group's schema-error
|
|
27
|
+
* policy's, or the binding's surface `errors`), a native `HttpClientError` when the
|
|
28
|
+
* server could not be reached or answered with a status or body the contract does not
|
|
29
|
+
* declare, or a `SchemaError` when the input does not encode or the success does not
|
|
30
|
+
* decode.
|
|
31
|
+
*/
|
|
32
|
+
export declare function promise<const Groups extends ReadonlyArray<Actions>, const Errors extends ReadonlyArray<Codec>>(http: Http<Groups, Errors>, options?: Options): Client<Groups>;
|
|
33
|
+
//#endregion
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { fetchApiClient } from "./internal/fetchClient.mjs";
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
//#region src/ActionHttpClient.ts
|
|
4
|
+
function promise(http, { baseUrl, transformClient, fetch } = {}) {
|
|
5
|
+
const send = fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
6
|
+
const native = Effect.runSync(fetchApiClient(http.api, send, {
|
|
7
|
+
baseUrl,
|
|
8
|
+
transformClient
|
|
9
|
+
}));
|
|
10
|
+
return Object.fromEntries(Object.entries(native).map(([group, endpoints]) => [group, Object.fromEntries(Object.entries(endpoints).map(([action, endpoint]) => {
|
|
11
|
+
const method = (input) => Effect.runPromise(endpoint({ payload: input ?? {} }));
|
|
12
|
+
return [action, method];
|
|
13
|
+
}))]));
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { promise };
|
package/dist/ActionMcp.d.mts
CHANGED
|
@@ -1,45 +1,35 @@
|
|
|
1
1
|
import { Action_d_exports, Codec } from "./Action.mjs";
|
|
2
|
-
import { AnyImplementation, BuildContext, BuildError, RequestContext } from "./internal/implementation.mjs";
|
|
2
|
+
import { AnyImplementation, BuildContext, BuildError, HiddenFromMcp, RequestContext } from "./internal/implementation.mjs";
|
|
3
3
|
import { SurfaceOptions } from "./internal/tools.mjs";
|
|
4
4
|
import { Cause, Layer } from "effect";
|
|
5
5
|
import { HttpRouter } from "effect/unstable/http";
|
|
6
6
|
import { McpSchema, McpServer } from "effect/unstable/ai";
|
|
7
7
|
import { Stdio } from "effect/Stdio";
|
|
8
8
|
//#region src/ActionMcp.d.ts
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
/** Browser origins accepted by the native server; passed through to `McpServer.layerHttp`. */
|
|
18
|
-
readonly allowedOrigins?: ReadonlyArray<string>;
|
|
19
|
-
readonly instructions?: string;
|
|
20
|
-
}
|
|
21
|
-
/** Server information for an MCP subprocess connected through standard I/O. */
|
|
22
|
-
export interface StdioOptions<Errors extends ReadonlyArray<Codec> = [], R = never> extends SurfaceOptions<Errors, R> {
|
|
23
|
-
readonly name: string;
|
|
24
|
-
readonly version: string;
|
|
25
|
-
/** Native protocol adapters to serve; negotiation is owned by Effect. */
|
|
26
|
-
readonly protocols: Parameters<typeof McpServer.layerStdio>[0]["protocols"];
|
|
27
|
-
readonly instructions?: string;
|
|
28
|
-
}
|
|
9
|
+
/**
|
|
10
|
+
* One Streamable HTTP MCP endpoint: every native `McpServer.layerHttp` option except
|
|
11
|
+
* `protocols` (server information, `path`, `allowedOrigins`, `instructions`,
|
|
12
|
+
* `extensions`, ...), plus the surface's `errors` and `before`.
|
|
13
|
+
*/
|
|
14
|
+
export interface Options<Errors extends ReadonlyArray<Codec> = [], R = never> extends Omit<Parameters<typeof McpServer.layerHttp>[0], "protocols">, SurfaceOptions<Errors, R> {}
|
|
15
|
+
/** An MCP subprocess on standard I/O: every native `McpServer.layerStdio` option except `protocols`. */
|
|
16
|
+
export interface StdioOptions<Errors extends ReadonlyArray<Codec> = [], R = never> extends Omit<Parameters<typeof McpServer.layerStdio>[0], "protocols">, SurfaceOptions<Errors, R> {}
|
|
29
17
|
/** The native server supplies its own request context to every tool call. */
|
|
30
|
-
type ToolRequestContext<App, RB> = Exclude<RequestContext<App,
|
|
18
|
+
type ToolRequestContext<App, RB> = Exclude<RequestContext<App, HiddenFromMcp> | RB, McpSchema.McpRequestContext>;
|
|
31
19
|
/**
|
|
32
|
-
* Serve MCP tools over one Streamable HTTP endpoint.
|
|
20
|
+
* Serve MCP tools over one Streamable HTTP endpoint, speaking MCP 2026-07-28 only.
|
|
33
21
|
*
|
|
34
22
|
* Middleware provided around this layer has the normal HTTP lifetime. Native
|
|
35
23
|
* context capture applies: never provide request-identity tags at startup.
|
|
36
24
|
*/
|
|
37
|
-
export declare function layerHttp<const Apps extends ReadonlyArray<AnyImplementation>, const Errors extends ReadonlyArray<Codec> = [], RB = never>(apps: readonly [...Apps], options: Options<Errors, RB>): Layer.Layer<never, BuildError<Apps[number],
|
|
25
|
+
export declare function layerHttp<const Apps extends ReadonlyArray<AnyImplementation>, const Errors extends ReadonlyArray<Codec> = [], RB = never>(apps: readonly [...Apps], options: Options<Errors, RB>): Layer.Layer<never, BuildError<Apps[number], HiddenFromMcp> | Cause.IllegalArgumentError, BuildContext<Apps[number], HiddenFromMcp> | HttpRouter.HttpRouter | HttpRouter.Request.From<"Requires", ToolRequestContext<Apps[number], RB>>>;
|
|
38
26
|
/**
|
|
39
|
-
* Serve MCP tools through newline-delimited JSON-RPC on standard I/O
|
|
27
|
+
* Serve MCP tools through newline-delimited JSON-RPC on standard I/O, speaking MCP
|
|
28
|
+
* 2026-07-28 only. An older host is refused deliberately, for uniformity with HTTP,
|
|
29
|
+
* even though stdio has no sessions.
|
|
40
30
|
*
|
|
41
31
|
* The host supplies the `Stdio` service. Arguments are tool input only and
|
|
42
32
|
* never establish request identity or authority.
|
|
43
33
|
*/
|
|
44
|
-
export declare function layerStdio<const Apps extends ReadonlyArray<AnyImplementation>, const Errors extends ReadonlyArray<Codec> = [], RB = never>(apps: readonly [...Apps], options: StdioOptions<Errors, RB>): Layer.Layer<never, BuildError<Apps[number],
|
|
34
|
+
export declare function layerStdio<const Apps extends ReadonlyArray<AnyImplementation>, const Errors extends ReadonlyArray<Codec> = [], RB = never>(apps: readonly [...Apps], options: StdioOptions<Errors, RB>): Layer.Layer<never, BuildError<Apps[number], HiddenFromMcp> | Cause.IllegalArgumentError, BuildContext<Apps[number], HiddenFromMcp> | Stdio | ToolRequestContext<Apps[number], RB>>;
|
|
45
35
|
//#endregion
|
package/dist/ActionMcp.mjs
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import "./internal/implementation.mjs";
|
|
2
2
|
import { bindTools } from "./internal/tools.mjs";
|
|
3
3
|
import { Layer } from "effect";
|
|
4
|
-
import { McpServer } from "effect/unstable/ai";
|
|
4
|
+
import { McpProtocol, McpServer } from "effect/unstable/ai";
|
|
5
5
|
//#region src/ActionMcp.ts
|
|
6
|
+
/**
|
|
7
|
+
* The one protocol revision served. 2026-07-28 is stateless over HTTP: no
|
|
8
|
+
* initialize handshake and no session, so every request stands alone.
|
|
9
|
+
*/
|
|
10
|
+
const protocols = [McpProtocol.v2026_07_28];
|
|
6
11
|
const registration = (apps, options) => {
|
|
7
12
|
const binding = bindTools(apps, "mcp", options);
|
|
8
13
|
return Layer.effectDiscard(McpServer.registerToolkit(binding.toolkit)).pipe(Layer.provide(binding.layer));
|
|
@@ -10,16 +15,15 @@ const registration = (apps, options) => {
|
|
|
10
15
|
const server = (apps, options, transport) => registration(apps, options).pipe(Layer.provide(transport), Layer.fresh);
|
|
11
16
|
function layerHttp(apps, options) {
|
|
12
17
|
return server(apps, options, McpServer.layerHttp({
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
instructions: options.instructions,
|
|
16
|
-
path: options.path,
|
|
17
|
-
protocols: options.protocols,
|
|
18
|
-
allowedOrigins: options.allowedOrigins
|
|
18
|
+
...options,
|
|
19
|
+
protocols
|
|
19
20
|
}));
|
|
20
21
|
}
|
|
21
22
|
function layerStdio(apps, options) {
|
|
22
|
-
return server(apps, options, McpServer.layerStdio(
|
|
23
|
+
return server(apps, options, McpServer.layerStdio({
|
|
24
|
+
...options,
|
|
25
|
+
protocols
|
|
26
|
+
}));
|
|
23
27
|
}
|
|
24
28
|
//#endregion
|
|
25
29
|
export { layerHttp, layerStdio };
|