functionalscript 0.38.0 → 0.40.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 +1 -1
- package/fjs/bnf/descent/module.f.d.ts +43 -2
- package/fjs/bnf/descent/module.f.js +37 -12
- package/fjs/bnf/descent/proof.f.d.ts +1 -0
- package/fjs/bnf/descent/proof.f.js +73 -32
- package/fjs/bnf/ll1/module.f.js +2 -2
- package/fjs/bnf/token_symbol/module.f.d.ts +38 -0
- package/fjs/bnf/token_symbol/module.f.js +48 -0
- package/fjs/bnf/token_symbol/proof.f.d.ts +10 -0
- package/fjs/bnf/token_symbol/proof.f.js +37 -0
- package/fjs/cas/cli/module.f.js +6 -9
- package/fjs/cas/evo/module.f.d.ts +77 -13
- package/fjs/cas/evo/module.f.js +130 -18
- package/fjs/cas/evo/proof.f.d.ts +7 -0
- package/fjs/cas/evo/proof.f.js +120 -1
- package/fjs/cas/module.f.js +64 -37
- package/fjs/cas/proof.f.d.ts +3 -9
- package/fjs/cas/proof.f.js +94 -74
- package/fjs/ci/bun/module.f.js +3 -3
- package/fjs/ci/common/module.f.d.ts +1 -2
- package/fjs/ci/common/module.f.js +1 -5
- package/fjs/ci/config/module.f.d.ts +11 -8
- package/fjs/ci/config/module.f.js +24 -9
- package/fjs/ci/deno/module.f.js +3 -3
- package/fjs/ci/module.f.js +13 -7
- package/fjs/ci/nix/module.f.d.ts +61 -0
- package/fjs/ci/nix/module.f.js +92 -0
- package/fjs/ci/nix/proof.f.d.ts +23 -0
- package/fjs/ci/nix/proof.f.js +109 -0
- package/fjs/ci/node/module.f.d.ts +23 -1
- package/fjs/ci/node/module.f.js +58 -12
- package/fjs/ci/proof.f.d.ts +2 -0
- package/fjs/ci/proof.f.js +49 -9
- package/fjs/cli/module.f.js +2 -2
- package/fjs/common/monoid/module.f.d.ts +46 -2
- package/fjs/common/monoid/module.f.js +44 -0
- package/fjs/common/monoid/proof.f.d.ts +5 -0
- package/fjs/common/monoid/proof.f.js +27 -1
- package/fjs/dev/module.f.d.ts +1 -0
- package/fjs/dev/module.f.js +38 -27
- package/fjs/dev/update/module.f.d.ts +11 -0
- package/fjs/dev/update/module.f.js +20 -0
- package/fjs/dev/update/proof.f.d.ts +7 -0
- package/fjs/dev/update/proof.f.js +35 -0
- package/fjs/djs/ast/module.f.d.ts +47 -0
- package/fjs/djs/ast/module.f.js +9 -0
- package/fjs/djs/module.f.js +5 -8
- package/fjs/djs/parser/module.f.d.ts +3 -0
- package/fjs/djs/parser/module.f.js +17 -0
- package/fjs/djs/tokenizer/module.f.js +2 -2
- package/fjs/djs/tokenizer/proof.f.d.ts +1 -0
- package/fjs/djs/tokenizer/proof.f.js +70 -16
- package/fjs/djs/transpiler/module.f.js +6 -6
- package/fjs/effects/eff/module.f.d.ts +72 -0
- package/fjs/effects/eff/module.f.js +46 -0
- package/fjs/effects/eff/proof.f.d.ts +11 -0
- package/fjs/effects/eff/proof.f.js +58 -0
- package/fjs/effects/list/module.f.d.ts +11 -5
- package/fjs/effects/list/module.f.js +2 -1
- package/fjs/effects/memory/proof.f.js +7 -8
- package/fjs/effects/module.f.d.ts +334 -52
- package/fjs/effects/module.f.js +253 -45
- package/fjs/effects/node/memory/proof.js +3 -2
- package/fjs/effects/node/module.d.ts +3 -3
- package/fjs/effects/node/module.f.d.ts +18 -8
- package/fjs/effects/node/module.f.js +31 -11
- package/fjs/effects/node/module.js +12 -12
- package/fjs/effects/node/proof.f.d.ts +1 -0
- package/fjs/effects/node/proof.f.js +32 -11
- package/fjs/effects/node/virtual/module.f.js +1 -1
- package/fjs/effects/proof.f.d.ts +32 -6
- package/fjs/effects/proof.f.js +131 -39
- package/fjs/emergent_testing/all.test.js +2 -1
- package/fjs/emergent_testing/module.f.d.ts +3 -3
- package/fjs/emergent_testing/module.f.js +63 -49
- package/fjs/emergent_testing/proof.f.d.ts +4 -0
- package/fjs/emergent_testing/proof.f.js +54 -21
- package/fjs/emergent_testing/scenarios/thenable.pass.js +1 -1
- package/fjs/fsc/module.f.js +4 -4
- package/fjs/fsm/module.f.js +1 -1
- package/fjs/js/tokenizer/module.f.d.ts +1 -0
- package/fjs/js/tokenizer/module.f.js +13 -6
- package/fjs/{cas/mcp → mcp/cas}/module.f.d.ts +3 -25
- package/fjs/{cas/mcp → mcp/cas}/module.f.js +26 -64
- package/fjs/mcp/evo/module.f.d.ts +27 -0
- package/fjs/mcp/evo/module.f.js +94 -0
- package/fjs/{cas/evo/mcp → mcp/evo}/proof.f.d.ts +2 -0
- package/fjs/{cas/evo/mcp → mcp/evo}/proof.f.js +40 -9
- package/fjs/mcp/module.f.d.ts +54 -237
- package/fjs/mcp/module.f.js +55 -258
- package/fjs/mcp/proof.f.d.ts +43 -32
- package/fjs/mcp/proof.f.js +509 -200
- package/fjs/media/nix/module.f.d.ts +30 -0
- package/fjs/media/nix/module.f.js +166 -0
- package/fjs/media/nix/proof.f.d.ts +32 -0
- package/fjs/media/nix/proof.f.js +127 -0
- package/fjs/media/type/module.f.js +2 -2
- package/fjs/media/type/proof.f.js +7 -10
- package/fjs/module.f.js +5 -9
- package/fjs/protocol/json_rpc/module.f.d.ts +114 -0
- package/fjs/{media/json/rpc → protocol/json_rpc}/module.f.js +3 -3
- package/fjs/{media/json/rpc → protocol/json_rpc}/proof.f.js +3 -3
- package/fjs/protocol/mcp/module.f.d.ts +239 -0
- package/fjs/protocol/mcp/module.f.js +272 -0
- package/fjs/protocol/mcp/proof.f.d.ts +34 -0
- package/fjs/protocol/mcp/proof.f.js +208 -0
- package/fjs/{mcp → protocol/mcp}/stdio/module.f.d.ts +7 -7
- package/fjs/{mcp → protocol/mcp}/stdio/module.f.js +22 -22
- package/fjs/{mcp → protocol/mcp}/stdio/proof.f.js +9 -9
- package/fjs/types/bigint/module.f.js +4 -3
- package/fjs/types/bit_vec/module.f.js +5 -15
- package/fjs/types/btree/remove/module.f.d.ts +5 -0
- package/fjs/types/btree/remove/module.f.js +12 -0
- package/fjs/types/list/module.f.d.ts +21 -0
- package/fjs/types/list/module.f.js +17 -0
- package/fjs/types/list/proof.f.d.ts +1 -0
- package/fjs/types/list/proof.f.js +24 -1
- package/fjs/types/number/module.f.js +2 -1
- package/fjs/types/range_map/module.f.d.ts +14 -13
- package/fjs/types/range_map/module.f.js +18 -13
- package/fjs/types/range_map/proof.f.js +26 -39
- package/fjs/types/range_set/module.f.d.ts +5 -0
- package/fjs/types/range_set/module.f.js +16 -0
- package/fjs/types/range_set/proof.f.d.ts +1 -0
- package/fjs/types/range_set/proof.f.js +19 -0
- package/fjs/types/string/module.f.js +10 -2
- package/fjs/website/module.f.js +2 -3
- package/package.json +5 -5
- package/fjs/cas/evo/mcp/module.f.d.ts +0 -19
- package/fjs/cas/evo/mcp/module.f.js +0 -56
- package/fjs/cas/mcp/proof.f.d.ts +0 -45
- package/fjs/cas/mcp/proof.f.js +0 -545
- package/fjs/ci/playwright/module.f.d.ts +0 -2
- package/fjs/ci/playwright/module.f.js +0 -25
- package/fjs/media/json/rpc/module.f.d.ts +0 -114
- /package/fjs/{media/json/rpc → protocol/json_rpc}/proof.f.d.ts +0 -0
- /package/fjs/{mcp → protocol/mcp}/stdio/proof.f.d.ts +0 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { unknown, type Unknown } from '../../media/json/module.f.ts';
|
|
2
|
+
import type { Ts } from '../../types/rtti/ts/module.f.ts';
|
|
3
|
+
import { type Operation, type Effect } from '../../effects/module.f.ts';
|
|
4
|
+
import { type Key, type MemOp } from '../../effects/memory/module.f.ts';
|
|
5
|
+
import { type Response } from '../json_rpc/module.f.ts';
|
|
6
|
+
import type { Type } from '../../types/rtti/module.f.ts';
|
|
7
|
+
/** Name + version pair sent in `initialize` requests and responses. */
|
|
8
|
+
export declare const implementation: {
|
|
9
|
+
readonly name: import("../../types/rtti/module.f.ts").String;
|
|
10
|
+
readonly version: import("../../types/rtti/module.f.ts").String;
|
|
11
|
+
};
|
|
12
|
+
export type Implementation = Ts<typeof implementation>;
|
|
13
|
+
/** Server capabilities advertised in the `initialize` response. */
|
|
14
|
+
export declare const serverCapabilities: {
|
|
15
|
+
readonly tools: import("../../types/rtti/module.f.ts").Or<readonly [{
|
|
16
|
+
readonly listChanged: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").Boolean, undefined]>;
|
|
17
|
+
}, undefined]>;
|
|
18
|
+
};
|
|
19
|
+
export type ServerCapabilities = Ts<typeof serverCapabilities>;
|
|
20
|
+
/** Params for the `initialize` request. */
|
|
21
|
+
export declare const initializeParams: {
|
|
22
|
+
readonly protocolVersion: import("../../types/rtti/module.f.ts").String;
|
|
23
|
+
readonly capabilities: typeof unknown;
|
|
24
|
+
readonly clientInfo: {
|
|
25
|
+
readonly name: import("../../types/rtti/module.f.ts").String;
|
|
26
|
+
readonly version: import("../../types/rtti/module.f.ts").String;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
export type InitializeParams = Ts<typeof initializeParams>;
|
|
30
|
+
/** Result for the `initialize` request. */
|
|
31
|
+
export declare const initializeResult: {
|
|
32
|
+
readonly protocolVersion: import("../../types/rtti/module.f.ts").String;
|
|
33
|
+
readonly capabilities: {
|
|
34
|
+
readonly tools: import("../../types/rtti/module.f.ts").Or<readonly [{
|
|
35
|
+
readonly listChanged: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").Boolean, undefined]>;
|
|
36
|
+
}, undefined]>;
|
|
37
|
+
};
|
|
38
|
+
readonly serverInfo: {
|
|
39
|
+
readonly name: import("../../types/rtti/module.f.ts").String;
|
|
40
|
+
readonly version: import("../../types/rtti/module.f.ts").String;
|
|
41
|
+
};
|
|
42
|
+
readonly instructions: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").String, undefined]>;
|
|
43
|
+
};
|
|
44
|
+
export type InitializeResult = Ts<typeof initializeResult>;
|
|
45
|
+
/** Plain-text content item returned by a tool call. */
|
|
46
|
+
export declare const textContent: {
|
|
47
|
+
readonly type: 'text';
|
|
48
|
+
readonly text: import("../../types/rtti/module.f.ts").String;
|
|
49
|
+
};
|
|
50
|
+
export type TextContent = Ts<typeof textContent>;
|
|
51
|
+
/**
|
|
52
|
+
* A binary resource carried inside an {@link embeddedResource}: a base64
|
|
53
|
+
* `blob`, an addressing `uri`, and an optional `mimeType`. This is MCP's
|
|
54
|
+
* `BlobResource` shape — the idiomatic way to return typed binary content so a
|
|
55
|
+
* `mimeType` travels alongside the bytes and clients know how to route them.
|
|
56
|
+
*/
|
|
57
|
+
export declare const blobResource: {
|
|
58
|
+
readonly uri: import("../../types/rtti/module.f.ts").String;
|
|
59
|
+
readonly mimeType: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").String, undefined]>;
|
|
60
|
+
readonly blob: import("../../types/rtti/module.f.ts").String;
|
|
61
|
+
};
|
|
62
|
+
export type BlobResource = Ts<typeof blobResource>;
|
|
63
|
+
/** An `EmbeddedResource` content item wrapping a {@link blobResource}. */
|
|
64
|
+
export declare const embeddedResource: {
|
|
65
|
+
readonly type: 'resource';
|
|
66
|
+
readonly resource: {
|
|
67
|
+
readonly uri: import("../../types/rtti/module.f.ts").String;
|
|
68
|
+
readonly mimeType: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").String, undefined]>;
|
|
69
|
+
readonly blob: import("../../types/rtti/module.f.ts").String;
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
export type EmbeddedResource = Ts<typeof embeddedResource>;
|
|
73
|
+
/**
|
|
74
|
+
* A single item in a `tools/call` result's `content` array: either plain
|
|
75
|
+
* {@link textContent} or an {@link embeddedResource} for typed binary. The
|
|
76
|
+
* `image` and `audio` variants are not modelled yet.
|
|
77
|
+
*/
|
|
78
|
+
export declare const contentItem: import("../../types/rtti/module.f.ts").Or<[{
|
|
79
|
+
readonly type: 'text';
|
|
80
|
+
readonly text: import("../../types/rtti/module.f.ts").String;
|
|
81
|
+
}, {
|
|
82
|
+
readonly type: 'resource';
|
|
83
|
+
readonly resource: {
|
|
84
|
+
readonly uri: import("../../types/rtti/module.f.ts").String;
|
|
85
|
+
readonly mimeType: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").String, undefined]>;
|
|
86
|
+
readonly blob: import("../../types/rtti/module.f.ts").String;
|
|
87
|
+
};
|
|
88
|
+
}]>;
|
|
89
|
+
export type ContentItem = Ts<typeof contentItem>;
|
|
90
|
+
/**
|
|
91
|
+
* A tool descriptor returned by `tools/list`.
|
|
92
|
+
* `inputSchema` is a JSON Schema object — use `toJsonSchema` to derive it from
|
|
93
|
+
* an rtti schema.
|
|
94
|
+
*/
|
|
95
|
+
export declare const tool: {
|
|
96
|
+
readonly name: import("../../types/rtti/module.f.ts").String;
|
|
97
|
+
readonly description: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").String, undefined]>;
|
|
98
|
+
readonly inputSchema: typeof unknown;
|
|
99
|
+
};
|
|
100
|
+
export type Tool = Ts<typeof tool>;
|
|
101
|
+
/**
|
|
102
|
+
* Params for the `tools/list` request. `cursor` is an opaque pagination token
|
|
103
|
+
* from a previous `ToolsListResult.nextCursor`.
|
|
104
|
+
*/
|
|
105
|
+
export declare const toolsListParams: {
|
|
106
|
+
readonly cursor: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").String, undefined]>;
|
|
107
|
+
};
|
|
108
|
+
export type ToolsListParams = Ts<typeof toolsListParams>;
|
|
109
|
+
export declare const toolsListResult: {
|
|
110
|
+
readonly tools: import("../../types/rtti/module.f.ts").Type1<"array", {
|
|
111
|
+
readonly name: import("../../types/rtti/module.f.ts").String;
|
|
112
|
+
readonly description: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").String, undefined]>;
|
|
113
|
+
readonly inputSchema: typeof unknown;
|
|
114
|
+
}>;
|
|
115
|
+
readonly nextCursor: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").String, undefined]>;
|
|
116
|
+
};
|
|
117
|
+
export type ToolsListResult = Ts<typeof toolsListResult>;
|
|
118
|
+
export declare const toolsCallParams: {
|
|
119
|
+
readonly name: import("../../types/rtti/module.f.ts").String;
|
|
120
|
+
readonly arguments: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").Type1<"record", typeof unknown>, undefined]>;
|
|
121
|
+
};
|
|
122
|
+
export type ToolsCallParams = Ts<typeof toolsCallParams>;
|
|
123
|
+
export declare const toolsCallResult: {
|
|
124
|
+
readonly content: import("../../types/rtti/module.f.ts").Type1<"array", import("../../types/rtti/module.f.ts").Or<[{
|
|
125
|
+
readonly type: 'text';
|
|
126
|
+
readonly text: import("../../types/rtti/module.f.ts").String;
|
|
127
|
+
}, {
|
|
128
|
+
readonly type: 'resource';
|
|
129
|
+
readonly resource: {
|
|
130
|
+
readonly uri: import("../../types/rtti/module.f.ts").String;
|
|
131
|
+
readonly mimeType: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").String, undefined]>;
|
|
132
|
+
readonly blob: import("../../types/rtti/module.f.ts").String;
|
|
133
|
+
};
|
|
134
|
+
}]>>;
|
|
135
|
+
readonly isError: import("../../types/rtti/module.f.ts").Or<readonly [import("../../types/rtti/module.f.ts").Boolean, undefined]>;
|
|
136
|
+
};
|
|
137
|
+
export type ToolsCallResult = Ts<typeof toolsCallResult>;
|
|
138
|
+
/** Per-method handlers for a hello-world MCP tool server. */
|
|
139
|
+
export type McpHandlers<O extends Operation> = {
|
|
140
|
+
readonly toolsList: (params: ToolsListParams) => Effect<O, ToolsListResult>;
|
|
141
|
+
readonly toolsCall: (params: ToolsCallParams) => Effect<O, ToolsCallResult>;
|
|
142
|
+
};
|
|
143
|
+
/**
|
|
144
|
+
* A single declarative tool entry combining metadata, input schema, and type-safe handler.
|
|
145
|
+
*
|
|
146
|
+
* The handler receives pre-validated arguments of type `Ts<inputRtti>`, eliminating the need
|
|
147
|
+
* for manual validation or type casting. All validation is encapsulated in the entry.
|
|
148
|
+
*/
|
|
149
|
+
export type ToolEntry<O extends Operation> = {
|
|
150
|
+
readonly name: string;
|
|
151
|
+
readonly description: string;
|
|
152
|
+
readonly inputRtti: Type;
|
|
153
|
+
readonly handle: (args: Unknown) => Effect<O, ToolsCallResult>;
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* Creates a type-safe tool entry that binds an RTTI schema with a handler.
|
|
157
|
+
*
|
|
158
|
+
* The builder validates arguments at runtime using the RTTI and passes pre-validated
|
|
159
|
+
* arguments (typed as `Ts<T>`) to the handler. This eliminates manual validation
|
|
160
|
+
* boilerplate and type assertions.
|
|
161
|
+
*
|
|
162
|
+
* @param name - The tool name (used in `tools/call` requests)
|
|
163
|
+
* @param description - Human-readable description for `tools/list`
|
|
164
|
+
* @param inputRtti - Runtime type info for input validation
|
|
165
|
+
* @param handle - Handler receiving validated arguments of type `Ts<inputRtti>`
|
|
166
|
+
* @returns A `ToolEntry` ready to be added to a registry
|
|
167
|
+
*/
|
|
168
|
+
export declare const toolEntry: <T extends Type, O extends Operation>(name: string, description: string, inputRtti: T, handle: (args: Ts<T>) => Effect<O, ToolsCallResult>) => ToolEntry<O>;
|
|
169
|
+
/**
|
|
170
|
+
* Helper to create a successful single-text-block tool result.
|
|
171
|
+
*
|
|
172
|
+
* @param text - The text to return to the client
|
|
173
|
+
* @returns A `ToolsCallResult` with the text content
|
|
174
|
+
*/
|
|
175
|
+
export declare const okResult: (text: string) => ToolsCallResult;
|
|
176
|
+
/**
|
|
177
|
+
* Helper to create a tool-level error result with plain text explanation.
|
|
178
|
+
*
|
|
179
|
+
* @param text - The error message to return to the client
|
|
180
|
+
* @returns A `ToolsCallResult` with `isError: true` and the text explanation
|
|
181
|
+
*/
|
|
182
|
+
export declare const errorResult: (text: string) => ToolsCallResult;
|
|
183
|
+
/**
|
|
184
|
+
* Builds `McpHandlers` from a registry of tool entries.
|
|
185
|
+
*
|
|
186
|
+
* This factory generates `toolsList` and `toolsCall` handlers that work with a
|
|
187
|
+
* declarative registry, eliminating boilerplate. The `toolsList` handler converts
|
|
188
|
+
* entries into MCP `Tool` descriptors, and `toolsCall` dispatches by name and
|
|
189
|
+
* delegates to the appropriate handler.
|
|
190
|
+
*
|
|
191
|
+
* @param registry - Array of tool entries
|
|
192
|
+
* @returns Complete `McpHandlers` ready for use with `mcpStep`
|
|
193
|
+
*/
|
|
194
|
+
export declare const fromRegistry: <O extends Operation>(registry: readonly ToolEntry<O>[]) => McpHandlers<O>;
|
|
195
|
+
/** Top-level handler: maps a raw JSON value to a JSON-RPC response (or `null` for notifications). */
|
|
196
|
+
export type Handle<O extends Operation> = (value: Unknown) => Effect<O, Response | null>;
|
|
197
|
+
/** MCP error -32002: the client called a method before `initialize`. */
|
|
198
|
+
export declare const notInitialized: import("../../types/rtti/ts/module.f.ts").StructTs<{
|
|
199
|
+
readonly code: import("../../types/rtti/module.f.ts").Number;
|
|
200
|
+
readonly message: import("../../types/rtti/module.f.ts").String;
|
|
201
|
+
readonly data: import("../../types/rtti/module.f.ts").Or<readonly [typeof unknown, undefined]>;
|
|
202
|
+
}>;
|
|
203
|
+
/** State carried before the peer sends `initialize`. */
|
|
204
|
+
export type Uninitialized = readonly ['uninitialized'];
|
|
205
|
+
/** State after `initialize` response was sent but before `notifications/initialized` arrives. */
|
|
206
|
+
export type Initializing = readonly ['initializing'];
|
|
207
|
+
/** State carried after a successful `initialize` exchange. */
|
|
208
|
+
export type InitializedState = true;
|
|
209
|
+
/** The three phases of an MCP session. */
|
|
210
|
+
export type McpSessionState = Uninitialized | Initializing | readonly ['initialized', InitializedState];
|
|
211
|
+
/** Initial session state — always start here. */
|
|
212
|
+
export declare const uninitializedState: McpSessionState;
|
|
213
|
+
/** Static configuration supplied by the server implementer. */
|
|
214
|
+
export type McpConfig = {
|
|
215
|
+
readonly serverInfo: Implementation;
|
|
216
|
+
readonly capabilities: ServerCapabilities;
|
|
217
|
+
readonly protocolVersion: string;
|
|
218
|
+
};
|
|
219
|
+
/**
|
|
220
|
+
* State-machine step for an MCP session using memory effects.
|
|
221
|
+
*
|
|
222
|
+
* Given configuration, handlers, and a memory key holding the session state,
|
|
223
|
+
* returns a function `(value) => Effect<MemOp | O, Response | null>`.
|
|
224
|
+
*
|
|
225
|
+
* Rules:
|
|
226
|
+
* - `ping` returns an empty success regardless of session state; non-object
|
|
227
|
+
* params → -32602.
|
|
228
|
+
* - `initialize` is accepted only while uninitialized; a second call returns -32600.
|
|
229
|
+
* On success the state moves to `initializing`, not `initialized`.
|
|
230
|
+
* - `notifications/initialized` (no `id`) transitions `initializing` → `initialized`;
|
|
231
|
+
* a malformed one (non-object params) is ignored and the session stays gated;
|
|
232
|
+
* other notifications are silently ignored in any state.
|
|
233
|
+
* - Any other method before `notifications/initialized` → error -32002 (not initialized).
|
|
234
|
+
* - Methods gated by a capability (e.g. `tools/list`) → -32601 when the capability
|
|
235
|
+
* is absent.
|
|
236
|
+
* - `tools/list` params (an optional pagination `cursor`) are validated and passed
|
|
237
|
+
* to the handler; invalid params → -32602.
|
|
238
|
+
*/
|
|
239
|
+
export declare const mcpStep: ({ protocolVersion, capabilities, serverInfo, }: McpConfig) => <O extends Operation>(handlers: McpHandlers<O>) => (stateKey: Key<McpSessionState>) => (value: Unknown) => Effect<MemOp | O, Response | null>;
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP (Model Context Protocol) message schemas — minimal subset for a
|
|
3
|
+
* hello-world tool server.
|
|
4
|
+
*
|
|
5
|
+
* Covers the three exchanges a minimal server must handle:
|
|
6
|
+
* - `initialize` / `notifications/initialized` lifecycle
|
|
7
|
+
* - `tools/list` — advertise available tools
|
|
8
|
+
* - `tools/call` — invoke a tool and return text content
|
|
9
|
+
*
|
|
10
|
+
* Each schema is both a runtime decoder (`validate(schema)`) and a static
|
|
11
|
+
* TypeScript type (`Ts<typeof schema>`). Transport framing (stdio) and the
|
|
12
|
+
* JSON-RPC dispatcher are in `fjs/protocol/json_rpc/module.f.ts`.
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
import { boolean, string, option, array, record, or } from '../../types/rtti/module.f.js';
|
|
17
|
+
import { unknown } from '../../media/json/module.f.js';
|
|
18
|
+
import { pure, step } from '../../effects/module.f.js';
|
|
19
|
+
import { read, write } from '../../effects/memory/module.f.js';
|
|
20
|
+
import { decodeRequest, rpcError, invalidRequest, invalidParams, methodNotFound, jsonrpc, } from '../json_rpc/module.f.js';
|
|
21
|
+
import { validate } from '../../types/rtti/validate/module.f.js';
|
|
22
|
+
import { toJsonSchema } from '../../media/json/schema/module.f.js';
|
|
23
|
+
// ── Shared ─────────────────────────────────────────────────────────────────────
|
|
24
|
+
/** Name + version pair sent in `initialize` requests and responses. */
|
|
25
|
+
export const implementation = {
|
|
26
|
+
name: string,
|
|
27
|
+
version: string,
|
|
28
|
+
};
|
|
29
|
+
// ── Capabilities ───────────────────────────────────────────────────────────────
|
|
30
|
+
const toolsCapability = { listChanged: option(boolean) };
|
|
31
|
+
/** Server capabilities advertised in the `initialize` response. */
|
|
32
|
+
export const serverCapabilities = {
|
|
33
|
+
tools: option(toolsCapability),
|
|
34
|
+
};
|
|
35
|
+
// ── Lifecycle ──────────────────────────────────────────────────────────────────
|
|
36
|
+
/** Params for the `initialize` request. */
|
|
37
|
+
export const initializeParams = {
|
|
38
|
+
protocolVersion: string,
|
|
39
|
+
capabilities: unknown,
|
|
40
|
+
clientInfo: implementation,
|
|
41
|
+
};
|
|
42
|
+
/** Result for the `initialize` request. */
|
|
43
|
+
export const initializeResult = {
|
|
44
|
+
protocolVersion: string,
|
|
45
|
+
capabilities: serverCapabilities,
|
|
46
|
+
serverInfo: implementation,
|
|
47
|
+
instructions: option(string),
|
|
48
|
+
};
|
|
49
|
+
// ── Content ────────────────────────────────────────────────────────────────────
|
|
50
|
+
/** Plain-text content item returned by a tool call. */
|
|
51
|
+
export const textContent = { type: 'text', text: string };
|
|
52
|
+
/**
|
|
53
|
+
* A binary resource carried inside an {@link embeddedResource}: a base64
|
|
54
|
+
* `blob`, an addressing `uri`, and an optional `mimeType`. This is MCP's
|
|
55
|
+
* `BlobResource` shape — the idiomatic way to return typed binary content so a
|
|
56
|
+
* `mimeType` travels alongside the bytes and clients know how to route them.
|
|
57
|
+
*/
|
|
58
|
+
export const blobResource = {
|
|
59
|
+
uri: string,
|
|
60
|
+
mimeType: option(string),
|
|
61
|
+
blob: string,
|
|
62
|
+
};
|
|
63
|
+
/** An `EmbeddedResource` content item wrapping a {@link blobResource}. */
|
|
64
|
+
export const embeddedResource = {
|
|
65
|
+
type: 'resource',
|
|
66
|
+
resource: blobResource,
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* A single item in a `tools/call` result's `content` array: either plain
|
|
70
|
+
* {@link textContent} or an {@link embeddedResource} for typed binary. The
|
|
71
|
+
* `image` and `audio` variants are not modelled yet.
|
|
72
|
+
*/
|
|
73
|
+
export const contentItem = or(textContent, embeddedResource);
|
|
74
|
+
// ── Tools ──────────────────────────────────────────────────────────────────────
|
|
75
|
+
/**
|
|
76
|
+
* A tool descriptor returned by `tools/list`.
|
|
77
|
+
* `inputSchema` is a JSON Schema object — use `toJsonSchema` to derive it from
|
|
78
|
+
* an rtti schema.
|
|
79
|
+
*/
|
|
80
|
+
export const tool = {
|
|
81
|
+
name: string,
|
|
82
|
+
description: option(string),
|
|
83
|
+
inputSchema: unknown,
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Params for the `tools/list` request. `cursor` is an opaque pagination token
|
|
87
|
+
* from a previous `ToolsListResult.nextCursor`.
|
|
88
|
+
*/
|
|
89
|
+
export const toolsListParams = {
|
|
90
|
+
cursor: option(string),
|
|
91
|
+
};
|
|
92
|
+
export const toolsListResult = {
|
|
93
|
+
tools: array(tool),
|
|
94
|
+
nextCursor: option(string),
|
|
95
|
+
};
|
|
96
|
+
export const toolsCallParams = {
|
|
97
|
+
name: string,
|
|
98
|
+
arguments: option(record(unknown)),
|
|
99
|
+
};
|
|
100
|
+
export const toolsCallResult = {
|
|
101
|
+
content: array(contentItem),
|
|
102
|
+
isError: option(boolean),
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Creates a type-safe tool entry that binds an RTTI schema with a handler.
|
|
106
|
+
*
|
|
107
|
+
* The builder validates arguments at runtime using the RTTI and passes pre-validated
|
|
108
|
+
* arguments (typed as `Ts<T>`) to the handler. This eliminates manual validation
|
|
109
|
+
* boilerplate and type assertions.
|
|
110
|
+
*
|
|
111
|
+
* @param name - The tool name (used in `tools/call` requests)
|
|
112
|
+
* @param description - Human-readable description for `tools/list`
|
|
113
|
+
* @param inputRtti - Runtime type info for input validation
|
|
114
|
+
* @param handle - Handler receiving validated arguments of type `Ts<inputRtti>`
|
|
115
|
+
* @returns A `ToolEntry` ready to be added to a registry
|
|
116
|
+
*/
|
|
117
|
+
export const toolEntry = (name, description, inputRtti, handle) => ({
|
|
118
|
+
name,
|
|
119
|
+
description,
|
|
120
|
+
inputRtti,
|
|
121
|
+
handle: (a) => {
|
|
122
|
+
const [t, r] = validate(inputRtti)(a);
|
|
123
|
+
return t === 'error'
|
|
124
|
+
? pure(errorResult(`invalid arguments: ${r.message}`))
|
|
125
|
+
: handle(r);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
/**
|
|
129
|
+
* Helper to create a successful single-text-block tool result.
|
|
130
|
+
*
|
|
131
|
+
* @param text - The text to return to the client
|
|
132
|
+
* @returns A `ToolsCallResult` with the text content
|
|
133
|
+
*/
|
|
134
|
+
export const okResult = (text) => ({ content: [{ type: 'text', text }] });
|
|
135
|
+
/**
|
|
136
|
+
* Helper to create a tool-level error result with plain text explanation.
|
|
137
|
+
*
|
|
138
|
+
* @param text - The error message to return to the client
|
|
139
|
+
* @returns A `ToolsCallResult` with `isError: true` and the text explanation
|
|
140
|
+
*/
|
|
141
|
+
export const errorResult = (text) => ({ ...okResult(text), isError: true });
|
|
142
|
+
/**
|
|
143
|
+
* Builds `McpHandlers` from a registry of tool entries.
|
|
144
|
+
*
|
|
145
|
+
* This factory generates `toolsList` and `toolsCall` handlers that work with a
|
|
146
|
+
* declarative registry, eliminating boilerplate. The `toolsList` handler converts
|
|
147
|
+
* entries into MCP `Tool` descriptors, and `toolsCall` dispatches by name and
|
|
148
|
+
* delegates to the appropriate handler.
|
|
149
|
+
*
|
|
150
|
+
* @param registry - Array of tool entries
|
|
151
|
+
* @returns Complete `McpHandlers` ready for use with `mcpStep`
|
|
152
|
+
*/
|
|
153
|
+
export const fromRegistry = (registry) => ({
|
|
154
|
+
toolsList: () => {
|
|
155
|
+
const tools = registry.map(entry => ({
|
|
156
|
+
name: entry.name,
|
|
157
|
+
description: entry.description,
|
|
158
|
+
inputSchema: toJsonSchema(entry.inputRtti),
|
|
159
|
+
}));
|
|
160
|
+
return pure({ tools });
|
|
161
|
+
},
|
|
162
|
+
toolsCall: ({ name, arguments: args }) => {
|
|
163
|
+
const entry = registry.find(e => e.name === name);
|
|
164
|
+
return entry === undefined
|
|
165
|
+
? pure(errorResult(`unknown tool: ${name}`))
|
|
166
|
+
: entry.handle(args === undefined ? {} : args);
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
// ── Lifecycle / capability state machine ───────────────────────────────────────
|
|
170
|
+
const _errResponse = (id) => (error) => ({ jsonrpc, error, id });
|
|
171
|
+
const _okResponse = (id) => (result) => ({ jsonrpc, result, id });
|
|
172
|
+
/** MCP error -32002: the client called a method before `initialize`. */
|
|
173
|
+
export const notInitialized = rpcError(-32002)('Server not initialized');
|
|
174
|
+
// Params for methods that take no arguments (`ping`, `notifications/initialized`):
|
|
175
|
+
// absent, or an object (which may carry `_meta`).
|
|
176
|
+
const _noParams = option(record(unknown));
|
|
177
|
+
/** Initial session state — always start here. */
|
|
178
|
+
export const uninitializedState = ['uninitialized'];
|
|
179
|
+
/**
|
|
180
|
+
* State-machine step for an MCP session using memory effects.
|
|
181
|
+
*
|
|
182
|
+
* Given configuration, handlers, and a memory key holding the session state,
|
|
183
|
+
* returns a function `(value) => Effect<MemOp | O, Response | null>`.
|
|
184
|
+
*
|
|
185
|
+
* Rules:
|
|
186
|
+
* - `ping` returns an empty success regardless of session state; non-object
|
|
187
|
+
* params → -32602.
|
|
188
|
+
* - `initialize` is accepted only while uninitialized; a second call returns -32600.
|
|
189
|
+
* On success the state moves to `initializing`, not `initialized`.
|
|
190
|
+
* - `notifications/initialized` (no `id`) transitions `initializing` → `initialized`;
|
|
191
|
+
* a malformed one (non-object params) is ignored and the session stays gated;
|
|
192
|
+
* other notifications are silently ignored in any state.
|
|
193
|
+
* - Any other method before `notifications/initialized` → error -32002 (not initialized).
|
|
194
|
+
* - Methods gated by a capability (e.g. `tools/list`) → -32601 when the capability
|
|
195
|
+
* is absent.
|
|
196
|
+
* - `tools/list` params (an optional pagination `cursor`) are validated and passed
|
|
197
|
+
* to the handler; invalid params → -32602.
|
|
198
|
+
*/
|
|
199
|
+
export const mcpStep = ({ protocolVersion, capabilities, serverInfo, }) => (handlers) => (stateKey) => (value) => {
|
|
200
|
+
const [t, message] = decodeRequest(value);
|
|
201
|
+
if (t === 'error') {
|
|
202
|
+
return pure(_errResponse(null)(invalidRequest));
|
|
203
|
+
}
|
|
204
|
+
const { id, method, params } = message;
|
|
205
|
+
// Notifications (no `id`) never receive a response.
|
|
206
|
+
// `notifications/initialized` transitions the session from initializing → initialized.
|
|
207
|
+
if (id === undefined) {
|
|
208
|
+
if (method === 'notifications/initialized') {
|
|
209
|
+
const [pt] = validate(_noParams)(params);
|
|
210
|
+
if (pt === 'error') {
|
|
211
|
+
// Malformed handshake — ignore it; the session stays gated.
|
|
212
|
+
return pure(null);
|
|
213
|
+
}
|
|
214
|
+
return step(read(stateKey), ([t]) => t === 'initializing'
|
|
215
|
+
? step(write(stateKey, ['initialized', true]), () => pure(null))
|
|
216
|
+
: pure(null));
|
|
217
|
+
}
|
|
218
|
+
return pure(null);
|
|
219
|
+
}
|
|
220
|
+
// `ping` is always valid regardless of session state, but its params
|
|
221
|
+
// (if present) must be an object.
|
|
222
|
+
if (method === 'ping') {
|
|
223
|
+
const [pt] = validate(_noParams)(params);
|
|
224
|
+
return pt === 'error'
|
|
225
|
+
? pure(_errResponse(id)(invalidParams))
|
|
226
|
+
: pure(_okResponse(id)({}));
|
|
227
|
+
}
|
|
228
|
+
// `initialize` transitions uninitialized → initializing; reject if already done.
|
|
229
|
+
if (method === 'initialize') {
|
|
230
|
+
return step(read(stateKey), ([t]) => {
|
|
231
|
+
if (t !== 'uninitialized') {
|
|
232
|
+
return pure(_errResponse(id)(invalidRequest));
|
|
233
|
+
}
|
|
234
|
+
const [pr] = validate(initializeParams)(params);
|
|
235
|
+
if (pr === 'error') {
|
|
236
|
+
return pure(_errResponse(id)(invalidParams));
|
|
237
|
+
}
|
|
238
|
+
const result = {
|
|
239
|
+
protocolVersion,
|
|
240
|
+
capabilities,
|
|
241
|
+
serverInfo,
|
|
242
|
+
};
|
|
243
|
+
return step(write(stateKey, ['initializing']), () => pure(_okResponse(id)(result)));
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
// All other methods require fully initialized state — read it first.
|
|
247
|
+
return step(read(stateKey), ([t]) => {
|
|
248
|
+
if (t !== 'initialized') {
|
|
249
|
+
return pure(_errResponse(id)(notInitialized));
|
|
250
|
+
}
|
|
251
|
+
if (method === 'tools/list') {
|
|
252
|
+
if (capabilities.tools === undefined) {
|
|
253
|
+
return pure(_errResponse(id)(methodNotFound));
|
|
254
|
+
}
|
|
255
|
+
// `params` may be absent — `tools/list` without a cursor.
|
|
256
|
+
const [t, pr] = validate(toolsListParams)(params === undefined ? {} : params);
|
|
257
|
+
return t === 'error'
|
|
258
|
+
? pure(_errResponse(id)(invalidParams))
|
|
259
|
+
: step(handlers.toolsList(pr), r => pure(_okResponse(id)(r)));
|
|
260
|
+
}
|
|
261
|
+
if (method === 'tools/call') {
|
|
262
|
+
if (capabilities.tools === undefined) {
|
|
263
|
+
return pure(_errResponse(id)(methodNotFound));
|
|
264
|
+
}
|
|
265
|
+
const [t, pr] = validate(toolsCallParams)(params);
|
|
266
|
+
return t === 'error'
|
|
267
|
+
? pure(_errResponse(id)(invalidParams))
|
|
268
|
+
: step(handlers.toolsCall(pr), r => pure(_okResponse(id)(r)));
|
|
269
|
+
}
|
|
270
|
+
return pure(_errResponse(id)(methodNotFound));
|
|
271
|
+
});
|
|
272
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export declare const proof: {
|
|
2
|
+
lifecycle: {
|
|
3
|
+
initialStateIsUninitialized: () => void;
|
|
4
|
+
initializeTransitionsToInitializing: () => void;
|
|
5
|
+
initializedNotificationTransitionsToInitialized: () => void;
|
|
6
|
+
initializeReturnsResult: () => void;
|
|
7
|
+
initializeWithBadParamsReturnsInvalidParams: () => void;
|
|
8
|
+
notificationBeforeInitReturnNull: () => void;
|
|
9
|
+
unknownNotificationReturnNull: () => void;
|
|
10
|
+
doubleInitializeReturnsInvalidRequest: () => void;
|
|
11
|
+
pingBeforeInitSucceeds: () => void;
|
|
12
|
+
pingDuringInitializingSucceeds: () => void;
|
|
13
|
+
pingAfterInitSucceeds: () => void;
|
|
14
|
+
pingWithObjectParamsSucceeds: () => void;
|
|
15
|
+
pingInvalidParamsReturnsInvalidParams: () => void;
|
|
16
|
+
initializedNotificationObjectParamsTransitions: () => void;
|
|
17
|
+
initializedNotificationBadParamsIgnored: () => void;
|
|
18
|
+
methodBeforeInitReturnsNotInitialized: () => void;
|
|
19
|
+
methodDuringInitializingReturnsNotInitialized: () => void;
|
|
20
|
+
invalidEnvelopeReturnsInvalidRequest: () => void;
|
|
21
|
+
};
|
|
22
|
+
tools: {
|
|
23
|
+
toolsListSucceeds: () => void;
|
|
24
|
+
toolsListPassesCursorToHandler: () => void;
|
|
25
|
+
toolsListInvalidCursorReturnsInvalidParams: () => void;
|
|
26
|
+
toolsCallSucceeds: () => void;
|
|
27
|
+
toolsCallBadParamsReturnsInvalidParams: () => void;
|
|
28
|
+
toolsCallAbsentArgumentsSucceeds: () => void;
|
|
29
|
+
toolsCallNullArgumentsReturnsInvalidParams: () => void;
|
|
30
|
+
toolsListWithoutCapabilityReturnsMethodNotFound: () => void;
|
|
31
|
+
toolsCallWithoutCapabilityReturnsMethodNotFound: () => void;
|
|
32
|
+
unknownMethodReturnsMethodNotFound: () => void;
|
|
33
|
+
};
|
|
34
|
+
};
|